mslxdff 0.1.106 → 0.1.107
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/upstream-responses.js +85 -100
package/package.json
CHANGED
|
@@ -25,7 +25,9 @@ export function chatToResponsesBody(chatBody) {
|
|
|
25
25
|
return base;
|
|
26
26
|
});
|
|
27
27
|
const input = inputParts.join("\n\n") || "hi";
|
|
28
|
-
|
|
28
|
+
// 流式意图透传:客户端要 SSE 就向上游要 SSE(reshapeResponsesSse 负责转回 chat SSE)。
|
|
29
|
+
// 写死 stream:false 是历史折衷(当时聚合 JSON 直回),已由完整 SSE 转换取代。
|
|
30
|
+
const out = { model: chatBody.model, input, stream: chatBody?.stream === true };
|
|
29
31
|
if (system) out.instructions = system;
|
|
30
32
|
// responses 的 tools 形状为平铺 {type,name,description,parameters},而 chat 为 {type,function:{name,...}}
|
|
31
33
|
if (Array.isArray(chatBody.tools) && chatBody.tools.length) {
|
|
@@ -120,7 +122,6 @@ export function reshapeResponsesSse(res, fallbackModel) {
|
|
|
120
122
|
const ct = res.headers?.get?.("content-type") || "";
|
|
121
123
|
if (res.status !== 200 || !ct.includes("text/event-stream") || !res.body) return res;
|
|
122
124
|
} catch { return res; }
|
|
123
|
-
const reader = res.body.getReader();
|
|
124
125
|
const decoder = new TextDecoder();
|
|
125
126
|
const encoder = new TextEncoder();
|
|
126
127
|
let buf = "";
|
|
@@ -143,22 +144,23 @@ export function reshapeResponsesSse(res, fallbackModel) {
|
|
|
143
144
|
return `data: ${JSON.stringify(payload)}\n\n`;
|
|
144
145
|
}
|
|
145
146
|
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
147
|
+
function sendRole(out) {
|
|
148
|
+
if (!hasSentRole) {
|
|
149
|
+
hasSentRole = true;
|
|
150
|
+
out += chatChunk({ role: "assistant" }, null);
|
|
151
|
+
}
|
|
152
|
+
return out;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
// TransformStream 泵:for await 直接驱动上游流,writer.write 背压回压。
|
|
156
|
+
// (自建 ReadableStream 的 pull 调度在本机 daemon 下出现"pull resolve 后不再续拉"
|
|
157
|
+
// 导致 muse SSE 卡死;async-iterator 泵是 undici 流已验证畅通的姿势)
|
|
158
|
+
const { readable, writable } = new TransformStream();
|
|
159
|
+
const writer = writable.getWriter();
|
|
160
|
+
(async () => {
|
|
161
|
+
try {
|
|
162
|
+
for await (const chunk of res.body) {
|
|
163
|
+
buf += decoder.decode(chunk, { stream: true });
|
|
162
164
|
let out = "";
|
|
163
165
|
// 按 \n\n 分事件
|
|
164
166
|
while (true) {
|
|
@@ -181,92 +183,75 @@ export function reshapeResponsesSse(res, fallbackModel) {
|
|
|
181
183
|
if (data.response?.id) respId = data.response.id;
|
|
182
184
|
if (data.response?.model) respModel = data.response.model;
|
|
183
185
|
if (data.response?.created_at) created = Math.floor(data.response.created_at);
|
|
184
|
-
|
|
185
|
-
//
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
}
|
|
241
|
-
out += chatChunk({ tool_calls: [{ index: entry.idx, function: { arguments: deltaArgs } }] }, null);
|
|
242
|
-
}
|
|
243
|
-
} else if (data.type === "response.function_call_arguments.done") {
|
|
244
|
-
const outIdx = Number(data.output_index ?? 1);
|
|
245
|
-
const entry = toolMap.get(outIdx) || { idx: Math.max(0, outIdx - 1) };
|
|
246
|
-
const args = data.arguments || "";
|
|
247
|
-
if (args && !toolMap.get(outIdx)?._done) {
|
|
248
|
-
// done 可能带全量,若未通过 delta 发送过,补发
|
|
249
|
-
// 已通过 delta 流式发送则忽略,避免重复
|
|
250
|
-
}
|
|
251
|
-
} else if (data.type === "response.output_item.done" && data.item?.type === "function_call") {
|
|
252
|
-
// 可忽略,已通过 added+delta 完整
|
|
253
|
-
}
|
|
254
|
-
// reasoning 加密块忽略
|
|
186
|
+
// created/in_progress 即发 role 帧:muse reasoning 阶段可达数十秒,
|
|
187
|
+
// 尽早产出首帧避免 relay 的首块超时(25s)误杀
|
|
188
|
+
if (data.type === "response.created" || data.type === "response.in_progress") {
|
|
189
|
+
out = sendRole(out);
|
|
190
|
+
}
|
|
191
|
+
if (curEvent === "response.output_text.delta" || data.type === "response.output_text.delta") {
|
|
192
|
+
const deltaText = data.delta || "";
|
|
193
|
+
if (deltaText) {
|
|
194
|
+
out = sendRole(out);
|
|
195
|
+
out += chatChunk({ content: deltaText }, null);
|
|
196
|
+
}
|
|
197
|
+
} else if (curEvent === "response.completed" || data.type === "response.completed") {
|
|
198
|
+
const usage = data.response?.usage || null;
|
|
199
|
+
// 若有 tool_calls,finish 应为 tool_calls
|
|
200
|
+
const hasTools = toolMap.size > 0;
|
|
201
|
+
const finish = hasTools ? "tool_calls" : (data.response?.status === "completed" ? "stop" : null);
|
|
202
|
+
// 末帧带 usage
|
|
203
|
+
const id = respId || `resp_${Date.now()}`;
|
|
204
|
+
const payload = {
|
|
205
|
+
id,
|
|
206
|
+
object: "chat.completion.chunk",
|
|
207
|
+
created,
|
|
208
|
+
model: respModel,
|
|
209
|
+
choices: [{ index: 0, delta: {}, finish_reason: finish }],
|
|
210
|
+
usage: usage || undefined,
|
|
211
|
+
};
|
|
212
|
+
out += `data: ${JSON.stringify(payload)}\n\n`;
|
|
213
|
+
} else if (data.type === "response.output_item.added" && data.item?.type === "message") {
|
|
214
|
+
// message 开始,可发送 role
|
|
215
|
+
out = sendRole(out);
|
|
216
|
+
} else if (data.type === "response.output_item.added" && data.item?.type === "function_call") {
|
|
217
|
+
const outIdx = Number(data.output_index ?? 1);
|
|
218
|
+
const toolIdx = Math.max(0, outIdx - 1);
|
|
219
|
+
const callId = data.item?.call_id || data.item?.id || "";
|
|
220
|
+
const name = data.item?.name || "";
|
|
221
|
+
toolMap.set(outIdx, { idx: toolIdx, id: callId, name });
|
|
222
|
+
out = sendRole(out);
|
|
223
|
+
const tc = { index: toolIdx, id: callId, type: "function", function: { name, arguments: "" } };
|
|
224
|
+
// 清理空字符串,避免 undefined
|
|
225
|
+
if (!callId) delete tc.id;
|
|
226
|
+
if (!name) delete tc.function.name;
|
|
227
|
+
out += chatChunk({ tool_calls: [tc] }, null);
|
|
228
|
+
} else if (data.type === "response.function_call_arguments.delta") {
|
|
229
|
+
const outIdx = Number(data.output_index ?? 1);
|
|
230
|
+
const entry = toolMap.get(outIdx) || { idx: Math.max(0, outIdx - 1) };
|
|
231
|
+
const deltaArgs = data.delta || "";
|
|
232
|
+
if (deltaArgs) {
|
|
233
|
+
out = sendRole(out);
|
|
234
|
+
out += chatChunk({ tool_calls: [{ index: entry.idx, function: { arguments: deltaArgs } }] }, null);
|
|
235
|
+
}
|
|
236
|
+
} else if (data.type === "response.function_call_arguments.done") {
|
|
237
|
+
// done 可能带全量,若未通过 delta 发送过则补发;已通过 delta 发送则忽略,避免重复
|
|
238
|
+
} else if (data.type === "response.output_item.done" && data.item?.type === "function_call") {
|
|
239
|
+
// 可忽略,已通过 added+delta 完整
|
|
240
|
+
}
|
|
241
|
+
// reasoning 加密块忽略
|
|
255
242
|
}
|
|
256
|
-
if (out)
|
|
257
|
-
} catch {
|
|
258
|
-
closed = true;
|
|
259
|
-
try { controller.close(); } catch {}
|
|
243
|
+
if (out) await writer.write(encoder.encode(out));
|
|
260
244
|
}
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
try {
|
|
265
|
-
}
|
|
266
|
-
});
|
|
245
|
+
await writer.write(encoder.encode("data: [DONE]\n\n"));
|
|
246
|
+
await writer.close();
|
|
247
|
+
} catch (e) {
|
|
248
|
+
try { await writer.abort(e instanceof Error ? e : new Error(String(e))); } catch { try { writer.close(); } catch {} }
|
|
249
|
+
}
|
|
250
|
+
})();
|
|
267
251
|
const headers = new Headers(res.headers);
|
|
268
252
|
headers.set("content-type", "text/event-stream");
|
|
269
|
-
const out = new Response(
|
|
253
|
+
const out = new Response(readable, { status: res.status, statusText: res.statusText, headers });
|
|
270
254
|
try { out._t = res._t; } catch {}
|
|
271
255
|
return out;
|
|
272
256
|
}
|
|
257
|
+
|