agentdog 1.2.0 → 1.3.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 (114) hide show
  1. package/README.md +5 -4
  2. package/dist/ai-gateway/auth.d.ts.map +1 -1
  3. package/dist/ai-gateway/auth.js +7 -1
  4. package/dist/ai-gateway/auth.js.map +1 -1
  5. package/dist/ai-gateway/auth.test.js +11 -4
  6. package/dist/ai-gateway/auth.test.js.map +1 -1
  7. package/dist/ai-gateway/call-log.d.ts +18 -0
  8. package/dist/ai-gateway/call-log.d.ts.map +1 -0
  9. package/dist/ai-gateway/call-log.js +136 -0
  10. package/dist/ai-gateway/call-log.js.map +1 -0
  11. package/dist/ai-gateway/call-log.test.d.ts +2 -0
  12. package/dist/ai-gateway/call-log.test.d.ts.map +1 -0
  13. package/dist/ai-gateway/call-log.test.js +136 -0
  14. package/dist/ai-gateway/call-log.test.js.map +1 -0
  15. package/dist/ai-gateway/count-tokens.d.ts.map +1 -1
  16. package/dist/ai-gateway/count-tokens.js +11 -1
  17. package/dist/ai-gateway/count-tokens.js.map +1 -1
  18. package/dist/ai-gateway/dialects.d.ts +1 -0
  19. package/dist/ai-gateway/dialects.d.ts.map +1 -1
  20. package/dist/ai-gateway/dialects.js +7 -0
  21. package/dist/ai-gateway/dialects.js.map +1 -1
  22. package/dist/ai-gateway/gateway-server.d.ts +2 -3
  23. package/dist/ai-gateway/gateway-server.d.ts.map +1 -1
  24. package/dist/ai-gateway/gateway-server.js +183 -67
  25. package/dist/ai-gateway/gateway-server.js.map +1 -1
  26. package/dist/ai-gateway/integration.test.js +237 -3
  27. package/dist/ai-gateway/integration.test.js.map +1 -1
  28. package/dist/ai-gateway/ir/anthropic-out.d.ts +15 -0
  29. package/dist/ai-gateway/ir/anthropic-out.d.ts.map +1 -0
  30. package/dist/ai-gateway/ir/anthropic-out.js +173 -0
  31. package/dist/ai-gateway/ir/anthropic-out.js.map +1 -0
  32. package/dist/ai-gateway/ir/anthropic-out.test.d.ts +2 -0
  33. package/dist/ai-gateway/ir/anthropic-out.test.d.ts.map +1 -0
  34. package/dist/ai-gateway/ir/anthropic-out.test.js +173 -0
  35. package/dist/ai-gateway/ir/anthropic-out.test.js.map +1 -0
  36. package/dist/ai-gateway/ir/openai-in.d.ts +3 -0
  37. package/dist/ai-gateway/ir/openai-in.d.ts.map +1 -0
  38. package/dist/ai-gateway/ir/openai-in.js +133 -0
  39. package/dist/ai-gateway/ir/openai-in.js.map +1 -0
  40. package/dist/ai-gateway/ir/openai-in.test.d.ts +2 -0
  41. package/dist/ai-gateway/ir/openai-in.test.d.ts.map +1 -0
  42. package/dist/ai-gateway/ir/openai-in.test.js +108 -0
  43. package/dist/ai-gateway/ir/openai-in.test.js.map +1 -0
  44. package/dist/ai-gateway/ir/openai-sse.d.ts +22 -0
  45. package/dist/ai-gateway/ir/openai-sse.d.ts.map +1 -0
  46. package/dist/ai-gateway/ir/openai-sse.js +166 -0
  47. package/dist/ai-gateway/ir/openai-sse.js.map +1 -0
  48. package/dist/ai-gateway/ir/openai-sse.test.d.ts +2 -0
  49. package/dist/ai-gateway/ir/openai-sse.test.d.ts.map +1 -0
  50. package/dist/ai-gateway/ir/openai-sse.test.js +119 -0
  51. package/dist/ai-gateway/ir/openai-sse.test.js.map +1 -0
  52. package/dist/ai-gateway/ir-channel.d.ts +14 -1
  53. package/dist/ai-gateway/ir-channel.d.ts.map +1 -1
  54. package/dist/ai-gateway/ir-channel.js +52 -11
  55. package/dist/ai-gateway/ir-channel.js.map +1 -1
  56. package/dist/ai-gateway/model-router.d.ts.map +1 -1
  57. package/dist/ai-gateway/model-router.js +3 -0
  58. package/dist/ai-gateway/model-router.js.map +1 -1
  59. package/dist/ai-gateway/model-router.test.js +10 -0
  60. package/dist/ai-gateway/model-router.test.js.map +1 -1
  61. package/dist/ai-gateway/models-list.d.ts +9 -0
  62. package/dist/ai-gateway/models-list.d.ts.map +1 -0
  63. package/dist/ai-gateway/models-list.js +42 -0
  64. package/dist/ai-gateway/models-list.js.map +1 -0
  65. package/dist/ai-gateway/models-list.test.d.ts +2 -0
  66. package/dist/ai-gateway/models-list.test.d.ts.map +1 -0
  67. package/dist/ai-gateway/models-list.test.js +47 -0
  68. package/dist/ai-gateway/models-list.test.js.map +1 -0
  69. package/dist/ai-gateway/openai-errors.d.ts +7 -0
  70. package/dist/ai-gateway/openai-errors.d.ts.map +1 -0
  71. package/dist/ai-gateway/openai-errors.js +23 -0
  72. package/dist/ai-gateway/openai-errors.js.map +1 -0
  73. package/dist/ai-gateway/openai-errors.test.d.ts +2 -0
  74. package/dist/ai-gateway/openai-errors.test.d.ts.map +1 -0
  75. package/dist/ai-gateway/openai-errors.test.js +38 -0
  76. package/dist/ai-gateway/openai-errors.test.js.map +1 -0
  77. package/dist/ai-gateway/passthrough.d.ts +1 -0
  78. package/dist/ai-gateway/passthrough.d.ts.map +1 -1
  79. package/dist/ai-gateway/passthrough.js +104 -0
  80. package/dist/ai-gateway/passthrough.js.map +1 -1
  81. package/dist/ai-gateway/passthrough.test.js +66 -1
  82. package/dist/ai-gateway/passthrough.test.js.map +1 -1
  83. package/dist/daemon/agentdog-daemon.d.ts +1 -1
  84. package/dist/daemon/agentdog-daemon.d.ts.map +1 -1
  85. package/dist/daemon/agentdog-daemon.js +27 -1
  86. package/dist/daemon/agentdog-daemon.js.map +1 -1
  87. package/dist/daemon/daemon-web-server.ai.test.js +30 -3
  88. package/dist/daemon/daemon-web-server.ai.test.js.map +1 -1
  89. package/dist/daemon/daemon-web-server.d.ts +2 -0
  90. package/dist/daemon/daemon-web-server.d.ts.map +1 -1
  91. package/dist/daemon/daemon-web-server.js +83 -11
  92. package/dist/daemon/daemon-web-server.js.map +1 -1
  93. package/dist/logging/jsonl-store.d.ts +23 -0
  94. package/dist/logging/jsonl-store.d.ts.map +1 -0
  95. package/dist/logging/jsonl-store.js +107 -0
  96. package/dist/logging/jsonl-store.js.map +1 -0
  97. package/dist/logging/jsonl-store.test.d.ts +2 -0
  98. package/dist/logging/jsonl-store.test.d.ts.map +1 -0
  99. package/dist/logging/jsonl-store.test.js +89 -0
  100. package/dist/logging/jsonl-store.test.js.map +1 -0
  101. package/dist/logging/server-log-manager.d.ts +3 -0
  102. package/dist/logging/server-log-manager.d.ts.map +1 -1
  103. package/dist/logging/server-log-manager.js +20 -0
  104. package/dist/logging/server-log-manager.js.map +1 -1
  105. package/dist/types/index.d.ts +20 -0
  106. package/dist/types/index.d.ts.map +1 -1
  107. package/package.json +3 -2
  108. package/web/dist/assets/index-CHSrrPtl.css +1 -0
  109. package/web/dist/assets/index-gdp_c7vR.js +106 -0
  110. package/web/dist/assets/index-gdp_c7vR.js.map +1 -0
  111. package/web/dist/index.html +2 -2
  112. package/web/dist/assets/index-BdEVyQ8g.js +0 -118
  113. package/web/dist/assets/index-BdEVyQ8g.js.map +0 -1
  114. package/web/dist/assets/index-Bsl7BWhF.css +0 -1
