thumbgate 1.30.0 → 1.34.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.
- package/.claude-plugin/plugin.json +1 -1
- package/.well-known/mcp/server-card.json +1 -1
- package/README.md +54 -16
- package/adapters/claude/.mcp.json +2 -2
- package/adapters/forge/forge.yaml +3 -3
- package/adapters/mcp/server-stdio.js +105 -10
- package/adapters/opencode/opencode.json +1 -1
- package/bench/observability-eval-suite.json +2 -2
- package/bin/cli.js +168 -31
- package/config/evals/generation-quality-golden.json +95 -0
- package/config/evals/rag-answer-quality-golden.json +91 -0
- package/config/evals/retrieval-hybrid-ablation.json +66 -0
- package/config/evals/retrieval-ranking-golden.json +522 -0
- package/config/gates/claim-verifiers.example.json +42 -0
- package/config/gates/claim-verifiers.json +25 -0
- package/config/gates/default.json +217 -50
- package/config/mcp-allowlists.json +233 -206
- package/config/model-tiers.json +7 -2
- package/glama.json +6 -0
- package/hooks/hooks.json +1 -1
- package/package.json +69 -12
- package/public/assets/diagrams/before-after.svg +17 -16
- package/public/assets/diagrams/hero-thumbs.svg +68 -0
- package/public/assets/diagrams/loop.svg +19 -13
- package/public/assets/diagrams/self-improving-thumbs-loop.svg +105 -0
- package/public/compare.html +1 -0
- package/public/dashboard.html +126 -28
- package/public/evaluations.html +1 -1
- package/public/index.html +142 -13
- package/public/numbers.html +3 -2
- package/public/pricing.html +143 -30
- package/scripts/a-plus-evidence-scorecard.js +303 -0
- package/scripts/agent-readiness.js +110 -0
- package/scripts/async-eval-observability.js +36 -11
- package/scripts/audit-trail.js +37 -1
- package/scripts/auto-promote-gates.js +149 -34
- package/scripts/auto-wire-hooks.js +20 -8
- package/scripts/cli-schema.js +14 -0
- package/scripts/colbert-style-maxsim.js +236 -0
- package/scripts/cross-encoder-reranker.js +356 -126
- package/scripts/dashboard-chat.js +350 -17
- package/scripts/document-intake.js +283 -7
- package/scripts/eval-quality-suite.js +204 -0
- package/scripts/feedback-loop.js +115 -7
- package/scripts/feedback-paths.js +32 -13
- package/scripts/feedback-quality.js +53 -0
- package/scripts/feedback-schema.js +3 -0
- package/scripts/file-ledger-lock.js +130 -0
- package/scripts/filesystem-search.js +17 -7
- package/scripts/financial-control-plane.js +1514 -0
- package/scripts/gates-engine.js +202 -7
- package/scripts/gemini-embedding-policy.js +1 -0
- package/scripts/harness-tool-names.js +70 -0
- package/scripts/hook-runtime.js +15 -3
- package/scripts/hook-stop-anti-claim.js +63 -3
- package/scripts/human-escalation.js +353 -41
- package/scripts/lesson-db.js +16 -5
- package/scripts/lesson-embedding-index.js +67 -20
- package/scripts/lesson-embedding-maintenance.js +177 -0
- package/scripts/lesson-reranker.js +55 -9
- package/scripts/lesson-retrieval.js +305 -29
- package/scripts/lesson-search.js +22 -8
- package/scripts/llm-client.js +304 -15
- package/scripts/model-tier-router.js +593 -0
- package/scripts/pragmatic-hybrid-search.js +379 -0
- package/scripts/provider-action-normalizer.js +11 -4
- package/scripts/rag-document-pipeline.js +461 -0
- package/scripts/rag-structured-output.js +441 -0
- package/scripts/ragas-style-metrics.js +351 -0
- package/scripts/request-envelope.js +178 -0
- package/scripts/rerank-pipeline.js +370 -0
- package/scripts/rerank-quality-eval.js +155 -0
- package/scripts/retrieval-hybrid-ablation.js +120 -0
- package/scripts/retrieval-quality-tier.js +118 -0
- package/scripts/secret-scanner.js +395 -4
- package/scripts/self-distill-agent.js +7 -1
- package/scripts/self-healing-check.js +25 -0
- package/scripts/skill-packs.js +183 -0
- package/scripts/slow-loop.js +72 -0
- package/scripts/statusline-links.js +1 -1
- package/scripts/statusline.sh +8 -1
- package/scripts/telemetry-analytics.js +13 -1
- package/scripts/thumbgate-search.js +98 -6
- package/scripts/tier-budget-guard.js +186 -0
- package/scripts/tool-registry.js +141 -5
- package/scripts/universal-claim-evaluator.js +767 -0
- package/scripts/vector-store.js +154 -17
- package/scripts/verify-marketing-pages-deployed.js +85 -3
- package/scripts/workflow-sentinel.js +77 -11
- package/server.json +44 -0
- package/smithery.yaml +17 -0
- package/src/api/server.js +196 -13
package/scripts/llm-client.js
CHANGED
|
@@ -14,12 +14,51 @@ const DEFAULT_MAX_TOKENS = 1024;
|
|
|
14
14
|
const DEFAULT_CACHE_TTL = '5m';
|
|
15
15
|
const DEFAULT_ZAI_BASE_URL = 'https://api.z.ai/api/paas/v4';
|
|
16
16
|
const DEFAULT_ZAI_MODEL = 'glm-5.2-flash';
|
|
17
|
+
const DEFAULT_GATEWAY_MODEL = 'glm-5.2';
|
|
18
|
+
const GATEWAY_TIMEOUT_MS = 30000;
|
|
17
19
|
|
|
18
20
|
let _anthropicClient = null;
|
|
19
21
|
let _geminiClient = null;
|
|
20
22
|
|
|
21
|
-
function
|
|
22
|
-
|
|
23
|
+
function getGatewayConfig(env = process.env) {
|
|
24
|
+
const baseUrl = String(env.THUMBGATE_LLM_GATEWAY_URL || '').trim();
|
|
25
|
+
if (!baseUrl) return null;
|
|
26
|
+
return {
|
|
27
|
+
baseUrl: baseUrl.replace(/\/+$/, ''),
|
|
28
|
+
model: String(env.THUMBGATE_LLM_GATEWAY_MODEL || DEFAULT_GATEWAY_MODEL).trim(),
|
|
29
|
+
credentialEnvVar: 'THUMBGATE_LLM_GATEWAY_TOKEN',
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function isGatewayConfigured(env = process.env) {
|
|
34
|
+
return Boolean(getGatewayConfig(env));
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function resolveGatewayModel(options = {}, config = getGatewayConfig()) {
|
|
38
|
+
const explicitGatewayModel = String(options.gatewayModel || '').trim();
|
|
39
|
+
if (explicitGatewayModel) return explicitGatewayModel;
|
|
40
|
+
return String(config?.model || DEFAULT_GATEWAY_MODEL).trim();
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function describeInferenceAvailability(env = process.env) {
|
|
44
|
+
if (env.ANTHROPIC_API_KEY) return { available: true, provider: 'anthropic' };
|
|
45
|
+
const gateway = getGatewayConfig(env);
|
|
46
|
+
if (gateway) {
|
|
47
|
+
return {
|
|
48
|
+
available: true,
|
|
49
|
+
provider: 'gateway',
|
|
50
|
+
model: gateway.model,
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
return {
|
|
54
|
+
available: false,
|
|
55
|
+
provider: 'none',
|
|
56
|
+
reason: 'no ANTHROPIC_API_KEY and no THUMBGATE_LLM_GATEWAY_URL',
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function isAvailable(env = process.env) {
|
|
61
|
+
return describeInferenceAvailability(env).available;
|
|
23
62
|
}
|
|
24
63
|
|
|
25
64
|
function getClient() {
|
|
@@ -155,6 +194,144 @@ function buildSafeProviderError(error) {
|
|
|
155
194
|
return summary;
|
|
156
195
|
}
|
|
157
196
|
|
|
197
|
+
function normalizeUsageTelemetry(usage = null) {
|
|
198
|
+
if (!usage || typeof usage !== 'object') {
|
|
199
|
+
return {
|
|
200
|
+
inputTokens: null,
|
|
201
|
+
outputTokens: null,
|
|
202
|
+
cacheReadInputTokens: null,
|
|
203
|
+
cacheWriteInputTokens: null,
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
const finiteOrNull = (...values) => {
|
|
207
|
+
const value = values.map(Number).find(Number.isFinite);
|
|
208
|
+
return value === undefined ? null : value;
|
|
209
|
+
};
|
|
210
|
+
return {
|
|
211
|
+
inputTokens: finiteOrNull(usage.input_tokens, usage.prompt_tokens, usage.promptTokenCount),
|
|
212
|
+
outputTokens: finiteOrNull(usage.output_tokens, usage.completion_tokens, usage.candidatesTokenCount),
|
|
213
|
+
cacheReadInputTokens: finiteOrNull(usage.cache_read_input_tokens),
|
|
214
|
+
cacheWriteInputTokens: finiteOrNull(usage.cache_creation_input_tokens),
|
|
215
|
+
};
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
function buildLlmTrace(options = {}, event = {}) {
|
|
219
|
+
const usage = normalizeUsageTelemetry(event.usage);
|
|
220
|
+
return {
|
|
221
|
+
timestamp: new Date().toISOString(),
|
|
222
|
+
traceId: String(options.traceId || options.metadata?.traceId || '').trim() || null,
|
|
223
|
+
provider: event.provider || 'unknown',
|
|
224
|
+
model: event.model || options.model || null,
|
|
225
|
+
outcome: event.outcome || 'unknown',
|
|
226
|
+
latencyMs: Number.isFinite(event.latencyMs) ? Math.max(0, event.latencyMs) : null,
|
|
227
|
+
...usage,
|
|
228
|
+
stopReason: event.stopReason || null,
|
|
229
|
+
requestId: event.requestId || null,
|
|
230
|
+
httpStatus: Number.isFinite(event.httpStatus) ? event.httpStatus : null,
|
|
231
|
+
errorCode: event.errorCode == null ? null : String(event.errorCode).slice(0, 80),
|
|
232
|
+
};
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
async function emitLlmTrace(options = {}, event = {}) {
|
|
236
|
+
if (typeof options.onTrace !== 'function') return;
|
|
237
|
+
try {
|
|
238
|
+
await options.onTrace(buildLlmTrace(options, event));
|
|
239
|
+
} catch {
|
|
240
|
+
// Observability must never break the generation path.
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
function gatewayRequestHeaders(config, env = process.env) {
|
|
245
|
+
const headers = { 'Content-Type': 'application/json' };
|
|
246
|
+
const token = env[config.credentialEnvVar];
|
|
247
|
+
if (token) headers.Authorization = `Bearer ${token}`;
|
|
248
|
+
return headers;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
function buildGatewayMessages(options = {}) {
|
|
252
|
+
const supplied = Array.isArray(options.messages)
|
|
253
|
+
? options.messages.filter(Boolean)
|
|
254
|
+
: [];
|
|
255
|
+
const messages = [];
|
|
256
|
+
if (options.systemPrompt) {
|
|
257
|
+
messages.push({ role: 'system', content: options.systemPrompt });
|
|
258
|
+
}
|
|
259
|
+
if (supplied.length > 0) messages.push(...supplied);
|
|
260
|
+
else messages.push({ role: 'user', content: options.userPrompt || '' });
|
|
261
|
+
return messages;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
async function callGatewayInternal(options = {}, env = process.env) {
|
|
265
|
+
const config = getGatewayConfig(env);
|
|
266
|
+
if (!config || typeof fetch !== 'function') return null;
|
|
267
|
+
const model = resolveGatewayModel(options, config);
|
|
268
|
+
const controller = new AbortController();
|
|
269
|
+
const timer = setTimeout(() => controller.abort(), GATEWAY_TIMEOUT_MS);
|
|
270
|
+
const startedAt = Date.now();
|
|
271
|
+
try {
|
|
272
|
+
const response = await fetch(`${config.baseUrl}/chat/completions`, {
|
|
273
|
+
method: 'POST',
|
|
274
|
+
headers: gatewayRequestHeaders(config, env),
|
|
275
|
+
body: JSON.stringify({
|
|
276
|
+
model,
|
|
277
|
+
messages: buildGatewayMessages(options),
|
|
278
|
+
max_tokens: options.maxTokens || DEFAULT_MAX_TOKENS,
|
|
279
|
+
temperature: Number.isFinite(options.temperature) ? options.temperature : 0,
|
|
280
|
+
}),
|
|
281
|
+
signal: controller.signal,
|
|
282
|
+
});
|
|
283
|
+
if (!response.ok) {
|
|
284
|
+
await emitLlmTrace(options, {
|
|
285
|
+
provider: 'gateway',
|
|
286
|
+
model,
|
|
287
|
+
outcome: 'error',
|
|
288
|
+
latencyMs: Date.now() - startedAt,
|
|
289
|
+
httpStatus: response.status,
|
|
290
|
+
errorCode: 'http_error',
|
|
291
|
+
});
|
|
292
|
+
return null;
|
|
293
|
+
}
|
|
294
|
+
const payload = await response.json();
|
|
295
|
+
const choice = payload?.choices?.[0] || {};
|
|
296
|
+
const message = choice.message || {};
|
|
297
|
+
const truncated = choice.finish_reason === 'length';
|
|
298
|
+
const raw = message.content || (truncated ? '' : message.reasoning_content) || '';
|
|
299
|
+
const text = stripCodeFences(raw);
|
|
300
|
+
if (!text) return null;
|
|
301
|
+
const result = {
|
|
302
|
+
text,
|
|
303
|
+
usage: payload?.usage || null,
|
|
304
|
+
stopReason: choice.finish_reason || null,
|
|
305
|
+
id: payload?.id || null,
|
|
306
|
+
model: payload?.model || model,
|
|
307
|
+
provider: 'gateway',
|
|
308
|
+
};
|
|
309
|
+
await emitLlmTrace(options, {
|
|
310
|
+
provider: 'gateway',
|
|
311
|
+
model: result.model,
|
|
312
|
+
outcome: 'success',
|
|
313
|
+
latencyMs: Date.now() - startedAt,
|
|
314
|
+
usage: result.usage,
|
|
315
|
+
stopReason: result.stopReason,
|
|
316
|
+
requestId: result.id,
|
|
317
|
+
});
|
|
318
|
+
return result;
|
|
319
|
+
} catch (error) {
|
|
320
|
+
const safe = buildSafeProviderError(error);
|
|
321
|
+
await emitLlmTrace(options, {
|
|
322
|
+
provider: 'gateway',
|
|
323
|
+
model,
|
|
324
|
+
outcome: 'error',
|
|
325
|
+
latencyMs: Date.now() - startedAt,
|
|
326
|
+
httpStatus: safe.status,
|
|
327
|
+
errorCode: safe.code || safe.name,
|
|
328
|
+
});
|
|
329
|
+
return null;
|
|
330
|
+
} finally {
|
|
331
|
+
clearTimeout(timer);
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
|
|
158
335
|
function getZaiApiKey(env = process.env) {
|
|
159
336
|
return env.ZAI_API_KEY || env.THUMBGATE_ZAI_API_KEY || '';
|
|
160
337
|
}
|
|
@@ -169,7 +346,18 @@ function getZaiModel(env = process.env) {
|
|
|
169
346
|
|
|
170
347
|
async function callZaiInternal(options = {}, env = process.env) {
|
|
171
348
|
const apiKey = getZaiApiKey(env);
|
|
172
|
-
|
|
349
|
+
const model = options.model || getZaiModel(env);
|
|
350
|
+
const startedAt = Date.now();
|
|
351
|
+
if (!apiKey || typeof fetch !== 'function') {
|
|
352
|
+
await emitLlmTrace(options, {
|
|
353
|
+
provider: 'zai',
|
|
354
|
+
model,
|
|
355
|
+
outcome: 'unavailable',
|
|
356
|
+
latencyMs: Date.now() - startedAt,
|
|
357
|
+
errorCode: !apiKey ? 'missing_api_key' : 'fetch_unavailable',
|
|
358
|
+
});
|
|
359
|
+
return null;
|
|
360
|
+
}
|
|
173
361
|
|
|
174
362
|
const messages = Array.isArray(options.messages) && options.messages.length > 0
|
|
175
363
|
? options.messages
|
|
@@ -186,23 +374,52 @@ async function callZaiInternal(options = {}, env = process.env) {
|
|
|
186
374
|
'Content-Type': 'application/json',
|
|
187
375
|
},
|
|
188
376
|
body: JSON.stringify({
|
|
189
|
-
model
|
|
377
|
+
model,
|
|
190
378
|
messages,
|
|
191
379
|
max_tokens: options.maxTokens || DEFAULT_MAX_TOKENS,
|
|
192
380
|
temperature: Number.isFinite(options.temperature) ? options.temperature : 0,
|
|
193
381
|
}),
|
|
194
382
|
});
|
|
195
383
|
|
|
196
|
-
if (!response.ok)
|
|
384
|
+
if (!response.ok) {
|
|
385
|
+
await emitLlmTrace(options, {
|
|
386
|
+
provider: 'zai',
|
|
387
|
+
model,
|
|
388
|
+
outcome: 'error',
|
|
389
|
+
latencyMs: Date.now() - startedAt,
|
|
390
|
+
httpStatus: response.status,
|
|
391
|
+
errorCode: 'http_error',
|
|
392
|
+
});
|
|
393
|
+
return null;
|
|
394
|
+
}
|
|
197
395
|
const json = await response.json();
|
|
198
|
-
|
|
396
|
+
const result = {
|
|
199
397
|
text: stripCodeFences(json?.choices?.[0]?.message?.content || ''),
|
|
200
398
|
usage: json?.usage || null,
|
|
201
399
|
stopReason: json?.choices?.[0]?.finish_reason || null,
|
|
202
400
|
id: json?.id || null,
|
|
203
|
-
model: json?.model ||
|
|
401
|
+
model: json?.model || model,
|
|
204
402
|
};
|
|
205
|
-
|
|
403
|
+
await emitLlmTrace(options, {
|
|
404
|
+
provider: 'zai',
|
|
405
|
+
model: result.model,
|
|
406
|
+
outcome: 'success',
|
|
407
|
+
latencyMs: Date.now() - startedAt,
|
|
408
|
+
usage: result.usage,
|
|
409
|
+
stopReason: result.stopReason,
|
|
410
|
+
requestId: result.id,
|
|
411
|
+
});
|
|
412
|
+
return result;
|
|
413
|
+
} catch (error) {
|
|
414
|
+
const safe = buildSafeProviderError(error);
|
|
415
|
+
await emitLlmTrace(options, {
|
|
416
|
+
provider: 'zai',
|
|
417
|
+
model,
|
|
418
|
+
outcome: 'error',
|
|
419
|
+
latencyMs: Date.now() - startedAt,
|
|
420
|
+
httpStatus: safe.status,
|
|
421
|
+
errorCode: safe.code || safe.name,
|
|
422
|
+
});
|
|
206
423
|
return null;
|
|
207
424
|
}
|
|
208
425
|
}
|
|
@@ -211,8 +428,19 @@ async function callGeminiInternal(options = {}) {
|
|
|
211
428
|
const env = process.env;
|
|
212
429
|
const { detectInferenceBackend } = require('./local-model-profile');
|
|
213
430
|
const providerMode = detectInferenceBackend(env).providerMode;
|
|
431
|
+
const provider = providerMode === 'vertex' ? 'vertex' : 'gemini';
|
|
432
|
+
const startedAt = Date.now();
|
|
214
433
|
|
|
215
|
-
if (providerMode !== 'vertex' && !env.GEMINI_API_KEY)
|
|
434
|
+
if (providerMode !== 'vertex' && !env.GEMINI_API_KEY) {
|
|
435
|
+
await emitLlmTrace(options, {
|
|
436
|
+
provider,
|
|
437
|
+
model: options.model,
|
|
438
|
+
outcome: 'unavailable',
|
|
439
|
+
latencyMs: Date.now() - startedAt,
|
|
440
|
+
errorCode: 'missing_api_key',
|
|
441
|
+
});
|
|
442
|
+
return null;
|
|
443
|
+
}
|
|
216
444
|
|
|
217
445
|
try {
|
|
218
446
|
const { GoogleGenAI } = require('@google/genai');
|
|
@@ -251,7 +479,7 @@ async function callGeminiInternal(options = {}) {
|
|
|
251
479
|
config,
|
|
252
480
|
}));
|
|
253
481
|
|
|
254
|
-
|
|
482
|
+
const result = {
|
|
255
483
|
text: response.text || '',
|
|
256
484
|
usage: response.usageMetadata ? {
|
|
257
485
|
input_tokens: response.usageMetadata.promptTokenCount,
|
|
@@ -261,8 +489,27 @@ async function callGeminiInternal(options = {}) {
|
|
|
261
489
|
id: null,
|
|
262
490
|
model: options.model,
|
|
263
491
|
};
|
|
492
|
+
await emitLlmTrace(options, {
|
|
493
|
+
provider,
|
|
494
|
+
model: result.model,
|
|
495
|
+
outcome: 'success',
|
|
496
|
+
latencyMs: Date.now() - startedAt,
|
|
497
|
+
usage: result.usage,
|
|
498
|
+
stopReason: result.stopReason,
|
|
499
|
+
requestId: result.id,
|
|
500
|
+
});
|
|
501
|
+
return result;
|
|
264
502
|
} catch (err) {
|
|
265
|
-
|
|
503
|
+
const safe = buildSafeProviderError(err);
|
|
504
|
+
console.error('Gemini/Vertex AI execution error:', JSON.stringify(safe));
|
|
505
|
+
await emitLlmTrace(options, {
|
|
506
|
+
provider,
|
|
507
|
+
model: options.model,
|
|
508
|
+
outcome: 'error',
|
|
509
|
+
latencyMs: Date.now() - startedAt,
|
|
510
|
+
httpStatus: safe.status,
|
|
511
|
+
errorCode: safe.code || safe.name,
|
|
512
|
+
});
|
|
266
513
|
return null;
|
|
267
514
|
}
|
|
268
515
|
}
|
|
@@ -296,7 +543,21 @@ async function callClaudeInternal(options = {}) {
|
|
|
296
543
|
}
|
|
297
544
|
|
|
298
545
|
const client = getClient();
|
|
299
|
-
|
|
546
|
+
const model = options.model || DEFAULT_MODEL;
|
|
547
|
+
const startedAt = Date.now();
|
|
548
|
+
if (!client) {
|
|
549
|
+
if (isGatewayConfigured()) {
|
|
550
|
+
return callGatewayInternal(options);
|
|
551
|
+
}
|
|
552
|
+
await emitLlmTrace(options, {
|
|
553
|
+
provider: 'anthropic',
|
|
554
|
+
model,
|
|
555
|
+
outcome: 'unavailable',
|
|
556
|
+
latencyMs: Date.now() - startedAt,
|
|
557
|
+
errorCode: 'missing_client_or_api_key',
|
|
558
|
+
});
|
|
559
|
+
return null;
|
|
560
|
+
}
|
|
300
561
|
|
|
301
562
|
try {
|
|
302
563
|
const response = await runStep('llm.callClaude', {
|
|
@@ -305,14 +566,33 @@ async function callClaudeInternal(options = {}) {
|
|
|
305
566
|
}, async () => client.messages.create(buildClaudeRequest(options)));
|
|
306
567
|
|
|
307
568
|
const text = stripCodeFences(extractTextContent(response));
|
|
308
|
-
|
|
569
|
+
const result = {
|
|
309
570
|
text,
|
|
310
571
|
usage: response?.usage || null,
|
|
311
572
|
stopReason: response?.stop_reason || null,
|
|
312
573
|
id: response?.id || null,
|
|
313
|
-
model: response?.model ||
|
|
574
|
+
model: response?.model || model,
|
|
314
575
|
};
|
|
315
|
-
|
|
576
|
+
await emitLlmTrace(options, {
|
|
577
|
+
provider: 'anthropic',
|
|
578
|
+
model: result.model,
|
|
579
|
+
outcome: 'success',
|
|
580
|
+
latencyMs: Date.now() - startedAt,
|
|
581
|
+
usage: result.usage,
|
|
582
|
+
stopReason: result.stopReason,
|
|
583
|
+
requestId: result.id,
|
|
584
|
+
});
|
|
585
|
+
return result;
|
|
586
|
+
} catch (error) {
|
|
587
|
+
const safe = buildSafeProviderError(error);
|
|
588
|
+
await emitLlmTrace(options, {
|
|
589
|
+
provider: 'anthropic',
|
|
590
|
+
model,
|
|
591
|
+
outcome: 'error',
|
|
592
|
+
latencyMs: Date.now() - startedAt,
|
|
593
|
+
httpStatus: safe.status,
|
|
594
|
+
errorCode: safe.code || safe.name,
|
|
595
|
+
});
|
|
316
596
|
return null;
|
|
317
597
|
}
|
|
318
598
|
}
|
|
@@ -372,6 +652,12 @@ async function callZaiJson(options = {}) {
|
|
|
372
652
|
|
|
373
653
|
module.exports = {
|
|
374
654
|
isAvailable,
|
|
655
|
+
getGatewayConfig,
|
|
656
|
+
isGatewayConfigured,
|
|
657
|
+
resolveGatewayModel,
|
|
658
|
+
describeInferenceAvailability,
|
|
659
|
+
callGatewayInternal,
|
|
660
|
+
buildGatewayMessages,
|
|
375
661
|
callClaude,
|
|
376
662
|
callClaudeJson,
|
|
377
663
|
callZaiJson,
|
|
@@ -383,5 +669,8 @@ module.exports = {
|
|
|
383
669
|
normalizeCacheOptions,
|
|
384
670
|
buildClaudeRequest,
|
|
385
671
|
buildSafeProviderError,
|
|
672
|
+
normalizeUsageTelemetry,
|
|
673
|
+
buildLlmTrace,
|
|
674
|
+
emitLlmTrace,
|
|
386
675
|
MODELS,
|
|
387
676
|
};
|