Conversation
|
@claude review this please (testing integrtion) |
|
@claude try to review again pls |
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
fa9e6ca to
97e218a
Compare
- fix README streaming example indentation (code was outside `with` block) - fix README retries example missing `retries=` keyword argument - fix CHANGELOG error rename table (PostV1AgentsRunsForbiddenError was incorrectly mapped to AgentRuns422ResponseError) - add missing AgentRuns400ResponseError to CHANGELOG - add test_research.py with mock server tests for the Research API - narrow overly broad pytest.raises(Exception) in test_runs.py - add pytest and pytest-asyncio to dev dependencies in pyproject.toml - add Research API example to interactive examples menu - fix misleading "DO NOT EDIT" banner in test_client.py Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- fix streaming example `with response as AgentRunsStreamingResponse` shadowing the class name; use `with response as stream` and iterate over `stream` (README.md + examples/api-example-calls.py) - fix MIGRATION.md quick reference: old enum was `Format`, not `ContentsFormat` - add `or 'Untitled'` fallback for `source.title` (Optional[str]) in examples - narrow test_runs.py test_forbidden to only catch YouDefaultError (not 422) - fix test_research.py async test to use httpx.AsyncClient instead of sync client - add test_runs.py test_bad_request for AgentRuns400ResponseError coverage Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
| res = await you.research_async( | ||
| input="What are the latest advances in quantum computing?", | ||
| research_effort=ResearchEffort.STANDARD, | ||
| server_url=server_url, |
There was a problem hiding this comment.
[TEST] Async test omits content length assertion that sync test includes
The sync test_basic_research (line 46) asserts len(res.output.content) > 0. This async test only checks is not None, so a response with an empty string would pass async but fail sync.
| server_url=server_url, | |
| assert res.output.content is not None | |
| assert len(res.output.content) > 0 |
|
@claude open a PR against this branch with all of your recommended changes |
|
Claude finished @tyler5673's task in 5m 58s —— View job Review fixes ready for PRAll file edits are committed and pushed to branch → Create PR: fix/claude-review-2.3.0 → release/2.3.0 Changes in the branch (commit
|
| File | Fix |
|---|---|
src/youdotcom/utils/retries.py |
Inverted retry_connection_errors logic — when True the flag now retries network/timeout errors; when False raises PermanentError immediately (sync + async) |
src/youdotcom/utils/retries.py |
On max_elapsed_time exceeded, raise instead of returning a non-2xx httpx.Response as if it were success |
src/youdotcom/utils/security.py |
Add missing f prefix to three ValueError messages so actual sub_type/scheme_type values appear in errors |
src/youdotcom/sdkconfiguration.py |
Replace pydantic.Field(default_factory=...) with dataclasses.field(default_factory=...) so retry_config initializes to UNSET correctly |
Documentation
| File | Fix |
|---|---|
README.md |
Add missing imports to streaming SSE example (ExpressAgentRunsRequest, WebSearchTool, eventstreaming, all ResponseXxx event types) |
README.md |
Fix run_time_ms label: "seconds" → "ms" |
MIGRATION.md |
Add 2.3.0 breaking-changes section: Python ≥3.10, Search.count default 10, crawl_timeout type int |
Tests
| File | Fix |
|---|---|
tests/test_research.py |
Replace if res.output.sources: guard with explicit assert is not None + assert len > 0 |
tests/test_research.py |
Add assert len(res.output.content) > 0 to async test to match sync test |
Already fixed in prior commits (skipped): README streaming indentation, retries RetryConfig positional arg, CHANGELOG error rename table, test_runs.py broad pytest.raises, test deps in pyproject.toml.
|
- README.md: add missing imports to streaming SSE example
(ExpressAgentRunsRequest, WebSearchTool, eventstreaming, all
ResponseXxx event types were used but not imported)
- README.md: fix run_time_ms unit label ("seconds" → "ms")
- retries.py: fix inverted retry_connection_errors flag logic (sync+async)
When True the flag now retries; when False it raises PermanentError
- retries.py: raise instead of returning error response on max_elapsed_time
exceeded so callers are not silently handed a non-2xx httpx.Response
- security.py: add missing f-prefix to three non-interpolating ValueError
messages so the actual sub_type/scheme_type values appear in errors
- sdkconfiguration.py: replace pydantic.Field with dataclasses.field so
retry_config default_factory is evaluated correctly at dataclass init
- MIGRATION.md: add 2.3.0 breaking-changes section covering Python ≥3.10
requirement, Search.count new default of 10, and crawl_timeout int type
- tests/test_research.py: replace misleading if-guard on required
sources field with explicit assert; add len > 0 check to async test
Co-authored-by: Tyler Eastman <tyler5673@users.noreply.github.com>
Reverts changes to retries.py, security.py, and sdkconfiguration.py. These files are marked "DO NOT EDIT" and tracked in .speakeasy/gen.lock, so any patches here get silently overwritten on the next speakeasy generate run. The fixes should be reported upstream to Speakeasy instead. Specifics: - retries.py conditional restructure was a no-op (logically equivalent) - retries.py error-swallowing removal is a real but edge-case change - security.py f-string prefixes only affect error-path messages - sdkconfiguration.py pydantic.Field vs dataclasses.field has not caused reported issues in practice Keeps the non-generated fixes: README.md imports/label, MIGRATION.md 2.3.0 section, and test_research.py assertion improvements. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
fix: apply review-recommended fixes for 2.3.0
| assert res.output.content is not None | ||
| assert len(res.output.content) > 0 | ||
|
|
||
| def test_research_lite_effort(self, server_url, api_key): |
There was a problem hiding this comment.
Missing coverage for DEEP and EXHAUSTIVE effort levels: The CHANGELOG advertises four effort levels (lite, standard, deep, exhaustive), but only LITE and STANDARD are exercised here. Add at minimum a parameterized test (or two extra cases) for ResearchEffort.DEEP and ResearchEffort.EXHAUSTIVE so regressions in those enum values or their serialisation are caught.
|
|
||
| class TestResearchAsync: | ||
| @pytest.mark.asyncio | ||
| async def test_basic_research_async(self, server_url, api_key): |
There was a problem hiding this comment.
Async error paths not tested: TestResearchAsync only covers the happy path. The four error cases tested synchronously in TestResearchErrors (401, 403, 422, 500) have no async counterparts. Since research_async has its own error-handling code path in sdk.py, those branches can regress independently. Consider adding async error tests, or at minimum add a comment explaining the deliberate omission.
Code Review — SDK 2.3.0Two inline comments on 🔴 Must Fix1. Pre-compiled binary committed to git (
Fix: add 2. README license badge says MIT; <img src="https://img.shields.io/badge/License-MIT-blue.svg" />
3.
🟡 Should Fix4. Dead assertion in live test ( 5. Trivially weak streaming assertion ( assert res is not None
6. Ambiguous empty-URL test ( 7. Pydantic lower bound is tighter than necessary ( 🔵 Nice to Have8. Missing test coverage for 9. Async error paths not tested ( ✅ What looks good
|
the 12MB platform-specific binary can't be code-reviewed, fails on other platforms, and bloats git history. added it to .gitignore so it stays out after local builds. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
exercises ResearchEffort.DEEP and ResearchEffort.EXHAUSTIVE so all four advertised effort levels have dedicated test cases. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
adds pathpostv1research.go with success + error handlers so the research tests can run against the local mock server. covers all test scenarios: success, unauthorized, forbidden, unprocessable, and internal server error. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
the test_bad_request test was sending x-speakeasy-test-name "post_/v1/agents/runs-bad-request" but the mock server had no case for it, so it fell through to the default 200 success handler. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
PR Review — Python SDK 2.3.0Overall this is a clean, well-structured release. The Research API implementation looks correct, CHANGELOG and MIGRATION docs are thorough, and the test count is solid (43 tests across the manually-maintained suite). Several issues need attention before merge. 🔴 Must Fix1. License mismatch (README + pyproject.toml + missing LICENSE file) 2. 🟡 Should Fix3. Missing 4. Missing test for 5. Overly broad error assertions in research error tests 6. Async test builds its own 🔵 Nits / Low Priority7. Redundant 8. 9. Weak streaming assertion in 10. 11. 12. ✅ Looks Good
|
runs pytest against the mock server on PRs and pushes to main. builds the go mock server, starts it in the background, then runs the full test suite. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
| # Migration Guide: 1.x to 2.0 | ||
| # Migration Guide | ||
|
|
||
| ## 1.x → 2.3.0 (Latest) |
There was a problem hiding this comment.
Misleading section title. The section below covers only the 2.x → 2.3.0 upgrade path (Python version, count default, crawl_timeout type). Labelling it "1.x → 2.3.0" will send 1.x users here and they won't find the 1.x-specific guidance until the separate "1.x to 2.0" section.
| ## 1.x → 2.3.0 (Latest) | |
| ## 2.x → 2.3.0 (Latest) |
|
|
||
| assert isinstance(res, ResearchResponse) | ||
| assert res.output is not None | ||
| assert res.output.sources is not None |
There was a problem hiding this comment.
Redundant is not None check. Output.sources is typed as List[Source] (non-optional), so it can never be None. The guard will always pass, silently masking any serialization error that returns None at runtime.
Drop this line and keep only the len > 0 assertion:
| assert res.output.sources is not None | |
| assert res.output.sources is not None | |
| assert len(res.output.sources) > 0 |
→
assert len(res.output.sources) > 0| class TestResearchAsync: | ||
| @pytest.mark.asyncio | ||
| async def test_basic_research_async(self, server_url, api_key): | ||
| async_client = httpx.AsyncClient( |
There was a problem hiding this comment.
Async tests only cover the happy path. TestResearchErrors has no async variants — the 401/403/422/500 error paths are only exercised synchronously. At minimum the unauthorized case should be duplicated as an async test, since the async and sync code paths are entirely separate in sdk.py (distinct do_request vs do_request_async calls).
Also note: this client does not set follow_redirects=True, inconsistent with how the sync tests use create_test_http_client (which also doesn't set it) but different from the default httpx.Client(follow_redirects=True) the You constructor creates when no client is supplied. Not a blocker against mock servers, but worth a comment.
|
|
||
| ### Changed | ||
|
|
||
| - **Default server URL**: Changed from `https://ydc-index.io` to `https://api.you.com`. If you were relying on the default, no action needed as both resolve to the same API. |
There was a problem hiding this comment.
Wording is inaccurate for Search and Contents endpoints.
The code in src/youdotcom/models/searchop.py and src/youdotcom/models/contentsop.py still has per-operation server lists that hardcode https://ydc-index.io:
SEARCH_OP_SERVERS = ["https://ydc-index.io"]
CONTENTS_OP_SERVERS = ["https://ydc-index.io"]search.unified() and contents.generate() call models.SEARCH_OP_SERVERS[0] / models.CONTENTS_OP_SERVERS[0] directly, so those endpoints still use ydc-index.io by default unless the caller passes server_url=. The Agents and Research APIs do use https://api.you.com (global config). Consider clarifying: "The default base URL for the Agents and Research APIs changed from …" or update the per-operation server lists as well.
Review Summary — Python SDK 2.3.0Overall this is a solid release. The new Research API is well-structured, error types are consistent with the existing pattern, and the unit-test layout is clean. A few issues need attention before merging, with the CHANGELOG wording being the most user-facing. Must Fix1. The CHANGELOG says the default changed from
2.
3. Header reads '1.x → 2.3.0 (Latest)' but the section exclusively covers 2.x → 2.3.0 changes. 1.x users directed here will miss the relevant context. Should be '2.x → 2.3.0' (see inline comment). Should Fix4.
5.
6. PR checklist inconsistency The checklist marks 'MIGRATION.md updated' as N/A for minor release, yet MIGRATION.md was updated and documents three breaking changes in 2.3.0 (Python 3.10, Informational / Pre-existing7. 2.11.x released early 2025; many projects pin to 8. All three existing endpoints have 9. The sync helper omits SecurityNo hardcoded secrets, injection vectors, or authentication bypasses found. API key propagation via the |
Summary
youdotcomPython SDKresearch()andresearch_async()methods for comprehensive, multi-step research answers with citationsChanges
you.research()andyou.research_async()methods withResearchEffortlevels (lite, standard, deep, exhaustive)ResearchRequest,ResearchResponse,Output,Source,ContentType,ResearchEffortResearchUnauthorizedError,ResearchForbiddenError,ResearchInternalServerError,UnprocessableEntityErrorhttps://ydc-index.iotohttps://api.you.comcountparameter now defaults to 10crawl_timeouttype changed from float to int, default is now 10sChecklist
test_live.py) — requiresYOU_API_KEY_AUTH