Skip to content

feat: support hosted web search across Responses and Messages - #74

Open
iAmir97 wants to merge 29 commits into
mainfrom
amer-hosted-web-search
Open

iAmir97 wants to merge 29 commits into
mainfrom
amer-hosted-web-search

Conversation

@iAmir97

@iAmir97 iAmir97 commented Sep 28, 2026 •

Copy link
Copy Markdown
Contributor

Summary

  • Added provider-hosted web search across Responses and Messages, including supported search options, tool selection, calls, results, URL citations, assistant history, and reported search usage in streaming and non-streaming responses.
  • Preserved native passthrough behavior and explicit rejection of unsupported translated request semantics. Chat Completions callers can request mandatory search through Responses or Messages upstreams when the returned reasoning is absent or unsigned.
  • Added Messages citation policies: drop streams text without citations by default; buffer preserves attribution by waiting for completed text parts; auto buffers when normalized tools or history indicate search or citations.
  • Preserved Anthropic search continuation through extra_content.anthropic, implemented by merged PR #92. Serialized Responses output retains source ciphertext/details and citation indices/excerpts for the next turn against the same Anthropic Messages upstream, with ordering and call/result linkage preserved.
  • Added matched fixtures, independent Anthropic search and continuation recordings, SDK parity and actual-output continuation coverage, matching-tool updates, and documentation.

Limitations

  • Translation supports basic hosted search. Normalized search is rejected on translated Chat upstream routes; native Chat search remains supported. Dynamic filtering and fetch remain unsupported for translation.
  • Translated Chat output omits structured citations and search activity, and rejects signed or encrypted reasoning.
  • Messages citation buffering increases text latency and changes chunk boundaries. Native Messages passthrough preserves citations under every policy.
  • Cross-protocol citations and history use the documented reductions. Anthropic continuation state is not portable across providers; missing or invalid required state and incompatible routes are rejected.
  • extra_body and extra_headers remain opaque upstream transport extensions, with credential-boundary header filtering. They do not control translation or citation policy. Broader provider extension support remains tracked in issue #78.

Verification

Verified at dcbf1a9d2d146087e3c9a3d0667e7de50a8ab4ea:

  • PYTHONPATH=python /home/akk/jiahuei/molto/.venv/bin/python -m pytest -q -o addopts='': 2,294 passed on Python 3.12.9, using the pinned SDK versions. Offline replay only; no live provider calls or cassette changes.
  • uv run --extra lint ruff check --output-format github --config pyproject.toml .: passed.
  • uv run --extra lint ruff format --diff --config pyproject.toml .: passed; 99 files already formatted.
  • npx prettier@3.8 --check .: passed.
  • git diff --check origin/main...HEAD: passed.
  • GitHub CI: all five required checks passed, including tests on Python 3.12, 3.13, and 3.14.

iAmir97 and others added 13 commits September 28, 2026 17:27
Signed-off-by: Amir Balwel <amoooori04@gmail.com>
Signed-off-by: Amir Balwel <amoooori04@gmail.com>
Signed-off-by: Amir Balwel <amoooori04@gmail.com>
Signed-off-by: Amir Balwel <amoooori04@gmail.com>
Signed-off-by: Amir Balwel <amoooori04@gmail.com>
Signed-off-by: Amir Balwel <amoooori04@gmail.com>
Signed-off-by: Amir Balwel <amoooori04@gmail.com>
Signed-off-by: Amir Balwel <amoooori04@gmail.com>
Signed-off-by: Amir Balwel <amoooori04@gmail.com>
Signed-off-by: Amir Balwel <amoooori04@gmail.com>
Signed-off-by: Amir Balwel <amoooori04@gmail.com>

@jiahuei jiahuei left a comment •

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Two confirmed P2 findings are attached inline. Reviewed commit c01c79a. The full offline suite passed (1,880 tests), focused transport-override/history tests passed (24 tests), and Ruff and Prettier checks passed. Both findings were reproduced locally; no live provider calls were made.