@@ -1,89 +1,205 @@
1
- // AiGatewayServer:AI API 网关宿主(daemon 内组件,独立端口)
2
- // 对外提供 Anthropic Messages 方言端点,按 slug:model 寻址路由到上游供应商。
1
+ // AI API 网关(daemon 内组件):按 slug:model 寻址路由到上游供应商。
2
+ // 对外路由带方言前缀(/anthropic、/openai 两种方言入口),两种宿主形态共用 createGatewayRouter:
3
+ // 1) Web 端口挂载(常态):DaemonWebServer 把 router 挂在 Web 管理端口(61125)上;
4
+ // 2) 独立端口(回退):Web 服务未启动时 AiGatewayServer 自行监听 aiGateway.port。
3
5
  import express from 'express';
4
6
  import { randomUUID } from 'crypto';
5
7
  import { createGatewayAuth } from './auth.js';
6
8
  import { anthropicError } from './anthropic-errors.js';
7
9
  import { resolveModel, ModelRouteError } from './model-router.js';
8
- import { passthroughMessages } from './passthrough.js';
10
+ import { passthroughMessages, passthroughOpenAI } from './passthrough.js';
9
11
  import { handleCountTokens } from './count-tokens.js';
10
- import { handleIRChannel } from './ir-channel.js';
11
- import { openaiCodec, geminiCodec } from './dialects.js';
12
+ import { buildAnthropicModelsList, buildOpenAIModelsList } from './models-list.js';
13
+ import { handleIRChannel, anthropicIngress, openaiIngress } from './ir-channel.js';
14
+ import { openaiCodec, geminiCodec, anthropicCodec } from './dialects.js';
15
+ import { openaiError } from './openai-errors.js';
16
+ import { beginCallLog, patchCallLog, requestExcerptOf } from './call-log.js';
12
17
  const DEFAULT_PORT = 62125;
