github-actions[bot] commited on
Commit
740a4ac
·
1 Parent(s): 396d368

Update from GitHub Actions

Browse files
Files changed (1) hide show
  1. server.js +192 -176
server.js CHANGED
@@ -14,6 +14,12 @@ const PROMPT_LIMIT = 4800;
14
  const SYSTEM_PROMPT_LIMIT = 4800;
15
  // === 限制定义结束 ===
16
 
 
 
 
 
 
 
17
  // 定义 fal-ai/any-llm 支持的模型列表
18
  const FAL_SUPPORTED_MODELS = [
19
  "anthropic/claude-3.7-sonnet",
@@ -58,183 +64,193 @@ app.get('/v1/models', (req, res) => {
58
  }
59
  });
60
 
61
-
62
- // === 修改后的 convertMessagesToFalPrompt 函数 (System置顶 + 分隔符 + 对话历史Recency) ===
63
- function convertMessagesToFalPrompt(messages) {
64
- let fixed_system_prompt_content = "";
65
- const conversation_message_blocks = [];
66
- console.log(`Original messages count: ${messages.length}`);
67
-
68
- // 1. 分离 System 消息,格式化 User/Assistant 消息
69
- for (const message of messages) {
70
- let content = (message.content === null || message.content === undefined) ? "" : String(message.content);
71
- switch (message.role) {
72
- case 'system':
73
- fixed_system_prompt_content += `${content}\n\n`;
74
- break;
75
- case 'user':
76
- conversation_message_blocks.push(`Human: ${content}\n\n`);
77
- break;
78
- case 'assistant':
79
- conversation_message_blocks.push(`Assistant: ${content}\n\n`);
80
- break;
81
- default:
82
- console.warn(`Unsupported role: ${message.role}`);
83
- continue;
84
- }
85
- }
86
-
87
- // 2. 截断合并后的 system 消息(如果超长)
88
- if (fixed_system_prompt_content.length > SYSTEM_PROMPT_LIMIT) {
89
- const originalLength = fixed_system_prompt_content.length;
90
- fixed_system_prompt_content = fixed_system_prompt_content.substring(0, SYSTEM_PROMPT_LIMIT);
91
- console.warn(`Combined system messages truncated from ${originalLength} to ${SYSTEM_PROMPT_LIMIT}`);
92
- }
93
- // 清理末尾可能多余的空白,以便后续判断和拼接
94
- fixed_system_prompt_content = fixed_system_prompt_content.trim();
95
-
96
-
97
- // 3. 计算 system_prompt 中留给对话历史的剩余空间
98
- // 注意:这里计算时要考虑分隔符可能占用的长度,但分隔符只在需要时添加
99
- // 因此先计算不含分隔符的剩余空间
100
- let space_occupied_by_fixed_system = 0;
101
- if (fixed_system_prompt_content.length > 0) {
102
- // 如果固定内容不为空,计算其长度 + 后面可能的分隔符的长度(如果需要)
103
- // 暂时只计算内容长度,分隔符在组合时再考虑
104
- space_occupied_by_fixed_system = fixed_system_prompt_content.length + 4; // 预留 \n\n...\n\n 的长度
105
- }
106
- const remaining_system_limit = Math.max(0, SYSTEM_PROMPT_LIMIT - space_occupied_by_fixed_system);
107
- console.log(`Trimmed fixed system prompt length: ${fixed_system_prompt_content.length}. Approx remaining system history limit: ${remaining_system_limit}`);
108
-
109
-
110
- // 4. 反向填充 User/Assistant 对话历史
111
- const prompt_history_blocks = [];
112
- const system_prompt_history_blocks = [];
113
- let current_prompt_length = 0;
114
- let current_system_history_length = 0;
115
- let promptFull = false;
116
- let systemHistoryFull = (remaining_system_limit <= 0);
117
-
118
- console.log(`Processing ${conversation_message_blocks.length} user/assistant messages for recency filling.`);
119
- for (let i = conversation_message_blocks.length - 1; i >= 0; i--) {
120
- const message_block = conversation_message_blocks[i];
121
- const block_length = message_block.length;
122
-
123
- if (promptFull && systemHistoryFull) {
124
- console.log(`Both prompt and system history slots full. Omitting older messages from index ${i}.`);
125
- break;
126
- }
127
-
128
- // 优先尝试放入 prompt
129
- if (!promptFull) {
130
- if (current_prompt_length + block_length <= PROMPT_LIMIT) {
131
- prompt_history_blocks.unshift(message_block);
132
- current_prompt_length += block_length;
133
- continue;
134
- } else {
135
- promptFull = true;
136
- console.log(`Prompt limit (${PROMPT_LIMIT}) reached. Trying system history slot.`);
137
- }
138
- }
139
-
140
- // 如果 prompt 满了,尝试放入 system_prompt 的剩余
141
- if (!systemHistoryFull) {
142
- if (current_system_history_length + block_length <= remaining_system_limit) {
143
- system_prompt_history_blocks.unshift(message_block);
144
- current_system_history_length += block_length;
145
- continue;
146
- } else {
147
- systemHistoryFull = true;
148
- console.log(`System history limit (${remaining_system_limit}) reached.`);
149
- }
150
- }
151
- }
152
-
153
- // 5. *** 组合最终的 prompt 和 system_prompt (包含分隔符逻辑) ***
154
- const system_prompt_history_content = system_prompt_history_blocks.join('').trim();
155
- const final_prompt = prompt_history_blocks.join('').trim();
156
-
157
- // 定义分隔符
158
- const SEPARATOR = "\n\n-------下面是比较早之前���对话内容-----\n\n";
159
-
160
- let final_system_prompt = "";
161
-
162
- // 检查各部分是否有内容 (使用 trim 后的固定部分)
163
- const hasFixedSystem = fixed_system_prompt_content.length > 0;
164
- const hasSystemHistory = system_prompt_history_content.length > 0;
165
-
166
- if (hasFixedSystem && hasSystemHistory) {
167
- // 部分都有,用分隔符连接
168
- final_system_prompt = fixed_system_prompt_content + SEPARATOR + system_prompt_history_content;
169
- console.log("Combining fixed system prompt and history with separator.");
170
- } else if (hasFixedSystem) {
171
- // 只有固定部分
172
- final_system_prompt = fixed_system_prompt_content;
173
- console.log("Using only fixed system prompt.");
174
- } else if (hasSystemHistory) {
175
- // 只有历史部分 (固定部分为空)
176
- final_system_prompt = system_prompt_history_content;
177
- console.log("Using only history in system prompt slot.");
178
- }
179
- // 如果两部分都为空,final_system_prompt 保持空字符串 ""
180
-
181
- // 6. 返回结果
182
- const result = {
183
- system_prompt: final_system_prompt, // 最终结果不需要再 trim
184
- prompt: final_prompt // final_prompt 在组合前已 trim
185
- };
186
-
187
- console.log(`Final system_prompt length (Sys+Separator+Hist): ${result.system_prompt.length}`);
188
- console.log(`Final prompt length (Hist): ${result.prompt.length}`);
189
-
190
- return result;
191
- }
192
- // === convertMessagesToFalPrompt 函数结束 ===
193
-
194
- // === 新的 convertMessagesToFal 函数 ===
195
- function convertMessagesToFal(messages) {
196
- let system_prompt = "";
197
- let prompt = "";
198
-
199
- // 遍历所有消息
200
- for (const message of messages) {
201
- let content = (message.content === null || message.content === undefined) ? "" : String(message.content);
202
-
203
- switch (message.role) {
204
- case 'system':
205
- // 系统消息添加到 system_prompt
206
- system_prompt += content;
207
- break;
208
- case 'user':
209
- // 用户消息添加到 prompt
210
- prompt += `Human: ${content}\n\n`;
211
- break;
212
- case 'assistant':
213
- // 助手消息添加到 prompt
214
- prompt += `Assistant: ${content}\n\n`;
215
- break;
216
- default:
217
- console.warn(`Unsupported role: ${message.role}`);
218
- continue;
219
- }
220
- }
221
-
222
- // 清理可能的多余空白
223
- system_prompt = system_prompt.trim();
224
- prompt = prompt.trim();
225
-
226
- // 返回结果对象
227
- const result = {
228
- system_prompt: system_prompt,
229
- prompt: prompt
230
- };
231
-
232
- console.log(`New function - system_prompt length: ${result.system_prompt.length}`);
233
- console.log(`New function - prompt length: ${result.prompt.length}`);
234
-
235
- return result;
 
 
 
 
 
 
 
 
 
 
 
236
  }
237
- // === convertMessagesToFal 函数结�� ===
238
 
239
 
240
  // POST /v1/chat/completions endpoint (保持不变)
 
14
  const SYSTEM_PROMPT_LIMIT = 4800;
15
  // === 限制定义结束 ===
16
 
17
+
18
+ export interface OpenAIMessage {
19
+ role: 'system' | 'user' | 'assistant';
20
+ content: string | null;
21
+ }
22
+
23
  // 定义 fal-ai/any-llm 支持的模型列表
24
  const FAL_SUPPORTED_MODELS = [
25
  "anthropic/claude-3.7-sonnet",
 
64
  }
65
  });
66
 
67
+
68
+
69
+ /**
70
+ * OpenAI 格式的消息转换为 Fal AI 格式的 prompt 和 system_prompt
71
+ *
72
+ * 核心逻辑:倒序遍历 messages,至多取 3 条 user/assistant 消息放到 prompt 部分,
73
+ * chat_history 最多包含 2 条消息(user + assistant),最后一个用户消息是最新提问,不属于对话历史
74
+ *
75
+ * @param messages - OpenAI 格式的消息数组
76
+ * @returns 包含 system_prompt、prompt 和可选错误信息的对象
77
+ *
78
+ * @example
79
+ * // 基本用法:系统消息 + 用户消息
80
+ * const messages = [
81
+ * { role: 'system', content: 'You are a helpful assistant.' },
82
+ * { role: 'user', content: 'Hello, how are you?' }
83
+ * ];
84
+ * const result = convertMessagesToFalPrompt(messages);
85
+ * // result.system_prompt: 'You are a helpful assistant.'
86
+ * // result.prompt: 'Hello, how are you?'
87
+ *
88
+ * @example
89
+ * // 多轮对话:最后一条是用户消息
90
+ * const messages = [
91
+ * { role: 'system', content: 'You are helpful.' },
92
+ * { role: 'user', content: 'What is AI?' },
93
+ * { role: 'assistant', content: 'AI is artificial intelligence.' },
94
+ * { role: 'user', content: 'Tell me more.' }
95
+ * ];
96
+ * const result = convertMessagesToFalPrompt(messages);
97
+ * // result.system_prompt: 'You are helpful.\n<chat_history>'
98
+ * // result.prompt: 'What is AI?\nAssistant: AI is artificial intelligence.\n</chat_history>\nTell me more.'
99
+ *
100
+ * @example
101
+ * // 多轮对话:最后一条不是用户消息
102
+ * const messages = [
103
+ * { role: 'user', content: 'Hello' },
104
+ * { role: 'assistant', content: 'Hi there!' }
105
+ * ];
106
+ * const result = convertMessagesToFalPrompt(messages);
107
+ * // result.system_prompt: '<chat_history>\nHuman: Hello\nAssistant: Hi there!\n</chat_history>'
108
+ * // result.prompt: ''
109
+ *
110
+ * @description
111
+ * 实现逻辑:
112
+ * 1. **系统消息处理**:只使用最后一个非空系统消息,如果超出 SYSTEM_PROMPT_LIMIT 则返回错误
113
+ * 2. **消息过滤**:自动过滤空内容消息(null、undefined、空字符串或纯空格)
114
+ * 3. **倒序遍历**:取最后 3 条消息,根据最后一条消息类型分两种情况:
115
+ *
116
+ * **情况 A - 最后一条是用户消息**:
117
+ * - 取倒数第 3、第 2 条作为 chat_history(最多 2 条:user + assistant)
118
+ * - system_prompt: `系统消息\n<chat_history>`
119
+ * - prompt: `<user message>\nAssistant: <assistant message>\n</chat_history>\n<最新用户消息>`
120
+ *
121
+ * **情况 B - 最后一条不是用户消息**:
122
+ * - 取最后 2 条消息作为 chat_history,放在 system_prompt 中
123
+ * - system_prompt: `系统消息\n<chat_history>\nHuman: <user message>\nAssistant: <assistant message>\n</chat_history>`
124
+ * - prompt: `""`(空字符串)
125
+ *
126
+ * 4. **格式约定**:
127
+ * - prompt 中会自动拼接 Human 消息,所以 user 消息不需要 "Human:" 前缀
128
+ * - system_prompt 中的 user 消息需要 "Human:" 前缀
129
+ * - assistant 消息始终使用 "Assistant:" 前缀
130
+ *
131
+ * @note
132
+ * - 字符限制:系统消息长度不能超过 SYSTEM_PROMPT_LIMIT (4800) 字符
133
+ * - 消息数量:最多处理最近的 3 条对话消息(倒数第 1、2、3 条)
134
+ * - 历史限制:chat_history 最多包含 2 条消息,避免 prompt 过长
135
+ * - 错误处理:系统消息超限时返回错误,其他情况尽力处理
136
+ */
137
+ export function convertMessagesToFalPrompt(messages: OpenAIMessage[]): { system_prompt: string; prompt: string; error?: string } {
138
+ // 第一步:过滤空内容消息,分离系统消息和对话消息
139
+ const filtered_messages: OpenAIMessage[] = [];
140
+ let system_message_content = "";
141
+
142
+ for (const message of messages) {
143
+ const content = (message.content === null || message.content === undefined) ? "" : String(message.content).trim();
144
+ if (content.length > 0) {
145
+ if (message.role === 'system') {
146
+ system_message_content = content; // 只保留最后一个非系统消息
147
+ } else {
148
+ filtered_messages.push({
149
+ ...message,
150
+ content: content
151
+ });
152
+ }
153
+ }
154
+ }
155
+
156
+ // 检查系统消息长度限制
157
+ if (system_message_content.length > SYSTEM_PROMPT_LIMIT) {
158
+ return {
159
+ system_prompt: "",
160
+ prompt: "",
161
+ error: `System message too long: ${system_message_content.length} characters exceeds limit of ${SYSTEM_PROMPT_LIMIT} characters`
162
+ };
163
+ }
164
+
165
+ // 如果没有对话消息,直接返回
166
+ if (filtered_messages.length === 0) {
167
+ return {
168
+ system_prompt: system_message_content,
169
+ prompt: ""
170
+ };
171
+ }
172
+
173
+ // 第二步:倒序遍历messages,至多取3条user/assistant消息放到prompt部分
174
+ const prompt_messages = filtered_messages.slice(-3); // 取最后3条消息
175
+ const remaining_messages = filtered_messages.slice(0, -3); // 剩余的消息
176
+
177
+ // 第三步:构建prompt部分
178
+ let prompt_parts: string[] = [];
179
+
180
+ for (const message of prompt_messages) {
181
+ if (message.role === 'user') {
182
+ prompt_parts.push(String(message.content));
183
+ } else if (message.role === 'assistant') {
184
+ prompt_parts.push(`Assistant: ${String(message.content)}`);
185
+ }
186
+ }
187
+
188
+ const final_prompt = prompt_parts.join('\n');
189
+
190
+ // 第四步:构建system_prompt部分
191
+ let system_prompt_parts: string[] = [];
192
+
193
+ // 添加系统消息(如果存在)
194
+ if (system_message_content.length > 0) {
195
+ system_prompt_parts.push(system_message_content);
196
+ }
197
+
198
+ // 添加剩余的对话消息
199
+ for (const message of remaining_messages) {
200
+ if (message.role === 'user') {
201
+ system_prompt_parts.push(`Human: ${String(message.content)}`);
202
+ } else if (message.role === 'assistant') {
203
+ system_prompt_parts.push(`Assistant: ${String(message.content)}`);
204
+ }
205
+ }
206
+
207
+ let final_system_prompt = system_prompt_parts.join('\n');
208
+
209
+ // 第五步:检查system_prompt字符限制并截断
210
+ if (final_system_prompt.length > SYSTEM_PROMPT_LIMIT) {
211
+ // 优先保留系统消息,然后从最新的对话开始截断
212
+ const system_part = system_message_content;
213
+ let remaining_space = SYSTEM_PROMPT_LIMIT - system_part.length - 1; // -1 for newline
214
+
215
+ if (remaining_space <= 0) {
216
+ final_system_prompt = system_part;
217
+ } else {
218
+ const conversation_parts: string[] = [];
219
+
220
+ // 倒序添加剩余对话,确保不超过字符限制
221
+ for (let i = remaining_messages.length - 1; i >= 0; i--) {
222
+ const message = remaining_messages[i];
223
+ let message_text = "";
224
+
225
+ if (message.role === 'user') {
226
+ message_text = `Human: ${String(message.content)}`;
227
+ } else if (message.role === 'assistant') {
228
+ message_text = `Assistant: ${String(message.content)}`;
229
+ }
230
+
231
+ if (message_text.length + 1 <= remaining_space) { // +1 for newline
232
+ conversation_parts.unshift(message_text);
233
+ remaining_space -= (message_text.length + 1);
234
+ } else {
235
+ break; // 无法添加更多消息
236
+ }
237
+ }
238
+
239
+ if (system_part.length > 0 && conversation_parts.length > 0) {
240
+ final_system_prompt = system_part + '\n' + conversation_parts.join('\n');
241
+ } else if (system_part.length > 0) {
242
+ final_system_prompt = system_part;
243
+ } else {
244
+ final_system_prompt = conversation_parts.join('\n');
245
+ }
246
+ }
247
+ }
248
+
249
+ return {
250
+ system_prompt: final_system_prompt,
251
+ prompt: final_prompt
252
+ };
253
  }
 
254
 
255
 
256
  // POST /v1/chat/completions endpoint (保持不变)