mslxdff 0.1.88 → 0.1.90

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.
Files changed (46) hide show
  1. package/package.json +1 -1
  2. package/src/bench/cline-bench.js +21 -33
  3. package/src/bench/probe.js +4 -11
  4. package/src/bench/runner.js +8 -23
  5. package/src/bench/via-probe.js +12 -30
  6. package/src/bench/workbuddy-bench.js +15 -31
  7. package/src/chat/engine.js +160 -0
  8. package/src/chat/repl.js +32 -299
  9. package/src/chat/terminal.js +95 -0
  10. package/src/chat/tool-handlers.js +67 -0
  11. package/src/chat/upstream.js +1 -4
  12. package/src/chat-pipeline/auto-race.js +124 -0
  13. package/src/chat-pipeline/engine.js +17 -0
  14. package/src/chat-pipeline/index.js +86 -0
  15. package/src/chat-pipeline/planner.js +31 -0
  16. package/src/chat-pipeline/policy.js +73 -0
  17. package/src/chat-pipeline/serial-trial.js +144 -0
  18. package/src/cli/commands/model/list-providers.js +75 -0
  19. package/src/cli/commands/model/list-render.js +79 -0
  20. package/src/cli/commands/model/list.js +208 -0
  21. package/src/cli/commands/model/picks.js +45 -0
  22. package/src/cli/commands/model/stats.js +43 -0
  23. package/src/cli/commands/model/status.js +47 -0
  24. package/src/cli/commands/model.js +17 -371
  25. package/src/cli/help.js +2 -1
  26. package/src/providers/cline/chat.js +26 -53
  27. package/src/providers/workbuddy/chat.js +109 -221
  28. package/src/routes/chat/gateway.js +23 -286
  29. package/src/routes/chat/relay-pipeline.js +26 -0
  30. package/src/routes/groups-relay.js +85 -1
  31. package/src/routes/index.js +7 -1
  32. package/src/routes/relay-queue.js +53 -0
  33. package/src/runtime/bootstrap.js +14 -387
  34. package/src/runtime/broadband-stream.js +76 -0
  35. package/src/runtime/broadband.js +97 -0
  36. package/src/runtime/group-sync.js +28 -0
  37. package/src/runtime/providers-setup.js +147 -0
  38. package/src/runtime/server-lifecycle.js +156 -0
  39. package/src/state/facade.js +1 -0
  40. package/src/state/schemas/model.js +41 -0
  41. package/src/transport/index.js +248 -0
  42. package/src/transport/pool.js +60 -0
  43. package/src/transport/retry.js +24 -0
  44. package/src/transport/sse.js +93 -0
  45. package/src/upstream-responses.js +64 -0
  46. package/src/upstream.js +106 -334