13
18
  const DEFAULT_HOST = '127.0.0.1';
14
19
  const MAX_BODY_BYTES = 32 * 1024 * 1024; // Claude Code 携带截图 base64
15
- export class AiGatewayServer {
16
- app;
17
- server;
18
- configManager;
19
- constructor(configManager) {
20
- this.configManager = configManager;
21
- this.app = express();
22
- this.setupRoutes();
20
+ // 网关方言路径:挂到 Web 端口时仅对这些前缀生效,/api 与静态资源不经过网关中间件
21
+ const GATEWAY_PATHS = ['/anthropic', '/openai', '/v1/messages', '/v1/models'];
22
+ function isGatewayPath(pathName) {
23
+ return GATEWAY_PATHS.some(p => pathName === p || pathName.startsWith(`${p}/`));
24
+ }
25
+ function handleMessages(configManager, req, res) {
26
+ beginCallLog(res, { ingress: 'anthropic', path: req.path, model: req.body?.model || '' });
27
+ try {
28
+ const model = req.body?.model || '';
29
+ const { provider, model: upstreamModel } = resolveModel(model, configManager.getAIProviders());
30
+ patchCallLog(res, { providerName: provider.slug, upstreamModel });
31
+ switch (provider.dialect) {
32
+ case 'anthropic':
33
+ void passthroughMessages(req, res, provider, upstreamModel);
34
+ break;
35
+ case 'openai':
36
+ void handleIRChannel(req, res, provider, upstreamModel, openaiCodec, anthropicIngress);
37
+ break;
38
+ case 'gemini':
39
+ void handleIRChannel(req, res, provider, upstreamModel, geminiCodec, anthropicIngress);
40
+ break;
41
+ }
23
42
  }
24
- setupRoutes() {
25
- this.app.use(express.json({ limit: `${MAX_BODY_BYTES / 1024 / 1024}mb` }));
26
- this.app.use((req, res, next) => {
27
- const gateway = this.configManager.getAIGatewayConfig();
28
- if (!gateway?.apiKey) {
29
- // 未配置(不应到达此处,start 前会生成)
30
- anthropicError(res, 503, 'api_error', 'AI gateway not configured');
31
- return;
43
+ catch (error) {
44
+ if (error instanceof ModelRouteError) {
45
+ patchCallLog(res, { error: error.message, requestExcerpt: requestExcerptOf(req) });
46
+ anthropicError(res, 400, 'invalid_request_error', error.message);
47
+ return;
48
+ }
49
+ patchCallLog(res, { error: error.message, requestExcerpt: requestExcerptOf(req) });
50
+ anthropicError(res, 500, 'api_error', error.message);
51
+ }
52
+ }
53
+ function handleCountTokensRoute(configManager, req, res) {
54
+ beginCallLog(res, { ingress: 'anthropic', path: req.path, model: req.body?.model || '' });
55
+ try {
56
+ const model = req.body?.model || '';
57
+ const { provider, model: upstreamModel } = resolveModel(model, configManager.getAIProviders());
58
+ patchCallLog(res, { providerName: provider.slug, upstreamModel });
59
+ void handleCountTokens(req, res, provider, upstreamModel);
60
+ }
61
+ catch (error) {
62
+ if (error instanceof ModelRouteError) {
63
+ patchCallLog(res, { error: error.message, requestExcerpt: requestExcerptOf(req) });
64
+ anthropicError(res, 400, 'invalid_request_error', error.message);
65
+ return;
66
+ }
67
+ patchCallLog(res, { error: error.message, requestExcerpt: requestExcerptOf(req) });
68
+ anthropicError(res, 500, 'api_error', error.message);
69
+ }
70
+ }
71
+ function isOpenAIPath(pathName) {
72
+ return pathName === '/openai' || pathName.startsWith('/openai/');
73
+ }
74
+ // OpenAI 入口方言:openai 上游直通(P1),anthropic/gemini 上游走 IR
75
+ function handleOpenAIMessages(configManager, req, res) {
76
+ beginCallLog(res, { ingress: 'openai', path: req.path, model: req.body?.model || '' });
77
+ try {
78
+ const n = req.body?.n;
79
+ if (typeof n === 'number' && n > 1) {
80
+ patchCallLog(res, { error: 'n > 1 is not supported' });
81
+ openaiError(res, 400, 'invalid_request_error', 'n > 1 is not supported; the gateway serves a single completion');
82
+ return;
83
+ }
84
+ const model = req.body?.model || '';
85
+ const { provider, model: upstreamModel } = resolveModel(model, configManager.getAIProviders());
86
+ patchCallLog(res, { providerName: provider.slug, upstreamModel });
87
+ switch (provider.dialect) {
88
+ case 'openai':
89
+ void passthroughOpenAI(req, res, provider, upstreamModel);
90
+ break;
91
+ case 'anthropic':
92
+ void handleIRChannel(req, res, provider, upstreamModel, anthropicCodec, openaiIngress);
93
+ break;
94
+ case 'gemini':
95
+ void handleIRChannel(req, res, provider, upstreamModel, geminiCodec, openaiIngress);
96
+ break;
97
+ }
98
+ }
99
+ catch (error) {
100
+ if (error instanceof ModelRouteError) {
101
+ patchCallLog(res, { error: error.message, requestExcerpt: requestExcerptOf(req) });
102
+ openaiError(res, 400, 'invalid_request_error', error.message, 'model_not_found');
103
+ return;
104
+ }
105
+ patchCallLog(res, { error: error.message, requestExcerpt: requestExcerptOf(req) });
106
+ openaiError(res, 500, 'api_error', error.message);
107
+ }
108
+ }
109
+ // /v1/models 模型列表:只读配置快照,无 body、无上游请求
110
+ function handleModelsList(configManager, format) {
111
+ return (_req, res) => {
112
+ const providers = configManager.getAIProviders();
113
+ res.status(200).json(format === 'openai' ? buildOpenAIModelsList(providers) : buildAnthropicModelsList(providers));
114
+ };
115
+ }
116
+ // 网关路由器:路径门控(32MB 解析 + apiKey 认证只作用于方言路径),
117
+ // 认证/启用状态每次请求实时读配置,设置变更即时生效无需重启监听
118
+ export function createGatewayRouter(configManager) {
119
+ const router = express.Router();
120
+ router.use((req, res, next) => {
121
+ if (!isGatewayPath(req.path)) {
122
+ next();
123
+ return;
124
+ }
125
+ express.json({ limit: `${MAX_BODY_BYTES / 1024 / 1024}mb` })(req, res, next);
126
+ });
127
+ router.use((req, res, next) => {
128
+ if (!isGatewayPath(req.path)) {
129
+ next();
130
+ return;
131
+ }
132
+ const gateway = configManager.getAIGatewayConfig();
133
+ if (!gateway?.enabled) {
134
+ if (isOpenAIPath(req.path)) {
135
+ openaiError(res, 503, 'api_error', 'AI gateway not enabled');
32
136
  }
33
- createGatewayAuth(gateway.apiKey)(req, res, next);
34
- });
35
- this.app.post('/v1/messages', this.handleMessages.bind(this));
36
- this.app.post('/v1/messages/count_tokens', this.handleCountTokensRoute.bind(this));
37
- // JSON 解析 / body 超限错误兜底
38
- this.app.use((err, _req, res, _next) => {
39
- if (err.type === 'entity.too.large') {
40
- anthropicError(res, 413, 'invalid_request_error', 'request body exceeds 32MB limit');
41
- return;
137
+ else {
138
+ anthropicError(res, 503, 'api_error', 'AI gateway not enabled');
42
139
  }
43
- if (err.type === 'entity.parse.failed' || err instanceof SyntaxError) {
44
- anthropicError(res, 400, 'invalid_request_error', `invalid JSON: ${err.message}`);
45
- return;
140
+ return;
141
+ }
142
+ if (!gateway.apiKey) {
143
+ // 未配置(不应到达此处,启用时会自动生成)
144
+ if (isOpenAIPath(req.path)) {
145
+ openaiError(res, 503, 'api_error', 'AI gateway not configured');
46
146
  }
47
- anthropicError(res, 500, 'api_error', err.message);
48
- });
49
- }
50
- handleMessages(req, res) {
51
- try {
52
- const model = req.body?.model || '';
53
- const { provider, model: upstreamModel } = resolveModel(model, this.configManager.getAIProviders());
54
- switch (provider.dialect) {
55
- case 'anthropic':
56
- void passthroughMessages(req, res, provider, upstreamModel);
57
- break;
58
- case 'openai':
59
- void handleIRChannel(req, res, provider, upstreamModel, openaiCodec);
60
- break;
61
- case 'gemini':
62
- void handleIRChannel(req, res, provider, upstreamModel, geminiCodec);
63
- break;
147
+ else {
148
+ anthropicError(res, 503, 'api_error', 'AI gateway not configured');
64
149
  }
150
+ return;
65
151
  }
66
- catch (error) {
67
- if (error instanceof ModelRouteError) {
68
- anthropicError(res, 400, 'invalid_request_error', error.message);
69
- return;
152
+ createGatewayAuth(gateway.apiKey)(req, res, next);
153
+ });
154
+ // 方言前缀路由:anthropic / openai 入口端点(同端口按路径区分协议)
155
+ router.post('/anthropic/v1/messages', (req, res) => handleMessages(configManager, req, res));
156
+ router.post('/anthropic/v1/messages/count_tokens', (req, res) => handleCountTokensRoute(configManager, req, res));
157
+ router.post('/openai/v1/chat/completions', (req, res) => handleOpenAIMessages(configManager, req, res));
158
+ // 裸路径为 1.2.0 起已发布的 Anthropic 端点,保留为兼容别名(同一 handler)
159
+ router.post('/v1/messages', (req, res) => handleMessages(configManager, req, res));
160
+ router.post('/v1/messages/count_tokens', (req, res) => handleCountTokensRoute(configManager, req, res));
161
+ router.get('/anthropic/v1/models', handleModelsList(configManager, 'anthropic'));
162
+ router.get('/openai/v1/models', handleModelsList(configManager, 'openai'));
163
+ router.get('/v1/models', handleModelsList(configManager, 'anthropic')); // 裸路径别名(与裸 /v1/messages 对称)
164
+ // JSON 解析 / body 超限错误兜底(错误体格式按入口方言前缀选)
165
+ router.use((err, _req, res, _next) => {
166
+ const openaiIngressReq = isOpenAIPath(_req.path);
167
+ if (err.type === 'entity.too.large') {
168
+ if (openaiIngressReq) {
169
+ openaiError(res, 413, 'invalid_request_error', 'request body exceeds 32MB limit');
170
+ }
171
+ else {
172
+ anthropicError(res, 413, 'invalid_request_error', 'request body exceeds 32MB limit');
70
173
  }
71
- anthropicError(res, 500, 'api_error', error.message);
174
+ return;
72
175
  }
73
- }
74
- handleCountTokensRoute(req, res) {
75
- try {
76
- const model = req.body?.model || '';
77
- const { provider, model: upstreamModel } = resolveModel(model, this.configManager.getAIProviders());
78
- void handleCountTokens(req, res, provider, upstreamModel);
79
- }
80
- catch (error) {
81
- if (error instanceof ModelRouteError) {
82
- anthropicError(res, 400, 'invalid_request_error', error.message);
83
- return;
176
+ if (err.type === 'entity.parse.failed' || err instanceof SyntaxError) {
177
+ if (openaiIngressReq) {
178
+ openaiError(res, 400, 'invalid_request_error', `invalid JSON: ${err.message}`);
179
+ }
180
+ else {
181
+ anthropicError(res, 400, 'invalid_request_error', `invalid JSON: ${err.message}`);
84
182
  }
85
- anthropicError(res, 500, 'api_error', error.message);
183
+ return;
86
184
  }
185
+ if (openaiIngressReq) {
186
+ openaiError(res, 500, 'api_error', err.message);
187
+ }
188
+ else {
189
+ anthropicError(res, 500, 'api_error', err.message);
190
+ }
191
+ });
192
+ return router;
193
+ }
194
+ // 独立监听形态(回退):Web 服务不启动时使用
195
+ export class AiGatewayServer {
196
+ app;
197
+ server;
198
+ configManager;
199
+ constructor(configManager) {
200
+ this.configManager = configManager;
201
+ this.app = express();
202
+ this.app.use(createGatewayRouter(configManager));
87
203
  }
88
204
  async start() {
89
205
  const gateway = this.configManager.getAIGatewayConfig();
@@ -1 +1 @@
1
- {"version":3,"file":"gateway-server.js","sourceRoot":"","sources":["../../src/ai-gateway/gateway-server.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"gateway-server.js","sourceRoot":"","sources":["../../src/ai-gateway/gateway-server.ts"],"names":[],"mappings":"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"}
@@ -1,12 +1,16 @@
1
1
  // 端到端集成测试:本地 mock 上游 + 真实 AiGatewayServer(随机端口)
2
- // 覆盖三条通道:anthropic 直通 / openai 转换 / 错误透传 / 客户端断连清理
2
+ // 覆盖通道:anthropic 直通 / openai 转换 / openai 入口(直通 + IR 转换)/ 错误透传 / 客户端断连清理 / 调用流水埋点
3
3
  import { describe, it, expect, beforeAll, afterAll } from 'vitest';
4
4
  import { createServer } from 'http';
5
5
  import { ConfigManager } from '../config/config-manager.js';
6
6
  import { AiGatewayServer } from './gateway-server.js';
7
+ import { getRecentAiCalls, resetAiCallStoreForTests } from './call-log.js';
7
8
  import { promises as fsPromises } from 'fs';
9
+ import { mkdtempSync, rmSync } from 'fs';
8
10
  import { tmpdir } from 'os';
9
11
  import { join } from 'path';
12
+ // 调用流水落盘隔离目录(在 store 首次初始化前设置即生效)
13
+ process.env.AGENTDOG_LOG_DIR = mkdtempSync(join(tmpdir(), 'agentdog-itest-logs-'));
10
14
  const GATEWAY_KEY = 'ad-sk-itest';
11
15
  // openai 流式 SSE fixture:文本 + finish + usage + [DONE]
12
16
  const OPENAI_SSE = [
@@ -63,9 +67,51 @@ describe('AI gateway 端到端', () => {
63
67
  return;
64
68
  }
65
69
  const parsed = JSON.parse(body);
70
+ if (parsed.stream) {
71
+ res.writeHead(200, { 'content-type': 'text/event-stream' });
72
+ res.end([
73
+ 'event: message_start',
74
+ 'data: {"type":"message_start","message":{"usage":{"input_tokens":5,"output_tokens":0}}}',
75
+ '',
76
+ 'event: content_block_start',
77
+ 'data: {"type":"content_block_start","index":0,"content_block":{"type":"text","text":""}}',
78
+ '',
79
+ 'event: content_block_delta',
80
+ 'data: {"type":"content_block_delta","index":0,"delta":{"type":"text_delta","text":"好的"}}',
81
+ '',
82
+ 'event: content_block_stop',
83
+ 'data: {"type":"content_block_stop","index":0}',
84
+ '',
85
+ 'event: content_block_start',
86
+ 'data: {"type":"content_block_start","index":1,"content_block":{"type":"tool_use","id":"toolu_1","name":"get_weather","input":{}}}',
87
+ '',
88
+ 'event: content_block_delta',
89
+ 'data: {"type":"content_block_delta","index":1,"delta":{"type":"input_json_delta","partial_json":"{\\"city\\":\\"北京\\"}"}}',
90
+ '',
91
+ 'event: content_block_stop',
92
+ 'data: {"type":"content_block_stop","index":1}',
93
+ '',
94
+ 'event: message_delta',
95
+ 'data: {"type":"message_delta","delta":{"stop_reason":"tool_use","stop_sequence":null},"usage":{"input_tokens":9,"output_tokens":8}}',
96
+ '',
97
+ 'event: message_stop',
98
+ 'data: {"type":"message_stop"}',
99
+ '',
100
+ ].join('\n'));
101
+ }
102
+ else {
103
+ res.writeHead(200, { 'content-type': 'application/json' });
104
+ res.end(JSON.stringify({
105
+ id: 'msg_x', type: 'message', role: 'assistant', model: 'claude-x',
106
+ content: [{ type: 'text', text: '你好' }], stop_reason: 'end_turn', stop_sequence: null,
107
+ usage: { input_tokens: 7, output_tokens: 2 },
108
+ }));
109
+ }
110
+ return;
111
+ }
112
+ if (url.startsWith('/gemini/v1beta/models/')) {
66
113
  res.writeHead(200, { 'content-type': 'text/event-stream' });
67
- res.end(`event: message_start\ndata: {"type":"message_start","message":{"usage":{"input_tokens":5,"output_tokens":0}}}\n\nevent: message_stop\ndata: {"type":"message_stop"}\n\n`);
68
- void parsed;
114
+ res.end(`data: ${JSON.stringify({ candidates: [{ content: { parts: [{ text: '好的' }] }, finishReason: 'STOP' }], usageMetadata: { promptTokenCount: 3, candidatesTokenCount: 2 } })}\n\n`);
69
115
  return;
70
116
  }
71
117
  res.writeHead(404);
@@ -94,6 +140,12 @@ describe('AI gateway 端到端', () => {
94
140
  id: 'p-anthropic', slug: 'official', dialect: 'anthropic',
95
141
  baseUrl: `http://127.0.0.1:${upstreamPort}/anthropic`, apiKey: 'sk-ant', enabled: true,
96
142
  });
143
+ cm.updateAIProvider('p-openai', { models: ['deepseek-chat', 'deepseek-reasoner'], disabledModels: ['deepseek-reasoner'] });
144
+ cm.updateAIProvider('p-anthropic', { models: ['claude-sonnet-4'] });
145
+ cm.addAIProvider({
146
+ id: 'p-gemini', slug: 'google', dialect: 'gemini',
147
+ baseUrl: `http://127.0.0.1:${upstreamPort}/gemini`, apiKey: 'g-key', enabled: true,
148
+ });
97
149
  gateway = new AiGatewayServer(cm);
98
150
  await gateway.start();
99
151
  });
@@ -101,6 +153,7 @@ describe('AI gateway 端到端', () => {
101
153
  await gateway.stop();
102
154
  await new Promise(r => upstream.close(() => r()));
103
155
  await fsPromises.unlink(configPath).catch(() => { });
156
+ rmSync(process.env.AGENTDOG_LOG_DIR, { recursive: true, force: true });
104
157
  });
105
158
  it('openai 转换通道:流式事件序列完整(延迟提交 + usage 汇总)', async () => {
106
159
  const resp = await fetch(`http://127.0.0.1:${gwPort}/v1/messages`, {
@@ -168,11 +221,62 @@ describe('AI gateway 端到端', () => {
168
221
  expect(json.type).toBe('error');
169
222
  expect(json.error.type).toBe('authentication_error');
170
223
  expect(json.error.message).toContain('Incorrect API key'); // 上游原文
224
+ // 调用流水:上游失败记状态码与响应正文摘要
225
+ await new Promise(r => setTimeout(r, 100));
226
+ const call = getRecentAiCalls().find(c => c.model === 'deepseek:m' && c.status === 401);
227
+ expect(call).toBeDefined();
228
+ expect(call.providerName).toBe('deepseek');
229
+ expect(call.responseExcerpt).toContain('Incorrect API key');
230
+ expect(call.error).toContain('upstream 401');
171
231
  }
172
232
  finally {
173
233
  cm.updateAIProvider('p-openai', { apiKey: 'sk-up' });
174
234
  }
175
235
  });
236
+ it('调用流水:成功调用记录路由命中与 usage', async () => {
237
+ resetAiCallStoreForTests();
238
+ const resp = await fetch(`http://127.0.0.1:${gwPort}/v1/messages`, {
239
+ method: 'POST',
240
+ headers: { 'content-type': 'application/json', 'x-api-key': GATEWAY_KEY },
241
+ body: JSON.stringify({
242
+ model: 'deepseek:deepseek-chat', max_tokens: 100,
243
+ messages: [{ role: 'user', content: '流水' }],
244
+ }),
245
+ });
246
+ expect(resp.status).toBe(200);
247
+ await resp.json();
248
+ await new Promise(r => setTimeout(r, 100));
249
+ const call = getRecentAiCalls().find(c => c.model === 'deepseek:deepseek-chat');
250
+ expect(call).toBeDefined();
251
+ expect(call.ingress).toBe('anthropic'); // 裸 /v1/messages 别名归 anthropic
252
+ expect(call.path).toBe('/v1/messages');
253
+ expect(call.providerName).toBe('deepseek');
254
+ expect(call.upstreamModel).toBe('deepseek-chat');
255
+ expect(call.status).toBe(200);
256
+ expect(call.durationMs).toBeGreaterThanOrEqual(0);
257
+ expect(call.usage).toEqual({ inputTokens: 12, outputTokens: 3 });
258
+ });
259
+ it('调用流水:路由失败(未知模型)记 400 与请求摘要', async () => {
260
+ resetAiCallStoreForTests();
261
+ const resp = await fetch(`http://127.0.0.1:${gwPort}/v1/messages`, {
262
+ method: 'POST',
263
+ headers: { 'content-type': 'application/json', 'x-api-key': GATEWAY_KEY },
264
+ body: JSON.stringify({
265
+ model: 'nosuch:model-x', max_tokens: 10,
266
+ messages: [{ role: 'user', content: 'x' }],
267
+ }),
268
+ });
269
+ expect(resp.status).toBe(400);
270
+ await resp.json();
271
+ await new Promise(r => setTimeout(r, 100));
272
+ const call = getRecentAiCalls().find(c => c.model === 'nosuch:model-x');
273
+ expect(call).toBeDefined();
274
+ expect(call.status).toBe(400);
275
+ expect(call.providerName).toBe(''); // 未命中路由
276
+ expect(call.error).toBeTruthy();
277
+ expect(call.requestExcerpt).toContain('nosuch:model-x'); // 请求结构摘要含模型名
278
+ expect(call.requestExcerpt).toContain('1 messages');
279
+ });
176
280
  it('客户端断连后上游连接被关闭(abort 链)', async () => {
177
281
  const before = upstreamConnectionsClosed;
178
282
  const ac = new AbortController();
@@ -201,5 +305,135 @@ describe('AI gateway 端到端', () => {
201
305
  expect(json.error.message).toContain('deepseek');
202
306
  expect(json.error.message).toContain('official');
203
307
  });
308
+ it('方言前缀路由:/anthropic/v1/messages 与裸路径同 handler', async () => {
309
+ const resp = await fetch(`http://127.0.0.1:${gwPort}/anthropic/v1/messages`, {
310
+ method: 'POST',
311
+ headers: { 'content-type': 'application/json', 'x-api-key': GATEWAY_KEY },
312
+ body: JSON.stringify({
313
+ model: 'deepseek:deepseek-chat', max_tokens: 100,
314
+ messages: [{ role: 'user', content: '你好' }],
315
+ }),
316
+ });
317
+ expect(resp.status).toBe(200);
318
+ const json = await resp.json();
319
+ expect(json.type).toBe('message');
320
+ expect(json.content).toEqual([{ type: 'text', text: '你好' }]);
321
+ });
322
+ it('方言前缀路由:/anthropic/v1/messages/count_tokens 已注册(错 slug 走 ModelRouteError)', async () => {
323
+ const resp = await fetch(`http://127.0.0.1:${gwPort}/anthropic/v1/messages/count_tokens`, {
324
+ method: 'POST',
325
+ headers: { 'content-type': 'application/json', 'x-api-key': GATEWAY_KEY },
326
+ body: JSON.stringify({ model: 'nope:m', messages: [] }),
327
+ });
328
+ expect(resp.status).toBe(400);
329
+ const json = await resp.json();
330
+ expect(json.type).toBe('error');
331
+ expect(json.error.message).toContain('available');
332
+ });
333
+ it('openai 入口直通:流式 chunk 原样回传', async () => {
334
+ const resp = await fetch(`http://127.0.0.1:${gwPort}/openai/v1/chat/completions`, {
335
+ method: 'POST',
336
+ headers: { 'content-type': 'application/json', 'x-api-key': GATEWAY_KEY },
337
+ body: JSON.stringify({ model: 'deepseek:deepseek-chat', stream: true, messages: [{ role: 'user', content: '你好' }] }),
338
+ });
339
+ expect(resp.status).toBe(200);
340
+ const text = await resp.text();
341
+ expect(text).toContain('"content":"你好"');
342
+ expect(text).toContain('data: [DONE]');
343
+ });
344
+ it('openai 入口直通:非流式 JSON', async () => {
345
+ const resp = await fetch(`http://127.0.0.1:${gwPort}/openai/v1/chat/completions`, {
346
+ method: 'POST',
347
+ headers: { 'content-type': 'application/json', 'x-api-key': GATEWAY_KEY },
348
+ body: JSON.stringify({ model: 'deepseek:deepseek-chat', messages: [{ role: 'user', content: '你好' }] }),
349
+ });
350
+ const json = await resp.json();
351
+ expect(json.choices[0].message.content).toBe('你好');
352
+ expect(json.usage.prompt_tokens).toBe(12);
353
+ });
354
+ it('openai 入口 → anthropic 上游 IR:流式工具调用转换', async () => {
355
+ const resp = await fetch(`http://127.0.0.1:${gwPort}/openai/v1/chat/completions`, {
356
+ method: 'POST',
357
+ headers: { 'content-type': 'application/json', 'x-api-key': GATEWAY_KEY },
358
+ body: JSON.stringify({ model: 'official:claude-sonnet-4', stream: true, messages: [{ role: 'user', content: '北京天气' }] }),
359
+ });
360
+ expect(resp.status).toBe(200);
361
+ const text = await resp.text();
362
+ expect(text).toContain('"role":"assistant"');
363
+ expect(text).toContain('"name":"get_weather"');
364
+ expect(text).toContain('"finish_reason":"tool_calls"');
365
+ expect(text).toContain('data: [DONE]');
366
+ });
367
+ it('openai 入口 → anthropic 上游 IR:非流式聚合 chat.completion', async () => {
368
+ const resp = await fetch(`http://127.0.0.1:${gwPort}/openai/v1/chat/completions`, {
369
+ method: 'POST',
370
+ headers: { 'content-type': 'application/json', 'x-api-key': GATEWAY_KEY },
371
+ body: JSON.stringify({ model: 'official:claude-sonnet-4', messages: [{ role: 'user', content: '你好' }] }),
372
+ });
373
+ const json = await resp.json();
374
+ expect(json.object).toBe('chat.completion');
375
+ expect(json.choices[0].message.content).toBe('你好');
376
+ expect(json.choices[0].finish_reason).toBe('stop');
377
+ expect(json.usage).toEqual({ prompt_tokens: 7, completion_tokens: 2, total_tokens: 9 });
378
+ });
379
+ it('openai 入口 → gemini 上游 IR:流式文本', async () => {
380
+ const resp = await fetch(`http://127.0.0.1:${gwPort}/openai/v1/chat/completions`, {
381
+ method: 'POST',
382
+ headers: { 'content-type': 'application/json', 'x-api-key': GATEWAY_KEY },
383
+ body: JSON.stringify({ model: 'google:gemini-2.0-flash', stream: true, messages: [{ role: 'user', content: 'hi' }] }),
384
+ });
385
+ expect(resp.status).toBe(200);
386
+ const text = await resp.text();
387
+ expect(text).toContain('"content":"好的"');
388
+ expect(text).toContain('data: [DONE]');
389
+ });
390
+ it('openai 入口:n>1 返回 400 OpenAI 格式错误体', async () => {
391
+ const resp = await fetch(`http://127.0.0.1:${gwPort}/openai/v1/chat/completions`, {
392
+ method: 'POST',
393
+ headers: { 'content-type': 'application/json', 'x-api-key': GATEWAY_KEY },
394
+ body: JSON.stringify({ model: 'deepseek:m', n: 2, messages: [{ role: 'user', content: 'x' }] }),
395
+ });
396
+ expect(resp.status).toBe(400);
397
+ const json = await resp.json();
398
+ expect(json.error.type).toBe('invalid_request_error');
399
+ expect(json.error.message).toContain('n > 1');
400
+ });
401
+ it('openai 入口:错 slug 返回 400 + code=model_not_found', async () => {
402
+ const resp = await fetch(`http://127.0.0.1:${gwPort}/openai/v1/chat/completions`, {
403
+ method: 'POST',
404
+ headers: { 'content-type': 'application/json', 'x-api-key': GATEWAY_KEY },
405
+ body: JSON.stringify({ model: 'nope:m', messages: [{ role: 'user', content: 'x' }] }),
406
+ });
407
+ expect(resp.status).toBe(400);
408
+ const json = await resp.json();
409
+ expect(json.error.code).toBe('model_not_found');
410
+ expect(json.error.message).toContain('deepseek');
411
+ });
412
+ it('GET /openai/v1/models:OpenAI 格式 + disabledModels 过滤', async () => {
413
+ const resp = await fetch(`http://127.0.0.1:${gwPort}/openai/v1/models`, { headers: { 'x-api-key': GATEWAY_KEY } });
414
+ expect(resp.status).toBe(200);
415
+ const json = await resp.json();
416
+ expect(json.object).toBe('list');
417
+ const ids = json.data.map((m) => m.id);
418
+ expect(ids).toContain('deepseek:deepseek-chat');
419
+ expect(ids).toContain('official:claude-sonnet-4');
420
+ expect(ids).not.toContain('deepseek:deepseek-reasoner'); // disabledModels 被过滤
421
+ });
422
+ it('GET /anthropic/v1/models 与裸 /v1/models:Anthropic 格式', async () => {
423
+ for (const path of ['/anthropic/v1/models', '/v1/models']) {
424
+ const resp = await fetch(`http://127.0.0.1:${gwPort}${path}`, { headers: { 'x-api-key': GATEWAY_KEY } });
425
+ expect(resp.status).toBe(200);
426
+ const json = await resp.json();
427
+ expect(json.data[0].type).toBe('model');
428
+ expect(json.data[0].id).toBe('deepseek:deepseek-chat');
429
+ expect(json.has_more).toBe(false);
430
+ }
431
+ });
432
+ it('openai 路径认证失败返回 OpenAI 格式 401(Task 8 遗留用例回收)', async () => {
433
+ const resp = await fetch(`http://127.0.0.1:${gwPort}/openai/v1/models`, { headers: { 'x-api-key': 'ad-sk-wrong' } });
434
+ expect(resp.status).toBe(401);
435
+ const json = await resp.json();
436
+ expect(json.error.code).toBe('invalid_api_key');
437
+ });
204
438
  });
205
439
  //# sourceMappingURL=integration.test.js.map