mslxdff 0.1.215 → 0.1.216

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mslxdff",
3
- "version": "0.1.215",
3
+ "version": "0.1.216",
4
4
  "description": "测试项目,请勿使用。",
5
5
  "type": "module",
6
6
  "bin": {
@@ -10,7 +10,11 @@ export function createGate({ limit = 3 } = {}) {
10
10
  if (w) w();
11
11
  }
12
12
 
13
- async function run(fn, onWait) {
13
+ // 取得一个槽位,返回「释放一次」的幂等函数。超限时 FIFO 排队(并回调 onWait)。
14
+ // 关键:释放的时机交给调用方——流式请求必须**等流真正读完/取消**才释放,
15
+ // 否则 runChat 一返回(流还没消费)就放行,上游其实仍占着会话 ⇒ 第 4 路被上游拒
16
+ // (TM.00001041 并发会话数已达上限)。见 index.js 的 chat 接线。
17
+ async function acquire(onWait) {
14
18
  if (active >= cap) {
15
19
  const t0 = Date.now();
16
20
  const queued = waiters.length + 1;
@@ -18,15 +22,26 @@ export function createGate({ limit = 3 } = {}) {
18
22
  if (onWait) { try { onWait({ waitedMs: Date.now() - t0, queued, limit: cap }); } catch {} }
19
23
  }
20
24
  active++;
25
+ let released = false;
26
+ return function releaseOnce() {
27
+ if (released) return;
28
+ released = true;
29
+ release();
30
+ };
31
+ }
32
+
33
+ async function run(fn, onWait) {
34
+ const rel = await acquire(onWait);
21
35
  try {
22
36
  return await fn();
23
37
  } finally {
24
- release();
38
+ rel();
25
39
  }
26
40
  }
27
41
 
28
42
  return {
29
43
  run,
44
+ acquire,
30
45
  stats: () => ({ active, queued: waiters.length, limit: cap }),
31
46
  get active() { return active; },
32
47
  get queued() { return waiters.length; },
@@ -82,9 +82,48 @@ export function createCodeartsProvider({
82
82
  const onWait = (info) => {
83
83
  try { appendEvent({ type: "codearts-queue", ...info }); } catch {}
84
84
  };
85
+ // 流式请求必须在**流真正读完/取消后**才释放并发槽——否则 runChat 一返回(body 还没消费)
86
+ // 就 release,上游其实仍占着会话 ⇒ 第 4 路被上游拒(TM.00001041 并发会话数已达上限)。
87
+ // 非流式(JSON)在 runChat 内部已聚合完,可立即释放。返回的 Response 保持原 body 引用,
88
+ // 只包一层 tee 触发 release;出错也保证释放一次(releaseOnce 幂等)。
89
+ function holdUntilBodyDone(res, releaseOnce) {
90
+ const ct = res?.headers?.get?.("content-type") || "";
91
+ if (!ct.includes("text/event-stream") || !res.body) {
92
+ releaseOnce();
93
+ return res;
94
+ }
95
+ let done = false;
96
+ const fin = () => { if (!done) { done = true; releaseOnce(); } };
97
+ const onErr = () => fin();
98
+ try {
99
+ const reader = res.body.getReader();
100
+ const stream = new ReadableStream({
101
+ async pull(controller) {
102
+ try {
103
+ const { done: d, value } = await reader.read();
104
+ if (d) { fin(); controller.close(); return; }
105
+ controller.enqueue(value);
106
+ } catch (e) { onErr(); controller.error(e); }
107
+ },
108
+ cancel(reason) { fin(); try { return reader.cancel(reason); } catch { /* ignore */ } },
109
+ });
110
+ return new Response(stream, { status: res.status, statusText: res.statusText, headers: res.headers });
111
+ } catch {
112
+ fin();
113
+ return res;
114
+ }
115
+ }
85
116
  async function chat(body, opts = {}) {
86
117
  if (!ring.size) throw new Error("codearts: missing credential — run: mslxdff -provider codearts login");
87
- return gate.run(() => chatSvc.runChat(body, ring, opts), onWait);
118
+ const rel = await gate.acquire(onWait);
119
+ let res;
120
+ try {
121
+ res = await chatSvc.runChat(body, ring, opts);
122
+ } catch (err) {
123
+ rel();
124
+ throw err;
125
+ }
126
+ return holdUntilBodyDone(res, rel);
88
127
  }
89
128
  async function chatWithKeys(body, keys, opts = {}) {
90
129
  const blobs = (keys || []).filter((k) => typeof k === "string" && k.trim().startsWith("{"));
@@ -92,7 +131,15 @@ export function createCodeartsProvider({
92
131
  const tmpRing = createKeyRing(blobs, { cooldownMs });
93
132
  const tmpPool = createAuthPool({ keys: blobs, fetchImpl, stsHost, clock }); // 借用凭据不写回本节点
94
133
  const tmpSvc = createChatService({ id, baseUrl: chatSvcBase, chatPath, fetchImpl, dispatcher: null, authPool: tmpPool, catalog, connectTimeoutMs, clock });
95
- return gate.run(() => tmpSvc.runChat(body, tmpRing, opts), onWait);
134
+ const rel = await gate.acquire(onWait);
135
+ let res;
136
+ try {
137
+ res = await tmpSvc.runChat(body, tmpRing, opts);
138
+ } catch (err) {
139
+ rel();
140
+ throw err;
141
+ }
142
+ return holdUntilBodyDone(res, rel);
96
143
  }
97
144
 
98
145
  // 三路发现(主账号);结果写目录(benefit 判定按账号隔离)