Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
91 changes: 37 additions & 54 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -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
Expand All @@ -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

Expand Down
69 changes: 33 additions & 36 deletions MIGRATION.md
Original file line number Diff line number Diff line change
@@ -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

Expand All @@ -19,18 +26,19 @@
| `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

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)
Expand All @@ -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())

Expand All @@ -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

Expand Down
2 changes: 1 addition & 1 deletion examples/api-example-calls.py
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -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"
Expand Down
2 changes: 1 addition & 1 deletion src/youdotcom/_version.py
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
4 changes: 2 additions & 2 deletions src/youdotcom/sdk.py
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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
Expand Down
4 changes: 2 additions & 2 deletions tests/test_search_defaults.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion uv.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading