@grafana/sigil-sdk-js 0.0.1 → 0.2.1
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/README.md +48 -3
- package/dist/client.d.ts +29 -1
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +143 -7
- package/dist/client.js.map +1 -1
- package/dist/config.d.ts +10 -2
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +201 -24
- package/dist/config.js.map +1 -1
- package/dist/frameworks/strands/index.d.ts +84 -0
- package/dist/frameworks/strands/index.d.ts.map +1 -0
- package/dist/frameworks/strands/index.js +750 -0
- package/dist/frameworks/strands/index.js.map +1 -0
- package/dist/frameworks/vercel-ai-sdk/hooks.d.ts +3 -0
- package/dist/frameworks/vercel-ai-sdk/hooks.d.ts.map +1 -1
- package/dist/frameworks/vercel-ai-sdk/hooks.js +58 -7
- package/dist/frameworks/vercel-ai-sdk/hooks.js.map +1 -1
- package/dist/frameworks/vercel-ai-sdk/types.d.ts +9 -0
- package/dist/frameworks/vercel-ai-sdk/types.d.ts.map +1 -1
- package/dist/hooks.d.ts +33 -0
- package/dist/hooks.d.ts.map +1 -0
- package/dist/hooks.js +210 -0
- package/dist/hooks.js.map +1 -0
- package/dist/index.d.ts +5 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -1
- package/dist/index.js.map +1 -1
- package/dist/redaction.d.ts +16 -0
- package/dist/redaction.d.ts.map +1 -0
- package/dist/redaction.js +155 -0
- package/dist/redaction.js.map +1 -0
- package/dist/types.d.ts +102 -0
- package/dist/types.d.ts.map +1 -1
- package/docs/frameworks/strands.md +88 -0
- package/docs/index.md +1 -0
- package/package.json +20 -2
package/README.md
CHANGED
|
@@ -54,6 +54,42 @@ await client.startGeneration(
|
|
|
54
54
|
await client.shutdown();
|
|
55
55
|
```
|
|
56
56
|
|
|
57
|
+
## Pre-Ingest Redaction
|
|
58
|
+
|
|
59
|
+
Use `generationSanitizer` when you want to redact substrings from normalized generations before
|
|
60
|
+
validation, span sync, debug snapshots, and export.
|
|
61
|
+
|
|
62
|
+
```ts
|
|
63
|
+
import {
|
|
64
|
+
SigilClient,
|
|
65
|
+
createSecretRedactionSanitizer,
|
|
66
|
+
} from "@grafana/sigil-sdk-js";
|
|
67
|
+
|
|
68
|
+
const client = new SigilClient({
|
|
69
|
+
generationSanitizer: createSecretRedactionSanitizer({
|
|
70
|
+
redactInputMessages: false,
|
|
71
|
+
redactEmailAddresses: true,
|
|
72
|
+
}),
|
|
73
|
+
});
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
The built-in sanitizer:
|
|
77
|
+
|
|
78
|
+
- redacts high-confidence secret formats in assistant text and thinking
|
|
79
|
+
- redacts secret formats plus env-style secret values in tool call inputs and tool results
|
|
80
|
+
- redacts email addresses by default
|
|
81
|
+
- leaves user input unchanged unless `redactInputMessages: true` is set
|
|
82
|
+
|
|
83
|
+
To preserve email addresses, opt out explicitly:
|
|
84
|
+
|
|
85
|
+
```ts
|
|
86
|
+
const client = new SigilClient({
|
|
87
|
+
generationSanitizer: createSecretRedactionSanitizer({
|
|
88
|
+
redactEmailAddresses: false,
|
|
89
|
+
}),
|
|
90
|
+
});
|
|
91
|
+
```
|
|
92
|
+
|
|
57
93
|
Configure OTEL exporters (traces/metrics) in your application OTEL SDK setup. You can optionally pass `tracer` and `meter` directly to `SigilClient`.
|
|
58
94
|
|
|
59
95
|
Quick OTEL setup pattern before creating the Sigil client:
|
|
@@ -167,12 +203,14 @@ Use module subpath exports for framework callback integrations:
|
|
|
167
203
|
- LlamaIndex: `@grafana/sigil-sdk-js/llamaindex`
|
|
168
204
|
- Google ADK: `@grafana/sigil-sdk-js/google-adk`
|
|
169
205
|
- Vercel AI SDK: `@grafana/sigil-sdk-js/vercel-ai-sdk`
|
|
206
|
+
- Strands Agents: `@grafana/sigil-sdk-js/strands`
|
|
170
207
|
- LangChain guide: `docs/frameworks/langchain.md`
|
|
171
208
|
- LangGraph guide: `docs/frameworks/langgraph.md`
|
|
172
209
|
- OpenAI Agents guide: `docs/frameworks/openai-agents.md`
|
|
173
210
|
- LlamaIndex guide: `docs/frameworks/llamaindex.md`
|
|
174
211
|
- Google ADK guide: `docs/frameworks/google-adk.md`
|
|
175
212
|
- Vercel AI SDK guide: `docs/frameworks/vercel-ai-sdk.md`
|
|
213
|
+
- Strands Agents guide: `docs/frameworks/strands.md`
|
|
176
214
|
|
|
177
215
|
```ts
|
|
178
216
|
import { SigilClient } from "@grafana/sigil-sdk-js";
|
|
@@ -182,6 +220,7 @@ import { withSigilOpenAIAgentsHooks } from "@grafana/sigil-sdk-js/openai-agents"
|
|
|
182
220
|
import { withSigilLlamaIndexCallbacks } from "@grafana/sigil-sdk-js/llamaindex";
|
|
183
221
|
import { withSigilGoogleAdkPlugins } from "@grafana/sigil-sdk-js/google-adk";
|
|
184
222
|
import { createSigilVercelAiSdk } from "@grafana/sigil-sdk-js/vercel-ai-sdk";
|
|
223
|
+
import { withSigilStrandsHooks } from "@grafana/sigil-sdk-js/strands";
|
|
185
224
|
import { Runner } from "@openai/agents";
|
|
186
225
|
import { CallbackManager } from "llamaindex";
|
|
187
226
|
|
|
@@ -194,13 +233,19 @@ const callbackManager = new CallbackManager();
|
|
|
194
233
|
const llamaIndexConfig = withSigilLlamaIndexCallbacks({ callbackManager }, client, { providerResolver: "auto" });
|
|
195
234
|
const googleAdkRunnerConfig = withSigilGoogleAdkPlugins(undefined, client, { providerResolver: "auto" });
|
|
196
235
|
const vercelAiSdk = createSigilVercelAiSdk(client, { agentName: "vercel-agent" });
|
|
236
|
+
const strandsConfig = withSigilStrandsHooks(undefined, client, { conversationId: "chat-123" });
|
|
197
237
|
```
|
|
198
238
|
|
|
239
|
+
Framework handlers use the `SigilClient` instance you pass in. If that client is configured with
|
|
240
|
+
`generationSanitizer`, the same redaction policy applies automatically to generations recorded
|
|
241
|
+
through LangChain, LangGraph, OpenAI Agents, LlamaIndex, Google ADK, and Vercel AI SDK integrations.
|
|
242
|
+
The same redaction policy also applies to Strands Agents generations.
|
|
243
|
+
|
|
199
244
|
Each framework handler injects:
|
|
200
245
|
|
|
201
|
-
- `sigil.framework.name` (`langchain`, `langgraph`, `openai-agents`, `llamaindex`, `google-adk`,
|
|
202
|
-
- `sigil.framework.source` (`handler` for existing callback handlers, `framework` for Vercel AI SDK hooks)
|
|
203
|
-
- `sigil.framework.language` (`javascript` for existing callback handlers, `typescript` for Vercel AI SDK hooks)
|
|
246
|
+
- `sigil.framework.name` (`langchain`, `langgraph`, `openai-agents`, `llamaindex`, `google-adk`, `vercel-ai-sdk`, or `strands`)
|
|
247
|
+
- `sigil.framework.source` (`handler` for existing callback handlers, `framework` for Vercel AI SDK hooks, `hooks` for Strands)
|
|
248
|
+
- `sigil.framework.language` (`javascript` for existing callback handlers, `typescript` for Vercel AI SDK and Strands hooks)
|
|
204
249
|
- `metadata["sigil.framework.run_id"]`
|
|
205
250
|
- `metadata["sigil.framework.thread_id"]` (when present)
|
|
206
251
|
- `metadata["sigil.framework.parent_run_id"]` (when available)
|
package/dist/client.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { type Span } from '@opentelemetry/api';
|
|
2
|
-
import type { ContentCaptureMode, ConversationRatingInput, EmbeddingRecorder, EmbeddingResult, EmbeddingStart, Generation, GenerationMode, GenerationRecorder, GenerationStart, RecorderCallback, SigilDebugSnapshot, SigilSdkConfigInput, SubmitConversationRatingResponse, ToolExecution, ToolExecutionRecorder, ToolExecutionStart } from './types.js';
|
|
2
|
+
import type { ContentCaptureMode, ConversationRatingInput, EmbeddingRecorder, EmbeddingResult, EmbeddingStart, Generation, GenerationMode, GenerationRecorder, GenerationStart, HookEvaluateRequest, HookEvaluateResponse, HooksConfig, RecorderCallback, SigilDebugSnapshot, SigilSdkConfigInput, SubmitConversationRatingResponse, ToolExecution, ToolExecutionRecorder, ToolExecutionStart } from './types.js';
|
|
3
3
|
export declare class SigilClient {
|
|
4
4
|
private readonly config;
|
|
5
5
|
private readonly nowFn;
|
|
@@ -63,6 +63,28 @@ export declare class SigilClient {
|
|
|
63
63
|
startToolExecution<TResult>(start: ToolExecutionStart, callback: RecorderCallback<ToolExecutionRecorder, TResult>): Promise<TResult>;
|
|
64
64
|
/** Submits a user-facing conversation rating through Sigil HTTP API. */
|
|
65
65
|
submitConversationRating(conversationId: string, input: ConversationRatingInput): Promise<SubmitConversationRatingResponse>;
|
|
66
|
+
/**
|
|
67
|
+
* Returns the resolved hook configuration. Framework adapters use this to
|
|
68
|
+
* decide whether to invoke `evaluateHook` and which phases are configured.
|
|
69
|
+
*/
|
|
70
|
+
get hooksConfig(): HooksConfig;
|
|
71
|
+
/**
|
|
72
|
+
* Evaluates synchronous hook rules for the given request.
|
|
73
|
+
*
|
|
74
|
+
* Use this to enforce preflight or postflight guardrails (PII, content
|
|
75
|
+
* policy, etc.) on the LLM call's critical path. The server returns
|
|
76
|
+
* `{ action: 'deny' }` to block; framework adapters typically translate that
|
|
77
|
+
* into a `HookDeniedError`.
|
|
78
|
+
*
|
|
79
|
+
* When `hooks.enabled` is false, this short-circuits to `allow`. When
|
|
80
|
+
* `hooks.failOpen` is true (default), network/timeout failures also resolve
|
|
81
|
+
* to `allow` so the LLM call can proceed.
|
|
82
|
+
*
|
|
83
|
+
* Framework adapters can pass `hooksConfigOverride` to override specific
|
|
84
|
+
* fields of the client's hooks config (e.g., force `enabled: true` when the
|
|
85
|
+
* adapter has its own `enableHooks` option).
|
|
86
|
+
*/
|
|
87
|
+
evaluateHook(request: HookEvaluateRequest, hooksConfigOverride?: Partial<HooksConfig>): Promise<HookEvaluateResponse>;
|
|
66
88
|
/** Forces immediate drain of queued generation exports. */
|
|
67
89
|
flush(): Promise<void>;
|
|
68
90
|
/** Flushes pending generations and shuts down the generation exporter. */
|
|
@@ -70,12 +92,18 @@ export declare class SigilClient {
|
|
|
70
92
|
/** Returns a cloned in-memory snapshot for debugging and tests. */
|
|
71
93
|
debugSnapshot(): SigilDebugSnapshot;
|
|
72
94
|
internalNow(): Date;
|
|
95
|
+
internalAgentName(): string | undefined;
|
|
96
|
+
internalAgentVersion(): string | undefined;
|
|
97
|
+
internalUserId(): string | undefined;
|
|
98
|
+
internalTags(): Record<string, string> | undefined;
|
|
73
99
|
internalRecordGeneration(generation: Generation): void;
|
|
74
100
|
internalRecordToolExecution(toolExecution: ToolExecution): void;
|
|
75
101
|
internalEnqueueGeneration(generation: Generation): void;
|
|
76
102
|
internalLogWarn(message: string, error?: unknown): void;
|
|
77
103
|
internalResolveGenerationContentCaptureMode(seed: GenerationStart): ContentCaptureMode;
|
|
78
104
|
internalResolveToolIncludeContent(seed: ToolExecutionStart): boolean;
|
|
105
|
+
internalHasGenerationSanitizer(): boolean;
|
|
106
|
+
internalSanitizeGeneration(generation: Generation): Generation;
|
|
79
107
|
internalStartGenerationSpan(seed: GenerationStart, mode: GenerationMode, startedAt: Date): Span;
|
|
80
108
|
internalStartEmbeddingSpan(seed: EmbeddingStart, startedAt: Date): Span;
|
|
81
109
|
internalStartToolExecutionSpan(seed: ToolExecutionStart, startedAt: Date): Span;
|
package/dist/client.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA,OAAO,EAIL,KAAK,IAAI,EAKV,MAAM,oBAAoB,CAAC;
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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"}
|
package/dist/client.js
CHANGED
|
@@ -3,6 +3,7 @@ import { defaultLogger, mergeConfig } from './config.js';
|
|
|
3
3
|
import { callContentCaptureResolver, resolveClientContentCaptureMode, resolveContentCaptureMode, shouldIncludeToolContent, stampContentCaptureMetadata, stripContent, } from './content_capture.js';
|
|
4
4
|
import { agentNameFromContext, agentVersionFromContext, conversationIdFromContext, conversationTitleFromContext, userIdFromContext, } from './context.js';
|
|
5
5
|
import { createDefaultGenerationExporter } from './exporters/default.js';
|
|
6
|
+
import { evaluateHook as evaluateHookImpl } from './hooks.js';
|
|
6
7
|
import { asError, cloneArtifact, cloneEmbeddingResult, cloneEmbeddingStart, cloneGeneration, cloneGenerationResult, cloneGenerationStart, cloneMessage, cloneModelRef, cloneToolDefinition, cloneToolExecution, cloneToolExecutionResult, cloneToolExecutionStart, defaultOperationNameForMode, defaultSleep, encodedSizeBytes, maybeUnref, newLocalID, validateEmbeddingResult, validateEmbeddingStart, validateGeneration, validateToolExecution, } from './utils.js';
|
|
7
8
|
const spanAttrGenerationID = 'sigil.generation.id';
|
|
8
9
|
const spanAttrSDKName = 'sigil.sdk.name';
|
|
@@ -67,6 +68,12 @@ const metricTokenTypeCacheRead = 'cache_read';
|
|
|
67
68
|
const metricTokenTypeCacheWrite = 'cache_write';
|
|
68
69
|
const metricTokenTypeCacheCreation = 'cache_creation';
|
|
69
70
|
const metricTokenTypeReasoning = 'reasoning';
|
|
71
|
+
const durationBucketsSeconds = [
|
|
72
|
+
0.01, 0.02, 0.04, 0.08, 0.16, 0.32, 0.64, 1.28, 2.56, 5.12, 10.24, 20.48, 40.96, 81.92,
|
|
73
|
+
];
|
|
74
|
+
const tokenUsageBuckets = [
|
|
75
|
+
1, 4, 16, 64, 256, 1024, 4096, 16384, 65536, 262144, 1048576, 4194304, 16777216, 67108864,
|
|
76
|
+
];
|
|
70
77
|
const instrumentationName = 'github.com/grafana/sigil/sdks/js';
|
|
71
78
|
const sdkName = 'sdk-js';
|
|
72
79
|
const defaultEmbeddingOperationName = 'embeddings';
|
|
@@ -107,9 +114,18 @@ export class SigilClient {
|
|
|
107
114
|
this.config.generationExporter ?? createDefaultGenerationExporter(this.config.generationExport);
|
|
108
115
|
this.tracer = this.config.tracer ?? trace.getTracer(instrumentationName);
|
|
109
116
|
this.meter = this.config.meter ?? metrics.getMeter(instrumentationName);
|
|
110
|
-
this.operationDurationHistogram = this.meter.createHistogram(metricOperationDuration, {
|
|
111
|
-
|
|
112
|
-
|
|
117
|
+
this.operationDurationHistogram = this.meter.createHistogram(metricOperationDuration, {
|
|
118
|
+
unit: 's',
|
|
119
|
+
advice: { explicitBucketBoundaries: durationBucketsSeconds },
|
|
120
|
+
});
|
|
121
|
+
this.tokenUsageHistogram = this.meter.createHistogram(metricTokenUsage, {
|
|
122
|
+
unit: 'token',
|
|
123
|
+
advice: { explicitBucketBoundaries: tokenUsageBuckets },
|
|
124
|
+
});
|
|
125
|
+
this.ttftHistogram = this.meter.createHistogram(metricTimeToFirstToken, {
|
|
126
|
+
unit: 's',
|
|
127
|
+
advice: { explicitBucketBoundaries: durationBucketsSeconds },
|
|
128
|
+
});
|
|
113
129
|
this.toolCallsHistogram = this.meter.createHistogram(metricToolCallsPerOperation, { unit: 'count' });
|
|
114
130
|
if (this.config.generationExport.flushIntervalMs > 0) {
|
|
115
131
|
this.flushTimer = setInterval(() => {
|
|
@@ -130,9 +146,21 @@ export class SigilClient {
|
|
|
130
146
|
if (!notEmpty(seed.agentName)) {
|
|
131
147
|
seed.agentName = agentNameFromContext();
|
|
132
148
|
}
|
|
149
|
+
if (!notEmpty(seed.agentName)) {
|
|
150
|
+
const fromConfig = this.internalAgentName();
|
|
151
|
+
if (fromConfig !== undefined && fromConfig.length > 0) {
|
|
152
|
+
seed.agentName = fromConfig;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
133
155
|
if (!notEmpty(seed.agentVersion)) {
|
|
134
156
|
seed.agentVersion = agentVersionFromContext();
|
|
135
157
|
}
|
|
158
|
+
if (!notEmpty(seed.agentVersion)) {
|
|
159
|
+
const fromConfig = this.internalAgentVersion();
|
|
160
|
+
if (fromConfig !== undefined && fromConfig.length > 0) {
|
|
161
|
+
seed.agentVersion = fromConfig;
|
|
162
|
+
}
|
|
163
|
+
}
|
|
136
164
|
const recorder = new EmbeddingRecorderImpl(this, seed);
|
|
137
165
|
if (callback === undefined) {
|
|
138
166
|
return recorder;
|
|
@@ -198,6 +226,40 @@ export class SigilClient {
|
|
|
198
226
|
}
|
|
199
227
|
return parseSubmitConversationRatingResponse(payload);
|
|
200
228
|
}
|
|
229
|
+
/**
|
|
230
|
+
* Returns the resolved hook configuration. Framework adapters use this to
|
|
231
|
+
* decide whether to invoke `evaluateHook` and which phases are configured.
|
|
232
|
+
*/
|
|
233
|
+
get hooksConfig() {
|
|
234
|
+
return this.config.hooks;
|
|
235
|
+
}
|
|
236
|
+
/**
|
|
237
|
+
* Evaluates synchronous hook rules for the given request.
|
|
238
|
+
*
|
|
239
|
+
* Use this to enforce preflight or postflight guardrails (PII, content
|
|
240
|
+
* policy, etc.) on the LLM call's critical path. The server returns
|
|
241
|
+
* `{ action: 'deny' }` to block; framework adapters typically translate that
|
|
242
|
+
* into a `HookDeniedError`.
|
|
243
|
+
*
|
|
244
|
+
* When `hooks.enabled` is false, this short-circuits to `allow`. When
|
|
245
|
+
* `hooks.failOpen` is true (default), network/timeout failures also resolve
|
|
246
|
+
* to `allow` so the LLM call can proceed.
|
|
247
|
+
*
|
|
248
|
+
* Framework adapters can pass `hooksConfigOverride` to override specific
|
|
249
|
+
* fields of the client's hooks config (e.g., force `enabled: true` when the
|
|
250
|
+
* adapter has its own `enableHooks` option).
|
|
251
|
+
*/
|
|
252
|
+
async evaluateHook(request, hooksConfigOverride) {
|
|
253
|
+
this.assertOpen();
|
|
254
|
+
const effectiveHooks = hooksConfigOverride !== undefined ? { ...this.config.hooks, ...hooksConfigOverride } : this.config.hooks;
|
|
255
|
+
return evaluateHookImpl({
|
|
256
|
+
apiEndpoint: this.config.api.endpoint,
|
|
257
|
+
insecure: this.config.generationExport.insecure,
|
|
258
|
+
extraHeaders: this.config.generationExport.headers,
|
|
259
|
+
hooks: effectiveHooks,
|
|
260
|
+
request,
|
|
261
|
+
});
|
|
262
|
+
}
|
|
201
263
|
/** Forces immediate drain of queued generation exports. */
|
|
202
264
|
async flush() {
|
|
203
265
|
this.assertOpen();
|
|
@@ -239,6 +301,18 @@ export class SigilClient {
|
|
|
239
301
|
internalNow() {
|
|
240
302
|
return this.nowFn();
|
|
241
303
|
}
|
|
304
|
+
internalAgentName() {
|
|
305
|
+
return this.config.agentName;
|
|
306
|
+
}
|
|
307
|
+
internalAgentVersion() {
|
|
308
|
+
return this.config.agentVersion;
|
|
309
|
+
}
|
|
310
|
+
internalUserId() {
|
|
311
|
+
return this.config.userId;
|
|
312
|
+
}
|
|
313
|
+
internalTags() {
|
|
314
|
+
return this.config.tags;
|
|
315
|
+
}
|
|
242
316
|
internalRecordGeneration(generation) {
|
|
243
317
|
this.generations.push(cloneGeneration(generation));
|
|
244
318
|
}
|
|
@@ -278,6 +352,20 @@ export class SigilClient {
|
|
|
278
352
|
const resolverMode = callContentCaptureResolver(this.config.contentCaptureResolver, undefined);
|
|
279
353
|
return shouldIncludeToolContent(seed.contentCapture ?? 'default', this.config.contentCapture, resolverMode, seed.includeContent ?? false);
|
|
280
354
|
}
|
|
355
|
+
internalHasGenerationSanitizer() {
|
|
356
|
+
return this.config.generationSanitizer !== undefined;
|
|
357
|
+
}
|
|
358
|
+
internalSanitizeGeneration(generation) {
|
|
359
|
+
const sanitizer = this.config.generationSanitizer;
|
|
360
|
+
if (sanitizer === undefined) {
|
|
361
|
+
return generation;
|
|
362
|
+
}
|
|
363
|
+
const sanitized = sanitizer(cloneGeneration(generation));
|
|
364
|
+
if (sanitized === undefined) {
|
|
365
|
+
throw new Error('generation sanitizer must return a generation');
|
|
366
|
+
}
|
|
367
|
+
return cloneGeneration(sanitized);
|
|
368
|
+
}
|
|
281
369
|
internalStartGenerationSpan(seed, mode, startedAt) {
|
|
282
370
|
const operationName = seed.operationName ?? defaultOperationNameForMode(mode);
|
|
283
371
|
const span = this.tracer.startSpan(generationSpanName(operationName, seed.model.name), {
|
|
@@ -635,12 +723,34 @@ class GenerationRecorderImpl {
|
|
|
635
723
|
if (!notEmpty(this.seed.userId)) {
|
|
636
724
|
this.seed.userId = userIdFromContext();
|
|
637
725
|
}
|
|
726
|
+
if (!notEmpty(this.seed.userId)) {
|
|
727
|
+
const fromConfig = this.client.internalUserId();
|
|
728
|
+
if (fromConfig !== undefined && fromConfig.length > 0) {
|
|
729
|
+
this.seed.userId = fromConfig;
|
|
730
|
+
}
|
|
731
|
+
}
|
|
638
732
|
if (!notEmpty(this.seed.agentName)) {
|
|
639
733
|
this.seed.agentName = agentNameFromContext();
|
|
640
734
|
}
|
|
735
|
+
if (!notEmpty(this.seed.agentName)) {
|
|
736
|
+
const fromConfig = this.client.internalAgentName();
|
|
737
|
+
if (fromConfig !== undefined && fromConfig.length > 0) {
|
|
738
|
+
this.seed.agentName = fromConfig;
|
|
739
|
+
}
|
|
740
|
+
}
|
|
641
741
|
if (!notEmpty(this.seed.agentVersion)) {
|
|
642
742
|
this.seed.agentVersion = agentVersionFromContext();
|
|
643
743
|
}
|
|
744
|
+
if (!notEmpty(this.seed.agentVersion)) {
|
|
745
|
+
const fromConfig = this.client.internalAgentVersion();
|
|
746
|
+
if (fromConfig !== undefined && fromConfig.length > 0) {
|
|
747
|
+
this.seed.agentVersion = fromConfig;
|
|
748
|
+
}
|
|
749
|
+
}
|
|
750
|
+
const tags = this.client.internalTags();
|
|
751
|
+
if (tags !== undefined && Object.keys(tags).length > 0) {
|
|
752
|
+
this.seed.tags = { ...tags, ...(this.seed.tags ?? {}) };
|
|
753
|
+
}
|
|
644
754
|
if (!notEmpty(this.seed.operationName)) {
|
|
645
755
|
this.seed.operationName = defaultOperationNameForMode(this.seed.mode ?? defaultMode);
|
|
646
756
|
}
|
|
@@ -675,7 +785,7 @@ class GenerationRecorderImpl {
|
|
|
675
785
|
return;
|
|
676
786
|
}
|
|
677
787
|
this.ended = true;
|
|
678
|
-
|
|
788
|
+
let generation = {
|
|
679
789
|
id: this.seed.id ?? newLocalID('gen'),
|
|
680
790
|
conversationId: firstNonEmptyString(this.result?.conversationId, this.seed.conversationId),
|
|
681
791
|
conversationTitle: firstNonEmptyString(this.result?.conversationTitle, this.seed.conversationTitle),
|
|
@@ -734,14 +844,28 @@ class GenerationRecorderImpl {
|
|
|
734
844
|
generation.metadata = {};
|
|
735
845
|
}
|
|
736
846
|
generation.metadata[spanAttrSDKName] = sdkName;
|
|
737
|
-
const validationError = validateGeneration(generation);
|
|
738
847
|
const callErrorCategory = errorCategoryFromError(this.callError, false);
|
|
848
|
+
let effectiveContentCaptureMode = this.contentCaptureMode;
|
|
849
|
+
let validationTarget = cloneGeneration(generation);
|
|
739
850
|
stampContentCaptureMetadata(generation, this.contentCaptureMode);
|
|
740
851
|
if (this.contentCaptureMode === 'metadata_only') {
|
|
741
852
|
stripContent(generation, callErrorCategory);
|
|
742
853
|
}
|
|
854
|
+
else if (this.client.internalHasGenerationSanitizer()) {
|
|
855
|
+
try {
|
|
856
|
+
generation = this.client.internalSanitizeGeneration(generation);
|
|
857
|
+
validationTarget = cloneGeneration(generation);
|
|
858
|
+
}
|
|
859
|
+
catch (error) {
|
|
860
|
+
effectiveContentCaptureMode = 'metadata_only';
|
|
861
|
+
stripContent(generation, callErrorCategory);
|
|
862
|
+
stampContentCaptureMetadata(generation, effectiveContentCaptureMode);
|
|
863
|
+
this.client.internalLogWarn('sigil generation sanitization failed; falling back to metadata_only', error);
|
|
864
|
+
}
|
|
865
|
+
}
|
|
866
|
+
const validationError = validateGeneration(validationTarget);
|
|
743
867
|
this.client.internalSyncGenerationSpan(this.span, generation);
|
|
744
|
-
if (
|
|
868
|
+
if (effectiveContentCaptureMode === 'metadata_only') {
|
|
745
869
|
this.client.internalClearSpanConversationTitle(this.span);
|
|
746
870
|
}
|
|
747
871
|
this.client.internalApplyTraceContextFromSpan(this.span, generation);
|
|
@@ -761,7 +885,7 @@ class GenerationRecorderImpl {
|
|
|
761
885
|
this.client.internalLogWarn('sigil generation enqueue failed', enqueueError);
|
|
762
886
|
}
|
|
763
887
|
}
|
|
764
|
-
const finalCallError =
|
|
888
|
+
const finalCallError = effectiveContentCaptureMode === 'metadata_only' ? generation.callError : this.callError;
|
|
765
889
|
this.client.internalFinalizeGenerationSpan(this.span, generation, finalCallError, validationError, enqueueError, this.firstTokenAt, callErrorCategory.length > 0 ? callErrorCategory : undefined);
|
|
766
890
|
}
|
|
767
891
|
getError() {
|
|
@@ -837,9 +961,21 @@ class ToolExecutionRecorderImpl {
|
|
|
837
961
|
if (!notEmpty(this.seed.agentName)) {
|
|
838
962
|
this.seed.agentName = agentNameFromContext();
|
|
839
963
|
}
|
|
964
|
+
if (!notEmpty(this.seed.agentName)) {
|
|
965
|
+
const fromConfig = this.client.internalAgentName();
|
|
966
|
+
if (notEmpty(fromConfig)) {
|
|
967
|
+
this.seed.agentName = fromConfig;
|
|
968
|
+
}
|
|
969
|
+
}
|
|
840
970
|
if (!notEmpty(this.seed.agentVersion)) {
|
|
841
971
|
this.seed.agentVersion = agentVersionFromContext();
|
|
842
972
|
}
|
|
973
|
+
if (!notEmpty(this.seed.agentVersion)) {
|
|
974
|
+
const fromConfig = this.client.internalAgentVersion();
|
|
975
|
+
if (notEmpty(fromConfig)) {
|
|
976
|
+
this.seed.agentVersion = fromConfig;
|
|
977
|
+
}
|
|
978
|
+
}
|
|
843
979
|
this.resolvedIncludeContent = this.client.internalResolveToolIncludeContent(this.seed);
|
|
844
980
|
this.startedAt = this.seed.startedAt ?? this.client.internalNow();
|
|
845
981
|
this.span = this.client.internalStartToolExecutionSpan(this.seed, this.startedAt);
|