@@ -0,0 +1,24 @@
1
+ export function backoffDelay(baseMs, attempt) {
2
+ // attempt 0 => base, 1 => base*2, 2 => base*4
3
+ return baseMs * (1 << attempt);
4
+ }
5
+
6
+ export function resolveRetry(status, attempt, config) {
7
+ const key = status === "network" ? "network" : String(status);
8
+ const entry = config?.[key];
9
+ if (!entry) return { shouldRetry: false, delayMs: 0 };
10
+ const attempts = Number(entry.attempts) || 0;
11
+ const base = Number(entry.delayMs) || 0;
12
+ if (attempt < attempts) {
13
+ return { shouldRetry: true, delayMs: backoffDelay(base, attempt) };
14
+ }
15
+ return { shouldRetry: false, delayMs: 0 };
16
+ }
17
+
18
+ export function shouldRetry(status, attempt, config) {
19
+ return resolveRetry(status, attempt, config).shouldRetry;
20
+ }
21
+
22
+ export function sleep(ms) {
23
+ return new Promise((r) => setTimeout(r, ms));
24
+ }
@@ -0,0 +1,93 @@
1
+ /**
2
+ * SSE 行协议深模块 — 纯函数 + 流式
3
+ * 负责 `data: / : / event: / [DONE]` 的解析与粘包 remain 处理
4
+ */
5
+
6
+ export function parseSseChunk(buf) {
7
+ const events = [];
8
+ let remain = String(buf || "");
9
+ // 按 \n\n 分帧,\r\n 也兼容
10
+ // 保留最后一帧若不以 \n\n 结尾则为 remain
11
+ const normalized = remain.replace(/\r\n/g, "\n");
12
+ const parts = normalized.split("\n\n");
13
+ // 最后一部分若原串不以 \n\n 结尾则为 remain
14
+ const endsWithDelim = normalized.endsWith("\n\n") || normalized.endsWith("\n\r\n");
15
+ // parts 最后一项在非结尾时为未完成帧
16
+ const complete = endsWithDelim ? parts : parts.slice(0, -1);
17
+ remain = endsWithDelim ? "" : parts[parts.length - 1] || "";
18
+ for (const frame of complete) {
19
+ if (!frame) continue; // 空帧
20
+ const lines = frame.split("\n");
21
+ const dataLines = [];
22
+ for (const line of lines) {
23
+ if (!line) continue;
24
+ if (line.startsWith(":")) continue; // 注释/keepalive
25
+ if (line.startsWith("event:")) continue; // 事件类型忽略
26
+ if (line.startsWith("data:")) {
27
+ let v = line.slice(5);
28
+ if (v.startsWith(" ")) v = v.slice(1);
29
+ dataLines.push(v);
30
+ }
31
+ }
32
+ if (dataLines.length === 0) continue;
33
+ // 多行 data 按 \n 拼接(SSE 规范)
34
+ const payload = dataLines.join("\n");
35
+ // 空 data: 仍产生事件,由上层过滤;此处保留以便测试可见
36
+ events.push(payload);
37
+ }
38
+ return { events, remain };
39
+ }
40
+
41
+ export function createSseParser() {
42
+ let buf = "";
43
+ return {
44
+ push(chunk) {
45
+ buf += String(chunk || "");
46
+ const { events, remain } = parseSseChunk(buf);
47
+ buf = remain;
48
+ return events;
49
+ },
50
+ flush() {
51
+ // 未形成完整帧的不发(避免半包误触发)
52
+ if (!buf.trim()) { buf = ""; return []; }
53
+ // 若残留包含完整 data: 行但缺结尾,不强行发
54
+ return [];
55
+ },
56
+ getRemain() { return buf; },
57
+ };
58
+ }
59
+
60
+ export async function* streamFromResponse(res) {
61
+ if (!res || !res.body) return;
62
+ const parser = createSseParser();
63
+ const reader = res.body.getReader ? res.body.getReader() : null;
64
+ const decoder = new TextDecoder();
65
+ if (reader) {
66
+ while (true) {
67
+ const { done, value } = await reader.read();
68
+ if (done) break;
69
+ const text = decoder.decode(value, { stream: true });
70
+ const evs = parser.push(text);
71
+ for (const e of evs) {
72
+ if (e === "[DONE]") return;
73
+ if (e === "") continue;
74
+ yield e;
75
+ }
76
+ }
77
+ // flush 残留
78
+ const rest = parser.flush();
79
+ for (const e of rest) {
80
+ if (e === "[DONE]") return;
81
+ if (e) yield e;
82
+ }
83
+ } else if (typeof res.text === "function") {
84
+ // 回退:一次性 text
85
+ const txt = await res.text();
86
+ const evs = parser.push(txt);
87
+ for (const e of evs) {
88
+ if (e === "[DONE]") return;
89
+ if (e === "") continue;
90
+ yield e;
91
+ }
92
+ }
93
+ }
@@ -0,0 +1,64 @@
1
+ /**
2
+ * responses 转换层 — 从 upstream.js 抽出的 muse-spark 专用形状转换。
3
+ * chat ⇄ responses 互转纯函数,无网络、无副作用。
4
+ */
5
+ export function isResponsesModel(model) {
6
+ return String(model || "").toLowerCase().startsWith("muse-spark");
7
+ }
8
+
9
+ export function chatToResponsesBody(chatBody) {
10
+ const msgs = Array.isArray(chatBody?.messages) ? chatBody.messages : [];
11
+ const system = msgs.filter((m) => m.role === "system").map((m) => String(m.content || "")).join("\n");
12
+ const nonSystem = msgs.filter((m) => m.role !== "system");
13
+ const inputParts = nonSystem.map((m) => {
14
+ const c = m.content;
15
+ if (typeof c === "string") return `${m.role}: ${c}`;
16
+ if (Array.isArray(c)) return `${m.role}: ${c.map((x) => x.text || "").join("")}`;
17
+ return `${m.role}: ${String(c || "")}`;
18
+ });
19
+ const input = inputParts.join("\n\n") || "hi";
20
+ const out = { model: chatBody.model, input, stream: false };
21
+ if (system) out.instructions = system;
22
+ if (chatBody.tools) out.tools = chatBody.tools;
23
+ if (chatBody.tool_choice) out.tool_choice = chatBody.tool_choice;
24
+ if (chatBody.temperature != null) out.temperature = chatBody.temperature;
25
+ if (chatBody.max_tokens != null) out.max_output_tokens = chatBody.max_tokens;
26
+ return out;
27
+ }
28
+
29
+ export function responsesToChatJson(respJson) {
30
+ let text = "";
31
+ for (const item of respJson.output || []) {
32
+ if (item.type === "message" && item.role === "assistant") {
33
+ for (const c of item.content || []) {
34
+ if (c.type === "output_text") text += c.text || "";
35
+ else if (c.type === "text") text += c.text || "";
36
+ }
37
+ }
38
+ }
39
+ if (!text) {
40
+ for (const item of respJson.output || []) {
41
+ if (item.type === "message") {
42
+ const t = item.content?.[0]?.text;
43
+ if (t) { text = t; break; }
44
+ }
45
+ }
46
+ }
47
+ const chatJson = {
48
+ id: respJson.id || `resp_${Date.now()}`,
49
+ object: "chat.completion",
50
+ created: Math.floor((respJson.created_at || Date.now() / 1000)),
51
+ model: respJson.model,
52
+ choices: [{ index: 0, finish_reason: respJson.status === "completed" ? "stop" : "length", message: { role: "assistant", content: text } }],
53
+ usage: respJson.usage || { prompt_tokens: 0, completion_tokens: 0, total_tokens: 0 },
54
+ };
55
+ return chatJson;
56
+ }
57
+
58
+ /** responses 成功 Response 转回 chat 形状(anon 兜底与主路径复用) */
59
+ export function toChatResponse(res, respJson) {
60
+ const chatJson = responsesToChatJson(respJson);
61
+ const headers = new Headers(res.headers);
62
+ headers.set("content-type", "application/json");
63
+ return new Response(JSON.stringify(chatJson), { status: res.status, headers });
64
+ }