Description
Request
Please let @ai-sdk/openai serialize an OpenAI Responses additional_tools item at an exact point in ModelMessage history, including immediately after a normal tool-result.
This is the concrete application case behind draft PR #17097. The proposed providerOptions.openai.additionalTools direction would work for eve if it can be attached to a tool-result part and produces a sibling additional_tools input item after the normal function_call_output.
eve does not need a provider-neutral abstraction for this. An OpenAI-specific option on the existing tool-result part is sufficient.
This request is narrower than adding tool search. AI SDK already supports OpenAI's hosted and client-executed tool_search. The missing operation is preserving a normal tool result while loading the tools discovered by that result:
R = the ordinary connection_search result
T = the function definitions discovered by that search
input = [
...history,
function_call("connection_search"),
function_call_output(R),
additional_tools(T),
]
OpenAI makes tools available only after the additional_tools item's position and requires that position to be preserved when history is replayed. OpenAI's tool-search documentation also states that injecting newly discovered tools at the end of the context preserves the model's cache.
Why eve needs both R and T
eve exposes connection_search as a normal function tool. Its available connection tools depend on runtime state such as the current tenant, installed connections, authorization, and the external tool registry.
The result is richer than a list of OpenAI function definitions:
type ConnectionSearchResult = Array<{
connection: string;
description: string;
tool?: string;
qualifiedName?: string;
inputSchema?: object;
outputSchema?: object;
needsAuthorization?: boolean;
error?: string;
}>;
That result tells the model which tools matched, but it also preserves connection summaries, partial failures, and authorization state. eve must send it back as the ordinary connection_search result.
For each successful match, eve also creates a normal AI SDK tool with its input schema and a local execute callback. For example, a result for Linear may create linear__list_issues. On the next model step, that function must be available to OpenAI as a structured tool, validated by AI SDK, and executed by eve when called.
The desired runtime path is:
- The request starts with a stable tool list that includes
connection_search, but not every possible connection tool schema.
- The model calls
connection_search.
- eve performs the external lookup and returns
R.
- eve builds the discovered AI SDK tools and their local executors.
- The next request replays
function_call_output(R) and immediately appends additional_tools(T).
- The model calls a discovered function such as
linear__list_issues.
- AI SDK validates the call against the full
tools map, then eve's local callback executes it.
- On later requests, the same
additional_tools item remains at the same history position.
Why client-executed tool_search is not equivalent
openai.tools.toolSearch({ execution: "client" }) is a good fit when the discovery result is the tool list itself. Its current output contract is only { tools: Array<JSONObject> }, and the OpenAI adapter serializes that result as tool_search_output:
Replacing eve's normal connection_search result with that output would discard the summaries, errors, and authorization state in R. Returning R in a separate text message would change the protocol and would no longer preserve the original tool result as one ordered history item.
Adding newly discovered tools to the request-level tools array on every later step is also not equivalent. It changes the early request prefix as the tool set grows. OpenAI's additional_tools guidance exists for applications that load tools outside the normal tool-search flow and need to preserve insertion order. OpenAI's prompt-caching guidance recommends appending runtime changes instead of modifying the existing prefix.
How eve would consume this
An illustrative AI SDK call would look like this. The exact option name is not important, but the placement and serialization are:
const tools = {
connection_search: connectionSearchTool,
linear__list_issues: tool({
description: "List Linear issues",
inputSchema: linearListIssuesSchema,
execute: executeLinearListIssues,
}),
};
await streamText({
model: openai.responses("gpt-5.4"),
// Keep discovered tools in the full ToolSet so AI SDK can validate and
// execute their calls, but do not serialize them in the top-level tools list.
tools,
activeTools: ["connection_search"],
messages: [
...history,
{
role: "tool",
content: [
{
type: "tool-result",
toolCallId: searchCallId,
toolName: "connection_search",
output: { type: "json", value: connectionSearchResult },
providerOptions: {
openai: {
additionalTools: [linearListIssuesOpenAIDefinition],
},
},
},
],
},
],
});
AI SDK already separates these two concerns: activeTools determines the tools prepared for the provider call, while tool-call parsing and local execution use the full tools map. See the current generateText provider preparation, parseToolCall invocation, and executeTools invocation. That gives eve a way to keep validation and execution without duplicating the discovered definitions in the request-level tools list.
The OpenAI adapter would serialize the tool-result part above as:
[
{
"type": "function_call_output",
"call_id": "call_connection_search",
"output": "[...the complete connection_search result...]"
},
{
"type": "additional_tools",
"role": "developer",
"tools": [
{
"type": "function",
"name": "linear__list_issues",
"description": "List Linear issues",
"parameters": { "type": "object" }
}
]
}
]
Acceptance criteria
- A tool-result part can request an OpenAI
additional_tools item without replacing or changing its normal function_call_output.
- The adapter emits
additional_tools immediately after that result and preserves the same order when messages are replayed.
- The loaded functions may remain in the full AI SDK
tools map with local execute callbacks while activeTools omits them from the request-level tools list.
- A subsequent function call to one of those tools passes AI SDK validation and invokes its local executor.
- The behavior works in both
generateText and streamText.
- Direct
@ai-sdk/openai support is the immediate need. The same message representation should eventually work through AI Gateway when it routes to OpenAI, or the Gateway limitation should be documented separately.
Related eve work:
AI SDK Version
Code of Conduct
Description
Request
Please let
@ai-sdk/openaiserialize an OpenAI Responsesadditional_toolsitem at an exact point inModelMessagehistory, including immediately after a normaltool-result.This is the concrete application case behind draft PR #17097. The proposed
providerOptions.openai.additionalToolsdirection would work for eve if it can be attached to a tool-result part and produces a siblingadditional_toolsinput item after the normalfunction_call_output.eve does not need a provider-neutral abstraction for this. An OpenAI-specific option on the existing tool-result part is sufficient.
This request is narrower than adding tool search. AI SDK already supports OpenAI's hosted and client-executed
tool_search. The missing operation is preserving a normal tool result while loading the tools discovered by that result:OpenAI makes tools available only after the
additional_toolsitem's position and requires that position to be preserved when history is replayed. OpenAI's tool-search documentation also states that injecting newly discovered tools at the end of the context preserves the model's cache.Why eve needs both
RandTeve exposes
connection_searchas a normal function tool. Its available connection tools depend on runtime state such as the current tenant, installed connections, authorization, and the external tool registry.The result is richer than a list of OpenAI function definitions:
That result tells the model which tools matched, but it also preserves connection summaries, partial failures, and authorization state. eve must send it back as the ordinary
connection_searchresult.For each successful match, eve also creates a normal AI SDK tool with its input schema and a local
executecallback. For example, a result for Linear may createlinear__list_issues. On the next model step, that function must be available to OpenAI as a structured tool, validated by AI SDK, and executed by eve when called.The desired runtime path is:
connection_search, but not every possible connection tool schema.connection_search.R.function_call_output(R)and immediately appendsadditional_tools(T).linear__list_issues.toolsmap, then eve's local callback executes it.additional_toolsitem remains at the same history position.Why client-executed
tool_searchis not equivalentopenai.tools.toolSearch({ execution: "client" })is a good fit when the discovery result is the tool list itself. Its current output contract is only{ tools: Array<JSONObject> }, and the OpenAI adapter serializes that result astool_search_output:toolSearchOutputSchematool_search_outputconversionReplacing eve's normal
connection_searchresult with that output would discard the summaries, errors, and authorization state inR. ReturningRin a separate text message would change the protocol and would no longer preserve the original tool result as one ordered history item.Adding newly discovered tools to the request-level
toolsarray on every later step is also not equivalent. It changes the early request prefix as the tool set grows. OpenAI'sadditional_toolsguidance exists for applications that load tools outside the normal tool-search flow and need to preserve insertion order. OpenAI's prompt-caching guidance recommends appending runtime changes instead of modifying the existing prefix.How eve would consume this
An illustrative AI SDK call would look like this. The exact option name is not important, but the placement and serialization are:
AI SDK already separates these two concerns:
activeToolsdetermines the tools prepared for the provider call, while tool-call parsing and local execution use the fulltoolsmap. See the currentgenerateTextprovider preparation,parseToolCallinvocation, andexecuteToolsinvocation. That gives eve a way to keep validation and execution without duplicating the discovered definitions in the request-level tools list.The OpenAI adapter would serialize the tool-result part above as:
[ { "type": "function_call_output", "call_id": "call_connection_search", "output": "[...the complete connection_search result...]" }, { "type": "additional_tools", "role": "developer", "tools": [ { "type": "function", "name": "linear__list_issues", "description": "List Linear issues", "parameters": { "type": "object" } } ] } ]Acceptance criteria
additional_toolsitem without replacing or changing its normalfunction_call_output.additional_toolsimmediately after that result and preserves the same order when messages are replayed.toolsmap with localexecutecallbacks whileactiveToolsomits them from the request-level tools list.generateTextandstreamText.@ai-sdk/openaisupport is the immediate need. The same message representation should eventually work through AI Gateway when it routes to OpenAI, or the Gateway limitation should be documented separately.Related eve work:
AI SDK Version
[email protected]@ai-sdk/[email protected][email protected]and@ai-sdk/[email protected]installed; the missing conversion is the same in those versions.Code of Conduct