Skip to content

Route provider-executed tool results into the assistant message in Prompt.fromResponseParts - #6836

Merged
IMax153 merged 3 commits into
Effect-TS:mainfrom
mkdynamic:fix/v4-provider-executed-tool-result-round-trip
Aug 4, 2026
Merged

Route provider-executed tool results into the assistant message in Prompt.fromResponseParts#6836
IMax153 merged 3 commits into
Effect-TS:mainfrom
mkdynamic:fix/v4-provider-executed-tool-result-round-trip

Conversation

@mkdynamic

@mkdynamic mkdynamic commented Aug 1, 2026

Copy link
Copy Markdown
Contributor

Summary

Ports #5944 to the v4 line. Prompt.fromResponseParts places provider-executed tool results (e.g. OpenAI web_search) in a tool message like framework results, so round-tripping a conversation sends them back to OpenAI as function_call_output items whose corresponding web_search_call was dropped from the request (prepareMessages skips provider-executed tool calls). OpenAI rejects the request:

HTTP 400 from POST /v1/responses
No tool call found for function call output with call_id ws_<...>.

The first turn with a provider web search succeeds; every follow-up turn that round-trips the history fails, deterministically. This is exactly #5939, which #5944 fixed on the v3 line (@effect/ai) -- neither of that PR's two changes exists in the v4 rewrite.

Changes

  • Prompt.ToolResultPart / ToolResultPartEncoded / schema gain providerExecuted (decoding default false), mirroring ToolCallPart.
  • fromResponseParts routes tool results with providerExecuted: true into assistantParts, keeping the provider call/result pair together in the assistant message. Framework results still form tool messages.
  • The consumer half already exists in v4: @effect/ai-openai's prepareMessages assistant branch has a tool-result case ("Assistant tool-result parts are always provider executed") that skips provider results or emits item_reference under store: true -- it was dead code until now, since fromResponseParts never delivered results to it.
  • Mechanical: the now-required field added at the two framework construction sites in LanguageModel.ts (providerExecuted: false), the JSDoc examples, and existing test fixtures.
  • Tests: fromResponseParts keeps a provider-executed call/result pair in the assistant message while a framework result still lands in the tool message; and on the Anthropic side, a history built via fromResponseParts containing a provider-executed web_search pair serializes to server_tool_use + web_search_tool_result blocks in the assistant turn with no client tool_result block (the assistant-branch serializer case this change activates).
  • Changeset: "effect": patch.

Validation

  • pnpm test packages/effect/test/unstable/ai packages/ai/openai/test/OpenAiLanguageModel.test.ts packages/ai/openai-compat/test/OpenAiLanguageModel.test.ts packages/ai/anthropic -- 642/642 across 24 files, run at the branch tip.
  • pnpm check -- clean.
  • pnpm lint-fix applied.

Notes

The store: true / item_reference path still cannot engage for web search because the streamed and non-streamed web_search_call parts carry no metadata.openai.itemId (unlike e.g. reasoning and apply_patch parts) and fromResponseParts drops response-part metadata for all part types. With this PR the pair is coherently omitted from the request, which unbreaks the round-trip; carrying item ids through for item_reference replay would be a follow-up.

🤖 Generated with Claude Code

…ompt.fromResponseParts

Ports Effect-TS#5944 to the v4 line: Prompt.ToolResultPart gains providerExecuted
(decoding default false) and fromResponseParts keeps provider-executed
results beside their calls in the assistant message. Round-tripping a
conversation containing a provider web_search no longer sends an orphaned
function_call_output for a ws_ call id, which OpenAI rejects with HTTP 400
"No tool call found for function call output with call_id ws_...".

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@github-project-automation github-project-automation Bot moved this to Discussion Ongoing in PR Backlog Aug 1, 2026
@changeset-bot

changeset-bot Bot commented Aug 1, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 7bf381a

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 30 packages
Name Type
effect Patch
@effect/opentelemetry Patch
@effect/platform-browser Patch
@effect/platform-bun Patch
@effect/platform-deno Patch
@effect/platform-node-shared Patch
@effect/platform-node Patch
@effect/vitest Patch
@effect/ai-anthropic Patch
@effect/ai-openai-compat Patch
@effect/ai-openai Patch
@effect/ai-openrouter Patch
@effect/atom-react Patch
@effect/atom-solid Patch
@effect/atom-vue Patch
@effect/sql-clickhouse Patch
@effect/sql-d1 Patch
@effect/sql-libsql Patch
@effect/sql-mssql Patch
@effect/sql-mysql2 Patch
@effect/sql-pg Patch
@effect/sql-pglite Patch
@effect/sql-sqlite-bun Patch
@effect/sql-sqlite-do Patch
@effect/sql-sqlite-node Patch
@effect/sql-sqlite-react-native Patch
@effect/sql-sqlite-wasm Patch
@effect/docgen Patch
@effect/doctest Patch
@effect/openapi-generator Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@effect-slopcop effect-slopcop Bot added 4.0 bug Something isn't working labels Aug 1, 2026

@pullfrog pullfrog Bot left a comment

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.

