|
@@ -0,0 +1,170 @@
|
|
|
+
|
|
|
+export interface TestMessage{
|
|
|
+ role:string
|
|
|
+ content:string
|
|
|
+}
|
|
|
+
|
|
|
+export class TestCompletion{
|
|
|
+ token:string = "r:60abef69e7cd8181b146ceaba1fdbf02"
|
|
|
+ messageList:any = []
|
|
|
+ stream:boolean = true;
|
|
|
+ constructor(messageList:any){
|
|
|
+ this.messageList = messageList || this.messageList
|
|
|
+ }
|
|
|
+ async sendMessage(messageList?:null|Array<TestMessage>,onMessage?: (content: string) => void):Promise<any>{
|
|
|
+
|
|
|
+ this.messageList = messageList || this.messageList
|
|
|
+ let body = {
|
|
|
+ "messages": this.messageList,
|
|
|
+ "stream": this.stream,
|
|
|
+ "model": "fmode-4.5-128k",
|
|
|
+ "temperature": 0.5,
|
|
|
+ "presence_penalty": 0,
|
|
|
+ "frequency_penalty": 0,
|
|
|
+ "token": "Bearer "+this.token
|
|
|
+ }
|
|
|
+
|
|
|
+ let response = await fetch("https://server.fmode.cn/api/apig/aigc/gpt/v1/chat/completions", {
|
|
|
+ "headers": {
|
|
|
+ },
|
|
|
+ "body": JSON.stringify(body),
|
|
|
+ "method": "POST",
|
|
|
+ "mode": "cors",
|
|
|
+ "credentials": "omit"
|
|
|
+ });
|
|
|
+
|
|
|
+
|
|
|
+ /** 单次响应 HTTP短连接请求
|
|
|
+ {"choices":[{"finish_reason":"stop","index":0,"logprobs":null,"message":{"annotations":[],"content":"您好!我是一个人工智能助手,旨在帮助您回答问题、提供信息和解决各种问题。我可以处理许多主题,包括科技、历史、文化、语言学习等。如果您有任何具体的问题或需要了解的内容,请随时告诉我!","refusal":null,"role":"assistant"}}],"created":1751509370,"id":"chatcmpl-Bp3t41MP4pb2NR38n1ylrJw922SBZ","model":"gpt-4o-mini-2024-07-18","object":"chat.completion","system_fingerprint":"fp_efad92c60b","usage":{"completion_tokens":55,"completion_tokens_details":{"accepted_prediction_tokens":0,"audio_tokens":0,"reasoning_tokens":0,"rejected_prediction_tokens":0},"prompt_tokens":15,"prompt_tokens_details":{"audio_tokens":0,"cached_tokens":0},"total_tokens":70}}
|
|
|
+ */
|
|
|
+ if(this.stream == false){
|
|
|
+ let data = await response.json()
|
|
|
+ console.log(data)
|
|
|
+ let lastContent = data?.choices?.[0]?.message?.content
|
|
|
+ return lastContent
|
|
|
+ }
|
|
|
+ /**
|
|
|
+ * 流式加载 HTTP Event Stream 模式 长连接获取
|
|
|
+ */
|
|
|
+
|
|
|
+ // Stream mode handling
|
|
|
+ if (!response.body) {
|
|
|
+ throw new Error("No response body in stream mode");
|
|
|
+ }
|
|
|
+
|
|
|
+ const reader = response.body.getReader();
|
|
|
+ const decoder = new TextDecoder("utf-8");
|
|
|
+ let accumulatedContent = "";
|
|
|
+ try {
|
|
|
+ while (true) {
|
|
|
+ const { done, value } = await reader.read();
|
|
|
+ if (done) break;
|
|
|
+
|
|
|
+ const chunk = decoder.decode(value, { stream: true });
|
|
|
+ const lines = chunk.split('\n').filter(line => line.trim() !== '');
|
|
|
+
|
|
|
+ for (const line of lines) {
|
|
|
+ if (line.startsWith('data:') && !line.includes('[DONE]')) {
|
|
|
+ try {
|
|
|
+ const jsonStr = line.substring(5).trim();
|
|
|
+ const data = JSON.parse(jsonStr);
|
|
|
+ const content = data?.choices?.[0]?.delta?.content || '';
|
|
|
+
|
|
|
+ if (content) {
|
|
|
+ accumulatedContent += content;
|
|
|
+ if (onMessage) {
|
|
|
+ onMessage(accumulatedContent);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ } catch (e) {
|
|
|
+ console.error("Error parsing stream data:", e);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ } finally {
|
|
|
+ reader.releaseLock();
|
|
|
+ }
|
|
|
+
|
|
|
+ return accumulatedContent;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * 使用AI生成符合指定结构的JSON数据
|
|
|
+ * @param prompt 任务要求的整体提示词
|
|
|
+ * @param jsonSchema 期望的JSON结构描述(用于提示词)
|
|
|
+ * @param example 可选的JSON示例(帮助AI理解格式)
|
|
|
+ * @param onMessage 实时生成内容回调
|
|
|
+ * @returns 解析后的JSON对象
|
|
|
+ */
|
|
|
+export async function completionJSON(
|
|
|
+ prompt:string,
|
|
|
+ jsonSchema: string,
|
|
|
+ example: object | null = null,
|
|
|
+ onMessage?: (content: string) => void
|
|
|
+): Promise<any> {
|
|
|
+ // 1. 构建提示词
|
|
|
+ const JsonResultParsePrompt = `请严格按照以下要求生成JSON数据:
|
|
|
+- 数据结构要求:${jsonSchema}
|
|
|
+- 只能返回一个完整的JSON对象 确保JSON格式正确
|
|
|
+- 注意返回的JSON格式每个KEY都有""包裹和Value之间都有:`;
|
|
|
+// ${example ? `2. 参考示例格式:\n${JSON.stringify(example, null, 2)}` : ''}
|
|
|
+
|
|
|
+ // 2. 初始化消息列表
|
|
|
+ const messages: TestMessage[] = [
|
|
|
+ {
|
|
|
+ role: "user",
|
|
|
+ content: prompt+JsonResultParsePrompt
|
|
|
+ }
|
|
|
+ ];
|
|
|
+
|
|
|
+ // 3. 创建TestCompletion实例
|
|
|
+ const completion = new TestCompletion(messages);
|
|
|
+
|
|
|
+ // 4. 存储累积内容和JSON对象
|
|
|
+ let fullContent = "";
|
|
|
+ let jsonObject: any = null;
|
|
|
+
|
|
|
+ // 5. 发送请求并处理流式响应
|
|
|
+ const result = await completion.sendMessage(null, (content) => {
|
|
|
+ fullContent = content;
|
|
|
+ });
|
|
|
+ console.log("fullContent",fullContent)
|
|
|
+ // 6. 最终JSON提取(确保获取完整内容)
|
|
|
+ try {
|
|
|
+ // 再次尝试从完整内容中提取JSON
|
|
|
+ jsonObject = extractJSON(fullContent);
|
|
|
+ } catch (e) {
|
|
|
+ console.error("JSON解析失败:", e);
|
|
|
+ console.log("原始内容:", fullContent);
|
|
|
+ throw new Error("生成的响应不符合JSON格式");
|
|
|
+ }
|
|
|
+
|
|
|
+ return jsonObject;
|
|
|
+}
|
|
|
+
|
|
|
+function extractJSON(str:string){
|
|
|
+ let stack = 0;
|
|
|
+ let startIndex = -1;
|
|
|
+ let result = null;
|
|
|
+
|
|
|
+ for (let i = 0; i < str.length; i++) {
|
|
|
+ if (str[i] === '{') {
|
|
|
+ if (stack === 0) startIndex = i;
|
|
|
+ stack++;
|
|
|
+ } else if (str[i] === '}') {
|
|
|
+ stack--;
|
|
|
+ if (stack === 0 && startIndex !== -1) {
|
|
|
+ try {
|
|
|
+ result = JSON.parse(str.slice(startIndex, i + 1));
|
|
|
+ break;
|
|
|
+ } catch (e) {
|
|
|
+ // 继续尝试下一个可能的 JSON
|
|
|
+ startIndex = -1;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return result;
|
|
|
+};
|