| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119 |
- import EncodeUtils from '../en-decode/endecode-lib.js';
- /**
- * 用零一万物大模型来进行流式问答输出
- */
- let AI = (() => {
- /**
- * 用 SiliconFlow 大模型(CoderVM)进行流式问答输出,支持多轮上下文对话
- * @param {Array} messages 聊天历史数组,每项格式: {role: 'user'|'assistant', content: string}
- * @param {function} receivingCallback 每次收到新内容时的回调,参数为 message 对象
- * @param {string} apiKey 可选,API Key
- * @example
- * const messages = [
- * { role: 'user', content: '你好' },
- * { role: 'assistant', content: '你好,有什么可以帮您?' },
- * { role: 'user', content: '帮我写个排序算法' }
- * ];
- * AI.askCoderLLM(messages, callback);
- */
- async function askCoderLLM(messages, receivingCallback, apiKey) {
- // 默认插入system prompt
- const systemPrompt = {
- role: 'system',
- content: '你是由FeHelper提供的,一个专为开发者服务的AI助手。' +
- '你的目标是精准理解开发者的技术需求,并以最简洁、直接、专业的方式输出高质量代码,并且保证代码的完整性。' +
- '请避免无关的解释和冗余描述,只输出开发者真正需要的代码和必要的技术要点说明。' +
- '遇到不明确的需求时,优先追问关键细节,绝不输出与开发无关的内容。' +
- '如果生成的是代码,一定要用```的markdown代码块包裹,并使用markdown语法渲染。'
- };
- let msgs;
- if (typeof messages === 'string') {
- // 单轮对话,自动组装为数组
- msgs = [systemPrompt, { role: 'user', content: messages }];
- } else if (Array.isArray(messages)) {
- // 多轮对话,插入system prompt(如未包含)
- const hasSystemPrompt = messages.some(m => m.role === 'system' && m.content === systemPrompt.content);
- msgs = hasSystemPrompt ? messages : [systemPrompt, ...messages];
- } else {
- // 其他类型,降级为空对话
- msgs = [systemPrompt];
- }
- const defaultKey = 'c2stamJ5eGlldmVmdmhnbnBnbGF3cmxlZ25uam9rY25kc3BpYndjZmh1d2Ntbm9jbmxp';
- const url = 'https://api.siliconflow.cn/v1/chat/completions';
- const options = {
- method: 'POST',
- headers: {
- 'Content-Type': 'application/json',
- 'Authorization': `Bearer ${apiKey || EncodeUtils.base64Decode(defaultKey)}`
- },
- body: JSON.stringify({
- "model": "Qwen/Qwen2.5-Coder-7B-Instruct",
- "messages": msgs, // 直接传递多轮历史
- "stream": true, // 开启流式输出
- "max_tokens": 4096,
- "enable_thinking": true,
- "thinking_budget": 4096,
- "min_p": 0.05,
- "stop": [],
- "temperature": 0.7,
- "top_p": 0.7,
- "top_k": 50,
- "frequency_penalty": 0.5,
- "n": 1,
- "response_format": {
- "type": "text"
- }
- })
- };
- try {
- const response = await fetch(url, options);
- if (!response.ok) {
- throw new Error(`HTTP error! status: ${response.status}`);
- }
- // 处理流式返回(text/event-stream)
- const reader = response.body.getReader();
- const decoder = new TextDecoder('utf-8');
- let buffer = '';
- let done = false;
- const msg = {id:'',content:''};
- while (!done) {
- const { value, done: readerDone } = await reader.read();
- done = readerDone;
- if (value) {
- buffer += decoder.decode(value, { stream: true });
- // 以换行分割,逐条处理
- let lines = buffer.split('\n');
- // 最后一行可能不完整,留到下次
- buffer = lines.pop();
- for (let line of lines) {
- line = line.trim();
- if (!line || !line.startsWith('data:')) continue;
- let jsonStr = line.replace(/^data:/, '').trim();
- if (jsonStr === '[DONE]') continue;
- try {
- let obj = JSON.parse(jsonStr);
- if (obj.choices && obj.choices[0] && obj.choices[0].delta) {
- msg.id = obj.id;
- msg.created = obj.created;
- msg.content += obj.choices[0].delta.content;
- receivingCallback && receivingCallback(msg);
- }
- } catch (e) {
- // 忽略解析失败的片段
- }
- }
- }
- }
- receivingCallback && receivingCallback(null,true);
- } catch (error) {
- console.error('Error fetching coderVM stream:', error);
- }
- }
-
- return {askCoderLLM};
- })();
- export default AI;
|