Skip to content

Python SDK 2.5.0: Research Background Mode + Frontier/Lite Effort Tiers - #25

Merged
tyler5673 merged 24 commits into
mainfrom
release/2.5.0
Jul 23, 2026
Merged

Python SDK 2.5.0: Research Background Mode + Frontier/Lite Effort Tiers#25
tyler5673 merged 24 commits into
mainfrom
release/2.5.0

Conversation

@tyler5673

@tyler5673 tyler5673 commented Jul 23, 2026

Copy link
Copy Markdown
Contributor

Summary

Python SDK 2.5.0 release: research background mode with polling/streaming, frontier research effort tier, lite finance research effort tier, auto-adjusting timeouts, and comprehensive sync/async parity.

Changes

New Features

  • ResearchEffort.FRONTIER ("frontier"): New highest research effort tier. Requires background=true; returns 422 otherwise.
  • FinanceResearchEffort.LITE ("lite"): New quick financial question tier. Default remains deep.
  • Background mode: research(background=True) returns a TaskResponse with task_id, stream_url, and status.
  • research_and_wait() / research_and_wait_async(): Convenience helpers that submit a background research task, stream SSE events until completion, and return the final TaskDetail.
    • Auto-adjusts timeout_s based on research_effort: 600s default, 14400s (4 hours) for frontier.
    • SSE per-read timeout decoupled from timeout_ms (always bounded to timeout_s).
    • Falls back to poll_research_task on transient httpx.TransportError (stream-open or mid-stream); typed auth errors (401/403/404/500) propagate to caller.
    • Bounded re-poll (3 attempts, 1s interval) on stream-completed-but-GET-not-yet-committed race.
    • Mid-stream TransportError closes stream before falling back to polling.

Infrastructure

  • Droid Auto Review: Added droid-review.yml workflow for automated PR reviews
  • Removed Claude Code review workflow (claude.yml) in favor of Droid
  • Removed Speakeasy SDK generation (.speakeasy/ config, generate-sdk.yml workflow) — no longer using Speakeasy for SDK generation

Files Changed

  • src/youdotcom/models/researcheffort.py: Added FRONTIER = "frontier"
  • src/youdotcom/models/financeresearcheffort.py: Added LITE = "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 docstrings
  • CHANGELOG.md, MIGRATION.md, docs/, README.md, examples/: Documentation updates
  • tests/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

  • 92 mock tests passing
  • mypy clean, pylint 10.00/10
  • Live tests: test_frontier_background_completes (PASSED, 607s), test_frontier_without_background_raises_422 (PASSED), test_finance_research_lite_effort (SKIPPED until server deploys lite)

tyler5673 and others added 19 commits July 21, 2026 15:02
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>
Comment thread pyproject.toml
tyler5673 and others added 2 commits July 23, 2026 12:26
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>
@youdotcom-oss youdotcom-oss deleted a comment from claude Bot Jul 23, 2026
@factory-droid

factory-droid Bot commented Jul 23, 2026

Copy link
Copy Markdown
Contributor

Droid encountered an error —— View job


Droid is reviewing code and running a security check…

@factory-droid

factory-droid Bot commented Jul 23, 2026

Copy link
Copy Markdown
Contributor

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 actions/checkout@v7 pin and tightening the workflow-dispatch installer step will make the Droid review workflow more reliable and safer.

Comment thread .github/workflows/droid-review.yml Outdated
Comment thread .github/workflows/droid-review.yml
Comment thread docs/sdks/you/README.md Outdated
Comment thread MIGRATION.md
- 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>
@factory-droid

factory-droid Bot commented Jul 23, 2026

Copy link
Copy Markdown
Contributor

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.

Comment thread docs/models/taskdetail.md Outdated
Comment thread docs/models/taskresponse.md Outdated
Comment thread .github/workflows/droid-review.yml
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>
@factory-droid

factory-droid Bot commented Jul 23, 2026

Copy link
Copy Markdown
Contributor

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 ResearchRequest.research_effort omits the new frontier tier.

@tyler5673

Copy link
Copy Markdown
Contributor Author

Re: P2 — pinning Factory-AI/droid-action to a commit SHA. Acknowledged as a best practice, but intentionally using the @v5 major tag here for the same reasons the agent-skills repo does: (1) SHA pinning creates manual maintenance overhead with no automated update mechanism in this repo, (2) the @v5 tag is maintained by Factory and follows semver (breaking changes require a major bump), (3) the workflow already gates on internal contributors only, limiting exposure, and (4) GitHub's own security hardening guide recommends SHA pinning for high-risk scenarios but this is a low-risk review-only workflow. Will revisit if we add Dependabot or Renovate for automated SHA bumps.

Comment thread docs/models/researchrequest.md Outdated
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>
@factory-droid

factory-droid Bot commented Jul 23, 2026

Copy link
Copy Markdown
Contributor

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.

@tyler5673
tyler5673 merged commit a88c92a into main Jul 23, 2026
3 checks passed
@tyler5673
tyler5673 deleted the release/2.5.0 branch July 23, 2026 21:53
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant