webaiapi 0.0.0-stage → 0.1.0

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 (205) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +184 -2
  3. package/bin/webapi.mjs +45 -0
  4. package/dist/browser/auth-capture.d.ts +110 -0
  5. package/dist/browser/auth-capture.d.ts.map +1 -0
  6. package/dist/browser/auth-capture.js +487 -0
  7. package/dist/browser/auth-capture.js.map +1 -0
  8. package/dist/browser/credentials.d.ts +102 -0
  9. package/dist/browser/credentials.d.ts.map +1 -0
  10. package/dist/browser/credentials.js +209 -0
  11. package/dist/browser/credentials.js.map +1 -0
  12. package/dist/browser/js-eval.d.ts +44 -0
  13. package/dist/browser/js-eval.d.ts.map +1 -0
  14. package/dist/browser/js-eval.js +20 -0
  15. package/dist/browser/js-eval.js.map +1 -0
  16. package/dist/browser/login-flow.d.ts +171 -0
  17. package/dist/browser/login-flow.d.ts.map +1 -0
  18. package/dist/browser/login-flow.js +606 -0
  19. package/dist/browser/login-flow.js.map +1 -0
  20. package/dist/browser/manager.d.ts +144 -0
  21. package/dist/browser/manager.d.ts.map +1 -0
  22. package/dist/browser/manager.js +586 -0
  23. package/dist/browser/manager.js.map +1 -0
  24. package/dist/browser/platforms.d.ts +96 -0
  25. package/dist/browser/platforms.d.ts.map +1 -0
  26. package/dist/browser/platforms.js +288 -0
  27. package/dist/browser/platforms.js.map +1 -0
  28. package/dist/browser/sse-capture.d.ts +23 -0
  29. package/dist/browser/sse-capture.d.ts.map +1 -0
  30. package/dist/browser/sse-capture.js +199 -0
  31. package/dist/browser/sse-capture.js.map +1 -0
  32. package/dist/browser/stealth.d.ts +12 -0
  33. package/dist/browser/stealth.d.ts.map +1 -0
  34. package/dist/browser/stealth.js +12 -0
  35. package/dist/browser/stealth.js.map +1 -0
  36. package/dist/cli.d.ts +12 -0
  37. package/dist/cli.d.ts.map +1 -0
  38. package/dist/cli.js +441 -0
  39. package/dist/cli.js.map +1 -0
  40. package/dist/config.d.ts +90 -0
  41. package/dist/config.d.ts.map +1 -0
  42. package/dist/config.js +246 -0
  43. package/dist/config.js.map +1 -0
  44. package/dist/controllers/history-db.d.ts +56 -0
  45. package/dist/controllers/history-db.d.ts.map +1 -0
  46. package/dist/controllers/history-db.js +260 -0
  47. package/dist/controllers/history-db.js.map +1 -0
  48. package/dist/controllers/request-pool.d.ts +78 -0
  49. package/dist/controllers/request-pool.d.ts.map +1 -0
  50. package/dist/controllers/request-pool.js +222 -0
  51. package/dist/controllers/request-pool.js.map +1 -0
  52. package/dist/controllers/router.d.ts +172 -0
  53. package/dist/controllers/router.d.ts.map +1 -0
  54. package/dist/controllers/router.js +569 -0
  55. package/dist/controllers/router.js.map +1 -0
  56. package/dist/index.d.ts +72 -0
  57. package/dist/index.d.ts.map +1 -0
  58. package/dist/index.js +61 -0
  59. package/dist/index.js.map +1 -0
  60. package/dist/log/api-logger.d.ts +55 -0
  61. package/dist/log/api-logger.d.ts.map +1 -0
  62. package/dist/log/api-logger.js +264 -0
  63. package/dist/log/api-logger.js.map +1 -0
  64. package/dist/log/debug-logger.d.ts +49 -0
  65. package/dist/log/debug-logger.d.ts.map +1 -0
  66. package/dist/log/debug-logger.js +285 -0
  67. package/dist/log/debug-logger.js.map +1 -0
  68. package/dist/log/logger.d.ts +16 -0
  69. package/dist/log/logger.d.ts.map +1 -0
  70. package/dist/log/logger.js +35 -0
  71. package/dist/log/logger.js.map +1 -0
  72. package/dist/postinstall.d.ts +3 -0
  73. package/dist/postinstall.d.ts.map +1 -0
  74. package/dist/postinstall.js +90 -0
  75. package/dist/postinstall.js.map +1 -0
  76. package/dist/providers/base.d.ts +83 -0
  77. package/dist/providers/base.d.ts.map +1 -0
  78. package/dist/providers/base.js +51 -0
  79. package/dist/providers/base.js.map +1 -0
  80. package/dist/providers/chatglm-dom-extract.d.ts +24 -0
  81. package/dist/providers/chatglm-dom-extract.d.ts.map +1 -0
  82. package/dist/providers/chatglm-dom-extract.js +175 -0
  83. package/dist/providers/chatglm-dom-extract.js.map +1 -0
  84. package/dist/providers/chatglm-dom.d.ts +110 -0
  85. package/dist/providers/chatglm-dom.d.ts.map +1 -0
  86. package/dist/providers/chatglm-dom.js +384 -0
  87. package/dist/providers/chatglm-dom.js.map +1 -0
  88. package/dist/providers/chatglm-sse.d.ts +119 -0
  89. package/dist/providers/chatglm-sse.d.ts.map +1 -0
  90. package/dist/providers/chatglm-sse.js +241 -0
  91. package/dist/providers/chatglm-sse.js.map +1 -0
  92. package/dist/providers/chatglm.d.ts +288 -0
  93. package/dist/providers/chatglm.d.ts.map +1 -0
  94. package/dist/providers/chatglm.js +1631 -0
  95. package/dist/providers/chatglm.js.map +1 -0
  96. package/dist/routes/chat.d.ts +117 -0
  97. package/dist/routes/chat.d.ts.map +1 -0
  98. package/dist/routes/chat.js +1346 -0
  99. package/dist/routes/chat.js.map +1 -0
  100. package/dist/routes/compress.d.ts +142 -0
  101. package/dist/routes/compress.d.ts.map +1 -0
  102. package/dist/routes/compress.js +551 -0
  103. package/dist/routes/compress.js.map +1 -0
  104. package/dist/routes/failover.d.ts +34 -0
  105. package/dist/routes/failover.d.ts.map +1 -0
  106. package/dist/routes/failover.js +221 -0
  107. package/dist/routes/failover.js.map +1 -0
  108. package/dist/routes/helpers.d.ts +174 -0
  109. package/dist/routes/helpers.d.ts.map +1 -0
  110. package/dist/routes/helpers.js +525 -0
  111. package/dist/routes/helpers.js.map +1 -0
  112. package/dist/routes/meta.d.ts +31 -0
  113. package/dist/routes/meta.d.ts.map +1 -0
  114. package/dist/routes/meta.js +184 -0
  115. package/dist/routes/meta.js.map +1 -0
  116. package/dist/routes/sse.d.ts +82 -0
  117. package/dist/routes/sse.d.ts.map +1 -0
  118. package/dist/routes/sse.js +77 -0
  119. package/dist/routes/sse.js.map +1 -0
  120. package/dist/server/app.d.ts +24 -0
  121. package/dist/server/app.d.ts.map +1 -0
  122. package/dist/server/app.js +292 -0
  123. package/dist/server/app.js.map +1 -0
  124. package/dist/server/middleware/api-key.d.ts +61 -0
  125. package/dist/server/middleware/api-key.d.ts.map +1 -0
  126. package/dist/server/middleware/api-key.js +95 -0
  127. package/dist/server/middleware/api-key.js.map +1 -0
  128. package/dist/server/middleware/rate-limit.d.ts +67 -0
  129. package/dist/server/middleware/rate-limit.d.ts.map +1 -0
  130. package/dist/server/middleware/rate-limit.js +134 -0
  131. package/dist/server/middleware/rate-limit.js.map +1 -0
  132. package/dist/server/middleware/timeout.d.ts +34 -0
  133. package/dist/server/middleware/timeout.d.ts.map +1 -0
  134. package/dist/server/middleware/timeout.js +64 -0
  135. package/dist/server/middleware/timeout.js.map +1 -0
  136. package/dist/server/types.d.ts +20 -0
  137. package/dist/server/types.d.ts.map +1 -0
  138. package/dist/server/types.js +2 -0
  139. package/dist/server/types.js.map +1 -0
  140. package/dist/tools/build.d.ts +14 -0
  141. package/dist/tools/build.d.ts.map +1 -0
  142. package/dist/tools/build.js +105 -0
  143. package/dist/tools/build.js.map +1 -0
  144. package/dist/tools/clean.d.ts +32 -0
  145. package/dist/tools/clean.d.ts.map +1 -0
  146. package/dist/tools/clean.js +65 -0
  147. package/dist/tools/clean.js.map +1 -0
  148. package/dist/tools/convert.d.ts +50 -0
  149. package/dist/tools/convert.d.ts.map +1 -0
  150. package/dist/tools/convert.js +289 -0
  151. package/dist/tools/convert.js.map +1 -0
  152. package/dist/tools/names.d.ts +138 -0
  153. package/dist/tools/names.d.ts.map +1 -0
  154. package/dist/tools/names.js +578 -0
  155. package/dist/tools/names.js.map +1 -0
  156. package/dist/tools/parse.d.ts +31 -0
  157. package/dist/tools/parse.d.ts.map +1 -0
  158. package/dist/tools/parse.js +273 -0
  159. package/dist/tools/parse.js.map +1 -0
  160. package/dist/tools/placeholder.d.ts +21 -0
  161. package/dist/tools/placeholder.d.ts.map +1 -0
  162. package/dist/tools/placeholder.js +135 -0
  163. package/dist/tools/placeholder.js.map +1 -0
  164. package/dist/tools/prompts.d.ts +42 -0
  165. package/dist/tools/prompts.d.ts.map +1 -0
  166. package/dist/tools/prompts.js +69 -0
  167. package/dist/tools/prompts.js.map +1 -0
  168. package/dist/tools/protocol.d.ts +58 -0
  169. package/dist/tools/protocol.d.ts.map +1 -0
  170. package/dist/tools/protocol.js +287 -0
  171. package/dist/tools/protocol.js.map +1 -0
  172. package/dist/tools/repair.d.ts +83 -0
  173. package/dist/tools/repair.d.ts.map +1 -0
  174. package/dist/tools/repair.js +510 -0
  175. package/dist/tools/repair.js.map +1 -0
  176. package/dist/tools/workspace.d.ts +40 -0
  177. package/dist/tools/workspace.d.ts.map +1 -0
  178. package/dist/tools/workspace.js +224 -0
  179. package/dist/tools/workspace.js.map +1 -0
  180. package/dist/types/message.d.ts +87 -0
  181. package/dist/types/message.d.ts.map +1 -0
  182. package/dist/types/message.js +85 -0
  183. package/dist/types/message.js.map +1 -0
  184. package/dist/types/openai.d.ts +100 -0
  185. package/dist/types/openai.d.ts.map +1 -0
  186. package/dist/types/openai.js +66 -0
  187. package/dist/types/openai.js.map +1 -0
  188. package/dist/utils/errors.d.ts +82 -0
  189. package/dist/utils/errors.d.ts.map +1 -0
  190. package/dist/utils/errors.js +115 -0
  191. package/dist/utils/errors.js.map +1 -0
  192. package/dist/utils/mutex.d.ts +31 -0
  193. package/dist/utils/mutex.d.ts.map +1 -0
  194. package/dist/utils/mutex.js +61 -0
  195. package/dist/utils/mutex.js.map +1 -0
  196. package/dist/utils/prompt.d.ts +150 -0
  197. package/dist/utils/prompt.d.ts.map +1 -0
  198. package/dist/utils/prompt.js +514 -0
  199. package/dist/utils/prompt.js.map +1 -0
  200. package/dist/utils/text.d.ts +30 -0
  201. package/dist/utils/text.d.ts.map +1 -0
  202. package/dist/utils/text.js +70 -0
  203. package/dist/utils/text.js.map +1 -0
  204. package/package.json +67 -4
  205. package/postinstall.mjs +34 -0
