Skip to content
Closed
Show file tree
Hide file tree
Changes from 5 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
6 changes: 3 additions & 3 deletions src/api/providers/__tests__/deepseek.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -494,7 +494,7 @@ describe("DeepSeekHandler", () => {
expect.objectContaining({
thinking: { type: "enabled" },
}),
{}, // Empty path options for non-Azure URLs
undefined, // No signal, non-Azure URL
)
const callArgs = mockCreate.mock.calls[0][0]
expect(callArgs.reasoning_effort).toBeUndefined()
Expand All @@ -517,7 +517,7 @@ describe("DeepSeekHandler", () => {
reasoning_effort: "high",
max_completion_tokens: 200_000,
}),
{},
undefined,
)
})

Expand Down Expand Up @@ -554,7 +554,7 @@ describe("DeepSeekHandler", () => {
thinking: { type: "enabled" },
reasoning_effort: "max",
}),
{},
undefined,
)
})

Expand Down
15 changes: 9 additions & 6 deletions src/api/providers/__tests__/lmstudio.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -133,12 +133,15 @@ describe("LmStudioHandler", () => {
it("should complete prompt successfully", async () => {
const result = await handler.completePrompt("Test prompt")
expect(result).toBe("Test response")
expect(mockCreate).toHaveBeenCalledWith({
model: mockOptions.lmStudioModelId,
messages: [{ role: "user", content: "Test prompt" }],
temperature: 0,
stream: false,
})
expect(mockCreate).toHaveBeenCalledWith(
{
model: mockOptions.lmStudioModelId,
messages: [{ role: "user", content: "Test prompt" }],
temperature: 0,
stream: false,
},
undefined,
)
})

it("should handle API errors", async () => {
Expand Down
1 change: 1 addition & 0 deletions src/api/providers/__tests__/mimo.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -376,6 +376,7 @@ describe("MimoHandler", () => {
expect.objectContaining({
extra_body: { thinking: { type: "enabled" } },
}),
undefined,
)
})

Expand Down
2 changes: 2 additions & 0 deletions src/api/providers/__tests__/minimax.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -305,6 +305,7 @@ describe("MiniMaxHandler", () => {
messages: expect.any(Array),
stream: true,
}),
undefined,
)
})

Expand All @@ -324,6 +325,7 @@ describe("MiniMaxHandler", () => {
expect.objectContaining({
temperature: 1,
}),
undefined,
)
})

Expand Down
33 changes: 21 additions & 12 deletions src/api/providers/__tests__/requesty.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,7 @@ describe("RequestyHandler", () => {
stream_options: { include_usage: true },
temperature: 0,
}),
undefined,
)
})

Expand Down Expand Up @@ -243,6 +244,7 @@ describe("RequestyHandler", () => {
thinking: { type: "adaptive" },
temperature: undefined,
}),
undefined,
)
})

Expand Down Expand Up @@ -314,6 +316,7 @@ describe("RequestyHandler", () => {
]),
tool_choice: "auto",
}),
undefined,
)
})

Expand Down Expand Up @@ -412,12 +415,15 @@ describe("RequestyHandler", () => {

expect(result).toBe("test completion")

expect(mockCreate).toHaveBeenCalledWith({
model: mockOptions.requestyModelId,
max_tokens: 8192,
messages: [{ role: "system", content: "test prompt" }],
temperature: 0,
})
expect(mockCreate).toHaveBeenCalledWith(
{
model: mockOptions.requestyModelId,
max_tokens: 8192,
messages: [{ role: "system", content: "test prompt" }],
temperature: 0,
},
undefined,
)
})

it("omits temperature for Claude Fable 5 in completePrompt", async () => {
Expand All @@ -429,12 +435,15 @@ describe("RequestyHandler", () => {

await handler.completePrompt("test prompt")

expect(mockCreate).toHaveBeenCalledWith({
model: "anthropic/claude-fable-5",
max_tokens: 8192,
messages: [{ role: "system", content: "test prompt" }],
temperature: undefined,
})
expect(mockCreate).toHaveBeenCalledWith(
{
model: "anthropic/claude-fable-5",
max_tokens: 8192,
messages: [{ role: "system", content: "test prompt" }],
temperature: undefined,
},
undefined,
)
})

it("handles API errors", async () => {
Expand Down
1 change: 1 addition & 0 deletions src/api/providers/__tests__/unbound.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ describe("UnboundHandler", () => {
mode: "architect",
},
}),
undefined,
)
})
})
12 changes: 12 additions & 0 deletions src/api/providers/__tests__/vercel-ai-gateway.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -263,6 +263,7 @@ describe("VercelAiGatewayHandler", () => {
expect.objectContaining({
temperature: customTemp,
}),
undefined,
)
})

