refactor(ai-client): extract shared createLanguageModel helper

De-duplicate the provider switch logic that was identical in chat.ts
and vision.ts into a shared model.ts module.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
yuanzonghao
2026-06-07 11:52:20 +08:00
parent 57bc6556ab
commit f4aca0b59c
3 changed files with 30 additions and 50 deletions
+3 -24
View File
@@ -1,10 +1,7 @@
import { generateText } from "ai";
import type { LanguageModelUsage, ModelMessage } from "ai";
import { createAnthropic } from "@ai-sdk/anthropic";
import { createGoogleGenerativeAI } from "@ai-sdk/google";
import { createOpenAI } from "@ai-sdk/openai";
import type { ProviderConfig, ProviderProtocol } from "@infiplot/types";
import { normalizeBaseUrl } from "./normalizeUrl";
import type { ProviderConfig } from "@infiplot/types";
import { createLanguageModel, resolveProtocol } from "./model";
export type ChatMessage = {
role: "system" | "user" | "assistant";
@@ -31,24 +28,6 @@ function summarizeSdkUsage(
return `[cache] ${tag} input=${input} completion=${output} (provider didn't report cache stats)`;
}
function resolveTextProtocol(config: ProviderConfig): ProviderProtocol {
return config.provider ?? "openai_compatible";
}
function createLanguageModel(config: ProviderConfig, protocol: ProviderProtocol) {
const baseURL = normalizeBaseUrl(config.baseUrl, protocol);
switch (protocol) {
case "anthropic":
return createAnthropic({ apiKey: config.apiKey, baseURL })(config.model);
case "google":
return createGoogleGenerativeAI({ apiKey: config.apiKey, baseURL })(config.model);
case "openai_compatible":
case "openai":
default:
return createOpenAI({ apiKey: config.apiKey, baseURL }).chat(config.model);
}
}
export async function chat(
config: ProviderConfig,
messages: ChatMessage[],
@@ -57,7 +36,7 @@ export async function chat(
tag?: string;
},
): Promise<string> {
const protocol = resolveTextProtocol(config);
const protocol = resolveProtocol(config);
const model = createLanguageModel(config, protocol);
const system = messages.find((m) => m.role === "system")?.content;
+23
View File
@@ -0,0 +1,23 @@
import { createAnthropic } from "@ai-sdk/anthropic";
import { createGoogleGenerativeAI } from "@ai-sdk/google";
import { createOpenAI } from "@ai-sdk/openai";
import type { ProviderConfig, ProviderProtocol } from "@infiplot/types";
import { normalizeBaseUrl } from "./normalizeUrl";
export function resolveProtocol(config: ProviderConfig): ProviderProtocol {
return config.provider ?? "openai_compatible";
}
export function createLanguageModel(config: ProviderConfig, protocol: ProviderProtocol) {
const baseURL = normalizeBaseUrl(config.baseUrl, protocol);
switch (protocol) {
case "anthropic":
return createAnthropic({ apiKey: config.apiKey, baseURL })(config.model);
case "google":
return createGoogleGenerativeAI({ apiKey: config.apiKey, baseURL })(config.model);
case "openai_compatible":
case "openai":
default:
return createOpenAI({ apiKey: config.apiKey, baseURL }).chat(config.model);
}
}
+4 -26
View File
@@ -1,10 +1,7 @@
import { generateText } from "ai";
import type { ModelMessage } from "ai";
import { createAnthropic } from "@ai-sdk/anthropic";
import { createGoogleGenerativeAI } from "@ai-sdk/google";
import { createOpenAI } from "@ai-sdk/openai";
import type { ProviderConfig, ProviderProtocol } from "@infiplot/types";
import { normalizeBaseUrl } from "./normalizeUrl";
import type { ProviderConfig } from "@infiplot/types";
import { createLanguageModel, resolveProtocol } from "./model";
const VISION_TIMEOUT_MS = 60_000;
@@ -20,32 +17,13 @@ export async function interpretClick(
);
}
function resolveVisionProtocol(config: ProviderConfig): ProviderProtocol {
return config.provider ?? "openai_compatible";
}
export async function analyzeImageDataUrl(
config: ProviderConfig,
imageDataUrl: string,
prompt: string,
): Promise<string> {
const protocol = resolveVisionProtocol(config);
const baseURL = normalizeBaseUrl(config.baseUrl, protocol);
let model;
switch (protocol) {
case "anthropic":
model = createAnthropic({ apiKey: config.apiKey, baseURL })(config.model);
break;
case "google":
model = createGoogleGenerativeAI({ apiKey: config.apiKey, baseURL })(config.model);
break;
case "openai_compatible":
case "openai":
default:
model = createOpenAI({ apiKey: config.apiKey, baseURL }).chat(config.model);
break;
}
const protocol = resolveProtocol(config);
const model = createLanguageModel(config, protocol);
const messages: ModelMessage[] = [
{