Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
5b3b93f
Python SDK: add search_helpers for keyless /v1/agents/search endpoint
tyler5673 Aug 4, 2026
0bc50cf
feat: add Answer API (DX-308) + keyless search host + first-class 402…
tyler5673 Aug 4, 2026
9124c36
fix: address PR review feedback
tyler5673 Aug 4, 2026
c8c881d
test: expand unit + live test coverage for answer API and search helpers
tyler5673 Aug 4, 2026
63aceac
test: add keyless live tests for search helpers
tyler5673 Aug 4, 2026
5cd1abe
fix: drift check fixes — answer language normalization + 422/500 erro…
tyler5673 Aug 4, 2026
54ac7c7
chore: remove Speakeasy disclaimers, update docs for Answer API + key…
tyler5673 Aug 4, 2026
623e4f5
docs: update all SDK documentation for Answer API + keyless search + …
tyler5673 Aug 4, 2026
360c50a
docs: remove remaining Speakeasy attribution from README, document An…
tyler5673 Aug 4, 2026
b000d4f
fix: remove stale Speakeasy regen note from search_helpers docstring
tyler5673 Aug 4, 2026
be62e04
fix: update user-agent hook prefix from speakeasy-sdk/ to youdotcom-p…
tyler5673 Aug 4, 2026
1536e28
refactor: remove YDCUserAgentOverrideHook — no longer needed
tyler5673 Aug 4, 2026
3b62dd7
fix: country normalization, keyless test skip, dead code — review fin…
tyler5673 Aug 4, 2026
8e50eb9
fix: address review round 3 feedback (4 of 6 comments)
tyler5673 Aug 4, 2026
d5cab7e
test: add user agent verification (unit + live)
tyler5673 Aug 4, 2026
45d1da7
fix: address review round 4 feedback
tyler5673 Aug 4, 2026
bf8a66d
refactor: move Answer from sub-SDK to direct method on You
tyler5673 Aug 4, 2026
5c945e3
fix: correct changelog wording about search endpoint defaults
tyler5673 Aug 4, 2026
cab318f
feat: add direct methods on You to replace sub-SDKs, deprecate sub-SD…
tyler5673 Aug 5, 2026
27bfc07
fix: dev install instructions, stale ydc-index.io in README — review …
tyler5673 Aug 5, 2026
351d0e0
Remove sub-SDKs, direct methods on You (breaking change)
tyler5673 Aug 5, 2026
651d9f2
Add POST /v1/search handler to mock server
tyler5673 Aug 5, 2026
e6e5263
Fix AnswerRequestBody docs: mark domain lists as Optional
tyler5673 Aug 5, 2026
4b92e82
Fix stale references across docs, examples, and tests
tyler5673 Aug 5, 2026
36bb2cb
Fix review findings: dead code, server_url bypass, type annotations
tyler5673 Aug 5, 2026
6d4e61d
Bump version to 2.6.0, update dev dependencies
tyler5673 Aug 5, 2026
84347ac
Update all surfaces for 2.6.0 release
tyler5673 Aug 5, 2026
82835a2
Fix P1 review findings: Optional model fields, dead code, docstring
tyler5673 Aug 5, 2026
a3a5a80
Merge search_helpers into you.search() — one keyless-capable search m…
tyler5673 Aug 5, 2026
1286475
Fix stale endpoint ref in deprecated search docs, add 402 to error table
tyler5673 Aug 5, 2026
39c2f78
Ensure keyless tests across all three layers
tyler5673 Aug 5, 2026
30bfd3f
Fix answer model docs to mark Optional fields, run mypy in CI
tyler5673 Aug 5, 2026
e3e2eed
Exclude live API tests from CI runs
tyler5673 Aug 5, 2026
b446ef8
Fix flaky livecrawl test: treat empty string as valid content
tyler5673 Aug 5, 2026
40ef155
Fix P0/P1 review findings: AgentType example, Speakeasy header, test …
tyler5673 Aug 5, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 9 additions & 9 deletions .agents/skills/generate-sdk-and-open-pr/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -301,18 +301,18 @@ grep -rl "YOU_API_KEY_AUTH" --include="*.py" --include="*.md" . | grep -v __pyca
# in the fallback precedence.
```

#### 4i-2. Verify server URLs (do NOT change search/contents URLs)
#### 4i-2. Verify server URLs

The OpenAPI specs for search and contents use `https://ydc-index.io` as the server URL. This is correct and documented at `you.com/docs/api-reference/search/v1-search` (the page explicitly shows `GET https://ydc-index.io/v1/search`). The `api.you.com` host is a free MCP-only proxy (`/v1/agents/search`, 100 searches/day, IP-tracked) — the SDK should NOT use it for search or contents.
The OpenAPI specs for search and contents use `https://api.you.com` as the server URL. This is the canonical host for the SDK and is documented at `you.com/docs/api-reference/search/v1-search`.