@@ -0,0 +1,78 @@
1
+ export type RequestStatus = 'pending' | 'processing' | 'completed' | 'failed';
2
+ /** 池中的一条请求记录。 */
3
+ export interface PooledRequest {
4
+ request_id: string;
5
+ platform: string;
6
+ model: string;
7
+ message: string;
8
+ conversation_id: string | null;
9
+ status: RequestStatus;
10
+ created_at: number;
11
+ started_at: number | null;
12
+ completed_at: number | null;
13
+ result: string | null;
14
+ error: string | null;
15
+ /** 分配的账号 ID。 */
16
+ account_id: string | null;
17
+ /** 分配的浏览器实例 ID。 */
18
+ instance_id: string | null;
19
+ }
20
+ /** 池状态(`/v1/status` 用)。 */
21
+ export interface RequestPoolStatus {
22
+ total_requests: number;
23
+ queue_size: number;
24
+ max_size: number;
25
+ status_counts: Record<RequestStatus, number>;
26
+ }
27
+ export declare class RequestPool {
28
+ private readonly maxSize;
29
+ private readonly requests;
30
+ private readonly pendingQueue;
31
+ private readonly lock;
32
+ constructor(maxSize?: number);
33
+ /** 添加请求到池中。 */
34
+ addRequest(params: {
35
+ requestId: string;
36
+ platform: string;
37
+ model: string;
38
+ message: string;
39
+ conversationId?: string | null;
40
+ }): Promise<PooledRequest>;
41
+ /** 标记请求为已完成。 */
42
+ markCompleted(requestId: string, result: string): Promise<void>;
43
+ /** 标记请求为失败。 */
44
+ markFailed(requestId: string, error: string): Promise<void>;
45
+ /** 当前队列长度。 */
46
+ getQueueSize(): number;
47
+ /** 池状态汇总。 */
48
+ getStatus(): RequestPoolStatus;
49
+ }
50
+ /**
51
+ * 智能调度器(负载视图)。
52
+ *
53
+ * ⚠️ 在 Python 版里这套负载计数**从来没有被写入过**(没人调用 increment/decrement),
54
+ * 所以 `getLoadStatus()` 一直返回两个空对象。这里保持同样的行为与字段形状,
55
+ * 免得 `/v1/status` 的消费方对不上。
56
+ */
57
+ export declare class IntelligentScheduler {
58
+ private readonly instanceLoads;
59
+ private readonly accountLoads;
60
+ private readonly lock;
61
+ constructor();
62
+ /** 增加负载计数。 */
63
+ incrementLoad(instanceId: string, accountId: string): Promise<void>;
64
+ /** 减少负载计数(减到 0 就移除条目,与 Python 一致)。 */
65
+ decrementLoad(instanceId: string, accountId: string): Promise<void>;
66
+ /** 负载状态(供 `/v1/status` 展示)。 */
67
+ getLoadStatus(): {
68
+ instance_loads: Record<string, number>;
69
+ account_loads: Record<string, number>;
70
+ };
71
+ }
72
+ /** 获取请求池实例(单例)。 */
73
+ export declare function getRequestPool(): RequestPool;
74
+ /** 获取调度器实例(单例)。 */
75
+ export declare function getScheduler(): IntelligentScheduler;
76
+ /** 重置单例(测试用)。 */
77
+ export declare function __setRequestPoolForTest(pool: RequestPool | null, sched: IntelligentScheduler | null): void;
78
+ //# sourceMappingURL=request-pool.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"request-pool.d.ts","sourceRoot":"","sources":["../../src/controllers/request-pool.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,222 @@
1
+ /**
2
+ * 请求池 —— `legacy-python/src/controllers/request_pool.py` 的移植。
3
+ *
4
+ * 用途:给**非流式**请求做一次「入池 → 完成/失败」的记账,
5
+ * 并把汇总状态暴露在 `/v1/status` 上(`total_requests` / `queue_size` /
6
+ * `status_counts`)。
7
+ *
8
+ * ## 有意不移植的部分(Python 里定义但从未被调用)
9
+ * - `RequestPool.get_next_request()` / `get_pending_request()` / `mark_processing()` /
10
+ * `get_request()`:全仓库零调用。请求的「排队-取出」流程从来没跑过 ——
11
+ * 实际调度是 `_streamWithFailover()` 直接串行调用 Provider,池只做了记账。
12
+ * - `IntelligentScheduler.select_best_instance()`:零调用,而且它依赖的
13
+ * `instance_loads` / `account_loads` 也**从来没有被增加过**
14
+ * (`increment_load` / `decrement_load` 零调用),返回的永远是空字典。
15
+ * 这里保留 `getLoadStatus()`(`/v1/status` 需要这个字段形状)与
16
+ * increment/decrement(成本极低、语义完整),只删掉那个永远选不出东西的选择器。
17
+ *
18
+ * ## 与原实现的差异
19
+ * - `asyncio.Queue(maxsize=N)` → {@link BoundedQueue}:`asyncio.Queue` 在满时
20
+ * `await put()` 会**阻塞**;这里保留同样语义(满时等待),因为
21
+ * `addRequest` 是 `await` 调用的。想改成「不阻塞直接丢弃」请先确认调用方。
22
+ * - `logging.success(...)` → `logger.info(...)`(pino 没有 success 级别)。
23
+ */
24
+ import { logger } from '../log/logger.js';
25
+ import { Mutex } from '../utils/mutex.js';
26
+ /** 有界队列:满时 `put()` 等待(等价于 `asyncio.Queue(maxsize)`)。 */
27
+ class BoundedQueue {
28
+ capacity;
29
+ items = [];
30
+ waiters = [];
31
+ constructor(capacity) {
32
+ this.capacity = capacity;
33
+ }
34
+ /** 入队(满时等待直到有空位)。 */
35
+ async put(item) {
36
+ while (this.items.length >= this.capacity) {
37
+ await new Promise((resolve) => this.waiters.push(resolve));
38
+ }
39
+ this.items.push(item);
40
+ }
41
+ /** 出队(空时返回 null,非阻塞)。 */
42
+ getNowait() {
43
+ const item = this.items.shift() ?? null;
44
+ if (item !== null && this.waiters.length > 0) {
45
+ // 让出一个空位,唤醒最早等待的写入者
46
+ const wake = this.waiters.shift();
47
+ wake?.();
48
+ }
49
+ return item;
50
+ }
51
+ get size() {
52
+ return this.items.length;
53
+ }
54
+ }
55
+ export class RequestPool {
56
+ maxSize;
57
+ requests = new Map();
58
+ pendingQueue;
59
+ lock = new Mutex();
60
+ constructor(maxSize = 100) {
61
+ this.maxSize = maxSize;
62
+ this.pendingQueue = new BoundedQueue(maxSize);
63
+ logger.info(`请求池初始化: max_size=${maxSize}`);
64
+ }
65
+ /** 添加请求到池中。 */
66
+ async addRequest(params) {
67
+ const release = await this.lock.acquire();
68
+ try {
69
+ const request = {
70
+ request_id: params.requestId,
71
+ platform: params.platform,
72
+ model: params.model,
73
+ message: params.message,
74
+ conversation_id: params.conversationId ?? null,
75
+ status: 'pending',
76
+ created_at: Date.now() / 1000,
77
+ started_at: null,
78
+ completed_at: null,
79
+ result: null,
80
+ error: null,
81
+ account_id: null,
82
+ instance_id: null,
83
+ };
84
+ this.requests.set(params.requestId, request);
85
+ await this.pendingQueue.put(request);
86
+ logger.info(`请求已加入池中: ${params.requestId}`);
87
+ return request;
88
+ }
89
+ finally {
90
+ release();
91
+ }
92
+ }
93
+ /** 标记请求为已完成。 */
94
+ async markCompleted(requestId, result) {
95
+ const release = await this.lock.acquire();
96
+ try {
97
+ const request = this.requests.get(requestId);
98
+ if (!request)
99
+ return;
100
+ request.status = 'completed';
101
+ request.completed_at = Date.now() / 1000;
102
+ request.result = result;
103
+ if (typeof request.started_at === 'number' && request.started_at > 0) {
104
+ const duration = request.completed_at - request.started_at;
105
+ logger.info(`请求完成: ${requestId}, 耗时: ${duration.toFixed(2)}s`);
106
+ }
107
+ }
108
+ finally {
109
+ release();
110
+ }
111
+ }
112
+ /** 标记请求为失败。 */
113
+ async markFailed(requestId, error) {
114
+ const release = await this.lock.acquire();
115
+ try {
116
+ const request = this.requests.get(requestId);
117
+ if (!request)
118
+ return;
119
+ request.status = 'failed';
120
+ request.completed_at = Date.now() / 1000;
121
+ request.error = error;
122
+ logger.error(`请求失败: ${requestId}, 错误: ${error}`);
123
+ }
124
+ finally {
125
+ release();
126
+ }
127
+ }
128
+ /** 当前队列长度。 */
129
+ getQueueSize() {
130
+ return this.pendingQueue.size;
131
+ }
132
+ /** 池状态汇总。 */
133
+ getStatus() {
134
+ const statusCounts = {
135
+ pending: 0,
136
+ processing: 0,
137
+ completed: 0,
138
+ failed: 0,
139
+ };
140
+ for (const request of this.requests.values()) {
141
+ statusCounts[request.status] += 1;
142
+ }
143
+ return {
144
+ total_requests: this.requests.size,
145
+ queue_size: this.getQueueSize(),
146
+ max_size: this.maxSize,
147
+ status_counts: statusCounts,
148
+ };
149
+ }
150
+ }
151
+ /**
152
+ * 智能调度器(负载视图)。
153
+ *
154
+ * ⚠️ 在 Python 版里这套负载计数**从来没有被写入过**(没人调用 increment/decrement),
155
+ * 所以 `getLoadStatus()` 一直返回两个空对象。这里保持同样的行为与字段形状,
156
+ * 免得 `/v1/status` 的消费方对不上。
157
+ */
158
+ export class IntelligentScheduler {
159
+ instanceLoads = new Map();
160
+ accountLoads = new Map();
161
+ lock = new Mutex();
162
+ constructor() {
163
+ logger.info('智能调度器初始化');
164
+ }
165
+ /** 增加负载计数。 */
166
+ async incrementLoad(instanceId, accountId) {
167
+ const release = await this.lock.acquire();
168
+ try {
169
+ this.instanceLoads.set(instanceId, (this.instanceLoads.get(instanceId) ?? 0) + 1);
170
+ this.accountLoads.set(accountId, (this.accountLoads.get(accountId) ?? 0) + 1);
171
+ logger.debug(`增加负载: instance=${instanceId}, account=${accountId}`);
172
+ }
173
+ finally {
174
+ release();
175
+ }
176
+ }
177
+ /** 减少负载计数(减到 0 就移除条目,与 Python 一致)。 */
178
+ async decrementLoad(instanceId, accountId) {
179
+ const release = await this.lock.acquire();
180
+ try {
181
+ const inst = (this.instanceLoads.get(instanceId) ?? 0) - 1;
182
+ if (inst <= 0)
183
+ this.instanceLoads.delete(instanceId);
184
+ else
185
+ this.instanceLoads.set(instanceId, inst);
186
+ const acc = (this.accountLoads.get(accountId) ?? 0) - 1;
187
+ if (acc <= 0)
188
+ this.accountLoads.delete(accountId);
189
+ else
190
+ this.accountLoads.set(accountId, acc);
191
+ logger.debug(`减少负载: instance=${instanceId}, account=${accountId}`);
192
+ }
193
+ finally {
194
+ release();
195
+ }
196
+ }
197
+ /** 负载状态(供 `/v1/status` 展示)。 */
198
+ getLoadStatus() {
199
+ return {
200
+ instance_loads: Object.fromEntries(this.instanceLoads),
201
+ account_loads: Object.fromEntries(this.accountLoads),
202
+ };
203
+ }
204
+ }
205
+ let requestPool = null;
206
+ let scheduler = null;
207
+ /** 获取请求池实例(单例)。 */
208
+ export function getRequestPool() {
209
+ requestPool ??= new RequestPool();
210
+ return requestPool;
211
+ }
212
+ /** 获取调度器实例(单例)。 */
213
+ export function getScheduler() {
214
+ scheduler ??= new IntelligentScheduler();
215
+ return scheduler;
216
+ }
217
+ /** 重置单例(测试用)。 */
218
+ export function __setRequestPoolForTest(pool, sched) {
219
+ requestPool = pool;
220
+ scheduler = sched;
221
+ }
222
+ //# sourceMappingURL=request-pool.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"request-pool.js","sourceRoot":"","sources":["../../src/controllers/request-pool.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,172 @@
1
+ import { type BaseProvider } from '../providers/base.js';
2
+ /**
3
+ * 平台名作为独立模型暴露给用户:调用 `model=<平台名>` 时只调度该平台的 Provider,
4
+ * 不跨平台(与 `mix` 不同)。
5
+ *
6
+ * ⚠️ **本列表由 `browser/platforms.ts` 派生**(`providerImplemented: true` 的平台),
7
+ * 不再手写 —— 手写一份必然会和注册表写岔,而写岔的表现是
8
+ * 「菜单里有这个平台、但 `model=xxx` 报找不到模型」这类难查的问题。
9
+ *
10
+ * 目前只有 chatglm。DeepSeek/Kimi/Qianwen/Doubao/Zhipu 五个 Provider 已于
11
+ * 2026-10-12 删除(它们的 `_ensure_browser()` 调用从不存在的
12
+ * `browser_utils.ensure_browser_page()` → 一调用就 AttributeError,跨平台
13
+ * failover 实际上一直是失效的)。将来要加平台:实现 Provider →
14
+ * 在 `platforms.ts` 里把 `providerImplemented` 翻成 `true` →
15
+ * 在 {@link PROVIDER_FACTORIES} 加一行。有单测核对三者一致。
16
+ */
17
+ export declare const PLATFORM_MODEL_NAMES: readonly string[];
18
+ /**
19
+ * **真的**实现了工厂的平台。
20
+ *
21
+ * 存在的唯一理由:让「注册表的 `providerImplemented`」与「工厂表」能被**自动核对**
22
+ * (`PLATFORM_MODEL_NAMES` 是从 `providerImplemented` 派生的,拿它比对是自说自话)。
23
+ * 两者写岔的表现很难查:菜单里能看到这个平台、`model=xxx` 却报找不到模型。
24
+ *
25
+ * ⚠️ 不要在业务逻辑里用它挑平台 —— 那是 {@link PLATFORM_MODEL_NAMES} 的职责。
26
+ */
27
+ export declare const PROVIDER_IMPLEMENTED_PLATFORMS: readonly string[];
28
+ /** `mix` 候选三元组:`[providerName, provider, accountCount]`。 */
29
+ export type MixCandidate = [name: string, provider: BaseProvider, accountCount: number];
30
+ /** 账号总览条目(`/v1/status` 用)。 */
31
+ export interface AccountSummaryItem {
32
+ account_id: string;
33
+ identity: string;
34
+ cookies: number;
35
+ roles: string[];
36
+ cooldown_seconds: number;
37
+ busy: boolean;
38
+ }
39
+ export declare class ModelRouter {
40
+ /** Provider 注册表(键 = 平台名)。 */
41
+ readonly providers: Map<string, BaseProvider>;
42
+ /** 模型名 → Provider 名。 */
43
+ readonly modelToProvider: Map<string, string>;
44
+ /**
45
+ * 辅助通道 Provider(**平台 → 实例**)。
46
+ *
47
+ * 辅助任务(后台预压缩等)用的「另一个账号 + 独立 context 的专用页面」。
48
+ * 有了它辅助任务才能与主对话**同时**跑。为空时辅助任务回落到主通道(串行,原行为)。
49
+ */
50
+ readonly auxProviders: Map<string, BaseProvider>;
51
+ private auxLastScan;
52
+ /**
53
+ * 目前只有 ChatGLM 的 Provider 实现了「通道页面」(`channelKey`)。
54
+ * 其他平台为什么没启用,见 {@link getAuxCandidates} 的说明。
55
+ */
56
+ private static readonly AUX_CHANNEL_PLATFORMS;
57
+ /** 运行中惰性重扫辅助通道的最小间隔(秒):登录完第二个账号后**免重启**。 */
58
+ private static readonly AUX_RESCAN_INTERVAL;
59
+ constructor(options?: {
60
+ verbose?: boolean;
61
+ });
62
+ /**
63
+ * 启动时**随机**挑一个账号做主通道(另一个留给辅助通道)。
64
+ *
65
+ * 为什么要随机:主通道是用户对话的主力。如果永远固定用 `default`,
66
+ * 那个账号会先被限流/耗尽额度,而另一个账号一直闲着。随机分配把负载摊到两个账号上。
67
+ *
68
+ * 可用 `CHATGLM_MAIN_ACCOUNT=<id>` 钉死(运维需要时避免每次重启换主力账号)。
69
+ */
70
+ static pickRandomMainAccount(platform: string): string;
71
+ /** 注册所有 Browser Provider。 */
72
+ private registerProviders;
73
+ /** 注册 Provider,并把它声明的所有模型登记进 `modelToProvider`。 */
74
+ registerProvider(name: string, provider: BaseProvider): void;
75
+ /** 取凭证的账号身份标识(用于判断「是不是同一个账号」)。 */
76
+ private static identityOf;
77
+ /** Cookie 指纹(把两个凭证文件其实是同一登录会话的情况识别出来)。 */
78
+ private static cookieFingerprintOf;
79
+ /** 主通道**当前**用的账号(provider 被限流换号后这里会跟着变)。 */
80
+ private currentMainAccount;
81
+ /** 该账号是否归主通道使用(避免辅助通道和主通道撞同一个账号)。 */
82
+ private isAccountOwnedByMain;
83
+ /**
84
+ * 为辅助通道挑选「与主通道**不同账号**」的凭证。
85
+ *
86
+ * 策略:**优先同平台的第二个账号**;没有则暂不启用
87
+ * (其他平台的独立通道尚未实现,原因见 {@link getAuxCandidates})。
88
+ *
89
+ * @param verbose 没有通道时是否打日志。启动时 true;运行中惰性重扫时 false,
90
+ * 避免每分钟刷一条「未启用」。
91
+ */
92
+ private setupAuxChannels;
93
+ /**
94
+ * 辅助任务(压缩 / 两轮转换等)可用的通道列表。
95
+ *
96
+ * 顺序与理由:
97
+ * 1. **同平台的第二个账号**(独立通道)—— 不同账号、不同页面、不同锁,
98
+ * 可以与主对话真正并行,且不占用主账号的并发额度。
99
+ * 2. 其他平台 —— **当前不存在**:原先那批 Provider 都用**全局共享页面**
100
+ * (`playwright_manager._page`),与主通道并行会互相劫持页面(这是架构问题,
101
+ * 不是额度问题),且实现差异很大(例如会把消息截断到 8000 字符,
102
+ * 而压缩动辄几万字符 → 必然被截断损坏)。它们已于 2026-10-12 整体删除。
103
+ * 将来新增平台要启用辅助通道:先实现 `channelKey` 独立 context 页面,
104
+ * 再把平台名加进 {@link AUX_CHANNEL_PLATFORMS}。
105
+ * 3. 调用方拿不到候选时**回落到主候选**(同账号同页面 → 与主对话串行),
106
+ * 保证功能不退化。
107
+ *
108
+ * @returns 三元组列表;空列表表示没有独立通道可用,调用方应回落主候选。
109
+ */
110
+ getAuxCandidates(): MixCandidate[];
111
+ /**
112
+ * 账号总览:每个平台的账号、身份、以及它被用作 main / aux 还是闲置。
113
+ *
114
+ * 用途:把「多账号到底有没有生效」变成可验证的事实(`/v1/status`)。
115
+ * 实测踩坑:登录了两个账号但其实是同一个(浏览器里还是旧账号的登录态),
116
+ * 只看「账号数」看不出来,看身份才能。
117
+ */
118
+ getAccountsSummary(): Record<string, AccountSummaryItem[]>;
119
+ /**
120
+ * 根据模型名取 Provider。
121
+ *
122
+ * 策略:
123
+ * - `mix`:跨平台调度,按优先级依次尝试各平台;
124
+ * - 平台名(如 `chatglm`):直接返回该平台的 Provider;
125
+ * - 具体模型名(如 `glm-4`):映射到对应平台的 Provider。
126
+ *
127
+ * @throws {ModelNotFoundError} 模型不可用 / 凭证失效
128
+ */
129
+ getProvider(model: string): Promise<BaseProvider>;
130
+ /** 自动选择最优 Provider(`mix`:按优先级取第一个候选)。 */
131
+ getBestProvider(): Promise<BaseProvider>;
132
+ /**
133
+ * `mix` 模式的候选 Provider 列表。
134
+ *
135
+ * ## ⚠️ 排序与兜底(2026-09-29 修正,不可回退)
136
+ * 1. **平台优先顺序固定按 {@link PLATFORM_MODEL_NAMES}**(chatglm 第一)。
137
+ * 登录检查的结果**只用来告警,不决定谁先谁后,更不能把平台整体剔除**。
138
+ * 原因:chatglm 的 `checkLoginStatus()` 是**整页 DOM 探测**,刚重启 / 页面未就绪
139
+ * 时会误判「未登录」。若按「已验证优先」排序,一次探测抖动就会把 chatglm
140
+ * 排到后面甚至完全丢失 —— 2026-09-29 22:46 实测:`model=mix` 直接打到了
141
+ * (当时还注册着、但从来不可用的)deepseek/kimi,报
142
+ * `browser_utils has no attribute ensure_browser_page`,chatglm 压根没被尝试。
143
+ * (那批坏 Provider 已于 2026-10-12 删除,但这条规则仍要遵守:将来新增平台时
144
+ * 同样的抖动还会发生。)
145
+ * 2. **有账号的平台全部保留**(未通过登录检查的排在同优先级之后),
146
+ * 避免一次误判导致「所有平台均不可用」。
147
+ */
148
+ getMixCandidates(): Promise<MixCandidate[]>;
149
+ /** 所有已注册的模型名(来自各 Provider 的 `getModels()`)。 */
150
+ getAllModels(): string[];
151
+ /** 模型别名对应的平台名(未登记返回 `undefined`)。 */
152
+ platformOfModel(model: string): string | undefined;
153
+ /**
154
+ * 检查模型是否可用。
155
+ *
156
+ * ## ⚠️ 这里曾与 {@link registerProvider} 自相矛盾(实测踩过)
157
+ * 启动日志明明打了「注册模型: glm-4 -> chatglm」,请求 `model=glm-4` 却报
158
+ * 「模型 'glm-4' 不存在或不可用」—— 因为旧实现只看 `mix` / 平台名 /
159
+ * `AVAILABLE_MODELS`,而 `AVAILABLE_MODELS` 的**默认值是 `'mix'`**,
160
+ * 别名根本不在清单里。修复后以**登记表为准**:启动时真的注册了的模型就能路由。
161
+ */
162
+ isModelAvailable(modelId: string): boolean;
163
+ /** 已登录(有凭证)的平台名列表,作为可调用的独立模型。 */
164
+ getAvailablePlatformModels(): string[];
165
+ /** 所有 Provider(键 = 平台名)。 */
166
+ getAllProviders(): Map<string, BaseProvider>;
167
+ }
168
+ /** 获取全局模型路由器实例(单例)。 */
169
+ export declare function getModelRouter(): ModelRouter;
170
+ /** 重置单例(测试用)。 */
171
+ export declare function __setModelRouterForTest(router: ModelRouter | null): void;
172
+ //# sourceMappingURL=router.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"router.d.ts","sourceRoot":"","sources":["../../src/controllers/router.ts"],"names":[],"mappings":"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"}