@juspay/neurolink 12.59.0 → 13.0.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 +13 -2
- package/dist/auth/anthropicOAuth.js +3 -3
- package/dist/auth/middleware/AuthMiddleware.d.ts +1 -1
- package/dist/auth/sessionManager.js +4 -4
- package/dist/auth/tokenStore.js +4 -4
- package/dist/browser/neurolink.min.js +474 -473
- package/dist/cli/commands/observability.d.ts +1 -6
- package/dist/cli/commands/observability.js +0 -106
- package/dist/cli/commands/proxy.js +9 -18
- package/dist/cli/commands/telemetry.d.ts +3 -13
- package/dist/cli/commands/telemetry.js +7 -354
- package/dist/cli/loop/session.js +1 -1
- package/dist/client/httpClient.js +1 -1
- package/dist/client/sseClient.js +1 -1
- package/dist/client/streamingClient.js +2 -2
- package/dist/client/wsClient.js +1 -1
- package/dist/constants/langfuse.d.ts +25 -0
- package/dist/constants/langfuse.js +38 -0
- package/dist/constants/telemetry.d.ts +20 -0
- package/dist/constants/telemetry.js +34 -0
- package/dist/core/baseProvider.d.ts +2 -1
- package/dist/core/baseProvider.js +4 -11
- package/dist/core/conversationMemoryManager.js +5 -8
- package/dist/core/factory.js +1 -1
- package/dist/core/geminiLoopAdapter.d.ts +8 -1
- package/dist/core/geminiLoopAdapter.js +25 -1
- package/dist/core/loopEngine.js +69 -40
- package/dist/core/modules/MessageBuilder.js +2 -2
- package/dist/core/modules/StreamHandler.js +2 -2
- package/dist/core/modules/TelemetryHandler.d.ts +2 -18
- package/dist/core/modules/TelemetryHandler.js +1 -66
- package/dist/core/modules/ToolsManager.js +40 -31
- package/dist/core/nativeGenerateLoop.js +78 -8
- package/dist/core/redisConversationMemoryManager.js +8 -15
- package/dist/core/toolExecutionGuards.js +1 -13
- package/dist/index.d.ts +5 -12
- package/dist/index.js +5 -37
- package/dist/mcp/batching/requestBatcher.js +1 -1
- package/dist/mcp/externalServerManager.js +3 -21
- package/dist/mcp/toolDiscoveryService.js +4 -16
- package/dist/mcp/toolRegistry.js +2 -5
- package/dist/memory/memoryRetrievalTools.js +1 -1
- package/dist/neurolink.d.ts +16 -27
- package/dist/neurolink.js +788 -792
- package/dist/observability/exporterSpanProcessor.d.ts +50 -0
- package/dist/observability/exporterSpanProcessor.js +181 -0
- package/dist/observability/exporters/langfuseExporter.d.ts +13 -34
- package/dist/observability/exporters/langfuseExporter.js +101 -154
- package/dist/observability/index.d.ts +1 -0
- package/dist/observability/index.js +1 -0
- package/dist/observability/langfuseMasking.d.ts +8 -0
- package/dist/observability/langfuseMasking.js +47 -0
- package/dist/observability/otlpEncoding.d.ts +20 -0
- package/dist/observability/otlpEncoding.js +85 -0
- package/dist/providers/amazonBedrock/client.js +142 -176
- package/dist/providers/amazonBedrock/loopAdapter.js +14 -1
- package/dist/providers/amazonSagemaker.js +118 -80
- package/dist/providers/anthropic/client.js +3 -52
- package/dist/providers/anthropic/loopAdapter.js +24 -1
- package/dist/providers/anthropic/rateLimitCapture.js +9 -12
- package/dist/providers/googleAiStudio/client.d.ts +4 -5
- package/dist/providers/googleAiStudio/client.js +38 -40
- package/dist/providers/googleVertex/client.d.ts +2 -7
- package/dist/providers/googleVertex/client.js +282 -378
- package/dist/providers/googleVertex/constants.js +2 -2
- package/dist/providers/litellm/client.d.ts +2 -9
- package/dist/providers/litellm/client.js +1 -63
- package/dist/providers/openAI/client.d.ts +1 -8
- package/dist/providers/openAI/client.js +0 -65
- package/dist/providers/openaiChatCompletionsBase.d.ts +1 -0
- package/dist/providers/openaiChatCompletionsBase.js +61 -4
- package/dist/providers/replicate.js +22 -4
- package/dist/proxy/codexUsage.js +7 -0
- package/dist/proxy/proxyFetch.js +19 -25
- package/dist/proxy/proxyTracer.d.ts +14 -7
- package/dist/proxy/proxyTracer.js +129 -83
- package/dist/proxy/proxyTranslationEngine.js +0 -1
- package/dist/proxy/requestLogger.d.ts +1 -1
- package/dist/proxy/requestLogger.js +39 -45
- package/dist/server/middleware/common.js +1 -1
- package/dist/server/routes/agentRoutes.js +4 -4
- package/dist/server/routes/claudeProxyRoutes.js +99 -196
- package/dist/server/routes/codexProxyRoutes.js +621 -591
- package/dist/server/routes/healthRoutes.js +6 -6
- package/dist/server/routes/mcpRoutes.js +7 -7
- package/dist/server/routes/memoryRoutes.js +7 -7
- package/dist/server/routes/openApiRoutes.js +3 -3
- package/dist/server/routes/toolRoutes.js +5 -5
- package/dist/server/routes/transcribeRoutes.js +1 -1
- package/dist/server/utils/validation.d.ts +3 -3
- package/dist/services/server/ai/observability/instrumentation.d.ts +34 -201
- package/dist/services/server/ai/observability/instrumentation.js +220 -1267
- package/dist/services/server/ai/observability/langfuseSetup.d.ts +14 -0
- package/dist/services/server/ai/observability/langfuseSetup.js +55 -0
- package/dist/services/server/ai/observability/otlpSetup.d.ts +16 -0
- package/dist/services/server/ai/observability/otlpSetup.js +58 -0
- package/dist/session/globalSessionState.js +0 -11
- package/dist/telemetry/attributes.d.ts +23 -46
- package/dist/telemetry/attributes.js +76 -162
- package/dist/telemetry/chatMessages.d.ts +13 -0
- package/dist/telemetry/chatMessages.js +59 -0
- package/dist/telemetry/index.d.ts +5 -19
- package/dist/telemetry/index.js +5 -26
- package/dist/telemetry/observation.d.ts +37 -0
- package/dist/telemetry/observation.js +112 -0
- package/dist/telemetry/observationNames.d.ts +18 -0
- package/dist/telemetry/observationNames.js +24 -0
- package/dist/telemetry/spanSanitizer.d.ts +39 -0
- package/dist/telemetry/spanSanitizer.js +92 -0
- package/dist/telemetry/spanSerialization.d.ts +13 -0
- package/dist/telemetry/spanSerialization.js +46 -0
- package/dist/telemetry/tracers.d.ts +37 -18
- package/dist/telemetry/tracers.js +97 -19
- package/dist/telemetry/withSpan.d.ts +47 -25
- package/dist/telemetry/withSpan.js +158 -71
- package/dist/types/cli.d.ts +0 -11
- package/dist/types/decision.d.ts +3 -0
- package/dist/types/exporter.d.ts +54 -0
- package/dist/types/exporter.js +0 -4
- package/dist/types/generate.d.ts +4 -1
- package/dist/types/isolatedAgent.d.ts +5 -4
- package/dist/types/loopEngine.d.ts +18 -28
- package/dist/types/observability.d.ts +127 -160
- package/dist/types/providers.d.ts +15 -0
- package/dist/types/proxy.d.ts +41 -2
- package/dist/utils/anthropicTraceSanitizer.d.ts +2 -2
- package/dist/utils/anthropicTraceSanitizer.js +3 -1
- package/dist/utils/conversationMemory.js +4 -4
- package/dist/utils/fileDetector.js +1 -1
- package/dist/utils/messageBuilder.js +1 -1
- package/dist/utils/noOutputSentinel.d.ts +4 -16
- package/dist/utils/noOutputSentinel.js +12 -28
- package/dist/utils/providerRetry.js +4 -4
- package/dist/voice/livekit/brain.js +1 -1
- package/dist/voice/livekit/realtimeMemory.d.ts +1 -1
- package/dist/voice/livekit/realtimeMemory.js +66 -1
- package/dist/voice/livekit/realtimeVoiceAgent.js +1 -1
- package/docs-site/static/search-index.json +41 -63
- package/package.json +5 -4
- package/dist/telemetry/telemetryService.d.ts +0 -79
- package/dist/telemetry/telemetryService.js +0 -572
- package/dist/utils/observabilityHelpers.d.ts +0 -27
- package/dist/utils/observabilityHelpers.js +0 -47
|
@@ -4,29 +4,8 @@
|
|
|
4
4
|
* to enable telemetry and observability features in Neurolink SDK
|
|
5
5
|
*/
|
|
6
6
|
import type { AttributeValue } from "@opentelemetry/api";
|
|
7
|
+
import type { JsonValue, RawUsageObject, Unknown } from "./common.js";
|
|
7
8
|
import type { SpanData } from "./span.js";
|
|
8
|
-
/**
|
|
9
|
-
* Trace name format for Langfuse traces
|
|
10
|
-
*
|
|
11
|
-
* Controls how userId and operationName are combined to form the trace name.
|
|
12
|
-
* Can be a predefined format string or a custom function.
|
|
13
|
-
*
|
|
14
|
-
* @example
|
|
15
|
-
* // Predefined formats:
|
|
16
|
-
* "userId:operationName" → "user@email.com:ai.streamText"
|
|
17
|
-
* "operationName:userId" → "ai.streamText:user@email.com"
|
|
18
|
-
* "operationName" → "ai.streamText"
|
|
19
|
-
* "userId" → "user@email.com" (legacy)
|
|
20
|
-
*
|
|
21
|
-
* @example
|
|
22
|
-
* // Custom function:
|
|
23
|
-
* (ctx) => `[${ctx.operationName}] ${ctx.userId}`
|
|
24
|
-
* // → "[ai.streamText] user@email.com"
|
|
25
|
-
*/
|
|
26
|
-
export type TraceNameFormat = "userId:operationName" | "operationName:userId" | "operationName" | "userId" | ((context: {
|
|
27
|
-
userId?: string;
|
|
28
|
-
operationName?: string;
|
|
29
|
-
}) => string);
|
|
30
9
|
/**
|
|
31
10
|
* Standard GenAI semantic convention attributes from OpenTelemetry
|
|
32
11
|
* These are the attributes that Vercel AI SDK's experimental_telemetry creates
|
|
@@ -55,121 +34,57 @@ export type LangfuseSpanAttributes = {
|
|
|
55
34
|
[key: string]: AttributeValue | undefined;
|
|
56
35
|
};
|
|
57
36
|
/**
|
|
58
|
-
* Langfuse
|
|
37
|
+
* @description Langfuse exporter options. Every field falls back to its `LANGFUSE_*` environment variable when omitted.
|
|
59
38
|
*/
|
|
60
|
-
export type
|
|
61
|
-
/**
|
|
62
|
-
|
|
63
|
-
/**
|
|
64
|
-
|
|
65
|
-
/**
|
|
66
|
-
* Langfuse secret key
|
|
67
|
-
* @sensitive
|
|
68
|
-
* WARNING: This is a sensitive credential. Handle securely.
|
|
69
|
-
* Do NOT log, expose, or share this key. Follow best practices for secret management.
|
|
70
|
-
*/
|
|
71
|
-
secretKey: string;
|
|
72
|
-
/** Langfuse base URL (default: https://cloud.langfuse.com) */
|
|
39
|
+
export type NeurolinkLangfuseConfig = {
|
|
40
|
+
/** Project public key; defaults to `LANGFUSE_PUBLIC_KEY`. */
|
|
41
|
+
publicKey?: string;
|
|
42
|
+
/** Project secret key; defaults to `LANGFUSE_SECRET_KEY`. */
|
|
43
|
+
secretKey?: string;
|
|
44
|
+
/** Langfuse host; defaults to `LANGFUSE_BASE_URL`, then Langfuse Cloud. */
|
|
73
45
|
baseUrl?: string;
|
|
74
|
-
/** Environment name
|
|
46
|
+
/** Environment name; defaults to `LANGFUSE_TRACING_ENVIRONMENT`. */
|
|
75
47
|
environment?: string;
|
|
76
|
-
/** Release
|
|
48
|
+
/** Release identifier; defaults to `LANGFUSE_RELEASE`. */
|
|
77
49
|
release?: string;
|
|
78
|
-
/**
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
/**
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
*
|
|
89
|
-
* @default false
|
|
90
|
-
*/
|
|
91
|
-
useExternalTracerProvider?: boolean;
|
|
92
|
-
/**
|
|
93
|
-
* If true, NeuroLink will automatically detect if a TracerProvider is already
|
|
94
|
-
* registered globally and skip its own registration to avoid conflicts.
|
|
95
|
-
*
|
|
96
|
-
* This is a convenience option that combines well with useExternalTracerProvider.
|
|
97
|
-
*
|
|
98
|
-
* @default false
|
|
99
|
-
*/
|
|
100
|
-
autoDetectExternalProvider?: boolean;
|
|
101
|
-
/**
|
|
102
|
-
* If true, NeuroLink will NOT register its own LangfuseSpanProcessor with the
|
|
103
|
-
* global TracerProvider when using external provider mode. Only the ContextEnricher
|
|
104
|
-
* will be registered. Use this when the host application already registers a
|
|
105
|
-
* LangfuseSpanProcessor (e.g., via a DeferredSpanProcessor) to prevent duplicate
|
|
106
|
-
* trace exports to Langfuse.
|
|
107
|
-
*
|
|
108
|
-
* @default false
|
|
109
|
-
*/
|
|
110
|
-
skipLangfuseSpanProcessor?: boolean;
|
|
111
|
-
/**
|
|
112
|
-
* Enable auto-detection of operation names from span names.
|
|
113
|
-
*
|
|
114
|
-
* When true (default), AI operation spans (ai.streamText, ai.generateText, etc.)
|
|
115
|
-
* will have their operation name automatically extracted and included in the
|
|
116
|
-
* trace name.
|
|
117
|
-
*
|
|
118
|
-
* @default true
|
|
119
|
-
*
|
|
120
|
-
* @example
|
|
121
|
-
* // With auto-detection enabled (default):
|
|
122
|
-
* // Span "ai.streamText" + userId "user@email.com"
|
|
123
|
-
* // → Trace name: "user@email.com:ai.streamText"
|
|
124
|
-
*
|
|
125
|
-
* @example
|
|
126
|
-
* // With auto-detection disabled:
|
|
127
|
-
* // → Trace name: "user@email.com" (legacy behavior)
|
|
128
|
-
*/
|
|
129
|
-
autoDetectOperationName?: boolean;
|
|
130
|
-
/**
|
|
131
|
-
* Format for trace names in Langfuse.
|
|
132
|
-
*
|
|
133
|
-
* Controls how userId and operationName are combined to form the trace name.
|
|
134
|
-
* Can be a predefined format string or a custom function for full control.
|
|
135
|
-
*
|
|
136
|
-
* @default "userId:operationName"
|
|
137
|
-
*
|
|
138
|
-
* @example
|
|
139
|
-
* // Predefined formats:
|
|
140
|
-
* traceNameFormat: "userId:operationName" // "user@email.com:ai.streamText"
|
|
141
|
-
* traceNameFormat: "operationName:userId" // "ai.streamText:user@email.com"
|
|
142
|
-
* traceNameFormat: "operationName" // "ai.streamText"
|
|
143
|
-
* traceNameFormat: "userId" // "user@email.com" (legacy)
|
|
144
|
-
*
|
|
145
|
-
* @example
|
|
146
|
-
* // Custom function:
|
|
147
|
-
* traceNameFormat: (ctx) => `[${ctx.operationName || 'unknown'}] ${ctx.userId}`
|
|
148
|
-
* // → "[ai.streamText] user@email.com"
|
|
149
|
-
*/
|
|
150
|
-
traceNameFormat?: TraceNameFormat;
|
|
50
|
+
/** Applied to input, output and metadata before they leave the process. */
|
|
51
|
+
mask?: (params: {
|
|
52
|
+
data: JsonValue;
|
|
53
|
+
}) => JsonValue;
|
|
54
|
+
/** `"immediate"` sends every span as it ends; `"batched"` (default) queues them. */
|
|
55
|
+
exportMode?: "batched" | "immediate";
|
|
56
|
+
/** Spans queued before a batch is sent; defaults to 15. */
|
|
57
|
+
flushAt?: number;
|
|
58
|
+
/** Seconds between batch sends; defaults to 5. */
|
|
59
|
+
flushInterval?: number;
|
|
151
60
|
};
|
|
152
61
|
/**
|
|
153
|
-
*
|
|
62
|
+
* @description Observability configuration for the NeuroLink SDK. Langfuse turns on automatically when `LANGFUSE_PUBLIC_KEY` and `LANGFUSE_SECRET_KEY` are set; `false` turns it off.
|
|
154
63
|
*/
|
|
155
|
-
export type
|
|
156
|
-
/**
|
|
157
|
-
|
|
158
|
-
/** OTLP endpoint URL */
|
|
159
|
-
endpoint?: string;
|
|
160
|
-
/** Service name for traces */
|
|
161
|
-
serviceName?: string;
|
|
162
|
-
/** Service version */
|
|
163
|
-
serviceVersion?: string;
|
|
64
|
+
export type ObservabilityConfig = {
|
|
65
|
+
/** `false` disables Langfuse, `true` or an options object enables it. */
|
|
66
|
+
langfuse?: boolean | NeurolinkLangfuseConfig;
|
|
164
67
|
};
|
|
165
68
|
/**
|
|
166
|
-
*
|
|
69
|
+
* @description Per-call trace attributes propagated to every span created inside the call.
|
|
167
70
|
*/
|
|
168
|
-
export type
|
|
169
|
-
/**
|
|
170
|
-
|
|
171
|
-
/**
|
|
172
|
-
|
|
71
|
+
export type NeurolinkTraceContext = {
|
|
72
|
+
/** End user the trace belongs to. */
|
|
73
|
+
userId?: string | null;
|
|
74
|
+
/** Session that groups related traces. */
|
|
75
|
+
sessionId?: string | null;
|
|
76
|
+
/** Conversation identifier, recorded as trace metadata. */
|
|
77
|
+
conversationId?: string | null;
|
|
78
|
+
/** Request identifier, recorded as trace metadata. */
|
|
79
|
+
requestId?: string | null;
|
|
80
|
+
/** Trace name shown in Langfuse. */
|
|
81
|
+
traceName?: string | null;
|
|
82
|
+
/** Trace tags. */
|
|
83
|
+
tags?: string[];
|
|
84
|
+
/** Trace metadata; values up to 200 characters once stringified are on every observation, longer values and arrays only on the trace. */
|
|
85
|
+
metadata?: Record<string, SpanContent | undefined>;
|
|
86
|
+
/** Name of the root observation of this `generate` or `stream` call; not propagated to child observations and not the trace name. */
|
|
87
|
+
observationName?: string | null;
|
|
173
88
|
};
|
|
174
89
|
/**
|
|
175
90
|
* Retry policy type for observability exporters.
|
|
@@ -225,45 +140,93 @@ export type TraceView = {
|
|
|
225
140
|
/** Overall trace status */
|
|
226
141
|
status: "ok" | "error" | "partial";
|
|
227
142
|
};
|
|
228
|
-
/**
|
|
229
|
-
* Extended context for Langfuse spans.
|
|
230
|
-
* Supports all Langfuse trace attributes for rich observability.
|
|
231
|
-
*/
|
|
232
143
|
/** A model request serialized onto a span: the system prompt plus messages, or a bare prompt. */
|
|
233
144
|
export type SpanModelRequest = {
|
|
234
145
|
system?: unknown;
|
|
235
146
|
messages?: readonly unknown[];
|
|
236
147
|
prompt?: string;
|
|
237
148
|
};
|
|
238
|
-
export type
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
149
|
+
export type GenerationOperation = "generate" | "stream";
|
|
150
|
+
/** @description Any value a span attribute can carry: JSON, or an object serialized on the way out. */
|
|
151
|
+
export type SpanContent = JsonValue | object;
|
|
152
|
+
/** @description Observation kinds Langfuse renders a span as. */
|
|
153
|
+
export type ObservationType = "span" | "generation" | "event" | "embedding" | "agent" | "tool" | "chain" | "retriever" | "evaluator" | "guardrail";
|
|
154
|
+
/** @description Severity of an observation. */
|
|
155
|
+
export type ObservationLevel = "DEBUG" | "DEFAULT" | "WARNING" | "ERROR";
|
|
156
|
+
/** @description Attributes stamped on an observation span by `observe`. */
|
|
157
|
+
export type ObservationAttributes = {
|
|
158
|
+
input?: Unknown;
|
|
159
|
+
output?: Unknown;
|
|
160
|
+
metadata?: Record<string, SpanContent | undefined>;
|
|
161
|
+
level?: ObservationLevel;
|
|
162
|
+
statusMessage?: string;
|
|
163
|
+
completionStartTime?: Date;
|
|
164
|
+
model?: string;
|
|
165
|
+
modelParameters?: Record<string, string | number>;
|
|
166
|
+
usageDetails?: Record<string, number>;
|
|
167
|
+
costDetails?: Record<string, number>;
|
|
168
|
+
};
|
|
169
|
+
/** @description Trace-level input, output and per-key metadata recorded by `observeTrace`. */
|
|
170
|
+
export type TraceContent = Pick<ObservationAttributes, "input" | "output"> & {
|
|
171
|
+
metadata?: Record<string, SpanContent | undefined>;
|
|
172
|
+
};
|
|
173
|
+
/** @description A tool call as a provider, the agentic loop or a tool execution names its fields. */
|
|
174
|
+
export type AssistantToolCall = {
|
|
175
|
+
id?: string;
|
|
176
|
+
toolCallId?: string;
|
|
177
|
+
name?: string;
|
|
178
|
+
toolName?: string;
|
|
179
|
+
args?: SpanContent;
|
|
180
|
+
input?: SpanContent;
|
|
181
|
+
arguments?: SpanContent;
|
|
182
|
+
function?: {
|
|
183
|
+
name?: string;
|
|
184
|
+
arguments?: SpanContent;
|
|
185
|
+
};
|
|
186
|
+
};
|
|
187
|
+
/** @description A model response split into text, reasoning and tool calls. */
|
|
188
|
+
export type AssistantReply = {
|
|
189
|
+
text?: string;
|
|
190
|
+
reasoning?: string;
|
|
191
|
+
toolCalls?: AssistantToolCall[];
|
|
192
|
+
};
|
|
193
|
+
/** @description A model response as plain text, a structured reply, or message or content-part arrays. */
|
|
194
|
+
export type AssistantOutput = string | AssistantReply | object[];
|
|
195
|
+
/** @description A tool call in OpenAI chat shape, as Langfuse renders it. */
|
|
196
|
+
export type ObservationToolCall = {
|
|
197
|
+
id?: string;
|
|
198
|
+
type: "function";
|
|
199
|
+
function: {
|
|
200
|
+
name?: string;
|
|
201
|
+
arguments: string;
|
|
202
|
+
};
|
|
203
|
+
};
|
|
204
|
+
/**
|
|
205
|
+
* @description One model request as a Langfuse generation. Every field is optional so the request half and the response half can be recorded separately.
|
|
206
|
+
*/
|
|
207
|
+
export type GenerationRecord = {
|
|
208
|
+
/** Public API the call serves; inferred from the enclosing span when unset. */
|
|
209
|
+
operation?: GenerationOperation;
|
|
210
|
+
/** Provider name, used with the model to price the call. */
|
|
211
|
+
provider?: string;
|
|
212
|
+
/** Model the request asked for. */
|
|
213
|
+
model?: string;
|
|
214
|
+
/** Model the response says served it, when the API reports one. */
|
|
215
|
+
responseModel?: string;
|
|
216
|
+
/** Sampling and thinking parameters sent; undefined entries are dropped. */
|
|
217
|
+
modelParameters?: Record<string, string | number | undefined>;
|
|
218
|
+
/** System prompt and messages, or the bare prompt, sent. */
|
|
219
|
+
input?: SpanModelRequest;
|
|
220
|
+
/** Text and tool calls returned. */
|
|
221
|
+
output?: AssistantOutput;
|
|
222
|
+
/** Token usage in any naming convention `extractTokenUsage` reads. */
|
|
223
|
+
usage?: RawUsageObject | null;
|
|
224
|
+
/** Provider finish reason. */
|
|
225
|
+
finishReason?: string;
|
|
226
|
+
/** Whether the caller aborted the call before it finished. */
|
|
227
|
+
aborted?: boolean;
|
|
228
|
+
/** When the first chunk of a streamed response arrived. */
|
|
229
|
+
completionStartTime?: Date;
|
|
267
230
|
};
|
|
268
231
|
/**
|
|
269
232
|
* Latency statistics with percentile calculations
|
|
@@ -426,6 +389,10 @@ export type SpanOptions = {
|
|
|
426
389
|
tracer: import("@opentelemetry/api").Tracer;
|
|
427
390
|
kind?: import("@opentelemetry/api").SpanKind;
|
|
428
391
|
attributes?: Record<string, string | number | boolean | undefined>;
|
|
392
|
+
/** Marks a thrown error as an expected skip whose Langfuse level the span set itself, so `withSpan` keeps it instead of raising ERROR. */
|
|
393
|
+
expectedFailure?: (error: unknown) => boolean;
|
|
394
|
+
/** Keeps the span out of the active context, so spans started inside `fn` parent to the nearest ancestor that is exported. */
|
|
395
|
+
detached?: boolean;
|
|
429
396
|
};
|
|
430
397
|
/** Trace + parent span IDs used to correlate metric records with spans. */
|
|
431
398
|
export type MetricsTraceContext = {
|
|
@@ -2797,3 +2797,18 @@ export type VertexAnthropicContentBlock = {
|
|
|
2797
2797
|
tool_use_id: string;
|
|
2798
2798
|
content: string;
|
|
2799
2799
|
};
|
|
2800
|
+
/** Content block as seen by the trace sanitizer; media blocks carry a `source`. */
|
|
2801
|
+
export type AnthropicTraceBlock = {
|
|
2802
|
+
type: string;
|
|
2803
|
+
source?: string | {
|
|
2804
|
+
type?: string;
|
|
2805
|
+
media_type?: string;
|
|
2806
|
+
data?: string;
|
|
2807
|
+
url?: string;
|
|
2808
|
+
};
|
|
2809
|
+
};
|
|
2810
|
+
/** Anthropic message shape accepted by the trace sanitizer. */
|
|
2811
|
+
export type AnthropicTraceMessage = {
|
|
2812
|
+
role: string;
|
|
2813
|
+
content: string | AnthropicTraceBlock[];
|
|
2814
|
+
};
|
package/dist/types/proxy.d.ts
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
import type { ProxyTokenBudgetSnapshot } from "./proxyBudget.js";
|
|
1
|
+
import type { ProxyTokenBudgetLease, ProxyTokenBudgetSnapshot } from "./proxyBudget.js";
|
|
2
|
+
import type { CodexRuntimeAccount } from "./codex.js";
|
|
3
|
+
import type { JsonObject, JsonValue } from "./common.js";
|
|
2
4
|
import type { ProxyContextEvidence, ProxyPreparedContext } from "./proxyContext.js";
|
|
3
5
|
/**
|
|
4
6
|
* Proxy type definitions for NeuroLink
|
|
@@ -27,7 +29,7 @@ import type { MCPToolRegistry } from "../mcp/toolRegistry.js";
|
|
|
27
29
|
import type { ProxyTracer } from "../proxy/proxyTracer.js";
|
|
28
30
|
import type { ACCOUNT_COOLING_REASONS, PROXY_ACCOUNT_TYPES, PROXY_ACCOUNT_ROUTING_MODES, PROXY_ACCOUNT_ROUTING_REASONS, PROXY_ACCOUNT_ROUTING_STRATEGIES } from "../proxy/routingEvidence.js";
|
|
29
31
|
import type { CodexFallbackTarget, FallbackEntry, ModelMapping, ProxyRoutingConfig, CloakingConfig } from "./subscription.js";
|
|
30
|
-
import type { RouteDeprecation } from "./server.js";
|
|
32
|
+
import type { RouteDeprecation, ServerContext } from "./server.js";
|
|
31
33
|
import type { StoredOAuthTokens } from "./auth.js";
|
|
32
34
|
/**
|
|
33
35
|
* Type describing the ModelRouter contract.
|
|
@@ -1033,6 +1035,37 @@ export type CodexFinalLogExtra = Partial<Pick<RequestLogEntry, "errorType" | "er
|
|
|
1033
1035
|
export type CodexOutboundServedAttribution = Required<Pick<RequestLogEntry, "account" | "accountType" | "provider" | "model">> & Pick<RequestLogEntry, "accountKey">;
|
|
1034
1036
|
/** Additional fields recorded for each upstream Codex account attempt. */
|
|
1035
1037
|
export type CodexAttemptLogExtra = Partial<Pick<RequestAttemptLogEntry, "inputTokens" | "outputTokens" | "cacheReadTokens" | "cacheCreationTokens" | "reasoningTokens" | "inputIncludesCachedTokens" | "errorType" | "errorMessage" | "errorCode" | "transportScope" | "retryable" | "rateLimitKind" | "cooldownReason" | "quotaResetAt" | "quotaScope">>;
|
|
1038
|
+
/** Mutable per-request state shared by the Codex request helpers. */
|
|
1039
|
+
export type CodexRequestState = {
|
|
1040
|
+
body: JsonObject;
|
|
1041
|
+
bodyStr: string;
|
|
1042
|
+
contextPreflight?: ProxyContextEvidence;
|
|
1043
|
+
budgetLease?: ProxyTokenBudgetLease;
|
|
1044
|
+
budgetDispatched: boolean;
|
|
1045
|
+
finalOutcomeRecorded: boolean;
|
|
1046
|
+
};
|
|
1047
|
+
/** Everything the Codex request helpers need to serve and account one turn. */
|
|
1048
|
+
export type CodexRequestRuntime = {
|
|
1049
|
+
ctx: ServerContext;
|
|
1050
|
+
model: string;
|
|
1051
|
+
tracer?: ProxyTracer;
|
|
1052
|
+
requestStartTime: number;
|
|
1053
|
+
isFallbackRequest: boolean;
|
|
1054
|
+
state: CodexRequestState;
|
|
1055
|
+
runtimeConfigProvider?: ProxyRuntimeConfigProvider;
|
|
1056
|
+
loopbackPort?: number;
|
|
1057
|
+
internalDispatch?: (request: Request) => Response | Promise<Response>;
|
|
1058
|
+
settleBudget: (actualTokens?: number) => Promise<void>;
|
|
1059
|
+
recordFinalOutcome: (account: CodexRuntimeAccount | undefined, responseStatus: number, extra?: CodexFinalLogExtra) => Promise<void>;
|
|
1060
|
+
writeAttempt: (account: CodexRuntimeAccount, attempt: number, startedAt: number, responseStatus: number, extra?: CodexAttemptLogExtra) => void;
|
|
1061
|
+
};
|
|
1062
|
+
/** Failure details recorded when a Codex account is rotated past. */
|
|
1063
|
+
export type CodexRotationFailure = {
|
|
1064
|
+
lastFailure: CodexFinalLogExtra;
|
|
1065
|
+
lastErrorStatus: number;
|
|
1066
|
+
lastErrorCode: string | undefined;
|
|
1067
|
+
lastErrorMessage: string;
|
|
1068
|
+
};
|
|
1036
1069
|
/** Minimal persistence contract needed by Codex rotating-token refreshes. */
|
|
1037
1070
|
export type CodexRefreshTokenStore = {
|
|
1038
1071
|
peekTokens(provider: string): Promise<StoredOAuthTokens | null>;
|
|
@@ -1928,6 +1961,11 @@ export type ResponseInfoContext = {
|
|
|
1928
1961
|
stopSequence?: string;
|
|
1929
1962
|
/** Names of the tools the model actually invoked (tool_use blocks). */
|
|
1930
1963
|
toolCalls?: string[];
|
|
1964
|
+
output?: JsonValue | Array<{
|
|
1965
|
+
type?: string;
|
|
1966
|
+
toolName?: string;
|
|
1967
|
+
}>;
|
|
1968
|
+
firstOutputAt?: number;
|
|
1931
1969
|
};
|
|
1932
1970
|
/** Context recorded when an account is selected for a proxy request. */
|
|
1933
1971
|
export type AccountSelectionContext = {
|
|
@@ -2445,6 +2483,7 @@ export type CodexStreamEvidence = {
|
|
|
2445
2483
|
errorType?: string;
|
|
2446
2484
|
errorMessage?: string;
|
|
2447
2485
|
errorCode?: string;
|
|
2486
|
+
response?: JsonObject;
|
|
2448
2487
|
};
|
|
2449
2488
|
/** Validated account-routing evidence joined to a final request log. */
|
|
2450
2489
|
export type ProxyAnalysisRoutingRecord = {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { AnthropicTraceMessage } from "../types/index.js";
|
|
2
2
|
/**
|
|
3
3
|
* Strips base64 image/PDF payloads from Anthropic messages before they go on a
|
|
4
4
|
* trace attribute — one screenshot would otherwise be megabytes on a span.
|
|
5
5
|
* Other block types pass through; the serializer still applies its length cap.
|
|
6
6
|
*/
|
|
7
|
-
export declare function sanitizeAnthropicMessagesForTrace(messages:
|
|
7
|
+
export declare function sanitizeAnthropicMessagesForTrace(messages: AnthropicTraceMessage[]): Array<Record<string, unknown>>;
|
|
@@ -11,7 +11,9 @@ export function sanitizeAnthropicMessagesForTrace(messages) {
|
|
|
11
11
|
return {
|
|
12
12
|
role: message.role,
|
|
13
13
|
content: message.content.map((block) => {
|
|
14
|
-
if (block.type === "image" || block.type === "document")
|
|
14
|
+
if ((block.type === "image" || block.type === "document") &&
|
|
15
|
+
typeof block.source === "object" &&
|
|
16
|
+
typeof block.source.data === "string") {
|
|
15
17
|
return {
|
|
16
18
|
type: block.type,
|
|
17
19
|
media_type: block.source.media_type,
|
|
@@ -143,7 +143,7 @@ export async function getConversationMessages(conversationMemory, options) {
|
|
|
143
143
|
});
|
|
144
144
|
return [];
|
|
145
145
|
}
|
|
146
|
-
return memoryTracer.startActiveSpan("
|
|
146
|
+
return memoryTracer.startActiveSpan("conversation.getMessages", {
|
|
147
147
|
kind: SpanKind.INTERNAL,
|
|
148
148
|
attributes: {
|
|
149
149
|
"session.id": sessionId,
|
|
@@ -174,7 +174,7 @@ export async function getConversationMessages(conversationMemory, options) {
|
|
|
174
174
|
const repair = repairToolPairs(filtered);
|
|
175
175
|
const messages = repair.messages;
|
|
176
176
|
if (repair.repaired) {
|
|
177
|
-
span.setAttribute("
|
|
177
|
+
span.setAttribute("memory.tool_pairs_repaired", repair.orphanedCallsFixed + repair.orphanedResultsFixed);
|
|
178
178
|
logger.debug("[conversationMemoryUtils] Repaired orphaned tool pairs on read", {
|
|
179
179
|
sessionId,
|
|
180
180
|
orphanedCallsFixed: repair.orphanedCallsFixed,
|
|
@@ -188,7 +188,7 @@ export async function getConversationMessages(conversationMemory, options) {
|
|
|
188
188
|
// signal. The warn log is deduped per session so a long-lived
|
|
189
189
|
// polluted conversation only generates one log line, not one per
|
|
190
190
|
// turn (would otherwise be noisy at scale).
|
|
191
|
-
span.setAttribute("
|
|
191
|
+
span.setAttribute("memory.polluted_turns_dropped", droppedCount);
|
|
192
192
|
const alreadyWarned = pollutedWarnedSessions.has(sessionId);
|
|
193
193
|
if (!alreadyWarned) {
|
|
194
194
|
if (pollutedWarnedSessions.size >= POLLUTED_WARN_DEDUP_MAX) {
|
|
@@ -364,7 +364,7 @@ export async function storeConversationTurn(conversationMemory, originalOptions,
|
|
|
364
364
|
});
|
|
365
365
|
}
|
|
366
366
|
}
|
|
367
|
-
await memoryTracer.startActiveSpan("
|
|
367
|
+
await memoryTracer.startActiveSpan("conversation.storeTurn", {
|
|
368
368
|
kind: SpanKind.INTERNAL,
|
|
369
369
|
attributes: {
|
|
370
370
|
"session.id": sessionId,
|
|
@@ -278,7 +278,7 @@ export class FileDetector {
|
|
|
278
278
|
const inputFilename = FileDetector.deriveInputFilename(input);
|
|
279
279
|
const inputSizeBytes = FileDetector.deriveInputSize(input);
|
|
280
280
|
return withSpan({
|
|
281
|
-
name: "
|
|
281
|
+
name: "file.detect_and_process",
|
|
282
282
|
tracer: tracers.file,
|
|
283
283
|
attributes: {
|
|
284
284
|
[ATTR.FILE_NAME]: inputFilename,
|
|
@@ -1318,7 +1318,7 @@ export async function processUnifiedFilesArray(options, maxSize, provider) {
|
|
|
1318
1318
|
const totalFiles = pending.length;
|
|
1319
1319
|
const files = pending;
|
|
1320
1320
|
return withSpan({
|
|
1321
|
-
name: "
|
|
1321
|
+
name: "file.process_all",
|
|
1322
1322
|
tracer: tracers.file,
|
|
1323
1323
|
attributes: {
|
|
1324
1324
|
[ATTR.FILE_TOTAL_COUNT]: totalFiles,
|
|
@@ -38,25 +38,13 @@ underlyingError?: unknown): Promise<{
|
|
|
38
38
|
error: Error;
|
|
39
39
|
} | null>;
|
|
40
40
|
/**
|
|
41
|
-
*
|
|
42
|
-
*
|
|
43
|
-
*
|
|
44
|
-
* observation with the enriched status message. Without this, only
|
|
45
|
-
* `StreamHandler`-based providers produced the rich telemetry; the
|
|
46
|
-
* provider-specific paths (openAI, openaiCompatible, litellm,
|
|
47
|
-
* huggingFace, openRouter, anthropic) yielded the sentinel
|
|
48
|
-
* to direct stream consumers but Pipeline B saw nothing.
|
|
49
|
-
*
|
|
50
|
-
* Stamps three attributes:
|
|
51
|
-
* - `neurolink.no_output = true` (Pipeline B trigger)
|
|
52
|
-
* - `langfuse.status_message` (enriched, with finishReason + tokens)
|
|
53
|
-
* - `neurolink.no_output.finish_reason` (raw finish reason)
|
|
54
|
-
*
|
|
55
|
-
* Safe to call when tracing isn't initialized — silently no-ops.
|
|
41
|
+
* Marks the active span as a no-output stream so Langfuse shows a WARNING
|
|
42
|
+
* observation with the enriched status message. Safe to call when tracing
|
|
43
|
+
* isn't initialized — silently no-ops.
|
|
56
44
|
*/
|
|
57
45
|
export declare function stampNoOutputSpan(sentinel: StreamNoOutputSentinel): void;
|
|
58
46
|
/**
|
|
59
|
-
* Build the OTel `langfuse.status_message` summary string for a no-output
|
|
47
|
+
* Build the OTel `langfuse.observation.status_message` summary string for a no-output
|
|
60
48
|
* stream. Used by `StreamHandler.createTextStream` and any future provider
|
|
61
49
|
* that wants to stamp the active span with the same enriched message.
|
|
62
50
|
*
|
|
@@ -1,4 +1,6 @@
|
|
|
1
|
+
import { LangfuseOtelSpanAttributes } from "@langfuse/tracing";
|
|
1
2
|
import { trace, context as otelContext } from "@opentelemetry/api";
|
|
3
|
+
import { observationLevel } from "../telemetry/observation.js";
|
|
2
4
|
import { NoOutputGeneratedError } from "./generationErrors.js";
|
|
3
5
|
export async function buildNoOutputSentinel(error, result,
|
|
4
6
|
/**
|
|
@@ -129,38 +131,20 @@ underlyingError) {
|
|
|
129
131
|
}
|
|
130
132
|
}
|
|
131
133
|
/**
|
|
132
|
-
*
|
|
133
|
-
*
|
|
134
|
-
*
|
|
135
|
-
* observation with the enriched status message. Without this, only
|
|
136
|
-
* `StreamHandler`-based providers produced the rich telemetry; the
|
|
137
|
-
* provider-specific paths (openAI, openaiCompatible, litellm,
|
|
138
|
-
* huggingFace, openRouter, anthropic) yielded the sentinel
|
|
139
|
-
* to direct stream consumers but Pipeline B saw nothing.
|
|
140
|
-
*
|
|
141
|
-
* Stamps three attributes:
|
|
142
|
-
* - `neurolink.no_output = true` (Pipeline B trigger)
|
|
143
|
-
* - `langfuse.status_message` (enriched, with finishReason + tokens)
|
|
144
|
-
* - `neurolink.no_output.finish_reason` (raw finish reason)
|
|
145
|
-
*
|
|
146
|
-
* Safe to call when tracing isn't initialized — silently no-ops.
|
|
134
|
+
* Marks the active span as a no-output stream so Langfuse shows a WARNING
|
|
135
|
+
* observation with the enriched status message. Safe to call when tracing
|
|
136
|
+
* isn't initialized — silently no-ops.
|
|
147
137
|
*/
|
|
148
138
|
export function stampNoOutputSpan(sentinel) {
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
activeSpan.setAttribute("langfuse.status_message", buildNoOutputStatusMessage(sentinel.metadata.finishReason, sentinel.metadata.usage));
|
|
156
|
-
activeSpan.setAttribute("neurolink.no_output.finish_reason", String(sentinel.metadata.finishReason));
|
|
157
|
-
}
|
|
158
|
-
catch {
|
|
159
|
-
// Tracing not initialized — ignore.
|
|
160
|
-
}
|
|
139
|
+
trace.getSpan(otelContext.active())?.setAttributes({
|
|
140
|
+
no_output: true,
|
|
141
|
+
"no_output.finish_reason": String(sentinel.metadata.finishReason),
|
|
142
|
+
[LangfuseOtelSpanAttributes.OBSERVATION_LEVEL]: observationLevel("no-output"),
|
|
143
|
+
[LangfuseOtelSpanAttributes.OBSERVATION_STATUS_MESSAGE]: buildNoOutputStatusMessage(sentinel.metadata.finishReason, sentinel.metadata.usage),
|
|
144
|
+
});
|
|
161
145
|
}
|
|
162
146
|
/**
|
|
163
|
-
* Build the OTel `langfuse.status_message` summary string for a no-output
|
|
147
|
+
* Build the OTel `langfuse.observation.status_message` summary string for a no-output
|
|
164
148
|
* stream. Used by `StreamHandler.createTextStream` and any future provider
|
|
165
149
|
* that wants to stamp the active span with the same enriched message.
|
|
166
150
|
*
|
|
@@ -270,7 +270,7 @@ export async function withProviderRetry(operation, span, label, sleep = sleepWit
|
|
|
270
270
|
try {
|
|
271
271
|
const result = await operation();
|
|
272
272
|
// Record how many attempts it took on the span
|
|
273
|
-
span?.setAttribute("
|
|
273
|
+
span?.setAttribute("provider.total_attempts", attempt + 1);
|
|
274
274
|
if (attempt > 0) {
|
|
275
275
|
logger.info(`[providerRetry] ${label} succeeded after ${attempt + 1} attempts`);
|
|
276
276
|
}
|
|
@@ -282,9 +282,9 @@ export async function withProviderRetry(operation, span, label, sleep = sleepWit
|
|
|
282
282
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
283
283
|
if (!retryable || attempt === MAX_PROVIDER_RETRIES) {
|
|
284
284
|
// Record failure details before re-throwing
|
|
285
|
-
span?.setAttribute("
|
|
285
|
+
span?.setAttribute("provider.total_attempts", attempt + 1);
|
|
286
286
|
if (attempt > 0) {
|
|
287
|
-
span?.setAttribute("
|
|
287
|
+
span?.setAttribute("provider.retries_exhausted", true);
|
|
288
288
|
}
|
|
289
289
|
logger.warn(`[providerRetry] ${label} failed (non-retryable or retries exhausted)`, {
|
|
290
290
|
attempt: attempt + 1,
|
|
@@ -296,7 +296,7 @@ export async function withProviderRetry(operation, span, label, sleep = sleepWit
|
|
|
296
296
|
}
|
|
297
297
|
const retryAfterMs = getRetryAfterMs(error);
|
|
298
298
|
if (retryAfterMs !== undefined && retryAfterMs > MAX_RETRY_AFTER_MS) {
|
|
299
|
-
span?.setAttribute("
|
|
299
|
+
span?.setAttribute("provider.total_attempts", attempt + 1);
|
|
300
300
|
span?.addEvent("gen_ai.provider.retry_suppressed", {
|
|
301
301
|
"retry.after_ms": retryAfterMs,
|
|
302
302
|
...(statusCode !== undefined && { "retry.status_code": statusCode }),
|
|
@@ -56,7 +56,7 @@ export function createVoiceBrain(config) {
|
|
|
56
56
|
systemPrompt,
|
|
57
57
|
temperature,
|
|
58
58
|
maxTokens,
|
|
59
|
-
context,
|
|
59
|
+
context: { ...context, tags: ["voice"] },
|
|
60
60
|
// Every turn through this brain is spoken; stored messages are marked so.
|
|
61
61
|
modality: "voice",
|
|
62
62
|
abortSignal: signal,
|
|
@@ -19,4 +19,4 @@
|
|
|
19
19
|
import type { JobContext } from "@livekit/agents";
|
|
20
20
|
import type { RealtimeCallMemory, RealtimeVoiceMemoryConfig } from "../../types/index.js";
|
|
21
21
|
/** Verify the call's binding and prepare its recorder; `undefined` means "no memory for this call". */
|
|
22
|
-
export declare function setUpRealtimeCallMemory(ctx: Pick<JobContext, "room" | "addShutdownCallback">, config: RealtimeVoiceMemoryConfig | undefined): Promise<RealtimeCallMemory | undefined>;
|
|
22
|
+
export declare function setUpRealtimeCallMemory(ctx: Pick<JobContext, "room" | "addShutdownCallback">, config: RealtimeVoiceMemoryConfig | undefined, model?: string): Promise<RealtimeCallMemory | undefined>;
|