mirror of
https://github.com/getcompanion-ai/co-mono.git
synced 2026-04-15 09:01:14 +00:00
add option to skip provider tool call validation
This commit is contained in:
parent
2e3ff4a15a
commit
0196308266
12 changed files with 38 additions and 4 deletions
|
|
@ -221,6 +221,7 @@ export class Agent {
|
|||
tools: this._state.tools,
|
||||
model,
|
||||
reasoning,
|
||||
validateToolCallsAtProvider: false,
|
||||
getQueuedMessages: async <T>() => {
|
||||
// Return queued messages based on queue mode
|
||||
if (this.queueMode === "one-at-a-time") {
|
||||
|
|
|
|||
|
|
@ -77,6 +77,7 @@ function streamSimpleProxy(
|
|||
temperature: options.temperature,
|
||||
maxTokens: options.maxTokens,
|
||||
reasoning: options.reasoning,
|
||||
validateToolCallsAtProvider: options.validateToolCallsAtProvider,
|
||||
// Don't send apiKey or signal - those are added server-side
|
||||
},
|
||||
}),
|
||||
|
|
@ -365,6 +366,7 @@ export class AppTransport implements AgentTransport {
|
|||
model: cfg.model,
|
||||
reasoning: cfg.reasoning,
|
||||
getQueuedMessages: cfg.getQueuedMessages,
|
||||
validateToolCallsAtProvider: cfg.validateToolCallsAtProvider ?? false,
|
||||
};
|
||||
|
||||
// Yield events from the upstream agentLoop iterator
|
||||
|
|
|
|||
|
|
@ -65,6 +65,7 @@ export class ProviderTransport implements AgentTransport {
|
|||
reasoning: cfg.reasoning,
|
||||
apiKey,
|
||||
getQueuedMessages: cfg.getQueuedMessages,
|
||||
validateToolCallsAtProvider: cfg.validateToolCallsAtProvider ?? false,
|
||||
};
|
||||
|
||||
// Yield events from agentLoop
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ export interface AgentRunConfig {
|
|||
model: Model<any>;
|
||||
reasoning?: "low" | "medium" | "high";
|
||||
getQueuedMessages?: <T>() => Promise<QueuedMessage<T>[]>;
|
||||
validateToolCallsAtProvider?: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -2,6 +2,10 @@
|
|||
|
||||
## [Unreleased]
|
||||
|
||||
### Added
|
||||
|
||||
- Added `validateToolCallsAtProvider` option to streaming and agent APIs to optionally skip provider-level tool-call validation (default on), allowing agent loops to surface schema errors as toolResult messages and retry.
|
||||
|
||||
## [0.13.0] - 2025-12-06
|
||||
|
||||
### Breaking Changes
|
||||
|
|
|
|||
|
|
@ -270,6 +270,20 @@ for await (const event of s) {
|
|||
- Full validation only occurs at `toolcall_end` when arguments are complete
|
||||
- The Google provider does not support function call streaming. Instead, you will receive a single `toolcall_delta` event with the full arguments.
|
||||
|
||||
### Provider tool-call validation
|
||||
|
||||
By default, providers validate streamed tool calls against your tool schema and abort the stream on validation errors. Set `validateToolCallsAtProvider: false` on `stream`, `streamSimple`, `complete`, `completeSimple`, or `AgentLoopConfig` to skip provider-level validation and let downstream code (for example, `agentLoop` via `executeToolCalls` → `validateToolArguments`) surface schema errors as `toolResult` messages. This enables the model to retry after receiving a validation error.
|
||||
|
||||
```typescript
|
||||
await streamSimple(model, context, {
|
||||
apiKey: 'your-key',
|
||||
validateToolCallsAtProvider: false
|
||||
});
|
||||
```
|
||||
|
||||
- `true` (default): Provider validates tool calls and emits an error if arguments do not match the schema
|
||||
- `false`: Provider emits tool calls even when arguments are invalid; callers must validate and handle errors themselves
|
||||
|
||||
### Complete Event Reference
|
||||
|
||||
All streaming events emitted during assistant message generation:
|
||||
|
|
|
|||
|
|
@ -92,6 +92,7 @@ export const streamAnthropic: StreamFunction<"anthropic-messages"> = (
|
|||
options?: AnthropicOptions,
|
||||
): AssistantMessageEventStream => {
|
||||
const stream = new AssistantMessageEventStream();
|
||||
const shouldValidateToolCalls = options?.validateToolCallsAtProvider !== false;
|
||||
|
||||
(async () => {
|
||||
const output: AssistantMessage = {
|
||||
|
|
@ -233,7 +234,7 @@ export const streamAnthropic: StreamFunction<"anthropic-messages"> = (
|
|||
block.arguments = parseStreamingJson(block.partialJson);
|
||||
|
||||
// Validate tool arguments if tool definition is available
|
||||
if (context.tools) {
|
||||
if (shouldValidateToolCalls && context.tools) {
|
||||
const tool = context.tools.find((t) => t.name === block.name);
|
||||
if (tool) {
|
||||
block.arguments = validateToolArguments(tool, block);
|
||||
|
|
|
|||
|
|
@ -43,6 +43,7 @@ export const streamGoogle: StreamFunction<"google-generative-ai"> = (
|
|||
options?: GoogleOptions,
|
||||
): AssistantMessageEventStream => {
|
||||
const stream = new AssistantMessageEventStream();
|
||||
const shouldValidateToolCalls = options?.validateToolCallsAtProvider !== false;
|
||||
|
||||
(async () => {
|
||||
const output: AssistantMessage = {
|
||||
|
|
@ -167,7 +168,7 @@ export const streamGoogle: StreamFunction<"google-generative-ai"> = (
|
|||
};
|
||||
|
||||
// Validate tool arguments if tool definition is available
|
||||
if (context.tools) {
|
||||
if (shouldValidateToolCalls && context.tools) {
|
||||
const tool = context.tools.find((t) => t.name === toolCall.name);
|
||||
if (tool) {
|
||||
toolCall.arguments = validateToolArguments(tool, toolCall);
|
||||
|
|
|
|||
|
|
@ -37,6 +37,7 @@ export const streamOpenAICompletions: StreamFunction<"openai-completions"> = (
|
|||
options?: OpenAICompletionsOptions,
|
||||
): AssistantMessageEventStream => {
|
||||
const stream = new AssistantMessageEventStream();
|
||||
const shouldValidateToolCalls = options?.validateToolCallsAtProvider !== false;
|
||||
|
||||
(async () => {
|
||||
const output: AssistantMessage = {
|
||||
|
|
@ -86,7 +87,7 @@ export const streamOpenAICompletions: StreamFunction<"openai-completions"> = (
|
|||
block.arguments = JSON.parse(block.partialArgs || "{}");
|
||||
|
||||
// Validate tool arguments if tool definition is available
|
||||
if (context.tools) {
|
||||
if (shouldValidateToolCalls && context.tools) {
|
||||
const tool = context.tools.find((t) => t.name === block.name);
|
||||
if (tool) {
|
||||
block.arguments = validateToolArguments(tool, block);
|
||||
|
|
|
|||
|
|
@ -45,6 +45,7 @@ export const streamOpenAIResponses: StreamFunction<"openai-responses"> = (
|
|||
options?: OpenAIResponsesOptions,
|
||||
): AssistantMessageEventStream => {
|
||||
const stream = new AssistantMessageEventStream();
|
||||
const shouldValidateToolCalls = options?.validateToolCallsAtProvider !== false;
|
||||
|
||||
// Start async processing
|
||||
(async () => {
|
||||
|
|
@ -240,7 +241,7 @@ export const streamOpenAIResponses: StreamFunction<"openai-responses"> = (
|
|||
};
|
||||
|
||||
// Validate tool arguments if tool definition is available
|
||||
if (context.tools) {
|
||||
if (shouldValidateToolCalls && context.tools) {
|
||||
const tool = context.tools.find((t) => t.name === toolCall.name);
|
||||
if (tool) {
|
||||
toolCall.arguments = validateToolArguments(tool, toolCall);
|
||||
|
|
|
|||
|
|
@ -120,6 +120,7 @@ function mapOptionsForApi<TApi extends Api>(
|
|||
maxTokens: options?.maxTokens || Math.min(model.maxTokens, 32000),
|
||||
signal: options?.signal,
|
||||
apiKey: apiKey || options?.apiKey,
|
||||
validateToolCallsAtProvider: options?.validateToolCallsAtProvider ?? true,
|
||||
};
|
||||
|
||||
switch (model.api) {
|
||||
|
|
|
|||
|
|
@ -37,6 +37,12 @@ export interface StreamOptions {
|
|||
maxTokens?: number;
|
||||
signal?: AbortSignal;
|
||||
apiKey?: string;
|
||||
/**
|
||||
* Controls whether providers validate streamed tool calls against the tool schema.
|
||||
* Defaults to true. Set to false to skip provider-level validation and allow
|
||||
* downstream consumers (e.g., agentLoop) to handle validation failures.
|
||||
*/
|
||||
validateToolCallsAtProvider?: boolean;
|
||||
}
|
||||
|
||||
// Unified options with reasoning passed to streamSimple() and completeSimple()
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue