71 lines
1.7 KiB
TypeScript
71 lines
1.7 KiB
TypeScript
import type { ChatMessage, RequestAIParams } from "./types"
|
|
|
|
/**
|
|
* Generic, reusable OpenAI-compatible chat completion request.
|
|
* Not coupled to any specific business logic.
|
|
*/
|
|
export async function requestOpenAICompatible(params: RequestAIParams): Promise<string> {
|
|
const {
|
|
baseUrl,
|
|
apiKey,
|
|
model,
|
|
systemPrompt,
|
|
customPrompt,
|
|
messages,
|
|
temperature = 0.3,
|
|
maxTokens = 4000,
|
|
stream = false,
|
|
signal,
|
|
} = params
|
|
|
|
const finalMessages: ChatMessage[] = []
|
|
|
|
if (systemPrompt) {
|
|
finalMessages.push({ role: "system", content: systemPrompt })
|
|
}
|
|
|
|
if (customPrompt) {
|
|
finalMessages.push({ role: "user", content: customPrompt })
|
|
}
|
|
|
|
finalMessages.push(...messages)
|
|
|
|
const url = `${baseUrl.replace(/\/$/, "")}/chat/completions`
|
|
|
|
let response: Response
|
|
try {
|
|
response = await fetch(url, {
|
|
method: "POST",
|
|
signal,
|
|
headers: {
|
|
"Content-Type": "application/json",
|
|
Authorization: `Bearer ${apiKey}`,
|
|
},
|
|
body: JSON.stringify({
|
|
model,
|
|
messages: finalMessages,
|
|
temperature,
|
|
max_tokens: maxTokens,
|
|
stream,
|
|
}),
|
|
})
|
|
} catch (err) {
|
|
if (err instanceof DOMException && err.name === "AbortError") throw err
|
|
throw new Error(`无法连接到 AI 服务:${(err as Error).message}`)
|
|
}
|
|
|
|
if (!response.ok) {
|
|
const errorText = await response.text().catch(() => "")
|
|
throw new Error(`AI 请求失败:${response.status} ${errorText}`)
|
|
}
|
|
|
|
const data = await response.json()
|
|
const content = data?.choices?.[0]?.message?.content
|
|
|
|
if (!content) {
|
|
throw new Error("AI 没有返回有效内容")
|
|
}
|
|
|
|
return content as string
|
|
}
|