Spaces:
Running
Running
| import { | |
| isAnthropicCompatibleBaseUrl, | |
| normalizeLlmConfig, | |
| type LlmConfig, | |
| } from "./llm-config"; | |
| import { compactTracePreview } from "./chat-trace"; | |
| export type LlmCallResult = { | |
| text: string; | |
| endpoint: string; | |
| requestPreview: string; | |
| responsePreview: string; | |
| }; | |
| function extractJsonText(value: unknown): string | undefined { | |
| if (typeof value === "string") return value; | |
| if (Array.isArray(value)) { | |
| const joined = value | |
| .map((item) => { | |
| if (typeof item === "string") return item; | |
| if ( | |
| item && | |
| typeof item === "object" && | |
| "type" in item && | |
| "text" in item && | |
| item.type === "text" && | |
| typeof item.text === "string" | |
| ) { | |
| return item.text; | |
| } | |
| return ""; | |
| }) | |
| .filter(Boolean) | |
| .join("\n"); | |
| return joined || undefined; | |
| } | |
| return undefined; | |
| } | |
| function extractOpenAiText(payload: unknown): string | undefined { | |
| const data = payload as | |
| | { | |
| choices?: Array<{ | |
| message?: { content?: unknown }; | |
| text?: unknown; | |
| }>; | |
| } | |
| | undefined; | |
| const choice = data?.choices?.[0]; | |
| return extractJsonText(choice?.message?.content) ?? extractJsonText(choice?.text); | |
| } | |
| function extractAnthropicText(payload: unknown): string | undefined { | |
| const data = payload as | |
| | { | |
| content?: Array<{ | |
| type?: string; | |
| text?: string; | |
| }>; | |
| } | |
| | undefined; | |
| return data?.content | |
| ?.filter((item) => item.type === "text" && typeof item.text === "string") | |
| .map((item) => item.text) | |
| .join("\n"); | |
| } | |
| export async function generateLlmText(args: { | |
| config: LlmConfig; | |
| systemPrompt: string; | |
| userPrompt: string; | |
| temperature?: number; | |
| maxTokens?: number; | |
| responseFormat?: "json_object"; | |
| }): Promise<LlmCallResult> { | |
| const { systemPrompt, userPrompt } = args; | |
| const config = normalizeLlmConfig(args.config); | |
| const temperature = args.temperature ?? 0.2; | |
| const maxTokens = args.maxTokens ?? 2200; | |
| const anthropicStyle = isAnthropicCompatibleBaseUrl(config.baseUrl); | |
| const responseFormat = args.responseFormat; | |
| const endpoint = anthropicStyle | |
| ? `${config.baseUrl}/v1/messages` | |
| : `${config.baseUrl}/chat/completions`; | |
| const response = await fetch(endpoint, { | |
| method: "POST", | |
| headers: anthropicStyle | |
| ? { | |
| "content-type": "application/json", | |
| "x-api-key": config.apiKey, | |
| "anthropic-version": "2023-06-01", | |
| } | |
| : { | |
| "content-type": "application/json", | |
| authorization: `Bearer ${config.apiKey}`, | |
| }, | |
| body: JSON.stringify( | |
| anthropicStyle | |
| ? { | |
| model: config.model, | |
| max_tokens: maxTokens, | |
| temperature, | |
| system: systemPrompt, | |
| messages: [ | |
| { | |
| role: "user", | |
| content: [ | |
| { | |
| type: "text", | |
| text: userPrompt, | |
| }, | |
| ], | |
| }, | |
| ], | |
| } | |
| : { | |
| model: config.model, | |
| temperature, | |
| messages: [ | |
| { role: "system", content: systemPrompt }, | |
| { role: "user", content: userPrompt }, | |
| ], | |
| ...(responseFormat | |
| ? { response_format: { type: responseFormat } } | |
| : {}), | |
| }, | |
| ), | |
| }); | |
| if (!response.ok) { | |
| const text = await response.text().catch(() => ""); | |
| throw new Error(`LLM request failed (${response.status}): ${text}`.slice(0, 1200)); | |
| } | |
| const rawText = await response.text(); | |
| let payload: unknown = null; | |
| if (rawText.trim()) { | |
| try { | |
| payload = JSON.parse(rawText); | |
| } catch { | |
| payload = null; | |
| } | |
| } | |
| const text = anthropicStyle | |
| ? extractAnthropicText(payload) | |
| : extractOpenAiText(payload) ?? rawText.trim(); | |
| if (!text) { | |
| throw new Error("LLM returned an empty response."); | |
| } | |
| return { | |
| text, | |
| endpoint, | |
| requestPreview: compactTracePreview( | |
| `SYSTEM:\n${systemPrompt}\n\nUSER:\n${userPrompt}`, | |
| ), | |
| responsePreview: compactTracePreview(text), | |
| }; | |
| } | |