|
@@ -0,0 +1,110 @@
|
|
|
+export interface ChatMessage {
|
|
|
+ role: 'system' | 'user' | 'assistant';
|
|
|
+ content: string;
|
|
|
+}
|
|
|
+
|
|
|
+export interface FmodeChatCompletionParams {
|
|
|
+ messages: ChatMessage[];
|
|
|
+ model?: string; // 默认为 "fmode-4.5-128k"
|
|
|
+ temperature?: number; // 默认为 0.5
|
|
|
+ presence_penalty?: number; // 默认为 0
|
|
|
+ frequency_penalty?: number; // 默认为 0
|
|
|
+ stream?: boolean; // 默认为 true
|
|
|
+ token?: string; // Bearer token
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * 流式调用 Fmode GPT API
|
|
|
+ * @param params 请求参数
|
|
|
+ * @param onData 接收到数据时的回调函数 (content: string, isLast: boolean) => void
|
|
|
+ * @param onError 错误处理函数
|
|
|
+ */
|
|
|
+export async function TestFmodeChatCompletion(
|
|
|
+ params: FmodeChatCompletionParams,
|
|
|
+ onData: (content: string, isLast: boolean) => void,
|
|
|
+ onError?: (error: Error) => void
|
|
|
+): Promise<void> {
|
|
|
+ const endpoint = 'https://server.fmode.cn/api/apig/aigc/gpt/v1/chat/completions';
|
|
|
+
|
|
|
+ try {
|
|
|
+ let token = localStorage.getItem("token") || params.token
|
|
|
+ const response = await fetch(endpoint, {
|
|
|
+ method: 'POST',
|
|
|
+ headers: {
|
|
|
+ 'accept': '*/*',
|
|
|
+ 'accept-language': 'zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6',
|
|
|
+ 'cache-control': 'no-cache',
|
|
|
+ 'content-type': 'text/plain; charset=utf-8',
|
|
|
+ 'sec-ch-ua': '"Chromium";v="130", "Microsoft Edge";v="130", "Not?A_Brand";v="99"',
|
|
|
+ 'sec-ch-ua-mobile': '?1',
|
|
|
+ 'sec-ch-ua-platform': '"Android"',
|
|
|
+ 'sec-fetch-dest': 'empty',
|
|
|
+ 'sec-fetch-mode': 'cors',
|
|
|
+ 'sec-fetch-site': 'same-site',
|
|
|
+ },
|
|
|
+ referrer: 'https://ai.fmode.cn/',
|
|
|
+ referrerPolicy: 'strict-origin-when-cross-origin',
|
|
|
+ body: JSON.stringify({
|
|
|
+ messages: params.messages,
|
|
|
+ stream: params.stream ?? true,
|
|
|
+ model: params.model ?? 'fmode-4.5-128k',
|
|
|
+ temperature: params.temperature ?? 0.5,
|
|
|
+ presence_penalty: params.presence_penalty ?? 0,
|
|
|
+ frequency_penalty: params.frequency_penalty ?? 0,
|
|
|
+ token: `Bearer ${token}`, // 注意这里根据您的实际token格式调整
|
|
|
+ }),
|
|
|
+ mode: 'cors',
|
|
|
+ credentials: 'omit',
|
|
|
+ });
|
|
|
+
|
|
|
+ if (!response.ok) {
|
|
|
+ const errorData = await response.json().catch(() => null);
|
|
|
+ throw new Error(
|
|
|
+ `HTTP error! status: ${response.status}, message: ${errorData?.message || 'Unknown error'}`
|
|
|
+ );
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!response.body) {
|
|
|
+ throw new Error('No response body');
|
|
|
+ }
|
|
|
+
|
|
|
+ const reader = response.body.getReader();
|
|
|
+ const decoder = new TextDecoder('utf-8');
|
|
|
+ let buffer = '';
|
|
|
+
|
|
|
+ while (true) {
|
|
|
+ const { done, value } = await reader.read();
|
|
|
+ if (done) break;
|
|
|
+
|
|
|
+ buffer += decoder.decode(value, { stream: true });
|
|
|
+
|
|
|
+ // 处理可能的多条消息
|
|
|
+ const lines = buffer.split('\n');
|
|
|
+ buffer = lines.pop() || ''; // 最后一行可能不完整,保留在buffer中
|
|
|
+
|
|
|
+ for (const line of lines) {
|
|
|
+ if (line.trim() === '') continue;
|
|
|
+
|
|
|
+ const message = line.replace(/^data: /, '').trim();
|
|
|
+ if (message === '[DONE]') {
|
|
|
+ onData('', true); // 通知完成
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ try {
|
|
|
+ const parsed = JSON.parse(message);
|
|
|
+ const content = parsed.choices[0]?.delta?.content || '';
|
|
|
+ if (content) {
|
|
|
+ onData(content, false);
|
|
|
+ }
|
|
|
+ } catch (err) {
|
|
|
+ console.error('Error parsing message:', err);
|
|
|
+ onError?.(err as Error);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ } catch (error) {
|
|
|
+ console.error('Fetch error:', error);
|
|
|
+ onError?.(error as Error);
|
|
|
+ }
|
|
|
+}
|