import type { ChatMessage, PlainRunResult, ReActStep, ToolDefinition } from '../types'
const BASE_URL = '/api/dashscope/compatible-mode/v1'
const DEFAULT_MODEL = 'qwen-plus'
const MAX_LOOPS = 6
interface CompletionResponse {
choices: Array<{
message: {
role: string
content: string | null
reasoning_content?: string | null
tool_calls?: Array<{
id: string
type: 'function'
function: { name: string; arguments: string }
}>
}
finish_reason: string
}>
error?: { message: string }
}
export class DashScopeError extends Error {
constructor(message: string) {
super(message)
this.name = 'DashScopeError'
}
}
async function chatCompletion(
apiKey: string,
messages: ChatMessage[],
tools?: ToolDefinition[],
): Promise<CompletionResponse['choices'][0]['message']> {
const body: Record<string, unknown> = {
model: DEFAULT_MODEL,
messages,
}
if (tools?.length) {
body.tools = tools
body.tool_choice = 'auto'
}
const res = await fetch(`${BASE_URL}/chat/completions`, {
method: 'POST',
headers: {
Authorization: `Bearer ${apiKey}`,
'Content-Type': 'application/json',
},
body: JSON.stringify(body),
})
const data = (await res.json()) as CompletionResponse
if (!res.ok) {
throw new DashScopeError(data.error?.message ?? `请求失败 (${res.status})`)
}
const choice = data.choices?.[0]
if (!choice?.message) {
throw new DashScopeError('模型返回为空')
}
return choice.message
}
function uid(): string {
return `${Date.now()}-${Math.random().toString(36).slice(2, 8)}`
}
export async function plainChat(
apiKey: string,
userQuestion: string,
onStep?: (step: ReActStep) => void,
): Promise<PlainRunResult> {
const steps: ReActStep[] = []
const push = (step: ReActStep) => {
steps.push(step)
onStep?.(step)
}
push({
id: uid(),
type: 'input',
round: 0,
title: '用户提问',
content: userQuestion,
})
push({
id: uid(),
type: 'plain',
round: 1,
title: '无工具 · 直接作答',
content: [
'【普通模式流程】',
'❌ 无 Thought(思考)',
'❌ 无 Action(工具调用)',
'❌ 无 Observation(真实数据)',
'→ 模型仅凭训练知识一次性生成回答',
'→ 涉及实时数据时,容易出现幻觉或计算错误',
].join('\n'),
})
const messages: ChatMessage[] = [
{
role: 'system',
content:
'你是一个有帮助的助手。请用中文简洁回答用户问题。注意:你没有接入任何外部工具,只能依靠自身知识回答,不要说明你无法调用工具。',
},
{ role: 'user', content: userQuestion },
]
const reply = await chatCompletion(apiKey, messages)
const finalAnswer = reply.content?.trim() || '(模型未返回内容)'
push({
id: uid(),
type: 'answer',
round: 1,
title: '直接输出(未经验证)',
content: finalAnswer,
})
return { steps, finalAnswer }
}
export { chatCompletion, MAX_LOOPS }