**Verify** (do not change) that these files still have `ydc-index.io`:
**Verify** (do not change) that these files still have `api.you.com`:

```bash
grep "ydc-index.io" src/youdotcom/models/searchop.py src/youdotcom/models/searchpostop.py src/youdotcom/models/contentsop.py
# All three should show "https://ydc-index.io"
grep "api.you.com" src/youdotcom/models/searchop.py src/youdotcom/models/searchpostop.py src/youdotcom/models/contentsop.py
# All three should show "https://api.you.com"
```

The base `SERVERS` in `src/youdotcom/sdkconfiguration.py` should remain `https://api.you.com` (used by research, finance_research, agents).
The base `SERVERS` in `src/youdotcom/sdkconfiguration.py` should also remain `https://api.you.com` (used by all endpoints: search, contents, research, finance_research, agents).

#### 4i-3. Preserve and verify hand-maintained files

Expand Down Expand Up @@ -412,7 +412,7 @@ Speakeasy assembles per-parameter `example` values into one combined request. Fo
- `boost_domains` **cannot** be combined with `include_domains` (returns `422`).
- `exclude_domains` + `boost_domains` **is** valid.

After every regen, grep the lead Search examples in `USAGE.md` and `README.md` (specifically the `<!-- Start SDK Example Usage -->` blocks) to confirm no `search_post`/`search.unified` example combines all three of `include_domains`, `exclude_domains`, and `boost_domains`:
After every regen, grep the lead Search examples in `USAGE.md` and `README.md` (specifically the `<!-- Start SDK Example Usage -->` blocks) to confirm no `search` example combines all three of `include_domains`, `exclude_domains`, and `boost_domains`:

