@juspay/neurolink 12.52.1 → 12.54.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/CHANGELOG.md +4 -3
- package/dist/browser/neurolink.min.js +439 -439
- package/dist/constants/enums.d.ts +4 -0
- package/dist/constants/enums.js +5 -0
- package/dist/core/toolExecutionGuards.js +9 -1
- package/dist/providers/catalog/index.generated.d.ts +1 -1
- package/dist/providers/catalog/index.generated.js +3 -0
- package/dist/providers/catalog/longcat.json +64 -0
- package/dist/providers/googleVertex/client.js +52 -12
- package/dist/services/server/ai/observability/instrumentation.d.ts +2 -0
- package/dist/services/server/ai/observability/instrumentation.js +4 -0
- package/dist/telemetry/attributes.d.ts +32 -0
- package/dist/telemetry/attributes.js +289 -0
- package/dist/telemetry/index.d.ts +1 -1
- package/dist/telemetry/index.js +1 -1
- package/dist/types/loopEngine.d.ts +12 -1
- package/dist/types/observability.d.ts +8 -0
- package/dist/types/providerCatalog.generated.d.ts +2 -2
- package/dist/types/providers.d.ts +4 -0
- package/docs-site/static/search-index.json +7 -0
- package/package.json +4 -2
|
@@ -60,6 +60,7 @@ export declare enum AIProviderName {
|
|
|
60
60
|
LEMONFOX_AI = "lemonfox-ai",
|
|
61
61
|
LILAC = "lilac",
|
|
62
62
|
LLMTECH = "llmtech",
|
|
63
|
+
LONGCAT = "longcat",
|
|
63
64
|
MANCER = "mancer",
|
|
64
65
|
META_MODEL_API = "meta-model-api",
|
|
65
66
|
MINIMAX = "minimax",
|
|
@@ -1346,6 +1347,9 @@ export declare enum LilacModels {
|
|
|
1346
1347
|
export declare enum LlmtechModels {
|
|
1347
1348
|
NVIDIA_QWEN3_8_27B_NVFP4 = "nvidia/Qwen3.8-27B-NVFP4"
|
|
1348
1349
|
}
|
|
1350
|
+
export declare enum LongCatModels {
|
|
1351
|
+
LONGCAT_2_5_PREVIEW = "LongCat-2.5-Preview"
|
|
1352
|
+
}
|
|
1349
1353
|
export declare enum MancerModels {
|
|
1350
1354
|
MYTHOMAX = "mythomax",
|
|
1351
1355
|
DEEPSEEK_V4_FLASH = "deepseek-v4-flash",
|
package/dist/constants/enums.js
CHANGED
|
@@ -65,6 +65,7 @@ export var AIProviderName;
|
|
|
65
65
|
AIProviderName["LEMONFOX_AI"] = "lemonfox-ai";
|
|
66
66
|
AIProviderName["LILAC"] = "lilac";
|
|
67
67
|
AIProviderName["LLMTECH"] = "llmtech";
|
|
68
|
+
AIProviderName["LONGCAT"] = "longcat";
|
|
68
69
|
AIProviderName["MANCER"] = "mancer";
|
|
69
70
|
AIProviderName["META_MODEL_API"] = "meta-model-api";
|
|
70
71
|
AIProviderName["MINIMAX"] = "minimax";
|
|
@@ -1626,6 +1627,10 @@ export var LlmtechModels;
|
|
|
1626
1627
|
(function (LlmtechModels) {
|
|
1627
1628
|
LlmtechModels["NVIDIA_QWEN3_8_27B_NVFP4"] = "nvidia/Qwen3.8-27B-NVFP4";
|
|
1628
1629
|
})(LlmtechModels || (LlmtechModels = {}));
|
|
1630
|
+
export var LongCatModels;
|
|
1631
|
+
(function (LongCatModels) {
|
|
1632
|
+
LongCatModels["LONGCAT_2_5_PREVIEW"] = "LongCat-2.5-Preview";
|
|
1633
|
+
})(LongCatModels || (LongCatModels = {}));
|
|
1629
1634
|
export var MancerModels;
|
|
1630
1635
|
(function (MancerModels) {
|
|
1631
1636
|
MancerModels["MYTHOMAX"] = "mythomax";
|
|
@@ -33,7 +33,15 @@ export function guardToolExecutor(name, execute, guards) {
|
|
|
33
33
|
// The span wraps the CALL, not the guard: a timeout or an abort is a fact
|
|
34
34
|
// about this tool invocation and belongs inside its observation.
|
|
35
35
|
const wrapInSpan = guards.withToolSpan;
|
|
36
|
-
const
|
|
36
|
+
const toolCallId = typeof opts === "object" &&
|
|
37
|
+
opts !== null &&
|
|
38
|
+
"toolCallId" in opts &&
|
|
39
|
+
typeof opts.toolCallId === "string"
|
|
40
|
+
? opts.toolCallId
|
|
41
|
+
: undefined;
|
|
42
|
+
const call = wrapInSpan
|
|
43
|
+
? () => wrapInSpan(name, invoke, { args, toolCallId })
|
|
44
|
+
: invoke;
|
|
37
45
|
guards.onProgress?.();
|
|
38
46
|
try {
|
|
39
47
|
const raced = guards.abortSignal
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { DecideCatalogJson, ProviderCatalogJson } from "../../types/index.js";
|
|
2
2
|
export declare const CATALOG_JSON_ENTRIES: ProviderCatalogJson[];
|
|
3
|
-
export declare const CATALOG_PROVIDER_IDS: readonly ["a2agent", "abliteration", "above-dev", "ai21", "aiand", "aihubmix", "aionlabs", "ambient", "api-route", "arcee", "atlas-cloud", "avian-io", "baidu-qianfan", "baseten", "bee-heossi", "byteplus-modelark", "bytez", "cerebras", "charm-hyper", "chutes", "cloudflare", "crusoe", "dashscope", "deepinfra", "deepseek", "empiriolabs", "featherless-ai", "fireworks", "friendli", "gmicloud", "gradientai", "groq", "hetzner-inference", "huggingface", "inception-labs", "inceptron", "inco", "inference-net", "io-intelligence", "koscompute", "lemonfox-ai", "lilac", "llmtech", "mancer", "meta-model-api", "minimax", "mistral", "moark", "modelscope", "moonshot-ai", "morph", "naver-clova-studio", "nebius", "neuralwatt", "novita", "ovhcloud", "parasail", "pareto-inference", "perplexity", "poolside", "prime-intellect", "reka", "sakana-ai", "sambanova", "sarvam", "scaleway", "siliconflow", "stackit", "stepfun", "subconscious", "synthetic", "telnyx", "thinking-machines", "tinfoil", "together-ai", "umans-ai", "upstage", "venice-ai", "vispark", "vultr-inference", "wafer", "wandb-inference", "xai", "xiaomi-mimo", "z-ai"];
|
|
3
|
+
export declare const CATALOG_PROVIDER_IDS: readonly ["a2agent", "abliteration", "above-dev", "ai21", "aiand", "aihubmix", "aionlabs", "ambient", "api-route", "arcee", "atlas-cloud", "avian-io", "baidu-qianfan", "baseten", "bee-heossi", "byteplus-modelark", "bytez", "cerebras", "charm-hyper", "chutes", "cloudflare", "crusoe", "dashscope", "deepinfra", "deepseek", "empiriolabs", "featherless-ai", "fireworks", "friendli", "gmicloud", "gradientai", "groq", "hetzner-inference", "huggingface", "inception-labs", "inceptron", "inco", "inference-net", "io-intelligence", "koscompute", "lemonfox-ai", "lilac", "llmtech", "longcat", "mancer", "meta-model-api", "minimax", "mistral", "moark", "modelscope", "moonshot-ai", "morph", "naver-clova-studio", "nebius", "neuralwatt", "novita", "ovhcloud", "parasail", "pareto-inference", "perplexity", "poolside", "prime-intellect", "reka", "sakana-ai", "sambanova", "sarvam", "scaleway", "siliconflow", "stackit", "stepfun", "subconscious", "synthetic", "telnyx", "thinking-machines", "tinfoil", "together-ai", "umans-ai", "upstage", "venice-ai", "vispark", "vultr-inference", "wafer", "wandb-inference", "xai", "xiaomi-mimo", "z-ai"];
|
|
4
4
|
export declare const DECIDE_CATALOG_JSON_ENTRIES: DecideCatalogJson[];
|
|
5
5
|
export declare const DECIDE_CATALOG_PROVIDER_IDS: readonly ["laya"];
|
|
@@ -44,6 +44,7 @@ import layaJson from "./laya.json" with { type: "json" };
|
|
|
44
44
|
import lemonfoxAiJson from "./lemonfox-ai.json" with { type: "json" };
|
|
45
45
|
import lilacJson from "./lilac.json" with { type: "json" };
|
|
46
46
|
import llmtechJson from "./llmtech.json" with { type: "json" };
|
|
47
|
+
import longcatJson from "./longcat.json" with { type: "json" };
|
|
47
48
|
import mancerJson from "./mancer.json" with { type: "json" };
|
|
48
49
|
import metaModelApiJson from "./meta-model-api.json" with { type: "json" };
|
|
49
50
|
import minimaxJson from "./minimax.json" with { type: "json" };
|
|
@@ -138,6 +139,7 @@ export const CATALOG_JSON_ENTRIES = [
|
|
|
138
139
|
lemonfoxAiJson,
|
|
139
140
|
lilacJson,
|
|
140
141
|
llmtechJson,
|
|
142
|
+
longcatJson,
|
|
141
143
|
mancerJson,
|
|
142
144
|
metaModelApiJson,
|
|
143
145
|
minimaxJson,
|
|
@@ -225,6 +227,7 @@ export const CATALOG_PROVIDER_IDS = [
|
|
|
225
227
|
"lemonfox-ai",
|
|
226
228
|
"lilac",
|
|
227
229
|
"llmtech",
|
|
230
|
+
"longcat",
|
|
228
231
|
"mancer",
|
|
229
232
|
"meta-model-api",
|
|
230
233
|
"minimax",
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "./provider-catalog.schema.json",
|
|
3
|
+
"id": "longcat",
|
|
4
|
+
"displayName": "LongCat",
|
|
5
|
+
"enumTypeName": "LongCatModels",
|
|
6
|
+
"aliases": [],
|
|
7
|
+
"tier": 2,
|
|
8
|
+
"wire": {
|
|
9
|
+
"baseURL": "https://api.longcat.chat/openai/v1"
|
|
10
|
+
},
|
|
11
|
+
"models": {
|
|
12
|
+
"default": "LongCat-2.5-Preview",
|
|
13
|
+
"fallbacks": ["LongCat-2.5-Preview"],
|
|
14
|
+
"defaultContextWindow": 1048576,
|
|
15
|
+
"defaultMaxOutputTokens": 131072,
|
|
16
|
+
"catalog": {
|
|
17
|
+
"LongCat-2.5-Preview": {
|
|
18
|
+
"contextWindow": 1048576,
|
|
19
|
+
"maxOutputTokens": 131072,
|
|
20
|
+
"vision": false,
|
|
21
|
+
"status": "preview",
|
|
22
|
+
"description": "Meituan LongCat 2.5 Preview, the only model this pilot lists. The 1M-token context rests on the vendor's Retrieve Model sample for this model id, not on a normative sentence. This pilot declares text generate/stream only; tools, vision, structured output and thinking await separate evidence. Account access is unverified."
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
},
|
|
26
|
+
"capabilities": {
|
|
27
|
+
"text": true,
|
|
28
|
+
"streaming": true,
|
|
29
|
+
"tools": false,
|
|
30
|
+
"toolsWithStreaming": false,
|
|
31
|
+
"structuredOutput": false,
|
|
32
|
+
"structuredOutputWithTools": false,
|
|
33
|
+
"embeddings": false,
|
|
34
|
+
"thinking": false
|
|
35
|
+
},
|
|
36
|
+
"errorRules": [
|
|
37
|
+
{
|
|
38
|
+
"status": 401,
|
|
39
|
+
"class": "authentication",
|
|
40
|
+
"message": "Invalid or expired LongCat API key. Check {apiKeyEnvVar} and the key in the LongCat platform console at {setupUrl}."
|
|
41
|
+
}
|
|
42
|
+
],
|
|
43
|
+
"setup": {
|
|
44
|
+
"url": "https://longcat.chat/platform",
|
|
45
|
+
"apiKeyFormat": null,
|
|
46
|
+
"billingPolicy": "no-free-tier",
|
|
47
|
+
"instructions": [
|
|
48
|
+
"1. Sign in to the LongCat API platform and create an API key.",
|
|
49
|
+
"2. LongCat-2.5-Preview is usage-priced pay-as-you-go. Promotional credits, if your account has any, are consumed first, but no free allowance is stated: do not assume free usage. The vendor's Pay-As-You-Go page says account verification is required before topping up, and its FAQ scopes that requirement to users in Mainland China (not tried on an account).",
|
|
50
|
+
"3. LongCat-2.0 is served on the same route but is outside this pilot's catalog scope, so it is not a default or a fallback here.",
|
|
51
|
+
"4. Set {apiKeyEnvVar} for this provider. The base URL is https://api.longcat.chat/openai/v1 and the chat route is POST /chat/completions with Authorization: Bearer authentication."
|
|
52
|
+
],
|
|
53
|
+
"description": "LongCat (Meituan) text/stream pilot with a usage-priced LongCat-2.5-Preview default. No free allowance is stated; no account entitlement, roster, live inference or delivery is verified."
|
|
54
|
+
},
|
|
55
|
+
"evidence": {
|
|
56
|
+
"rosterVerified": {
|
|
57
|
+
"date": "2026-10-12",
|
|
58
|
+
"code": "VENDOR_DOCS_ONLY_NOT_ROSTER_VERIFIED",
|
|
59
|
+
"method": "Vendor API documentation facts supplied to this change: default model id, 1048576 context from the vendor's Retrieve Model sample plus the 1M prose, 131072 maximum output, base URL https://api.longcat.chat/openai/v1 and route POST /chat/completions with Bearer auth. No GET /models, credentials, account or inference ran; this legacy field does not assert roster verification."
|
|
60
|
+
},
|
|
61
|
+
"liveMatrix": null,
|
|
62
|
+
"addedInPR": "https://github.com/juspay/neurolink/pull/1980"
|
|
63
|
+
}
|
|
64
|
+
}
|
|
@@ -43,7 +43,7 @@ import { createStreamChannel } from "../../core/streamChannel.js";
|
|
|
43
43
|
import { toNativeToolDeclarations } from "../../core/nativeToolFormat.js";
|
|
44
44
|
import { getAvailableInputTokens, getContextWindowSize, } from "../../constants/contextWindows.js";
|
|
45
45
|
import { resolveLiveTool } from "../../tools/toolDiscovery.js";
|
|
46
|
-
import { ATTR, LANGFUSE_ATTR, spanJsonAttribute, tracers, withClientSpan, withClientStreamSpan, withSpan, } from "../../telemetry/index.js";
|
|
46
|
+
import { ATTR, LANGFUSE_ATTR, spanContentAttribute, spanJsonAttribute, spanModelRequestAttribute, tracers, withClientSpan, withClientStreamSpan, withSpan, } from "../../telemetry/index.js";
|
|
47
47
|
import { SpanKind, SpanStatusCode, context as otelContext, trace as otelTrace, } from "@opentelemetry/api";
|
|
48
48
|
import { calculateCost } from "../../utils/pricing.js";
|
|
49
49
|
import { transformToolExecutions } from "../../utils/transformationUtils.js";
|
|
@@ -3459,7 +3459,7 @@ export class GoogleVertexProvider extends BaseProvider {
|
|
|
3459
3459
|
// Usage lives ONLY on the generation spans (Langfuse sums usage across
|
|
3460
3460
|
// observations for trace totals, so repeating it on the turn double-counts).
|
|
3461
3461
|
const offeredToolNames = (tools ?? []).map((anthropicTool) => anthropicTool.name);
|
|
3462
|
-
const turnInputAttribute =
|
|
3462
|
+
const turnInputAttribute = spanModelRequestAttribute({
|
|
3463
3463
|
system: systemPromptWithSchema,
|
|
3464
3464
|
messages: sanitizeAnthropicMessagesForTrace(messages),
|
|
3465
3465
|
});
|
|
@@ -3543,6 +3543,7 @@ export class GoogleVertexProvider extends BaseProvider {
|
|
|
3543
3543
|
const loopPromise = (async () => {
|
|
3544
3544
|
let step = 0;
|
|
3545
3545
|
const currentMessages = [...messages];
|
|
3546
|
+
let stepConversation = currentMessages;
|
|
3546
3547
|
// Step-cap / abort bookkeeping (mirrors the generate twin): read by the
|
|
3547
3548
|
// terminal recovery block after the loop and by the finishReason
|
|
3548
3549
|
// mapping written into finishReasonRef.
|
|
@@ -3569,18 +3570,34 @@ export class GoogleVertexProvider extends BaseProvider {
|
|
|
3569
3570
|
// migration. Shared with resolveToolOnMiss below: the old dispatch
|
|
3570
3571
|
// opened the span before the executor lookup, so a tool hydrated
|
|
3571
3572
|
// mid-turn was observed exactly like one declared up front.
|
|
3572
|
-
const withToolSpan = (toolCallName, run) => {
|
|
3573
|
+
const withToolSpan = (toolCallName, run, call) => {
|
|
3574
|
+
// Arguments are inputs: honour the same opt-out the AI SDK path uses.
|
|
3575
|
+
const argsAttribute = call?.args !== undefined &&
|
|
3576
|
+
process.env.NEUROLINK_RECORD_INPUTS?.trim().toLowerCase() !==
|
|
3577
|
+
"false"
|
|
3578
|
+
? spanContentAttribute(call.args)
|
|
3579
|
+
: undefined;
|
|
3573
3580
|
const toolSpan = tracers.mcp.startSpan("ai.toolCall", {
|
|
3574
3581
|
kind: SpanKind.INTERNAL,
|
|
3575
3582
|
attributes: {
|
|
3576
3583
|
[LANGFUSE_ATTR.OBSERVATION_TYPE]: "tool",
|
|
3577
3584
|
[ATTR.GEN_AI_TOOL_NAME]: toolCallName,
|
|
3578
3585
|
"ai.toolCall.name": toolCallName,
|
|
3586
|
+
...(call?.toolCallId
|
|
3587
|
+
? { "ai.toolCall.id": call.toolCallId }
|
|
3588
|
+
: {}),
|
|
3589
|
+
...(argsAttribute !== undefined
|
|
3590
|
+
? {
|
|
3591
|
+
"ai.toolCall.args": argsAttribute,
|
|
3592
|
+
[LANGFUSE_ATTR.OBSERVATION_INPUT]: argsAttribute,
|
|
3593
|
+
}
|
|
3594
|
+
: {}),
|
|
3579
3595
|
},
|
|
3580
3596
|
}, turnContext);
|
|
3581
3597
|
const finish = (output, errorMessage) => {
|
|
3582
|
-
|
|
3583
|
-
toolSpan.setAttribute(
|
|
3598
|
+
const outputAttribute = spanContentAttribute(output);
|
|
3599
|
+
toolSpan.setAttribute("ai.toolCall.result", outputAttribute);
|
|
3600
|
+
toolSpan.setAttribute(LANGFUSE_ATTR.OBSERVATION_OUTPUT, outputAttribute);
|
|
3584
3601
|
if (errorMessage) {
|
|
3585
3602
|
toolSpan.setAttribute(LANGFUSE_ATTR.OBSERVATION_LEVEL, "ERROR");
|
|
3586
3603
|
toolSpan.setAttribute(LANGFUSE_ATTR.OBSERVATION_STATUS_MESSAGE, errorMessage);
|
|
@@ -3742,6 +3759,7 @@ export class GoogleVertexProvider extends BaseProvider {
|
|
|
3742
3759
|
...baseAdapter,
|
|
3743
3760
|
buildStepRequest: (conversation, engineStep) => {
|
|
3744
3761
|
step = engineStep + 1;
|
|
3762
|
+
stepConversation = conversation;
|
|
3745
3763
|
turnClock.noteProgress();
|
|
3746
3764
|
return baseAdapter.buildStepRequest(conversation, engineStep);
|
|
3747
3765
|
},
|
|
@@ -3782,9 +3800,9 @@ export class GoogleVertexProvider extends BaseProvider {
|
|
|
3782
3800
|
temperature: requestParams.temperature,
|
|
3783
3801
|
top_p: requestParams.top_p,
|
|
3784
3802
|
}),
|
|
3785
|
-
[LANGFUSE_ATTR.OBSERVATION_INPUT]:
|
|
3803
|
+
[LANGFUSE_ATTR.OBSERVATION_INPUT]: spanModelRequestAttribute({
|
|
3786
3804
|
system: systemPromptWithSchema,
|
|
3787
|
-
messages: sanitizeAnthropicMessagesForTrace(
|
|
3805
|
+
messages: sanitizeAnthropicMessagesForTrace(stepConversation),
|
|
3788
3806
|
}),
|
|
3789
3807
|
[LANGFUSE_ATTR.OBSERVATION_METADATA]: spanJsonAttribute({
|
|
3790
3808
|
step,
|
|
@@ -3812,7 +3830,13 @@ export class GoogleVertexProvider extends BaseProvider {
|
|
|
3812
3830
|
turnCacheUsage.creation += result.usage.cacheWriteTokens ?? 0;
|
|
3813
3831
|
turnCacheUsage.creation5m += result.usage.cacheWrite5mTokens ?? 0;
|
|
3814
3832
|
turnCacheUsage.creation1h += result.usage.cacheWrite1hTokens ?? 0;
|
|
3815
|
-
generationSpan.setAttribute(LANGFUSE_ATTR.OBSERVATION_OUTPUT,
|
|
3833
|
+
generationSpan.setAttribute(LANGFUSE_ATTR.OBSERVATION_OUTPUT, spanContentAttribute({
|
|
3834
|
+
text: result.text,
|
|
3835
|
+
...(result.toolCalls.length > 0
|
|
3836
|
+
? { toolCalls: result.toolCalls }
|
|
3837
|
+
: {}),
|
|
3838
|
+
}));
|
|
3839
|
+
generationSpan.setAttribute(`${LANGFUSE_ATTR.OBSERVATION_METADATA}.finishReason`, result.rawStopReason ?? "unknown");
|
|
3816
3840
|
return result;
|
|
3817
3841
|
}
|
|
3818
3842
|
catch (error) {
|
|
@@ -4339,7 +4363,7 @@ export class GoogleVertexProvider extends BaseProvider {
|
|
|
4339
4363
|
if (turnCacheUsage.creation > 0) {
|
|
4340
4364
|
usage.cacheCreationTokens = turnCacheUsage.creation;
|
|
4341
4365
|
}
|
|
4342
|
-
const turnOutputAttribute =
|
|
4366
|
+
const turnOutputAttribute = spanContentAttribute({
|
|
4343
4367
|
text: aggregatedTurnText,
|
|
4344
4368
|
...(structuredOutputRef.value
|
|
4345
4369
|
? { structuredOutput: structuredOutputRef.value }
|
|
@@ -4357,6 +4381,7 @@ export class GoogleVertexProvider extends BaseProvider {
|
|
|
4357
4381
|
toolCallCount: metadata.totalToolExecutions,
|
|
4358
4382
|
toolsCalled: toolsUsedRef.filter((name) => name !== "final_result"),
|
|
4359
4383
|
structuredOutput: useFinalResultTool,
|
|
4384
|
+
...(stopReasonRef.value ? { stopReason: stopReasonRef.value } : {}),
|
|
4360
4385
|
usage: {
|
|
4361
4386
|
input: usage.input,
|
|
4362
4387
|
output: usage.output,
|
|
@@ -4366,10 +4391,25 @@ export class GoogleVertexProvider extends BaseProvider {
|
|
|
4366
4391
|
input_cache_creation_1h: turnCacheUsage.creation1h,
|
|
4367
4392
|
},
|
|
4368
4393
|
}));
|
|
4394
|
+
// How the turn ended, on the trace itself, so an evaluator can select
|
|
4395
|
+
// only turns that finished ("completed") and skip capped, timed-out,
|
|
4396
|
+
// aborted and failed ones. A trace holding several turns keeps the last
|
|
4397
|
+
// one's; each turn's own is in its observation metadata above.
|
|
4398
|
+
if (stopReasonRef.value) {
|
|
4399
|
+
turnSpan.setAttribute(`${LANGFUSE_ATTR.TRACE_METADATA}.stopReason`, stopReasonRef.value);
|
|
4400
|
+
}
|
|
4369
4401
|
turnSpan.setStatus({ code: SpanStatusCode.OK });
|
|
4370
4402
|
channel.close();
|
|
4371
4403
|
}
|
|
4372
4404
|
catch (err) {
|
|
4405
|
+
turnSpan.setAttribute(LANGFUSE_ATTR.OBSERVATION_METADATA, spanJsonAttribute({
|
|
4406
|
+
toolsOffered: offeredToolNames,
|
|
4407
|
+
toolCount: offeredToolNames.length,
|
|
4408
|
+
maxSteps,
|
|
4409
|
+
structuredOutput: useFinalResultTool,
|
|
4410
|
+
stopReason: "provider-error",
|
|
4411
|
+
}));
|
|
4412
|
+
turnSpan.setAttribute(`${LANGFUSE_ATTR.TRACE_METADATA}.stopReason`, "provider-error");
|
|
4373
4413
|
turnSpan.setStatus({
|
|
4374
4414
|
code: SpanStatusCode.ERROR,
|
|
4375
4415
|
message: err instanceof Error ? err.message : String(err),
|
|
@@ -5635,7 +5675,7 @@ export class GoogleVertexProvider extends BaseProvider {
|
|
|
5635
5675
|
"";
|
|
5636
5676
|
// Set generation input before the call so error paths still carry the
|
|
5637
5677
|
// request; output is set after the native call resolves.
|
|
5638
|
-
const generationInputAttribute =
|
|
5678
|
+
const generationInputAttribute = spanModelRequestAttribute({
|
|
5639
5679
|
...(mergedOptions.systemPrompt
|
|
5640
5680
|
? { system: mergedOptions.systemPrompt }
|
|
5641
5681
|
: {}),
|
|
@@ -5676,7 +5716,7 @@ export class GoogleVertexProvider extends BaseProvider {
|
|
|
5676
5716
|
});
|
|
5677
5717
|
nativeResult = await this.runNativeGenerateWithMiddleware(mergedOptions, modelName, (effectiveOptions) => this.executeNativeGemini3GenerateWithSchemaFallback(effectiveOptions, modelName));
|
|
5678
5718
|
}
|
|
5679
|
-
executionSpan.setAttribute(LANGFUSE_ATTR.OBSERVATION_OUTPUT,
|
|
5719
|
+
executionSpan.setAttribute(LANGFUSE_ATTR.OBSERVATION_OUTPUT, spanContentAttribute(nativeResult?.content ?? ""));
|
|
5680
5720
|
return nativeResult;
|
|
5681
5721
|
});
|
|
5682
5722
|
this.attachUsageAndCostAttributes(generateSpan, modelName, result?.usage);
|
|
@@ -5685,7 +5725,7 @@ export class GoogleVertexProvider extends BaseProvider {
|
|
|
5685
5725
|
// enabled / useAiResponse is false, so the cost is zero on
|
|
5686
5726
|
// non-TTS paths.
|
|
5687
5727
|
result = await this.synthesizeAIResponseIfNeeded(result, options);
|
|
5688
|
-
generateSpan.setAttribute(LANGFUSE_ATTR.OBSERVATION_OUTPUT,
|
|
5728
|
+
generateSpan.setAttribute(LANGFUSE_ATTR.OBSERVATION_OUTPUT, spanContentAttribute(result?.content ?? ""));
|
|
5689
5729
|
// Fire onFinish lifecycle callback for the native generate path.
|
|
5690
5730
|
// Pipeline A providers get this for free via the AI SDK middleware
|
|
5691
5731
|
// wrapper (LifecycleMiddleware); native @google/genai bypasses
|
|
@@ -124,6 +124,8 @@ export declare function setLangfuseContext<T = void>(context: {
|
|
|
124
124
|
requestId?: string | null;
|
|
125
125
|
traceName?: string | null;
|
|
126
126
|
metadata?: Record<string, unknown> | null;
|
|
127
|
+
/** Langfuse trace tags, set on the trace's root span */
|
|
128
|
+
tags?: string[] | null;
|
|
127
129
|
/** Explicit operation name (overrides auto-detection) */
|
|
128
130
|
operationName?: string | null;
|
|
129
131
|
/** Override global autoDetectOperationName for this context */
|
|
@@ -298,6 +298,9 @@ class ContextEnricher {
|
|
|
298
298
|
span.setAttribute("langfuse.trace.name", traceName);
|
|
299
299
|
span.setAttribute("trace.name", traceName);
|
|
300
300
|
}
|
|
301
|
+
if (isRootSpan && context?.tags && context.tags.length > 0) {
|
|
302
|
+
span.setAttribute("langfuse.trace.tags", [...context.tags]);
|
|
303
|
+
}
|
|
301
304
|
// Set operation name as separate attribute for filtering/analytics
|
|
302
305
|
if (operationName) {
|
|
303
306
|
span.setAttribute("gen_ai.operation.name", operationName);
|
|
@@ -1163,6 +1166,7 @@ export async function setLangfuseContext(context, callback) {
|
|
|
1163
1166
|
metadata: context.metadata !== undefined
|
|
1164
1167
|
? context.metadata
|
|
1165
1168
|
: currentContext.metadata,
|
|
1169
|
+
tags: context.tags !== undefined ? context.tags : currentContext.tags,
|
|
1166
1170
|
// Operation name support
|
|
1167
1171
|
operationName: context.operationName !== undefined
|
|
1168
1172
|
? context.operationName
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { SpanModelRequest } from "../types/index.js";
|
|
1
2
|
export declare const ATTR: {
|
|
2
3
|
readonly GEN_AI_SYSTEM: "gen_ai.system";
|
|
3
4
|
readonly GEN_AI_MODEL: "gen_ai.request.model";
|
|
@@ -114,6 +115,8 @@ export declare const LANGFUSE_ATTR: {
|
|
|
114
115
|
readonly TRACE_NAME: "langfuse.trace.name";
|
|
115
116
|
readonly TRACE_INPUT: "langfuse.trace.input";
|
|
116
117
|
readonly TRACE_OUTPUT: "langfuse.trace.output";
|
|
118
|
+
readonly TRACE_TAGS: "langfuse.trace.tags";
|
|
119
|
+
readonly TRACE_METADATA: "langfuse.trace.metadata";
|
|
117
120
|
readonly OBSERVATION_TYPE: "langfuse.observation.type";
|
|
118
121
|
readonly OBSERVATION_INPUT: "langfuse.observation.input";
|
|
119
122
|
readonly OBSERVATION_OUTPUT: "langfuse.observation.output";
|
|
@@ -127,6 +130,24 @@ export declare const LANGFUSE_ATTR: {
|
|
|
127
130
|
};
|
|
128
131
|
/** Default ceiling for serialized span attribute values. */
|
|
129
132
|
export declare const SPAN_ATTRIBUTE_MAX_CHARS = 40000;
|
|
133
|
+
/**
|
|
134
|
+
* Returns a copy of `value` with credentials masked: values under keys such as
|
|
135
|
+
* `access_token`, `password`, `authorization` or `set-cookie`, bearer tokens
|
|
136
|
+
* and `name=value` credentials inside text. Content with nothing sensitive in
|
|
137
|
+
* it serializes exactly as it did before. Throws only if the value cannot be
|
|
138
|
+
* walked; callers on the tracing path use `spanContentAttribute`, which turns
|
|
139
|
+
* that into an omitted value instead of a leak or a failed turn.
|
|
140
|
+
*/
|
|
141
|
+
export declare function redactSpanContent(value: unknown): unknown;
|
|
142
|
+
/**
|
|
143
|
+
* `spanJsonAttribute` for content a person or model wrote or a tool returned
|
|
144
|
+
* (arguments, results, answers), with credentials masked first. Use the plain
|
|
145
|
+
* form for metadata such as usage counts and server ids.
|
|
146
|
+
*
|
|
147
|
+
* A structured value over the cap is shortened by trimming its longest
|
|
148
|
+
* strings, so the attribute stays valid JSON instead of ending mid-token.
|
|
149
|
+
*/
|
|
150
|
+
export declare function spanContentAttribute(value: unknown, maxChars?: number): string;
|
|
130
151
|
/**
|
|
131
152
|
* Serialize an arbitrary value for a span attribute, hard-capped at
|
|
132
153
|
* `maxChars` so a pathological prompt or tool result can't put megabytes
|
|
@@ -134,3 +155,14 @@ export declare const SPAN_ATTRIBUTE_MAX_CHARS = 40000;
|
|
|
134
155
|
* JSON-stringified with a String() fallback for circular structures.
|
|
135
156
|
*/
|
|
136
157
|
export declare function spanJsonAttribute(value: unknown, maxChars?: number): string;
|
|
158
|
+
/**
|
|
159
|
+
* Serialize a model request for a span attribute without losing the
|
|
160
|
+
* conversation to the cap.
|
|
161
|
+
*
|
|
162
|
+
* A head cut keeps the system prompt and drops everything after it, so a long
|
|
163
|
+
* system prompt hides every message, the user's question included. Here the
|
|
164
|
+
* prompt and the newest messages are budgeted first, older messages are
|
|
165
|
+
* dropped whole and counted, and the system prompt gets whatever room is left.
|
|
166
|
+
* The result is always valid JSON.
|
|
167
|
+
*/
|
|
168
|
+
export declare function spanModelRequestAttribute(rawRequest: SpanModelRequest, maxChars?: number): string;
|