From 85e1319e01b68033c70a72dd9e9cdb781c505ef0 Mon Sep 17 00:00:00 2001 From: e3mrah <81884938+emrahbaysal@users.noreply.github.com> Date: Sun, 26 Apr 2026 07:54:07 +0200 Subject: [PATCH] fix(axon): resolve unknown model names to vLLM default Clients (e.g. ChatWidget) send OpenAI model names like gpt-4o-mini which vLLM doesn't recognize. The provider now queries available models on startup and remaps any unrecognized name to the configured default. Co-Authored-By: Claude Opus 4.6 --- products/axon/src/index.ts | 1 + products/axon/src/providers/vllm.ts | 43 +++++++++++++++++++---------- 2 files changed, 30 insertions(+), 14 deletions(-) diff --git a/products/axon/src/index.ts b/products/axon/src/index.ts index d8d87f4a..ff719737 100644 --- a/products/axon/src/index.ts +++ b/products/axon/src/index.ts @@ -18,6 +18,7 @@ let vllm: VllmProvider | undefined; if (isVllm) { vllm = new VllmProvider(config.vllm); + await vllm.init(); app.log.info(`Provider: vllm (${config.vllm.baseUrl}), default model: ${config.vllm.defaultModel}`); } else { app.log.info(`Provider: claude, default model: ${config.defaultModel}`); diff --git a/products/axon/src/providers/vllm.ts b/products/axon/src/providers/vllm.ts index 1f12174a..63e12291 100644 --- a/products/axon/src/providers/vllm.ts +++ b/products/axon/src/providers/vllm.ts @@ -5,6 +5,7 @@ export class VllmProvider { private baseUrl: string; private apiKey: string; private defaultModel: string; + private availableModels: Set = new Set(); constructor(config: VllmConfig) { if (!config.baseUrl) throw new Error("AXON_VLLM_BASE_URL must be set when provider=vllm"); @@ -14,37 +15,51 @@ export class VllmProvider { this.defaultModel = config.defaultModel; } - async chat(body: ChatCompletionRequest): Promise { - const payload = { ...body, model: body.model ?? this.defaultModel, stream: false }; - delete (payload as Record).conversation_id; - delete (payload as Record).thinking; - delete (payload as Record).effort; - delete (payload as Record).profile; + async init(): Promise { + try { + const list = await this.models(); + for (const m of list.data) this.availableModels.add(m.id); + console.log(`[vllm] available models: ${[...this.availableModels].join(", ")}`); + } catch (err) { + console.warn("[vllm] could not fetch model list at init:", err); + } + } + private resolveModel(requested?: string): string { + if (!requested) return this.defaultModel; + if (this.availableModels.size === 0) return this.defaultModel; + if (this.availableModels.has(requested)) return requested; + return this.defaultModel; + } + + private cleanPayload(body: ChatCompletionRequest, stream: boolean): Record { + const payload: Record = { ...body, model: this.resolveModel(body.model), stream }; + delete payload.conversation_id; + delete payload.thinking; + delete payload.effort; + delete payload.profile; + return payload; + } + + async chat(body: ChatCompletionRequest): Promise { return fetch(`${this.baseUrl}/v1/chat/completions`, { method: "POST", headers: { "Content-Type": "application/json", Authorization: `Bearer ${this.apiKey}`, }, - body: JSON.stringify(payload), + body: JSON.stringify(this.cleanPayload(body, false)), }); } async chatStream(body: ChatCompletionRequest): Promise { - const payload = { ...body, model: body.model ?? this.defaultModel, stream: true }; - delete (payload as Record).conversation_id; - delete (payload as Record).thinking; - delete (payload as Record).effort; - delete (payload as Record).profile; - return fetch(`${this.baseUrl}/v1/chat/completions`, { method: "POST", headers: { "Content-Type": "application/json", Authorization: `Bearer ${this.apiKey}`, }, - body: JSON.stringify(payload), + body: JSON.stringify(this.cleanPayload(body, true)), }); }