```bash
# Each occurrence with all three is a bug — drop include_domains (keep
Expand All @@ -424,11 +424,11 @@ grep -nE 'include_domains=\[' USAGE.md README.md

The long-term fix lives upstream: add a request-level `example` block on `SearchRequestBody` / `SearchRequest` in `overlays/python_overlay.yaml` (or the front-end OpenAPI specs) that uses a single valid pair, so Speakeasy prefers that example instead of concatenating per-field ones. Track that as a follow-up; the hand-fix above is what keeps 2.4.0 correct in the meantime.

Also scan for the `RetryConfig(...)` positional-after-kwargs regression that Speakeasy can produce when `search_post` is the lead example operation:
Also scan for the `RetryConfig(...)` positional-after-kwargs regression that Speakeasy can produce when `search` is the lead example operation:

```bash
# If you see ", RetryConfig(...)" or similar after a keyword argument in a
# search_post example, the generated Python is a SyntaxError. Fix by
# search example, the generated Python is a SyntaxError. Fix by
# passing `retries=RetryConfig(...)`.
grep -nE ', RetryConfig\(' README.md USAGE.md
```
Expand Down
9 changes: 6 additions & 3 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,8 @@ jobs:
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install -e ".[dev]"
pip install pytest pytest-asyncio
pip install -e .
pip install "mypy>=2.3.0" "pytest>=9.0.0" "pytest-asyncio>=1.0.0"

- name: Build and start mock server
working-directory: tests/mockserver
Expand All @@ -40,4 +40,7 @@ jobs:
curl -sf http://localhost:18080/ || echo "mock server ready"

- name: Run tests
run: pytest tests/ -v --tb=short -x
run: pytest tests/ -v --tb=short -x --ignore=tests/test_live.py

- name: Run mypy
run: mypy src/youdotcom/
40 changes: 40 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,46 @@ All notable changes to the You.com Python SDK will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [2.6.0] - 2026-08-04

### Breaking Changes

- **Sub-SDK Removal**: The sub-SDKs (`Agents`, `Search`, `ContentsSDK`) and the `search_helpers` module have been removed. Methods are now direct on the `You` class. The `you.agents`, `you.search`, and `you.contents` attributes no longer resolve to sub-SDK objects — they are now method calls. `you.search.unified()` (GET `/v1/search`) has been removed; use `you.search()` (POST `/v1/agents/search`) instead. The standalone `search_helpers.search()` function has been merged into `you.search()`. The old→new mapping:

| Old (removed) | New |
|---------------|-----|
| `you.agents.runs.create(request=...)` | `you.agents(request=...)` |
| `you.agents.runs.create_async(request=...)` | `you.agents_async(request=...)` |
| `you.search.unified(query=...)` | `you.search(query=...)` (POST `/v1/agents/search`) |
| `you.search_post(query=...)` | `you.search(query=...)` |
| `you.search_post_async(query=...)` | `you.search_async(query=...)` |
| `you.contents.generate(urls=...)` | `you.contents(urls=...)` |
| `you.contents.generate_async(urls=...)` | `you.contents_async(urls=...)` |
| `search_helpers.search(client, query=...)` | `you.search(query=...)` |
| `search_helpers.search_async(client, query=...)` | `you.search_async(query=...)` |

Accessing `you.agents`, `you.search`, or `you.contents` as attributes will now fail at runtime — update to the direct method calls above.

### Added

- **Answer API**: New direct method `you.answer()` / `you.answer_async()` for `POST /v1/answer`. Returns a synthesized markdown answer with inline citations (`[[1, 2]]`), a citations array (source URLs + supporting excerpts), and web results. Accepts `query` (required), `freshness`, `country`, `language`, `include_domains`, `exclude_domains`, `boost_domains`. Requires an API key. Country and language accept plain strings (e.g. `"us"`, `"en"`) and are normalized to uppercase automatically.
- **Keyless search**: `you.search()` / `you.search_async()` now target `POST /v1/agents/search` on `api.you.com` — the keyless-capable proxy. With no API key, runs in the free tier (100 queries/day, count ≤ 50, no livecrawl). With a key, the proxy forwards to the full search endpoint. Country and language strings are normalized to uppercase. The standalone `search_helpers` module has been removed; its functionality is now a direct method on `You`.
- **`PaymentRequiredResponseError`**: New first-class error class for HTTP 402 responses, matching the `UpgradeRequiredResponse` schema (`error`, `message`, `upgrade_url`, `limit`, `used`, `period`, `reset_at`). Shared by both search and answer 402 handlers. Replaces the previous `FreeTierLimitError`.

### Changed

- **Direct methods on `You`**: The sub-SDK access patterns (`you.agents.runs.create()`, `you.search.unified()`, `you.contents.generate()`) and the direct aliases (`you.create_run()`, `you.search_unified()`, `you.generate_contents()`, `you.search_post()`) from the prior deprecation have all been replaced with the final direct method names: `you.agents()`, `you.search()`, `you.contents()` (plus async variants). See the Breaking Changes table above.
- **Search/Contents host**: `SEARCH_OP_SERVERS`, `SEARCH_POST_OP_SERVERS`, and `CONTENTS_OP_SERVERS` changed from `https://ydc-index.io` to `https://api.you.com` to align with the MCP server and published docs. `you.search()` targets the keyless-capable proxy at `api.you.com/v1/agents/search`.
- **422 error data model**: `UnprocessableEntityResponseErrorData` now includes optional `detail` (FastAPI validation array) and `errors` (JSON:API array) fields in addition to the existing `error` field. All three 422 response shapes deserialize without crashing. Backward compatible — existing code accessing `.error` still works.
- **500 error data model**: `InternalServerErrorResponseData` now includes an optional `errors` field for JSON:API format 500 responses. Backward compatible.
- **No longer generated by Speakeasy**: Removed all "Code generated by Speakeasy — DO NOT EDIT" disclaimers and the Speakeasy badge from the README. The SDK is now hand-maintained.
- **Removed `YDCUserAgentOverrideHook`**: The hook existed to rewrite Speakeasy's default UA (`speakeasy-sdk/python ...`) to `youdotcom-python-sdk/{version}`. Now that `__user_agent__` is already `youdotcom-python-sdk/{version}`, `BaseSDK._build_request` sets it directly — the hook was a no-op. Integrations that need a custom UA still just set `client.sdk_configuration.user_agent`.
- **`__user_agent__` derived from resolved `__version__`**: The user-agent string is now built from the package's resolved version at runtime rather than a hardcoded value.
- **Dead code removal**: Deleted `_hooks/registration.py` (no-op `init_hooks`), `models/answerop.py` (unused `ANSWER_OP_SERVERS`), unused `importlib` and `TYPE_CHECKING` imports from `sdk.py`.
- **`search()` server_url fix**: `search()` and `search_async()` now use `self._get_url(None, None)` instead of hardcoded `SEARCH_POST_OP_SERVERS[0]`, so custom `server_url` passed to `You()` is respected (previously ignored for search only).
- **`answer()` type annotations**: `country` and `language` parameters changed from `Optional[str]` to `Optional[models.Country]` / `Optional[models.Language]` to match `search()` and reflect the actual enum constraint. String values are still accepted and normalized via `.upper()`.
- **Dev dependencies updated**: mypy `1.15.0` → `>=2.3.0`, pylint `3.2.3` → `>=4.0.0`, pytest floor `>=8.0.0` → `>=9.0.0`, pytest-asyncio floor `>=0.24.0` → `>=1.0.0`. Runtime dependencies (httpx, httpcore, pydantic) unchanged — already at latest stable.

