Python SDK 2.5.0: Research Background Mode + Frontier/Lite Effort Tiers - #25
Conversation
Adds background-mode research (async task submission, polling, streaming) to the SDK. Adds you.research(background=True), you.get_research_task(), you.stream_research_task(), and the hand-maintained youdotcom.research_helpers module. All 2.4.0 features unchanged. Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
- Remove dead RawStreamEvent.retry field (never read by helpers/tests) - Extract _resolve_from_final_get[_async] helpers (dedup 4x final-GET logic) - Table-drive _raise_stream_error[_async] via _STREAM_ERROR_BRANCHES tuple - Factor _stream_build_kwargs/_stream_hook_ctx shared by sync/async stream-open - Inline _poll_detail[_async] into their single callers - P1 fix: replace daemon-thread + consumer.join with single-threaded loop using httpx read timeout (no more leaked blocked threads on socket hangs) - P2 fix: remove dead deadline/time.monotonic() inner check (unreachable - httpx.TimeoutException already enforces the timeout) - P2 fix: remove stream_exc/except BaseException indirection (fossil of threaded design; exceptions now propagate naturally) - P3: fix research_and_wait docstring (one GET -> a GET, second in fallback) - P3: document retry_config=None divergence from generated stream_research_task - Add 7 new typed-error tests (403/500/4XX/5XX/async 401/403/500) - Fix live test TypeError masking: check 'TaskResponse' in str(e) before skip - Extract _make_wait_handler factory + shared mock stream classes in tests 158/158 tests pass, pylint 10.00/10, mypy clean. Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
…fallback, TypeError masking) - CHANGELOG: fix stream_research_task terminal event list (was missing 'completed' in ok set and 'failed' in err set; now matches the actual _TERMINAL_STREAM_EVENTS_OK/ERR frozensets in research_helpers.py) - CHANGELOG: document research_and_wait timeout/stream-close fallback GET behavior (was described as just 'fetch the final TaskDetail') - MIGRATION: fix streaming example terminal event set to include 'complete' (was only 'response.done' + 'completed') - MIGRATION: document research_and_wait fallback GET behavior - examples: fix research_and_wait_example TypeError masking — check 'TaskResponse' in str(e) before skipping, re-raise real TypeErrors (matches the fix applied to test_live.py) - PR #23 description: fix research_and_wait row (was 'No polling fallback'; now documents the final-GET fallback) and stream_research_task terminal event list Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
…atches async) Addresses PR feedback (claude[bot] review 2026-07-22): sync research_and_wait used timeout_s only as a per-read stall timeout (httpx read timeout), while the async variant used asyncio.wait_for as a total wall-clock budget. This meant a server streaming heartbeats forever would never time out in sync mode. Fix: add a total deadline check inside the stream loop that breaks and sets timed_out=True when time.monotonic() >= deadline. Both sync and async now enforce timeout_s as a total budget. Added test_research_and_wait_total_deadline_not_stall_timeout: streams non-terminal 'ping' events every 10ms (fast enough to not trip the per-read timeout) and verifies the total deadline fires at timeout_s=0.5s. 159/159 tests pass, pylint 10.00/10, mypy clean. Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
…t-non-completed test Addresses PR feedback (claude[bot] review 2026-07-22T00:57): - Fix Go mockserver comment: said 'four' terminal events but SDK treats six (OK: response.done, complete, completed; ERROR: error, failed, cancelled) - Add test_research_and_wait_ok_event_but_get_non_completed_raises: covers the defensive branch where stream emits response.done but the follow-up GET returns non-completed status (RuntimeError path in _resolve_from_final_get) 160/160 tests pass, pylint 10.00/10, mypy clean, Go builds clean. Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
Add ResearchEffort.FRONTIER for the highest-quality, longest-running research tasks (up to 4 hours). Frontier only works with background=true; sending it without returns 422. The enum value is injected via overlay (overlays/python_overlay.yaml) so it survives Speakeasy regenerations. - Update docstrings in researcheffort.py, researchop.py, sdk.py - Update CHANGELOG.md, MIGRATION.md, docs/models/researcheffort.md, docs/sdks/you/README.md - Add tests: frontier+background happy path, frontier-without-background 422 - Add research_stream_example() to examples/api-example-calls.py - Update PR #23 description Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
…ll defaults research_and_wait now auto-selects timeout_s based on research_effort when omitted: 600s (10 min) for standard/deep/exhaustive, 14400s (4 hr) for frontier. Explicit timeout_s always takes precedence. poll_research_task cannot auto-detect (receives task_id, not effort) so its defaults (interval_s=2.0, timeout_s=600.0) are now documented in docstrings and MIGRATION.md with a per-tier guidance table. Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
Add LITE tier to FinanceResearchEffort enum (overlay-injected, same pattern as ResearchEffort.FRONTIER). Update docstrings, docs, CHANGELOG, and MIGRATION. Add test_finance_research_lite_effort. Fix three PR #24 review items: 1. _resolve_from_final_get now re-polls up to 3 times (1s interval) when the stream signals OK but GET returns non-completed, tolerating backend commit races. Updated test_research_and_wait_ok_event_but_get_non_completed_raises and added test_research_and_wait_ok_event_repoll_succeeds. 2. research_and_wait_async now catches httpx.TimeoutException in addition to asyncio.TimeoutError, matching sync behavior. Added test_async_research_and_wait_httpx_readtimeout_falls_back_to_get. 3. Removed misplaced pylint disable-next=protected-access in stream_research. Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
- test_frontier_background_completes: submits frontier task with background=true via research_and_wait, verifies TaskDetail completed. Passed live in 607s. - test_frontier_without_background_raises_422: verifies 422 when frontier is sent without background=true. Passed live. - test_finance_research_lite_effort: verifies LITE returns a quick answer. Skips gracefully when server hasn't deployed the tier yet (422). Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
research_and_wait_async now passes stream_timeout_ms=int(timeout_s*1000) when the caller didn't set an explicit timeout_ms, matching the sync variant. Without this, a caller-set You(timeout_ms=60000) would cap SSE reads at 60s and cause premature TimeoutError on frontier tasks with auto timeout_s=14400. Also updated the async docstring to note the per-read timeout parity. Addresses PR #24 review comment 3640119913. Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
…final_get When the re-poll loop hits a terminal non-completed status (failed/ cancelled) on the first GET, raise immediately with a clear message instead of falling through to the misleading after-N-attempts error. Added test_research_and_wait_ok_event_but_get_failed_raises_immediately to cover the early-break branch. Addresses PR #24 review comments 3640244682 and 3640245293. Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
…ests Sync research_and_wait now honors caller timeout_ms for the stream read when provided (matching async), falling back to timeout_s*1000 when not. Updated both docstrings to accurately describe the shared behavior. Added async mirrors for repoll-succeeds and ok-event-but-get-failed tests, closing the coverage gap flagged in review. Addresses PR #24 review comments 3640262585 and 3640262855 (the latter was already fixed in 039fee1 but commented on an older revision). Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
… at timeout_s 1. research_and_wait[_async] now catches stream-open failures (transient 500, 404 on just-created task, etc.) and falls back to poll_research_task so a successfully-submitted task is never abandoned. 2. SSE per-read timeout is now capped at timeout_s*1000 in both variants, preventing a caller-set timeout_ms larger than timeout_s from causing the sync wall-clock to exceed the budget. 3. Added test_async_frontier_auto_timeout_completes_without_premature_timeout to mirror the sync frontier auto-timeout coverage. Addresses PR #24 review comments 3640308132, 3640308929, 3640309427. Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
The sync variant previously only caught httpx.TimeoutException during stream iteration, letting non-timeout errors (RemoteProtocolError, ConnectError on dropped connections) propagate unrecoverably. Now catches Exception and falls back to poll_research_task, matching the async variant's behavior and the stream-open failure fallback. Addresses PR #24 review comment 3640374576. Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
…from timeout_ms - Change `except Exception` to `except httpx.TransportError` for both stream-open and mid-stream fallbacks (sync + async). Typed auth errors (401/403/404/500 from _raise_stream_error) now propagate to the caller instead of being swallowed and masked by a different error type from poll_research_task. - Decouple SSE per-read timeout from caller's timeout_ms. Always use int(timeout_s * 1000) for stream reads. Previously, a caller with timeout_ms=30000 on a frontier task (timeout_s=14400) would get a misleading "did not complete within 14400s" after only 30s. - Add 5 new tests: stream-open TransportError -> poll succeeds (sync + async), stream-open 401 -> typed error propagates (sync + async), mid-stream TransportError -> poll succeeds (sync). Addresses PR #24 review comments 3640425979 and 3640426348. Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
…andler The `return poll_research_task(...)` inside the `try` block meant `finally: stream.close()` only ran after the entire poll loop completed, keeping the failed SSE connection open during polling. Now calling `stream.close()` explicitly before the return so cleanup happens first. Addresses PR #24 review comment 3640517073. Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
The SSE per-read timeout was decoupled from timeout_ms in a previous commit but the sync docstring still mentioned "(or the caller's timeout_ms when provided)". Now matches the actual behavior and the async variant's docstring. Addresses PR #24 review comment 3640569229. Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
Triggers on PR open/synchronize/reopen for internal contributors. External contributors can be reviewed via workflow_dispatch. Requires FACTORY_API_KEY secret and Factory Droid GitHub App installed. Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
The droid-review.yml workflow now handles automated PR reviews. Removes the claude.yml workflow that used anthropics/claude-code-action. Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
gen.yaml python.version and gen.lock releaseVersion were left at 2.4.0 while pyproject.toml and _version.py were already 2.5.0. Without this sync, the next speakeasy run would silently revert the version bump. Addresses PR #25 review comment 3640806529. Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
No longer using Speakeasy for SDK generation. Removes: - .speakeasy/ (gen.yaml, gen.lock, workflow.yaml, workflow.lock, etc.) - .github/workflows/generate-sdk.yml (depended on speakeasy bump/run) Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
|
Droid encountered an error —— View job Droid is reviewing code and running a security check… |
|
Droid finished @tyler5673's task —— View job Overall, the 2.5.0 changes look solid, and the new background research helpers appear well-tested. Fixing the invalid |
- P0: Fix actions/checkout@v7 -> @v4 (v7 not published) - P1: Fix broken link in README.md (models/.md -> researchtaskstreamevent.md) - P1: Make MIGRATION.md code blocks self-contained with imports + You() - P2: Add TODO comment for curl|sh integrity verification in workflow_dispatch Addresses PR #25 review comments 3641083815, 3641083827, 3641083831, 3641083837. Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
|
Droid finished @tyler5673's task —— View job The PR adds substantial background research functionality with good parity across sync/async paths and broad test coverage. The main issues I found are doc mismatches for task timestamp types and a CI hardening opportunity to pin a third-party action used with elevated permissions. |
Generated docs incorrectly linked to Python date objects instead of datetime. Updated to match the actual SDK types in taskdetail.py and taskresponse.py. Addresses PR #25 review comments 3641189866 and 3641189887. Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
|
Droid finished @tyler5673's task —— View job The 2.5.0 background research changes look cohesive and the helper layer is thoughtfully designed with sync/async parity. The main remaining issue I found is a documentation mismatch where |
|
Re: P2 — pinning |
The researchrequest.md docs listed lite/standard/deep/exhaustive but omitted frontier (which requires background=true). Added frontier with the background constraint note. Addresses PR #25 review comment 3641288647. Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
|
Droid finished @tyler5673's task —— View job No additional high-confidence issues found beyond existing PR review threads, the 2.5.0 changes look cohesive and well-covered by tests. |
Summary
Python SDK 2.5.0 release: research background mode with polling/streaming,
frontierresearch effort tier,litefinance research effort tier, auto-adjusting timeouts, and comprehensive sync/async parity.Changes
New Features
ResearchEffort.FRONTIER("frontier"): New highest research effort tier. Requiresbackground=true; returns 422 otherwise.FinanceResearchEffort.LITE("lite"): New quick financial question tier. Default remainsdeep.research(background=True)returns aTaskResponsewithtask_id,stream_url, andstatus.research_and_wait()/research_and_wait_async(): Convenience helpers that submit a background research task, stream SSE events until completion, and return the finalTaskDetail.timeout_sbased onresearch_effort: 600s default, 14400s (4 hours) for frontier.timeout_ms(always bounded totimeout_s).poll_research_taskon transienthttpx.TransportError(stream-open or mid-stream); typed auth errors (401/403/404/500) propagate to caller.TransportErrorcloses stream before falling back to polling.Infrastructure
droid-review.ymlworkflow for automated PR reviewsclaude.yml) in favor of Droid.speakeasy/config,generate-sdk.ymlworkflow) — no longer using Speakeasy for SDK generationFiles Changed
src/youdotcom/models/researcheffort.py: AddedFRONTIER = "frontier"src/youdotcom/models/financeresearcheffort.py: AddedLITE = "lite"src/youdotcom/research_helpers.py: Hand-maintained helpers (research_and_wait,poll_research_task,stream_research,_resolve_default_timeout, etc.)src/youdotcom/sdk.py,src/youdotcom/models/researchop.py,src/youdotcom/models/finance_researchop.py: Updated docstringsCHANGELOG.md,MIGRATION.md,docs/,README.md,examples/: Documentation updatestests/test_research.py,tests/test_research_helpers.py,tests/test_live.py: 92 mock tests + live tests.github/workflows/droid-review.yml: New automated review workflow.github/workflows/claude.yml: Removed (replaced by Droid Auto Review).speakeasy/,.github/workflows/generate-sdk.yml: Removed (no longer using Speakeasy)Test Results
test_frontier_background_completes(PASSED, 607s),test_frontier_without_background_raises_422(PASSED),test_finance_research_lite_effort(SKIPPED until server deploys lite)