Skip to content

Commit 6c2154f

Browse files
committed
feat(api): add completePrompt abort signal support and tests for qwen-code, lmstudio-native
1 parent df81cf1 commit 6c2154f

3 files changed

Lines changed: 117 additions & 2 deletions

File tree

src/api/providers/__tests__/lmstudio-native-tools.spec.ts

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -376,4 +376,55 @@ describe("LmStudioHandler Native Tools", () => {
376376
expect(endChunks).toHaveLength(1)
377377
})
378378
})
379+
380+
describe("completePrompt", () => {
381+
it("should complete prompt successfully", async () => {
382+
mockCreate.mockResolvedValueOnce({
383+
choices: [{ message: { content: "This is a test response" } }],
384+
})
385+
386+
const result = await handler.completePrompt("test prompt")
387+
expect(result).toBe("This is a test response")
388+
})
389+
390+
it("should handle errors in completePrompt", async () => {
391+
mockCreate.mockRejectedValueOnce(new Error("API error"))
392+
393+
await expect(handler.completePrompt("test prompt")).rejects.toThrow()
394+
})
395+
396+
it("completePrompt should pass abort signal through to client", async () => {
397+
const controller = new AbortController()
398+
mockCreate.mockResolvedValueOnce({
399+
choices: [{ message: { content: "response" } }],
400+
})
401+
402+
await handler.completePrompt("test prompt", { abortSignal: controller.signal })
403+
expect(mockCreate).toHaveBeenCalledWith(
404+
expect.objectContaining({ model: expect.any(String) }),
405+
expect.objectContaining({ signal: controller.signal }),
406+
)
407+
})
408+
409+
it("completePrompt should pass timeout through to client", async () => {
410+
mockCreate.mockResolvedValueOnce({
411+
choices: [{ message: { content: "response" } }],
412+
})
413+
414+
await handler.completePrompt("test prompt", { timeoutMs: 5000 })
415+
expect(mockCreate).toHaveBeenCalledWith(
416+
expect.objectContaining({ model: expect.any(String) }),
417+
expect.objectContaining({ timeout: 5000 }),
418+
)
419+
})
420+
421+
it("completePrompt should work without options (backward compatible)", async () => {
422+
mockCreate.mockResolvedValueOnce({
423+
choices: [{ message: { content: "response" } }],
424+
})
425+
426+
const result = await handler.completePrompt("test prompt")
427+
expect(result).toBe("response")
428+
})
429+
})
379430
})

src/api/providers/__tests__/qwen-code-native-tools.spec.ts

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -444,4 +444,56 @@ describe("QwenCodeHandler Native Tools", () => {
444444
expect(endChunks).toHaveLength(1)
445445
})
446446
})
447+
448+
describe("completePrompt", () => {
449+
it("should complete prompt successfully", async () => {
450+
mockCreate.mockResolvedValueOnce({
451+
choices: [{ message: { content: "This is a test response" } }],
452+
})
453+
454+
const result = await handler.completePrompt("test prompt")
455+
expect(result).toBe("This is a test response")
456+
})
457+
458+
it("should handle errors in completePrompt", async () => {
459+
const errorMessage = "Qwen API error"
460+
mockCreate.mockRejectedValueOnce(new Error(errorMessage))
461+
462+
await expect(handler.completePrompt("test prompt")).rejects.toThrow(errorMessage)
463+
})
464+
465+
it("completePrompt should pass abort signal through to client", async () => {
466+
const controller = new AbortController()
467+
mockCreate.mockResolvedValueOnce({
468+
choices: [{ message: { content: "response" } }],
469+
})
470+
471+
await handler.completePrompt("test prompt", { abortSignal: controller.signal })
472+
expect(mockCreate).toHaveBeenCalledWith(
473+
expect.objectContaining({ model: expect.any(String) }),
474+
expect.objectContaining({ signal: controller.signal }),
475+
)
476+
})
477+
478+
it("completePrompt should pass timeout through to client", async () => {
479+
mockCreate.mockResolvedValueOnce({
480+
choices: [{ message: { content: "response" } }],
481+
})
482+
483+
await handler.completePrompt("test prompt", { timeoutMs: 5000 })
484+
expect(mockCreate).toHaveBeenCalledWith(
485+
expect.objectContaining({ model: expect.any(String) }),
486+
expect.objectContaining({ timeout: 5000 }),
487+
)
488+
})
489+
490+
it("completePrompt should work without options (backward compatible)", async () => {
491+
mockCreate.mockResolvedValueOnce({
492+
choices: [{ message: { content: "response" } }],
493+
})
494+
495+
const result = await handler.completePrompt("test prompt")
496+
expect(result).toBe("response")
497+
})
498+
})
447499
})

src/api/providers/qwen-code.ts

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -327,7 +327,7 @@ export class QwenCodeHandler extends BaseProvider implements SingleCompletionHan
327327
return { id, info }
328328
}
329329

330-
async completePrompt(prompt: string): Promise<string> {
330+
async completePrompt(prompt: string, options?: import("../index").CompletePromptOptions): Promise<string> {
331331
await this.ensureAuthenticated()
332332
const client = this.ensureClient()
333333
const model = this.getModel()
@@ -338,7 +338,19 @@ export class QwenCodeHandler extends BaseProvider implements SingleCompletionHan
338338
max_completion_tokens: model.info.maxTokens,
339339
}
340340

341-
const response = await this.callApiWithRetry(() => client.chat.completions.create(requestOptions))
341+
const fetchOptions: Record<string, unknown> = {}
342+
343+
if (options?.abortSignal) {
344+
fetchOptions.signal = options.abortSignal
345+
}
346+
347+
if (options?.timeoutMs) {
348+
fetchOptions.timeout = options.timeoutMs
349+
}
350+
351+
const response = await this.callApiWithRetry(() =>
352+
client.chat.completions.create(requestOptions, fetchOptions as any),
353+
)
342354

343355
return response.choices[0]?.message.content || ""
344356
}

0 commit comments

Comments
 (0)