## [2.5.0] - 2026-07-20

### Added
Expand Down
26 changes: 23 additions & 3 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Contributing to This Repository

Thank you for your interest in contributing to this repository. Please note that this repository contains generated code. As such, we do not accept direct changes or pull requests. Instead, we encourage you to follow the guidelines below to report issues and suggest improvements.
Thank you for your interest in contributing to the You.com Python SDK! This SDK is hand-maintained (not generated) and we welcome pull requests.

## How to Report Issues

Expand All @@ -13,9 +13,29 @@ If you encounter any bugs or have suggestions for improvements, please open an i
- Information about your environment (e.g., operating system, software versions)
- For example can be collected using the `npx envinfo` command from your terminal if you have Node.js installed

## Issue Triage and Upstream Fixes
## Pull Requests

We will review and triage issues as quickly as possible. Our goal is to address bugs and incorporate improvements in the upstream source code. Fixes will be included in the next generation of the generated code.
1. Fork the repository and create a branch from `main`.
2. Make your changes. Follow existing code style and patterns.
3. Add or update tests as needed. Run `pytest tests/ --ignore=tests/test_live.py` for unit tests (live tests require `YDC_API_KEY`).
4. Run `mypy src/youdotcom/` to ensure type safety.
5. Update documentation (README, CHANGELOG, `docs/` directory) if your change adds or modifies public API surface.
6. Open a pull request with a clear description of the change.

## Development Setup

```bash
python -m venv .venv
source .venv/bin/activate
pip install -e .
pip install mypy pylint pyright pytest pytest-asyncio
```

Or with [uv](https://docs.astral.sh/uv/):

```bash
uv sync --dev
```

## Contact

Expand Down
85 changes: 84 additions & 1 deletion MIGRATION.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,89 @@
# Migration Guide

## 2.4.0 → 2.5.0 (Latest)
## 2.5.0 → 2.6.0 (major version)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P2] Migration guide: “major version” wording vs SemVer 2.6.0

The 2.5.0 → 2.6.0 section calls this a “major version release,” but the package version is 2.6.0 and the changelog explicitly states Semantic Versioning; consider rephrasing this as “breaking changes” (or bumping to 3.0.0 if a semver-major is intended) to avoid confusing upgrade guidance.


> **This is a major version release with breaking changes.** Sub-SDKs have been removed and methods are now direct on the `You` class. Update your code before upgrading.

### Answer API

New direct method `you.answer()` for `POST /v1/answer`:

```python
import os
from youdotcom import You

with You(api_key_auth=os.getenv("YDC_API_KEY")) as you:
res = you.answer(query="What causes the 2008 financial crisis?")
print(res.answer) # markdown with [[1, 2]] citations
print(res.citations[0].source) # source URL
print(res.results.web[0].title) # web result title
```

Requires an API key. `country` and `language` accept plain strings (e.g. `"us"`, `"en"`) and are normalized to uppercase.

### Keyless Search

`you.search()` / `you.search_async()` target `POST /v1/agents/search` on `api.you.com` — the keyless-capable proxy. No API key required for the free tier (100 queries/day, count ≤ 50, no livecrawl). With a key, the proxy forwards to the full search endpoint.

The standalone `search_helpers` module has been removed. Its `search()` / `search_async()` functions are now direct methods on `You`:

```python
# Before (2.5.x): from youdotcom.search_helpers import search
# search(you, query="...")

# After (2.6.0):
you.search(query="...")
```

### Host Change: ydc-index.io → api.you.com

`SEARCH_OP_SERVERS`, `SEARCH_POST_OP_SERVERS`, and `CONTENTS_OP_SERVERS` changed from `https://ydc-index.io` to `https://api.you.com`. This aligns with the MCP server and published docs. No code changes required — the SDK resolves the server automatically.

### FreeTierLimitError → PaymentRequiredResponseError

The standalone `FreeTierLimitError` exception has been replaced with the first-class `PaymentRequiredResponseError` (extends `YouError`). The new error provides structured data:

```python
from youdotcom import You
from youdotcom.errors import PaymentRequiredResponseError

with You() as you:
try:
you.search(query="test", count=100) # exceeds free tier
except PaymentRequiredResponseError as e:
print(e.data.message) # "Insufficient credits"
print(e.data.upgrade_url) # "https://you.com/platform"
print(e.data.limit) # 100
print(e.data.reset_at) # "2026-08-05T00:00:00Z"
```

### 422/500 Error Models Expanded

`UnprocessableEntityResponseErrorData` now includes optional `detail` (FastAPI validation array) and `errors` (JSON:API array) fields in addition to the existing `error` field. `InternalServerErrorResponseData` now includes an optional `errors` field. These are additive — existing code accessing `.error` or `.detail` still works.

### No Longer Generated by Speakeasy

The SDK is now hand-maintained. All "Code generated by Speakeasy — DO NOT EDIT" disclaimers have been removed. The `__gen_version__` / `SPEAKEASY_GENERATOR_VERSION` exports have been removed.

### Sub-SDK Removal (Breaking)

The sub-SDKs (`Agents`, `Search`, `ContentsSDK`) and the `search_helpers` module have been removed. The `you.agents`, `you.search`, and `you.contents` attributes no longer resolve to sub-SDK objects — they are now method calls. Methods are now direct on the `You` class:

| Old (removed) | New |
|---------------|-----|
| `you.agents.runs.create(request=...)` | `you.agents(request=...)` |
| `you.agents.runs.create_async(request=...)` | `you.agents_async(request=...)` |
| `you.search.unified(query=...)` | `you.search(query=...)` (POST `/v1/agents/search`) |
| `you.search_post(query=...)` | `you.search(query=...)` |
| `you.search_post_async(query=...)` | `you.search_async(query=...)` |
| `you.contents.generate(urls=...)` | `you.contents(urls=...)` |
| `you.contents.generate_async(urls=...)` | `you.contents_async(urls=...)` |
| `search_helpers.search(client, query=...)` | `you.search(query=...)` |
| `search_helpers.search_async(client, query=...)` | `you.search_async(query=...)` |

`GET /v1/search` (`you.search.unified()`) has been removed. Use `POST /v1/agents/search` (`you.search()`) instead. Accessing `you.agents`, `you.search`, or `you.contents` as sub-SDK attributes will now fail at runtime — update to the direct method calls above. Importing from `youdotcom.search_helpers` will also fail — use `you.search()` directly.

## 2.4.0 → 2.5.0

### New `frontier` Research Effort Tier

Expand Down
Loading
Loading