import { ChatOpenAI } from "@langchain/openai" import { ChatGoogleGenerativeAI } from "@langchain/google-genai" import { ChatMistralAI } from "@langchain/mistralai" import { BaseMessage, HumanMessage } from "@langchain/core/messages" export type LLMProvider = "openai" | "google" | "mistral" export interface LLMConfig { provider: LLMProvider apiKey: string model: string } export interface LLMSettings { providers: LLMConfig[] } export interface LLMRequest { prompt: string schema?: Record attachments?: any[] } export interface LLMResponse { output: Record tokensUsed?: number provider: LLMProvider error?: string } async function requestLLMUnified(config: LLMConfig, req: LLMRequest): Promise { try { const temperature = 0 let model: any if (config.provider === "openai") { model = new ChatOpenAI({ apiKey: config.apiKey, model: config.model, temperature: temperature, }) } else if (config.provider === "google") { model = new ChatGoogleGenerativeAI({ apiKey: config.apiKey, model: config.model, temperature: temperature, }) } else if (config.provider === "mistral") { model = new ChatMistralAI({ apiKey: config.apiKey, model: config.model, temperature: temperature, }) } else { return { output: {}, provider: config.provider, error: "Unknown provider", } } const structuredModel = model.withStructuredOutput(req.schema, { name: "transaction" }) let message_content: any = [{ type: "text", text: req.prompt }] if (req.attachments && req.attachments.length > 0) { const images = req.attachments.map((att) => ({ type: "image_url", image_url: { url: `data:${att.contentType};base64,${att.base64}`, }, })) message_content.push(...images) } const messages: BaseMessage[] = [new HumanMessage({ content: message_content })] const response = await structuredModel.invoke(messages) return { output: response, provider: config.provider, } } catch (error: any) { return { output: {}, provider: config.provider, error: error instanceof Error ? error.message : `${config.provider} request failed`, } } } export async function requestLLM(settings: LLMSettings, req: LLMRequest): Promise { for (const config of settings.providers) { if (!config.apiKey || !config.model) { console.info("Skipping provider:", config.provider) continue } console.info("Use provider:", config.provider) const response = await requestLLMUnified(config, req) if (!response.error) { return response } else { console.error(response.error) } } return { output: {}, provider: settings.providers[0]?.provider || "openai", error: "All LLM providers failed or are not configured", } }