Expand All @@ -278,6 +279,7 @@ describe("VercelAiGatewayHandler", () => {
expect.objectContaining({
temperature: VERCEL_AI_GATEWAY_DEFAULT_TEMPERATURE,
}),
undefined,
)
})

Expand All @@ -295,6 +297,7 @@ describe("VercelAiGatewayHandler", () => {
temperature: undefined,
max_completion_tokens: 128000,
}),
undefined,
)
})

Expand Down Expand Up @@ -325,6 +328,7 @@ describe("VercelAiGatewayHandler", () => {
expect.objectContaining({
max_completion_tokens: 64000, // max tokens for sonnet 4
}),
undefined,
)
})

Expand Down Expand Up @@ -403,6 +407,7 @@ describe("VercelAiGatewayHandler", () => {
}),
]),
}),
undefined,
)
})

Expand All @@ -420,6 +425,7 @@ describe("VercelAiGatewayHandler", () => {
expect.objectContaining({
tool_choice: "auto",
}),
undefined,
)
})

Expand All @@ -437,6 +443,7 @@ describe("VercelAiGatewayHandler", () => {
expect.objectContaining({
parallel_tool_calls: true,
}),
undefined,
)
})

Expand All @@ -454,6 +461,7 @@ describe("VercelAiGatewayHandler", () => {
tools: expect.any(Array),
parallel_tool_calls: true,
}),
undefined,
)
})

Expand Down Expand Up @@ -553,6 +561,7 @@ describe("VercelAiGatewayHandler", () => {
expect.objectContaining({
stream_options: { include_usage: true },
}),
undefined,
)
})
})
Expand Down Expand Up @@ -591,6 +600,7 @@ describe("VercelAiGatewayHandler", () => {
temperature: VERCEL_AI_GATEWAY_DEFAULT_TEMPERATURE,
max_completion_tokens: 64000,
}),
undefined,
)
})

Expand All @@ -607,6 +617,7 @@ describe("VercelAiGatewayHandler", () => {
expect.objectContaining({
temperature: customTemp,
}),
undefined,
)
})

Expand Down Expand Up @@ -655,6 +666,7 @@ describe("VercelAiGatewayHandler", () => {
expect.objectContaining({
temperature: 0.9,
}),
undefined,
)
})
})
Expand Down
7 changes: 7 additions & 0 deletions src/api/providers/__tests__/zai.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -502,6 +502,7 @@ describe("ZAiHandler", () => {
model: "glm-5.1",
max_tokens: 40_000,
}),
undefined,
)
})

Expand Down Expand Up @@ -542,6 +543,7 @@ describe("ZAiHandler", () => {
model: "glm-5.1",
max_tokens: 100_000,
}),
undefined,
)
})

Expand Down Expand Up @@ -572,6 +574,7 @@ describe("ZAiHandler", () => {
model: "glm-4.7",
thinking: { type: "enabled" },
}),
undefined,
)
})

Expand Down Expand Up @@ -603,6 +606,7 @@ describe("ZAiHandler", () => {
model: "glm-4.7",
thinking: { type: "disabled" },
}),
undefined,
)
})

Expand Down Expand Up @@ -634,6 +638,7 @@ describe("ZAiHandler", () => {
model: "glm-4.7",
thinking: { type: "enabled" },
}),
undefined,
)
})

Expand Down Expand Up @@ -687,6 +692,7 @@ describe("ZAiHandler", () => {
model: "glm-5-turbo",
thinking: { type: "enabled" },
}),
undefined,
)
})