Comment thread python/molto/utils/web_search.py Resolved
Comment thread python/molto/upstream/openai.py Resolved
Signed-off-by: Amir Balwel <amoooori04@gmail.com>
Signed-off-by: Amir Balwel <amoooori04@gmail.com>

@jiahuei jiahuei left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Review at 995304f

Reviewed the full diff against acf4abe (merge base with main). The two inline findings from my previous review (utils/web_search.py multi-query history, and effective history after extra_body.input) are addressed in 79b0c7a and are not repeated below.

Verification in a clean worktree at 995304f: python -m pytest (1920 passed, Python 3.12), ruff check, ruff format --diff and npx prettier --check . all clean, and tests/cassettes unchanged after the run. Every behavioural claim in the inline comments was reproduced at this commit and compared against origin/main (acf4abe) with identical request bodies over MockTransport.

Inline findings:

Severity Location Issue
P1 upstream/openai.py:1035 Native Chat passthrough rejects web_search_options (regression on an untranslated route)
P1 upstream/openai.py:932 incomplete_details: null silently becomes pause on every route
P1 utils/web_search.py:32 Citation splitting emits empty and whitespace-only text blocks that Anthropic rejects
P1 adapters/openai.py:1419 Search-gated reasoning drop hides an unrepresentable signature
P2 upstream/openai.py:936 Search count belongs on Usage, not three extra_fields channels
P2 utils/web_search.py:157 UnsupportedInput raised while normalizing provider output
P3 upstream/openai.py:676 tool["type"] KeyError on raw extra_body tools
P3 upstream/anthropic.py:440 Typo in an error message, plus duplicated range validation
P3 utils/test.py:55 Unrelated churn (_reject_input_extras as well)
P3 utils/web_search.py:1 Module ownership not stated in CONVENTIONS

Not a code finding: the four web_search upstream requests are genuine recordings, but per tests/cassettes/README.md the Messages response is rewritten from the Responses recording by scripts/match_anthropic_cassette_*.py. The Messages-upstream path therefore rests on the Responses recording for citations_delta, server_tool_use input_json_delta and usage.server_tool_use. One real Anthropic search recording would de-risk that direction, and would also confirm that the reduced history ("N/A" placeholders, empty titles) is accepted by the API.

Comment thread python/molto/upstream/openai.py Resolved
Comment thread python/molto/upstream/openai.py Resolved
Comment thread python/molto/utils/web_search.py Resolved
Comment thread python/molto/adapters/openai.py Resolved
Comment thread python/molto/upstream/openai.py Resolved
Comment thread python/molto/utils/web_search.py Resolved
Comment thread python/molto/upstream/openai.py Resolved
Comment thread python/molto/upstream/anthropic.py Resolved
Comment thread python/molto/utils/test.py Resolved
Comment thread python/molto/utils/web_search.py Resolved
Signed-off-by: Amir Balwel <amoooori04@gmail.com>
Comment thread python/molto/adapters/anthropic.py Resolved
Signed-off-by: Amir Balwel <amoooori04@gmail.com>
Signed-off-by: Amir Balwel <amoooori04@gmail.com>
Signed-off-by: Amir Balwel <amoooori04@gmail.com>
…ider output shape by ignoring unknown input keys

Signed-off-by: Amir Balwel <amoooori04@gmail.com>
Signed-off-by: Amir Balwel <amoooori04@gmail.com>
Signed-off-by: Amir Balwel <amoooori04@gmail.com>
Signed-off-by: Amir Balwel <amoooori04@gmail.com>
Signed-off-by: Amir Balwel <amoooori04@gmail.com>
Signed-off-by: Amir Balwel <amoooori04@gmail.com>
Signed-off-by: Amir Balwel <amoooori04@gmail.com>
## Summary

