Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
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
35 changes: 1 addition & 34 deletions llm/ai-sdk/provider/stripe-language-model-v3.ts
Original file line number Diff line number Diff line change
Expand Up @@ -231,6 +231,7 @@ export class StripeLanguageModelV3 implements LanguageModelV3 {

const messages = convertToOpenAIMessagesV3(options.prompt);

// Tool calling is not supported by the Stripe AI SDK Provider.
if (options.tools && options.tools.length > 0) {
throw new Error(
'Tool calling is not supported by the Stripe AI SDK Provider. ' +
Expand All @@ -239,38 +240,6 @@ export class StripeLanguageModelV3 implements LanguageModelV3 {
);
}

Copy link

Copilot AI Mar 30, 2026

Choose a reason for hiding this comment

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

options.toolChoice is no longer forwarded (and is silently ignored) when options.tools is empty. Given the explicit “tool calling not supported” stance, consider explicitly throwing when toolChoice is provided as well (and adjusting the message), so callers don’t think the setting is taking effect.

Suggested change
if (options.toolChoice !== undefined) {
throw new Error(
'Tool calling is not supported by the Stripe AI SDK Provider. ' +
'The llm.stripe.com API does not currently support function calling or tool use. ' +
'Please remove the toolChoice parameter (and any tools) from your request.'
);
}

Copilot uses AI. Check for mistakes.
const tools =
options.tools && options.tools.length > 0
? options.tools.map((tool) => {
if (tool.type === 'function') {
return {
type: 'function',
function: {
name: tool.name,
description: tool.description,
parameters: tool.inputSchema,
},
};
}
return tool;
})
: undefined;

let toolChoice:
| string
| {type: string; function?: {name: string}}
| undefined;
if (options.toolChoice) {
if (options.toolChoice.type === 'tool') {
toolChoice = {
type: 'function',
function: {name: options.toolChoice.toolName},
};
} else {
toolChoice = options.toolChoice.type;
}
}

const body: Record<string, any> = {
model: this.modelId,
messages,
Expand All @@ -291,8 +260,6 @@ export class StripeLanguageModelV3 implements LanguageModelV3 {
if (options.stopSequences !== undefined)
body.stop = options.stopSequences;
if (options.seed !== undefined) body.seed = options.seed;
if (tools !== undefined) body.tools = tools;
if (toolChoice !== undefined) body.tool_choice = toolChoice;

return {args: body, warnings};
}
Expand Down
50 changes: 9 additions & 41 deletions llm/ai-sdk/provider/stripe-language-model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -261,47 +261,15 @@ export class StripeLanguageModel implements LanguageModelV2 {
// Convert AI SDK prompt to OpenAI-compatible format
const messages = convertToOpenAIMessages(options.prompt);

// Check if tools are provided and throw error (tool calling not supported by Stripe API)
// Tool calling is not supported by the Stripe AI SDK Provider.
if (options.tools && options.tools.length > 0) {
throw new Error(
'Tool calling is not supported by the Stripe AI SDK Provider. ' +
'The llm.stripe.com API does not currently support function calling or tool use. ' +
'Please remove the tools parameter from your request.'
'The llm.stripe.com API does not currently support function calling or tool use. ' +
'Please remove the tools parameter from your request.'
);
}
Comment on lines +264 to 279
Copy link

Copilot AI Mar 30, 2026

Choose a reason for hiding this comment

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

After removing the toolChoice mapping, options.toolChoice is now silently ignored when options.tools is empty. Since tool calling is explicitly unsupported, it would be clearer/safer to also reject options.toolChoice (or at least warn) and update the error message to mention toolChoice as unsupported, to avoid a behavior change that’s hard for callers to notice.

Copilot uses AI. Check for mistakes.

// Prepare tools if provided
const tools =
options.tools && options.tools.length > 0
? options.tools.map((tool) => {
if (tool.type === 'function') {
return {
type: 'function',
function: {
name: tool.name,
description: tool.description,
parameters: tool.inputSchema,
},
};
}
// Provider-defined tools
return tool;
})
: undefined;

// Map tool choice
let toolChoice: string | {type: string; function?: {name: string}} | undefined;
if (options.toolChoice) {
if (options.toolChoice.type === 'tool') {
toolChoice = {
type: 'function',
function: {name: options.toolChoice.toolName},
};
} else {
toolChoice = options.toolChoice.type; // 'auto', 'none', 'required'
}
}

// Build request body, only including defined values
const body: Record<string, any> = {
model: this.modelId,
Expand All @@ -310,18 +278,18 @@ export class StripeLanguageModel implements LanguageModelV2 {

// Add optional parameters only if they're defined
if (options.temperature !== undefined) body.temperature = options.temperature;

// Handle max_tokens with model-specific defaults for Anthropic
const maxTokens = options.maxOutputTokens ?? this.getDefaultMaxTokens(this.modelId);
if (maxTokens !== undefined) body.max_tokens = maxTokens;

if (options.topP !== undefined) body.top_p = options.topP;
if (options.frequencyPenalty !== undefined) body.frequency_penalty = options.frequencyPenalty;
if (options.presencePenalty !== undefined) body.presence_penalty = options.presencePenalty;
if (options.frequencyPenalty !== undefined)
body.frequency_penalty = options.frequencyPenalty;
if (options.presencePenalty !== undefined)
body.presence_penalty = options.presencePenalty;
if (options.stopSequences !== undefined) body.stop = options.stopSequences;
if (options.seed !== undefined) body.seed = options.seed;
if (tools !== undefined) body.tools = tools;
if (toolChoice !== undefined) body.tool_choice = toolChoice;

return {args: body, warnings};
}
Expand Down
8 changes: 7 additions & 1 deletion tools/typescript/src/shared/mcp-client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,13 @@ export class StripeMcpClient {
const result = await this.client.listTools();
this.tools = result.tools as McpTool[];
} catch (error) {
// Clean up on failure
// Close the active connection before clearing references to avoid leaking
// the underlying transport if connect() succeeded but listTools() failed.
try {
if (this.client) await this.client.close();
} catch {
// Ignore close errors during cleanup
}
this.client = null;
this.transport = null;
Comment on lines +116 to 124
Copy link

Copilot AI Mar 30, 2026

Choose a reason for hiding this comment

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

The new cleanup closes this.client, but there’s no test coverage that close() is invoked when connect() succeeds and listTools() throws. Adding a Jest test that mocks listTools to reject and asserts client.close is awaited (and that connect() rejects with the wrapped error) would prevent regressions of the transport leak fix.

Copilot uses AI. Check for mistakes.

Expand Down
Loading