Expand Down Expand Up @@ -717,6 +723,7 @@ describe("ZAiHandler", () => {
model: "glm-5-turbo",
thinking: { type: "disabled" },
}),
undefined,
)
})
})
Expand Down
6 changes: 5 additions & 1 deletion src/api/providers/deepseek.ts
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,11 @@ export class DeepSeekHandler extends OpenAiHandler {
try {
stream = await this.client.chat.completions.create(
requestOptions as OpenAI.Chat.Completions.ChatCompletionCreateParamsStreaming,
isAzureAiInference ? { path: OPENAI_AZURE_AI_INFERENCE_PATH } : {},
metadata?.abortSignal
? isAzureAiInference
? { path: OPENAI_AZURE_AI_INFERENCE_PATH, signal: metadata.abortSignal }
: { signal: metadata.abortSignal }
: undefined,
Comment on lines +136 to +140

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.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Preserve Azure path even when no abort signal is provided.

Line 136 currently ties Azure routing and cancellation together; when metadata?.abortSignal is missing, Azure requests lose path: OPENAI_AZURE_AI_INFERENCE_PATH and fall back to undefined options. That changes request routing for non-cancelled Azure calls.

Suggested fix
 			stream = await this.client.chat.completions.create(
 				requestOptions as OpenAI.Chat.Completions.ChatCompletionCreateParamsStreaming,
-				metadata?.abortSignal
-					? isAzureAiInference
-						? { path: OPENAI_AZURE_AI_INFERENCE_PATH, signal: metadata.abortSignal }
-						: { signal: metadata.abortSignal }
-					: undefined,
+				isAzureAiInference
+					? {
+							path: OPENAI_AZURE_AI_INFERENCE_PATH,
+							...(metadata?.abortSignal ? { signal: metadata.abortSignal } : {}),
+						}
+					: metadata?.abortSignal
+						? { signal: metadata.abortSignal }
+						: undefined,
 			)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/api/providers/deepseek.ts` around lines 136 - 140, The Azure path routing
is currently coupled with the abort signal check, causing Azure requests without
an abort signal to lose their path information and return undefined options
instead. Decouple these concerns by restructuring the conditional logic to
independently check isAzureAiInference and metadata?.abortSignal. When
isAzureAiInference is true, always include path: OPENAI_AZURE_AI_INFERENCE_PATH
in the options object. Separately, when metadata?.abortSignal exists, include
signal: metadata.abortSignal. Return undefined only when neither condition is
met, ensuring Azure routing is preserved regardless of whether an abort signal
is provided.

)
} catch (error) {
const { handleOpenAIError } = await import("./utils/openai-error-handler")
Expand Down
12 changes: 9 additions & 3 deletions src/api/providers/lm-studio.ts
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,10 @@ export class LmStudioHandler extends BaseProvider implements SingleCompletionHan

let results
try {
results = await this.client.chat.completions.create(params)
results = await this.client.chat.completions.create(
params,
metadata?.abortSignal ? { signal: metadata.abortSignal } : undefined,
)
} catch (error) {
throw handleOpenAIError(error, this.providerName)
}
Expand Down Expand Up @@ -185,7 +188,7 @@ export class LmStudioHandler extends BaseProvider implements SingleCompletionHan
}
}

async completePrompt(prompt: string): Promise<string> {
async completePrompt(prompt: string, metadata?: ApiHandlerCreateMessageMetadata): Promise<string> {
try {
// Create params object with optional draft model
const params: any = {
Expand All @@ -202,7 +205,10 @@ export class LmStudioHandler extends BaseProvider implements SingleCompletionHan

let response
try {
response = await this.client.chat.completions.create(params)
response = await this.client.chat.completions.create(
params,
metadata?.abortSignal ? { signal: metadata.abortSignal } : undefined,
)
} catch (error) {
throw handleOpenAIError(error, this.providerName)
}
Expand Down
5 changes: 4 additions & 1 deletion src/api/providers/mimo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,10 @@ export class MimoHandler extends OpenAiHandler {

let stream: AsyncIterable<OpenAI.Chat.Completions.ChatCompletionChunk>
try {
stream = (await this.client.chat.completions.create(params as any)) as any
stream = (await this.client.chat.completions.create(
params as any,
metadata?.abortSignal ? { signal: metadata.abortSignal } : undefined,
)) as any
} catch (error) {
throw handleProviderError(error, "MiMo")
}
Expand Down
24 changes: 15 additions & 9 deletions src/api/providers/minimax.ts
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,10 @@ export class MiniMaxHandler extends BaseProvider implements SingleCompletionHand
tool_choice: convertOpenAIToolChoice(metadata?.tool_choice),
}

stream = await this.client.messages.create(requestParams)
stream = await this.client.messages.create(
requestParams,
metadata?.abortSignal ? { signal: metadata.abortSignal } : undefined,
)

let inputTokens = 0
let outputTokens = 0
Expand Down Expand Up @@ -289,16 +292,19 @@ export class MiniMaxHandler extends BaseProvider implements SingleCompletionHand
}
}

async completePrompt(prompt: string) {
async completePrompt(prompt: string, metadata?: ApiHandlerCreateMessageMetadata) {
const { id: model, temperature } = this.getModel()

const message = await this.client.messages.create({
model,
max_tokens: 16_384,
temperature: temperature ?? 1.0,
messages: [{ role: "user", content: prompt }],
stream: false,
})
const message = await this.client.messages.create(
{
model,
max_tokens: 16_384,
temperature: temperature ?? 1.0,
messages: [{ role: "user", content: prompt }],
stream: false,
},
metadata?.abortSignal ? { signal: metadata.abortSignal } : undefined,
)

const content = message.content.find(({ type }) => type === "text")
return content?.type === "text" ? content.text : ""
Expand Down
Loading
Loading