diff --git a/CHANGELOG.md b/CHANGELOG.md index 67d0c25..e303e02 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,11 +5,38 @@ 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). -## [3.8.0] - 2026-10-14 +## [3.6.0] - 2026-10-14 -Minor release. Flips `knowledge` and `extraction` on by default for search, per the -product call to lead with the richer response shape. Behavior change for default -callers — see [MIGRATION.md](MIGRATION.md#370--380). +Minor release, three changes. The finance-research drift against that endpoint's +published OpenAPI spec is closed — the last two fields the spec declared that the +SDK didn't model, plus the response widening that lets a structured `output_schema` +call round-trip. `extraction.highlights` on `POST /v1/search` gets its public shape: +an optional whole-response token cap. And `knowledge` and `extraction` flip on +by default for search, leading with the richer response shape — +a behavior change for default callers, see +[MIGRATION.md](MIGRATION.md#350--360). + +### Added + +- **`output_schema` parameter on `finance_research()` and `finance_research_async()`** — + beta structured output on the Finance Research API, mirroring the Research API's + `output_schema`. Pass a JSON Schema subset and `output.content` comes back as a + JSON object conforming to it. Supported with every `research_effort` value, and + the rules and limits match the Research API, so the same schema works on both + endpoints. +- **`warnings` field on `FinanceResearchResponse`** — non-fatal warnings generated + during the request, such as a deprecation notice for an `output_schema` that will + stop being accepted in a future release. `None` when the response omits the key. + The field was previously recorded in `scripts/audit_wire.py`'s `KNOWN_WIRE_EXTRAS` + as wire-observed-but-undeclared; the spec now declares it, so the entry is removed. +- **`total_max_tokens` on `extraction.highlights`** — caps the tokens of highlights + across the whole response. Optional; must be between 1024 and 8192 when set. + On the server, a request that sets it is served by the Brave provider alone, + because in-house substituted highlights would not be bound by that budget. +- The cap's bound is enforced locally: an out-of-range value raises + `pydantic.ValidationError` before the request is sent, mirroring the server's 422 + instead of a round-trip. `extraction.highlights` remains strict — unknown keys + raise locally too. ### Changed @@ -19,12 +46,12 @@ callers — see [MIGRATION.md](MIGRATION.md#370--380). `knowledge="core"` and `extraction_mode="highlights"` on every search request that does not use the deprecated `livecrawl` / `livecrawl_formats` parameters. Pass `knowledge=None` and/or `extraction=None` to opt out of each; pass both - to fully restore the pre-3.8.0 default response shape (the parameters + to fully restore the pre-3.6.0 default response shape (the parameters distinguish omitted vs `None` with the same `UNSET` sentinel the `language` parameter uses). - **Default responses change shape**: web results carry `contents.highlights` and omit `snippets` under the default highlights mode, and a `results.knowledge` - section appears when relevant cards exist. All affected fields remain + section appears when relevant cards exist. The search fields involved stay `Optional` and typed as before — no model or signature break. Callers that already passed explicit values are byte-identical. - **Billing**: highlights is a paid feature and knowledge cards come from licensed @@ -34,60 +61,16 @@ callers — see [MIGRATION.md](MIGRATION.md#370--380). highlights default (an injected default would trip the existing conflict `ValueError`), but `knowledge` still defaults to `"core"` there unless you pass `knowledge=None`. - -## [3.7.0] - 2026-10-13 - -Minor release. Gives `extraction.highlights` on `POST /v1/search` its public -shape: an optional whole-response token cap. Purely additive — no breaking -changes. - -### Added - -- **`total_max_tokens` on `extraction.highlights`** — caps the tokens of highlights - across the whole response. Optional; must be between 1024 and 8192 when set. - On the server, a request that sets it is served by the Brave provider alone, - because in-house substituted highlights would not be bound by that budget. -- The bound is enforced locally: an out-of-range value raises - `pydantic.ValidationError` before the request is sent, mirroring the server's 422 - instead of a round-trip. `extraction.highlights` remains strict — unknown keys - raise locally too. - -### Changed - -- **`ExtractionHighlights` (and its TypedDict) previously carried no fields** — - the container shipped in 3.1.0 as a forward-compat stub. The cap serializes away - when unset, so existing `extraction={"extraction_mode": "highlights"}` requests - are unchanged on the wire. - -## [3.6.0] - 2026-10-13 - -Minor release. Closes the finance-research drift against that endpoint's published -OpenAPI spec — the last two fields the spec declared that the SDK didn't model — -and widens the finance-research response so a structured `output_schema` call -round-trips. Purely additive — no breaking changes. - -### Added - -- **`output_schema` parameter on `finance_research()` and `finance_research_async()`** — - beta structured output on the Finance Research API, mirroring the Research API's - `output_schema`. Pass a JSON Schema subset and `output.content` comes back as a - JSON object conforming to it. Supported with every `research_effort` value, and - the rules and limits match the Research API, so the same schema works on both - endpoints. -- **`warnings` field on `FinanceResearchResponse`** — non-fatal warnings generated - during the request, such as a deprecation notice for an `output_schema` that will - stop being accepted in a future release. `None` when the response omits the key. - The field was previously recorded in `scripts/audit_wire.py`'s `KNOWN_WIRE_EXTRAS` - as wire-observed-but-undeclared; the spec now declares it, so the entry is removed. - -### Changed - - **`FinanceResearchOutput.content` widened from `str` to `Union[str, Dict[str, Any]]`** — a structured `output_schema` request returns `content_type: "object"` with a JSON object payload, so a `str`-only annotation could not parse those responses. One type widens — see [MIGRATION.md](MIGRATION.md#350--360). - **`FinanceResearchContentType` gains the `OBJECT` (`"object"`) member** — the spec declares `text` and `object`; the SDK previously modeled `text` only. +- **`ExtractionHighlights` (and its TypedDict) previously carried no fields** — + the container shipped in 3.1.0 as a forward-compat stub. The cap serializes away + when unset, so existing `extraction={"extraction_mode": "highlights"}` requests + are unchanged on the wire. ## [3.5.0] - 2026-09-22 diff --git a/MIGRATION.md b/MIGRATION.md index adf0b03..315042c 100644 --- a/MIGRATION.md +++ b/MIGRATION.md @@ -1,15 +1,22 @@ # Migration Guide -## 3.7.0 → 3.8.0 +## 3.5.0 → 3.6.0 -> **Default change.** `knowledge` and `extraction` are now on by default on -> `you.search()`, `you.search_async()`, and the deprecated -> `you.search.unified(...)` / `you.search.unified_async(...)` shims: an omitted -> pair sends `knowledge="core"` and `extraction_mode="highlights"` (the -> highlights default does not apply on the deprecated `livecrawl` / -> `livecrawl_formats` path — see the note below). Pass `None` for either to -> opt out of that one default; pass **both** to fully restore the pre-3.8.0 -> plain-search shape. +> **Default change, and one type widens.** `knowledge` and `extraction` are +> now on by default on `you.search()`, `you.search_async()`, and the +> deprecated `you.search.unified(...)` / `you.search.unified_async(...)` +> shims: an omitted pair sends `knowledge="core"` and +> `extraction_mode="highlights"` (the highlights default does not apply on +> the deprecated `livecrawl` / `livecrawl_formats` path — see the note +> below). Pass `None` for either to opt out of that one default; pass +> **both** to fully restore the pre-3.6.0 plain-search shape. +> +> Separately, `FinanceResearchOutput.content` is now +> `Union[str, Dict[str, Any]]`, matching the Research API. Plain finance +> research responses still return a Markdown `str` (with +> `content_type="text"`); only when you pass `output_schema=...` does +> `output.content` arrive as a structured JSON object (with +> `content_type="object"`). ### Action required @@ -19,6 +26,7 @@ | `extraction` defaults to highlights | Default callers reading `result.snippets` | Pass `extraction=None` (snippets return; `knowledge` still defaults to `"core"`); or read `result.contents.highlights` | | `snippets` is `None` under highlights | Code doing unguarded `result.snippets[0]` | Guard for `None`, or opt out with `extraction=None` | | Billing | Every default search call now requests highlights and knowledge cards, which may incur additional cost depending on your plan | Opt out per call with `knowledge=None, extraction=None` if cost-sensitive | +| `FinanceResearchOutput.content` is `Union[str, Dict[str, Any]]` | Type-checked code calling string methods on `output.content` | Narrow `output.content` with `isinstance` before calling string methods. See below | ```python import os @@ -26,11 +34,11 @@ import os from youdotcom import You with You(api_key_auth=os.getenv("YDC_API_KEY"), timeout_ms=60_000) as you: - # Before (3.7.0): plain search, snippets only, no cards. + # Before (3.5.0): plain search, snippets only, no cards. # res = you.search(query="nvda stock price", count=5) # print(res.results.web[0].snippets) - # After (3.8.0): same call now returns highlights + knowledge cards. + # After (3.6.0): same call now returns highlights + knowledge cards. res = you.search(query="nvda stock price", count=5) for hit in res.results.web or []: print(hit.contents.highlights if hit.contents else None) @@ -44,34 +52,13 @@ with You(api_key_auth=os.getenv("YDC_API_KEY"), timeout_ms=60_000) as you: print(res.results.web[0].snippets) ``` -No signature break: every affected field stays `Optional` and typed as -before. Callers that already passed `knowledge="core"` or an explicit -`extraction` are byte-identical. The deprecated `livecrawl` / -`livecrawl_formats` path suppresses the highlights default (it would trip -the existing conflict `ValueError`), but `knowledge` still defaults to -`"core"` there unless you pass `knowledge=None`. - -## 3.5.0 → 3.6.0 - -> **One type widens.** `FinanceResearchOutput.content` is now -> `Union[str, Dict[str, Any]]`, matching the Research API. Plain finance -> research responses still return a Markdown `str` (with `content_type="text"`); -> only when you pass `output_schema=...` does `output.content` arrive as a -> structured JSON object (with `content_type="object"`). - -### Action required - -| Change | Who is affected | What to do | -|--------|-----------------|------------| -| `FinanceResearchOutput.content` is `Union[str, Dict[str, Any]]` | Type-checked code calling string methods on `output.content` | Narrow `output.content` with `isinstance` before calling string methods. See below | - ```python import os from youdotcom import You with You(api_key_auth=os.getenv("YDC_API_KEY"), timeout_ms=60_000) as you: - # Before (3.5.0): content was always a str. + # Before (3.5.0): finance research content was always a str. # res = you.finance_research(input="What was NVIDIA's fiscal year 2025 revenue and what drove it?") # print(res.output.content.lower()) @@ -93,9 +80,19 @@ with You(api_key_auth=os.getenv("YDC_API_KEY"), timeout_ms=60_000) as you: print(content.lower()) ``` -Responses without `output_schema` are unchanged at runtime — the value -remains a `str`, exactly as before — so existing code needs no change unless -it opts in. +No signature break anywhere in 3.6.0: the search request and response +fields affected by the flip stay `Optional` and typed as before. Search +callers that already passed `knowledge="core"` or an explicit `extraction` +are byte-identical, and the deprecated `livecrawl` / `livecrawl_formats` +path suppresses the highlights default (it would trip the existing +conflict `ValueError`) — but `knowledge` still defaults to `"core"` there +unless you pass `knowledge=None`. The one type that changes in 3.6.0 is +`FinanceResearchOutput.content`, widened to `Union[str, Dict[str, Any]]` +to support structured `output_schema` responses; finance research calls +without `output_schema` are unchanged at runtime — the value remains a +`str`, exactly as before — so existing code needs no change unless it +opts in. + ## 3.3.0 → 3.4.0 diff --git a/examples/api-example-calls.py b/examples/api-example-calls.py index 296fc95..43066ca 100755 --- a/examples/api-example-calls.py +++ b/examples/api-example-calls.py @@ -56,7 +56,7 @@ def search_request(): from: ``BLEND`` (default) uses the cache when possible, ``FETCH`` always crawls live, ``CACHE`` is cache-only. - ``highlights.total_max_tokens`` (3.7.0+) bounds the ``HIGHLIGHTS`` + ``highlights.total_max_tokens`` (3.6.0+) bounds the ``HIGHLIGHTS`` budget across the whole response (1024-8192). """ print("\n🚀 Running Search Request (extraction)...\n") diff --git a/pyproject.toml b/pyproject.toml index c778fd5..a1ae47f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "youdotcom" -version = "3.5.0" +version = "3.6.0" description = "The official You.com Python SDK." authors = [{ name = "You.com" },] readme = "README.md" diff --git a/src/youdotcom/_version.py b/src/youdotcom/_version.py index b2a33e2..4a69685 100644 --- a/src/youdotcom/_version.py +++ b/src/youdotcom/_version.py @@ -2,7 +2,7 @@ import importlib.metadata __title__: str = "youdotcom" -__version__: str = "3.5.0" +__version__: str = "3.6.0" __openapi_doc_version__: str = "1.0.0" try: diff --git a/src/youdotcom/sdk.py b/src/youdotcom/sdk.py index 4227545..717e703 100644 --- a/src/youdotcom/sdk.py +++ b/src/youdotcom/sdk.py @@ -84,7 +84,7 @@ def _build_search_request( ``knowledge`` and ``extraction`` are on by default: a caller that omits both gets ``knowledge="core"`` and a highlights ``extraction`` on the wire. Passing ``None`` for either opts out of that default; passing - ``knowledge=None, extraction=None`` restores the pre-3.8.0 plain-search + ``knowledge=None, extraction=None`` restores the pre-3.6.0 plain-search shape. The deprecated ``livecrawl`` / ``livecrawl_formats`` suppress the ``extraction`` injection (an injected default would trip the conflict ``ValueError`` below); ``knowledge`` still flips for them. @@ -1038,7 +1038,7 @@ def _search_impl( ``knowledge`` and ``extraction`` are on by default: an omitted pair sends ``knowledge="core"`` and highlights extraction. Pass ``None`` - for either to opt out of that default; restoring the pre-3.8.0 + for either to opt out of that default; restoring the pre-3.6.0 plain-search shape takes ``knowledge=None, extraction=None``. ``livecrawl`` and ``livecrawl_formats`` are deprecated; prefer diff --git a/tests/test_search_defaults.py b/tests/test_search_defaults.py index 2f05449..5eb1192 100644 --- a/tests/test_search_defaults.py +++ b/tests/test_search_defaults.py @@ -9,7 +9,7 @@ call spelling at once. - Explicit ``None`` is the opt-out for either: ``knowledge=None`` sends no knowledge key and ``extraction=None`` sends no extraction key, restoring - the pre-3.8.0 plain-search shape. Omitted vs ``None`` is distinguished + the pre-3.6.0 plain-search shape. Omitted vs ``None`` is distinguished with the same ``UNSET`` sentinel the ``language`` parameter uses. - The deprecated ``livecrawl`` / ``livecrawl_formats`` parameters suppress the injection: an injected default ``extraction`` would trip the existing @@ -97,7 +97,7 @@ def test_extraction_none_sends_no_extraction(self): assert "extraction" not in _search_body(extraction=None) def test_both_none_restores_plain_shape(self): - """``knowledge=None, extraction=None`` restores the pre-3.8.0 body.""" + """``knowledge=None, extraction=None`` restores the pre-3.6.0 body.""" body = _search_body(knowledge=None, extraction=None) assert "knowledge" not in body assert "extraction" not in body diff --git a/uv.lock b/uv.lock index af5ed4f..40f86ae 100644 --- a/uv.lock +++ b/uv.lock @@ -954,7 +954,7 @@ wheels = [ [[package]] name = "youdotcom" -version = "3.5.0" +version = "3.6.0" source = { editable = "." } dependencies = [ { name = "httpcore" },