ℹ️ One minor doc suggestion inline — otherwise looks solid.

Reviewed changes

  • ToolResultPart gains providerExecuted field with decoding default false, matching the existing ToolCallPart pattern
  • fromResponseParts routes provider-executed results into the assistant message so they stay paired with their ToolCallPart, which unbreaks round-trips where the provider had dropped the call
  • Mechanical providerExecuted: false additions at all framework construction sites and test fixtures

All tests pass (Prompt.test.ts 16/16, LanguageModel.test.ts 33/33, LanguageModelTrackerLifecycle.test.ts 6/6, ResponseIdTracker.test.ts 24/24).


Note: 1 inline comment(s) dropped because they did not anchor to lines inside the PR diff:

  • packages/effect/src/unstable/ai/Prompt.ts:2003 (RIGHT) — line 2003 (RIGHT) is not inside a diff hunk

Pullfrog  | Fix it ➔View workflow run | Using DeepSeek Pro (free via Pullfrog for OSS) (Kimi K2 not used — the program covers this model; add its provider key to run your pick) | 𝕏

@github-actions

github-actions Bot commented Aug 1, 2026

Copy link
Copy Markdown
Contributor

Bundle Size Analysis

Generated from PR build output; treat the content below as untrusted.

File Name Current Size Previous Size Difference
basic.ts 7.06 KB 7.06 KB 0.00 KB (0.00%)
batching.ts 9.86 KB 9.86 KB 0.00 KB (0.00%)
brand.ts 6.34 KB 6.34 KB 0.00 KB (0.00%)
cache.ts 10.62 KB 10.62 KB 0.00 KB (0.00%)
config.ts 20.60 KB 20.60 KB 0.00 KB (0.00%)
differ.ts 20.20 KB 20.20 KB 0.00 KB (0.00%)
http-client.ts 21.49 KB 21.49 KB 0.00 KB (0.00%)
logger.ts 10.76 KB 10.76 KB 0.00 KB (0.00%)
metric.ts 8.99 KB 8.99 KB 0.00 KB (0.00%)
optic.ts 7.18 KB 7.18 KB 0.00 KB (0.00%)
pubsub.ts 14.90 KB 14.90 KB 0.00 KB (0.00%)
queue.ts 11.58 KB 11.58 KB 0.00 KB (0.00%)
schedule.ts 10.74 KB 10.74 KB 0.00 KB (0.00%)
schema-class.ts 19.14 KB 19.14 KB 0.00 KB (0.00%)
schema-fromJsonSchemaDocument.ts 28.96 KB 28.96 KB 0.00 KB (0.00%)
schema-representation-roundtrip.ts 25.29 KB 25.29 KB 0.00 KB (0.00%)
schema-string-transformation.ts 13.30 KB 13.30 KB 0.00 KB (0.00%)
schema-string.ts 10.94 KB 10.94 KB 0.00 KB (0.00%)
schema-template-literal.ts 15.17 KB 15.17 KB 0.00 KB (0.00%)
schema-toArbitraryLazy.ts 21.94 KB 21.94 KB 0.00 KB (0.00%)
schema-toCodeDocument.ts 24.34 KB 24.34 KB 0.00 KB (0.00%)
schema-toCodecJson.ts 19.18 KB 19.18 KB 0.00 KB (0.00%)
schema-toEquivalence.ts 19.01 KB 19.01 KB 0.00 KB (0.00%)
schema-toFormatter.ts 18.87 KB 18.87 KB 0.00 KB (0.00%)
schema-toJsonSchemaDocument.ts 22.60 KB 22.60 KB 0.00 KB (0.00%)
schema-toRepresentation.ts 19.52 KB 19.52 KB 0.00 KB (0.00%)
schema.ts 18.41 KB 18.41 KB 0.00 KB (0.00%)
stm.ts 12.54 KB 12.54 KB 0.00 KB (0.00%)
stream.ts 9.79 KB 9.79 KB 0.00 KB (0.00%)

…assistant message

A history built via Prompt.fromResponseParts containing a provider-executed
web_search call/result pair serializes to server_tool_use and
web_search_tool_result blocks in the assistant turn, with no client
tool_result block emitted.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

@pullfrog pullfrog Bot left a comment

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.

✅ No new issues found.

Reviewed changes

  • Added Anthropic serialization test for provider-executed web_search parts — validates that Prompt.fromResponseParts-routed provider-executed tool results serialize to server_tool_use / web_search_tool_result blocks in the assistant message (not tool_result blocks), end-to-end through LanguageModel.generateText

Pullfrog  | View workflow run | Using DeepSeek Pro (free via Pullfrog for OSS) (Kimi K2 not used — the program covers this model; add its provider key to run your pick) | 𝕏

Comment thread .changeset/provider-executed-tool-results.md Outdated
@IMax153
IMax153 merged commit 82a3fbf into Effect-TS:main Aug 4, 2026
16 checks passed
@github-project-automation github-project-automation Bot moved this from Discussion Ongoing to Done in PR Backlog Aug 4, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

4.0 bug Something isn't working

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

2 participants