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
3 changes: 3 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -137,12 +137,15 @@ export type {
StepResult,
StopCondition,
StopWhen,
ToModelOutputFunction,
ToModelOutputResult,
Tool,
ToolApprovalCheck,
ToolCallOutputEvent,
ToolExecutionResult,
ToolExecutionResultUnion,
ToolHasApproval,
ToolOutputContentItem,
ToolPreliminaryResultEvent,
ToolResultEvent,
ToolStreamEvent,
Expand Down
26 changes: 21 additions & 5 deletions src/lib/model-result.ts
Original file line number Diff line number Diff line change
Expand Up @@ -841,15 +841,31 @@ export class ModelResult<
value.preliminaryResultsForCall.length > 0 ? value.preliminaryResultsForCall : undefined,
);

let outputForModel: string | models.FunctionCallOutputItemOutputUnion1[];

if (value.result.error) {
outputForModel = JSON.stringify({
error: value.result.error.message,
});
} else if (value.tool.function.toModelOutput) {
// toModelOutput exists - call it (may throw, which surfaces the error)
const modelOutputResult = await value.tool.function.toModelOutput({
output: value.result.result,
input: value.toolCall.arguments,
});
outputForModel =
modelOutputResult.type === 'content'
? modelOutputResult.value
: JSON.stringify(value.result.result);
} else {
outputForModel = JSON.stringify(value.result.result);
}

const executedOutput: models.FunctionCallOutputItem = {
type: 'function_call_output' as const,
id: `output_${value.toolCall.id}`,
callId: value.toolCall.id,
output: value.result.error
? JSON.stringify({
error: value.result.error.message,
})
: JSON.stringify(value.result.result),
output: outputForModel,
};
toolResults.push(executedOutput);
this.turnBroadcaster?.push({
Expand Down
44 changes: 44 additions & 0 deletions src/lib/tool-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,46 @@ export type ToolApprovalCheck<TInput> = (
context: TurnContext,
) => boolean | Promise<boolean>;

/**
* Content item types for tool output to model.
* These match the Responses API format for multimodal tool outputs.
*/
export type ToolOutputContentItem =
| {
type: 'input_text';
text: string;
}
| {
type: 'input_image';
detail: 'auto' | 'low' | 'high';
imageUrl: string;
}
| {
type: 'input_file';
fileId: string;
filename?: string;
};

/**
* Result of toModelOutput function.
* The 'content' type passes value array directly as tool output.
*/
export type ToModelOutputResult = {
type: 'content';
value: ToolOutputContentItem[];
};

/**
* Function to convert tool execution output to model-facing output.
* Receives the execute result and input arguments for full context.
* @template TInput - The tool's input type
* @template TOutput - The tool's output type
*/
export type ToModelOutputFunction<TInput, TOutput> = (params: {
output: TOutput;
input: TInput;
}) => ToModelOutputResult | Promise<ToModelOutputResult>;

/**
* Base tool function interface with inputSchema
* @template TInput - Zod schema for tool input
Expand Down Expand Up @@ -185,6 +225,8 @@ export interface ToolFunctionWithExecute<
params: zodInfer<TInput>,
context?: ToolExecuteContext<TName, TContext>,
) => Promise<zodInfer<TOutput>> | zodInfer<TOutput>;
/** Convert tool execution output to model-facing output */
toModelOutput?: ToModelOutputFunction<zodInfer<TInput>, zodInfer<TOutput>>;
}

/**
Expand Down Expand Up @@ -222,6 +264,8 @@ export interface ToolFunctionWithGenerator<
params: zodInfer<TInput>,
context?: ToolExecuteContext<TName, TContext>,
) => AsyncGenerator<zodInfer<TEvent> | zodInfer<TOutput>, zodInfer<TOutput> | undefined>;
/** Convert tool execution output to model-facing output */
toModelOutput?: ToModelOutputFunction<zodInfer<TInput>, zodInfer<TOutput>>;
}

/**
Expand Down
17 changes: 17 additions & 0 deletions src/lib/tool.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import type { $ZodObject, $ZodShape, $ZodType, infer as zodInfer } from 'zod/v4/
import type {
ManualTool,
NextTurnParamsFunctions,
ToModelOutputFunction,
Tool,
ToolApprovalCheck,
ToolExecuteContext,
Expand Down Expand Up @@ -34,6 +35,8 @@ type RegularToolConfigWithOutput<
params: zodInfer<TInput>,
context?: ToolExecuteContext<TName, TContext>,
) => Promise<zodInfer<TOutput>> | zodInfer<TOutput>;
/** Convert tool execution output to model-facing output */
toModelOutput?: ToModelOutputFunction<zodInfer<TInput>, zodInfer<TOutput>>;
};

/**
Expand All @@ -58,6 +61,8 @@ type RegularToolConfigWithoutOutput<
params: zodInfer<TInput>,
context?: ToolExecuteContext<TName, TContext>,
) => Promise<TReturn> | TReturn;
/** Convert tool execution output to model-facing output */
toModelOutput?: ToModelOutputFunction<zodInfer<TInput>, TReturn>;
};

/**
Expand All @@ -83,6 +88,8 @@ type GeneratorToolConfig<
params: zodInfer<TInput>,
context?: ToolExecuteContext<TName, TContext>,
) => AsyncGenerator<zodInfer<TEvent> | zodInfer<TOutput>>;
/** Convert tool execution output to model-facing output */
toModelOutput?: ToModelOutputFunction<zodInfer<TInput>, zodInfer<TOutput>>;
};

/**
Expand Down Expand Up @@ -122,6 +129,8 @@ type ToolConfigWithSharedContext<TShared extends Record<string, unknown>> = {
context?: ToolExecuteContext<string, Record<string, unknown>, TShared>,
) => AsyncGenerator<unknown>)
| false;
/** Convert tool execution output to model-facing output */
toModelOutput?: ToModelOutputFunction<Record<string, unknown>, unknown>;
};

//#endregion
Expand Down Expand Up @@ -285,6 +294,10 @@ export function tool(
fn.requireApproval = config.requireApproval;
}

if ('toModelOutput' in config && config.toModelOutput !== undefined) {
fn.toModelOutput = config.toModelOutput;
}

return {
type: ToolType.Function,
function: fn,
Expand All @@ -311,6 +324,10 @@ export function tool(
...(config.requireApproval !== undefined && {
requireApproval: config.requireApproval,
}),
...('toModelOutput' in config &&
config.toModelOutput !== undefined && {
toModelOutput: config.toModelOutput,
}),
};

return {
Expand Down
Loading
Loading