- Adds `Molto.messages(..., citation_policy="auto")` for conditional
citation buffering on translated Responses routes. It buffers text when
normalized tools or history signal possible citations; otherwise text
streams immediately and unexpected citations are dropped. `"buffer"`
always buffers each text part, `"drop"` remains the default, and opaque
transport extensions do not select automatic buffering. Native Messages
and Chat upstream behavior remain unchanged.
- Preserves point citations without splitting text at zero-length
ranges. When no original excerpt exists, the containing block supplies
the fallback `cited_text`. Existing offset validation, blank-text
rejection, provider metadata, and SDK-compatible text deltas remain
intact.
- Extends terminal-reason coverage with normal completion and refusal,
and adds coverage for automatic buffering, history, transport overrides,
point citations, and SDK text iteration.
- Merges `amer-hosted-web-search`, retaining its Anthropic continuation
metadata, provider-specific missing/empty/placeholder ciphertext
rejection, and recorded continuation coverage. No cassette contents
differ from that branch.

Removal of output-only missing reasoning-signature checks is tracked
independently in [PR #90](#90).

## Verification

- `.venv/bin/python -m pytest`: 2,293 passed in 35.78 seconds on Python
3.12.9 after separating the reasoning-output work. Tests used offline
replay only.
- Focused citation and terminal-reason tests: 154 passed, 267
deselected. Earlier outdated point-citation expectations were corrected
before the passing full suite.
- `uv run --extra lint ruff check --output-format github --config
pyproject.toml .`: passed.
- `uv run --extra lint ruff format --diff --config pyproject.toml .`:
passed.
- `npx prettier@3.8 --check .`: passed.
- `git diff 4235d01 HEAD --exit-code`: passed; the final tree exactly
matches the verified hosted-search-only merge. Cassettes remain
unchanged relative to `amer-hosted-web-search`.
- The offline replay workflow targets PRs against `main`, so PR #89
relies on the local suite above while its base remains
`amer-hosted-web-search`.
## Summary

- Replaced PR #74's `provider_metadata.anthropic` Responses extension
with `extra_content.anthropic`. Search sources retain Anthropic
encrypted content and source details; URL citations retain encrypted
indices and original excerpts so serialized Responses output can
continue against the same Anthropic Messages upstream.
- Updated typed history validation, projection/conversion helpers, route
errors, existing cassette-backed SDK parity and continuation tests, and
documentation. The colocated Responses-to-IR normalization test asserts
complete ordered IR with and without Anthropic extra_content, including
source ciphertext/details and citation encrypted indices/excerpts.
Reused the existing IR fields; transport-level `extra_body` is
unchanged. The old unmerged envelope is not retained as an alias.
- Targets `amer-hosted-web-search` (PR #74). Implements the Anthropic
continuation slice of #78; Gemini support remains tracked there.

## Verification

- `PYTHONPATH=python /home/akk/jiahuei/molto/.venv/bin/python -m pytest
-q tests/core/test_anthropic_web_search.py -k translates_sdk_semantics`:
both updated assertions failed before implementation because
`extra_content` was absent.
- `PYTHONPATH=python /home/akk/jiahuei/molto/.venv/bin/python -m pytest
-q tests/core/test_anthropic_web_search.py tests/core/test_web_search.py
tests/types/test_openai_responses.py`: 405 passed after implementation.
- `PYTHONPATH=python /home/akk/jiahuei/molto/.venv/bin/python -m pytest
-q -o addopts=''`: 2,293 passed on Python 3.12.9, offline replay only.
No live provider calls or cassette changes.
- `uv run --extra lint ruff check --output-format github --config
pyproject.toml .`: passed.
- `uv run --extra lint ruff format --diff --config pyproject.toml .`:
passed, 99 files already formatted. Initial changed-file formatting
check identified two files, corrected with `uv run --extra lint ruff
format --config pyproject.toml python/molto/types/openai_responses.py
tests/core/test_web_search.py`.
- `npx prettier@3.8 --check .`: passed. Initial changed-document check
identified `docs/web-search.md`, corrected with `npx prettier@3.8
--write docs/web-search.md`.
- `git diff --check`: passed. `git status --short -- tests/cassettes`:
empty.

- `PYTHONPATH=python /home/akk/jiahuei/molto/.venv/bin/python -m pytest
-q tests/adapters/test_openai_search.py
tests/core/test_anthropic_web_search.py`: 55 passed after adding direct
IR assertions. Repository-wide Ruff and Prettier checks also passed for
this test-only update; the full-suite result above predates this
additional parameterized case.
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.

2 participants