@aryasaatvik/motel 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +180 -0
- package/LICENSE +21 -0
- package/README.md +137 -0
- package/package.json +119 -0
- package/skills/motel-debug/SKILL.md +203 -0
- package/skills/motel-debug/references/effect.md +38 -0
- package/src/App.tsx +414 -0
- package/src/StartupGate.test.ts +80 -0
- package/src/StartupGate.tsx +263 -0
- package/src/cli.ts +205 -0
- package/src/config.ts +45 -0
- package/src/daemon.test.ts +547 -0
- package/src/daemon.ts +486 -0
- package/src/domain.test.ts +62 -0
- package/src/domain.ts +291 -0
- package/src/httpApi.ts +391 -0
- package/src/httpListPolicy.test.ts +76 -0
- package/src/httpListPolicy.ts +129 -0
- package/src/index.tsx +25 -0
- package/src/instructions.ts +72 -0
- package/src/launchAgent.test.ts +430 -0
- package/src/launchAgent.ts +462 -0
- package/src/localServer.ts +568 -0
- package/src/locator.ts +138 -0
- package/src/mcp.ts +433 -0
- package/src/motel.test.ts +176 -0
- package/src/motel.ts +88 -0
- package/src/motelClient.ts +353 -0
- package/src/opentui-jsx.d.ts +11 -0
- package/src/otlp.test.ts +65 -0
- package/src/otlp.ts +162 -0
- package/src/otlpProtobuf.ts +35 -0
- package/src/queryFilters.ts +39 -0
- package/src/registry.ts +115 -0
- package/src/runtime.ts +40 -0
- package/src/server.ts +11 -0
- package/src/services/AsyncIngest.ts +82 -0
- package/src/services/LogQueryService.ts +29 -0
- package/src/services/TelemetryQuery.ts +62 -0
- package/src/services/TelemetryStore.ts +2554 -0
- package/src/services/TraceQueryService.ts +43 -0
- package/src/services/ingestRpc.ts +39 -0
- package/src/services/queryRpc.ts +15 -0
- package/src/services/telemetryQueryWorker.ts +32 -0
- package/src/services/telemetryWorker.ts +59 -0
- package/src/startupBench.ts +19 -0
- package/src/startupLifecycle.ts +57 -0
- package/src/storybook/aiChatStory.tsx +244 -0
- package/src/storybook/fixtures/errorState.ts +44 -0
- package/src/storybook/fixtures/imagePaste.ts +34 -0
- package/src/storybook/fixtures/index.ts +62 -0
- package/src/storybook/fixtures/kitchenSink.ts +148 -0
- package/src/storybook/fixtures/rawPrompt.ts +15 -0
- package/src/storybook/fixtures/short.ts +27 -0
- package/src/storybook/fixtures/toolHeavy.ts +65 -0
- package/src/telemetry.test.ts +1053 -0
- package/src/ui/AiChatView.tsx +308 -0
- package/src/ui/AttrFilterModal.tsx +120 -0
- package/src/ui/ServiceLogs.tsx +115 -0
- package/src/ui/SpanContentView.tsx +184 -0
- package/src/ui/SpanDetail.tsx +215 -0
- package/src/ui/SpanDetailFull.tsx +224 -0
- package/src/ui/SpanDetailPane.tsx +90 -0
- package/src/ui/TraceDetailsPane.tsx +175 -0
- package/src/ui/TraceList.tsx +254 -0
- package/src/ui/Waterfall.tsx +389 -0
- package/src/ui/aiChatModel.test.ts +391 -0
- package/src/ui/aiChatModel.ts +773 -0
- package/src/ui/aiState.ts +59 -0
- package/src/ui/app/TraceListPane.tsx +39 -0
- package/src/ui/app/TraceWorkspace.tsx +452 -0
- package/src/ui/app/useAppLayout.ts +101 -0
- package/src/ui/app/useTraceScreenData.ts +590 -0
- package/src/ui/atoms.ts +131 -0
- package/src/ui/cachedLoader.test.ts +23 -0
- package/src/ui/cachedLoader.ts +60 -0
- package/src/ui/filterParser.test.ts +56 -0
- package/src/ui/filterParser.ts +45 -0
- package/src/ui/format.ts +128 -0
- package/src/ui/loaders.ts +101 -0
- package/src/ui/persistence.ts +41 -0
- package/src/ui/primitives.tsx +198 -0
- package/src/ui/state.ts +6 -0
- package/src/ui/theme.ts +160 -0
- package/src/ui/traceDetailsWidth.repro.test.ts +126 -0
- package/src/ui/traceSortNav.repro.seed.ts +62 -0
- package/src/ui/traceSortNav.repro.test.ts +231 -0
- package/src/ui/useAttrFilterPicker.ts +89 -0
- package/src/ui/useKeyboardNav.ts +1023 -0
- package/src/ui/waterfallFilter.test.ts +84 -0
- package/src/ui/waterfallFilter.ts +59 -0
- package/src/ui/waterfallModel.ts +130 -0
- package/src/ui/waterfallNav.repro.seed.ts +86 -0
- package/src/ui/waterfallNav.repro.test.ts +271 -0
- package/src/ui/waterfallNav.test.ts +453 -0
- package/src/ui/waterfallNav.ts +75 -0
- package/web/dist/assets/index-B01z9BaO.css +2 -0
- package/web/dist/assets/index-CYlBaY9O.js +22 -0
- package/web/dist/index.html +13 -0
package/src/domain.ts
ADDED
|
@@ -0,0 +1,291 @@
|
|
|
1
|
+
import { Schema } from "effect"
|
|
2
|
+
|
|
3
|
+
// ---------------------------------------------------------------------------
|
|
4
|
+
// Reusable helpers
|
|
5
|
+
// ---------------------------------------------------------------------------
|
|
6
|
+
|
|
7
|
+
const StringRecord = Schema.Record(Schema.String, Schema.String)
|
|
8
|
+
|
|
9
|
+
const DateFromString = Schema.DateFromString
|
|
10
|
+
|
|
11
|
+
// ---------------------------------------------------------------------------
|
|
12
|
+
// Core telemetry domain types (Schema is the single source of truth)
|
|
13
|
+
// ---------------------------------------------------------------------------
|
|
14
|
+
|
|
15
|
+
export const TraceSpanStatus = Schema.Literals(["ok", "error"])
|
|
16
|
+
export type TraceSpanStatus = typeof TraceSpanStatus.Type
|
|
17
|
+
|
|
18
|
+
export const TraceSpanEvent = Schema.Struct({
|
|
19
|
+
name: Schema.String.pipe(Schema.annotateKey({ description: "Event name" })),
|
|
20
|
+
timestamp: DateFromString.pipe(Schema.annotateKey({ description: "ISO 8601 timestamp" })),
|
|
21
|
+
attributes: StringRecord.pipe(Schema.annotateKey({ description: "Key-value attributes attached to the event" })),
|
|
22
|
+
}).annotate({ identifier: "TraceSpanEvent" })
|
|
23
|
+
|
|
24
|
+
export type TraceSpanEvent = typeof TraceSpanEvent.Type
|
|
25
|
+
|
|
26
|
+
export const TraceSpanItem = Schema.Struct({
|
|
27
|
+
spanId: Schema.String,
|
|
28
|
+
parentSpanId: Schema.NullOr(Schema.String),
|
|
29
|
+
serviceName: Schema.String,
|
|
30
|
+
scopeName: Schema.NullOr(Schema.String).pipe(Schema.annotateKey({ description: "Instrumentation scope (e.g. module or library name)" })),
|
|
31
|
+
kind: Schema.NullOr(Schema.String).pipe(Schema.annotateKey({ description: "Span kind: client, server, producer, consumer, or internal" })),
|
|
32
|
+
operationName: Schema.String.pipe(Schema.annotateKey({ description: "The operation this span represents (e.g. HTTP handler, DB query)" })),
|
|
33
|
+
startTime: DateFromString.pipe(Schema.annotateKey({ description: "ISO 8601 timestamp" })),
|
|
34
|
+
isRunning: Schema.Boolean.pipe(Schema.annotateKey({ description: "True when the span has not reported an end timestamp yet" })),
|
|
35
|
+
durationMs: Schema.Number.pipe(Schema.annotateKey({ description: "Wall-clock duration in milliseconds" })),
|
|
36
|
+
status: TraceSpanStatus.pipe(Schema.annotateKey({ description: "ok or error" })),
|
|
37
|
+
depth: Schema.Number.pipe(Schema.annotateKey({ description: "Nesting depth in the span tree (root = 0)" })),
|
|
38
|
+
tags: StringRecord.pipe(Schema.annotateKey({ description: "Span attributes as key-value pairs" })),
|
|
39
|
+
warnings: Schema.Array(Schema.String).pipe(Schema.annotateKey({ description: "Structural warnings (e.g. missing parent span)" })),
|
|
40
|
+
events: Schema.Array(TraceSpanEvent),
|
|
41
|
+
}).annotate({ identifier: "TraceSpan" })
|
|
42
|
+
|
|
43
|
+
export type TraceSpanItem = typeof TraceSpanItem.Type
|
|
44
|
+
|
|
45
|
+
export const TraceItem = Schema.Struct({
|
|
46
|
+
traceId: Schema.String,
|
|
47
|
+
serviceName: Schema.String.pipe(Schema.annotateKey({ description: "Service that owns the root span" })),
|
|
48
|
+
rootOperationName: Schema.String.pipe(Schema.annotateKey({ description: "Operation name of the root span" })),
|
|
49
|
+
startedAt: DateFromString.pipe(Schema.annotateKey({ description: "ISO 8601 timestamp of the earliest span" })),
|
|
50
|
+
isRunning: Schema.Boolean.pipe(Schema.annotateKey({ description: "True when any span in the trace is still open" })),
|
|
51
|
+
durationMs: Schema.Number.pipe(Schema.annotateKey({ description: "End-to-end trace duration in milliseconds" })),
|
|
52
|
+
spanCount: Schema.Number,
|
|
53
|
+
errorCount: Schema.Number.pipe(Schema.annotateKey({ description: "Number of spans with status=error" })),
|
|
54
|
+
warnings: Schema.Array(Schema.String),
|
|
55
|
+
spans: Schema.Array(TraceSpanItem).pipe(Schema.annotateKey({ description: "Spans ordered by parent-child hierarchy, depth-first" })),
|
|
56
|
+
}).annotate({ identifier: "Trace" })
|
|
57
|
+
|
|
58
|
+
export type TraceItem = typeof TraceItem.Type
|
|
59
|
+
|
|
60
|
+
export const TraceSummaryItem = Schema.Struct({
|
|
61
|
+
traceId: Schema.String,
|
|
62
|
+
serviceName: Schema.String,
|
|
63
|
+
rootOperationName: Schema.String,
|
|
64
|
+
startedAt: DateFromString.pipe(Schema.annotateKey({ description: "ISO 8601 timestamp" })),
|
|
65
|
+
isRunning: Schema.Boolean,
|
|
66
|
+
durationMs: Schema.Number,
|
|
67
|
+
spanCount: Schema.Number,
|
|
68
|
+
errorCount: Schema.Number,
|
|
69
|
+
warnings: Schema.Array(Schema.String),
|
|
70
|
+
}).annotate({ identifier: "TraceSummary" })
|
|
71
|
+
|
|
72
|
+
export type TraceSummaryItem = typeof TraceSummaryItem.Type
|
|
73
|
+
|
|
74
|
+
export const SpanItem = Schema.Struct({
|
|
75
|
+
traceId: Schema.String,
|
|
76
|
+
rootOperationName: Schema.String.pipe(Schema.annotateKey({ description: "Operation name of the trace's root span, for context" })),
|
|
77
|
+
parentOperationName: Schema.NullOr(Schema.String).pipe(Schema.annotateKey({ description: "Parent span operation name, if present" })),
|
|
78
|
+
span: TraceSpanItem,
|
|
79
|
+
}).annotate({ identifier: "SpanWithContext" })
|
|
80
|
+
|
|
81
|
+
export type SpanItem = typeof SpanItem.Type
|
|
82
|
+
|
|
83
|
+
export const LogItem = Schema.Struct({
|
|
84
|
+
id: Schema.String,
|
|
85
|
+
timestamp: DateFromString.pipe(Schema.annotateKey({ description: "ISO 8601 timestamp" })),
|
|
86
|
+
serviceName: Schema.String,
|
|
87
|
+
severityText: Schema.String.pipe(Schema.annotateKey({ description: "Log level: TRACE, DEBUG, INFO, WARN, ERROR, FATAL" })),
|
|
88
|
+
body: Schema.String.pipe(Schema.annotateKey({ description: "Log message body" })),
|
|
89
|
+
traceId: Schema.NullOr(Schema.String).pipe(Schema.annotateKey({ description: "Associated trace ID, if the log was emitted inside a span" })),
|
|
90
|
+
spanId: Schema.NullOr(Schema.String).pipe(Schema.annotateKey({ description: "Associated span ID, if the log was emitted inside a span" })),
|
|
91
|
+
scopeName: Schema.NullOr(Schema.String),
|
|
92
|
+
attributes: StringRecord.pipe(Schema.annotateKey({ description: "Merged resource + log attributes as key-value pairs" })),
|
|
93
|
+
}).annotate({ identifier: "Log" })
|
|
94
|
+
|
|
95
|
+
export type LogItem = typeof LogItem.Type
|
|
96
|
+
|
|
97
|
+
// ---------------------------------------------------------------------------
|
|
98
|
+
// Shared query result types
|
|
99
|
+
// ---------------------------------------------------------------------------
|
|
100
|
+
|
|
101
|
+
export const FacetItem = Schema.Struct({
|
|
102
|
+
value: Schema.String.pipe(Schema.annotateKey({ description: "Distinct value for the faceted field" })),
|
|
103
|
+
count: Schema.Number.pipe(Schema.annotateKey({ description: "Number of occurrences" })),
|
|
104
|
+
}).annotate({ identifier: "Facet" })
|
|
105
|
+
|
|
106
|
+
export type FacetItem = typeof FacetItem.Type
|
|
107
|
+
|
|
108
|
+
export const StatsItem = Schema.Struct({
|
|
109
|
+
group: Schema.String.pipe(Schema.annotateKey({ description: "Grouping key" })),
|
|
110
|
+
value: Schema.Number.pipe(Schema.annotateKey({ description: "Aggregate value for the chosen metric" })),
|
|
111
|
+
count: Schema.Number.pipe(Schema.annotateKey({ description: "Number of samples in the group" })),
|
|
112
|
+
}).annotate({ identifier: "Stat" })
|
|
113
|
+
|
|
114
|
+
export type StatsItem = typeof StatsItem.Type
|
|
115
|
+
|
|
116
|
+
// ---------------------------------------------------------------------------
|
|
117
|
+
// AI Call types
|
|
118
|
+
// ---------------------------------------------------------------------------
|
|
119
|
+
|
|
120
|
+
/** Maps normalized field names to the AI SDK attribute keys in span_attributes */
|
|
121
|
+
export const AI_ATTR_MAP = {
|
|
122
|
+
operationId: "ai.operationId",
|
|
123
|
+
functionId: "ai.telemetry.functionId",
|
|
124
|
+
provider: "ai.model.provider",
|
|
125
|
+
model: "ai.model.id",
|
|
126
|
+
sessionId: "ai.telemetry.metadata.sessionId",
|
|
127
|
+
userId: "ai.telemetry.metadata.userId",
|
|
128
|
+
finishReason: "ai.response.finishReason",
|
|
129
|
+
inputTokens: "ai.usage.inputTokens",
|
|
130
|
+
outputTokens: "ai.usage.outputTokens",
|
|
131
|
+
totalTokens: "ai.usage.totalTokens",
|
|
132
|
+
cachedInputTokens: "ai.usage.cachedInputTokens",
|
|
133
|
+
reasoningTokens: "ai.usage.reasoningTokens",
|
|
134
|
+
msToFirstChunk: "ai.response.msToFirstChunk",
|
|
135
|
+
msToFinish: "ai.response.msToFinish",
|
|
136
|
+
avgOutputTokensPerSecond: "ai.response.avgOutputTokensPerSecond",
|
|
137
|
+
promptMessages: "ai.prompt.messages",
|
|
138
|
+
prompt: "ai.prompt",
|
|
139
|
+
responseText: "ai.response.text",
|
|
140
|
+
tools: "ai.prompt.tools",
|
|
141
|
+
toolChoice: "ai.prompt.toolChoice",
|
|
142
|
+
providerMetadata: "ai.response.providerMetadata",
|
|
143
|
+
responseModel: "ai.response.model",
|
|
144
|
+
responseId: "ai.response.id",
|
|
145
|
+
responseTimestamp: "ai.response.timestamp",
|
|
146
|
+
} as const
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Attribute keys that carry LLM prompt/response content and should be
|
|
150
|
+
* indexed in the span-attribute FTS table. These are the keys emitted by
|
|
151
|
+
* well-known LLM instrumentation conventions:
|
|
152
|
+
*
|
|
153
|
+
* - **Vercel AI SDK** (`ai.*`): rich, SDK-specific attributes captured by
|
|
154
|
+
* `experimental_telemetry` on `generateText` / `streamText` / `generateObject`.
|
|
155
|
+
* - **OpenTelemetry GenAI semantic conventions** (`gen_ai.*`): the
|
|
156
|
+
* cross-vendor standard. The singular `prompt`/`completion` attrs are
|
|
157
|
+
* deprecated in favor of event-based capture but are still emitted by
|
|
158
|
+
* most instrumentations, so we keep them.
|
|
159
|
+
* - **OpenInference** (`input.value` / `output.value`): Arize Phoenix /
|
|
160
|
+
* LangChain-style normalized input/output.
|
|
161
|
+
*
|
|
162
|
+
* Keys here trigger FTS indexing on insert via a trigger in TelemetryStore.
|
|
163
|
+
* Adding a key requires a one-time backfill; removing one leaves orphan
|
|
164
|
+
* FTS entries that get cleaned up on next retention pass.
|
|
165
|
+
*/
|
|
166
|
+
export const AI_FTS_KEYS = [
|
|
167
|
+
// Vercel AI SDK
|
|
168
|
+
"ai.prompt",
|
|
169
|
+
"ai.prompt.messages",
|
|
170
|
+
"ai.prompt.tools",
|
|
171
|
+
"ai.prompt.toolChoice",
|
|
172
|
+
"ai.response.text",
|
|
173
|
+
"ai.response.toolCalls",
|
|
174
|
+
"ai.response.reasoning",
|
|
175
|
+
"ai.response.object",
|
|
176
|
+
"ai.toolCall.args",
|
|
177
|
+
"ai.toolCall.result",
|
|
178
|
+
// OpenTelemetry GenAI semantic conventions
|
|
179
|
+
"gen_ai.prompt",
|
|
180
|
+
"gen_ai.completion",
|
|
181
|
+
"gen_ai.input.messages",
|
|
182
|
+
"gen_ai.output.messages",
|
|
183
|
+
"gen_ai.system_instructions",
|
|
184
|
+
"gen_ai.tool.definitions",
|
|
185
|
+
"gen_ai.tool.message.content",
|
|
186
|
+
// OpenInference (Phoenix, LangChain, etc.)
|
|
187
|
+
"input.value",
|
|
188
|
+
"output.value",
|
|
189
|
+
] as const
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
* Back-compat alias. The `text` filter on `/api/ai/calls` historically
|
|
193
|
+
* LIKE-searched these four keys; now FTS indexes the broader AI_FTS_KEYS
|
|
194
|
+
* set so the filter transparently covers more content.
|
|
195
|
+
*/
|
|
196
|
+
export const AI_TEXT_SEARCH_KEYS = AI_FTS_KEYS
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* True if a span's tags contain any of the AI content keys we track.
|
|
200
|
+
* Used as the single source of truth for "this span has LLM payloads
|
|
201
|
+
* worth a specialized view" — drives the ✦ marker in the waterfall row
|
|
202
|
+
* and picks the chat-flavored renderer when the user drills into the
|
|
203
|
+
* span's detail. Scanning happens once per row during render so this
|
|
204
|
+
* needs to stay O(AI_FTS_KEYS.length) with cheap `in` checks rather
|
|
205
|
+
* than an `Object.keys(...).some(...)` allocation.
|
|
206
|
+
*/
|
|
207
|
+
export const isAiSpan = (tags: Readonly<Record<string, string>>): boolean => {
|
|
208
|
+
for (const key of AI_FTS_KEYS) {
|
|
209
|
+
if (key in tags) return true
|
|
210
|
+
}
|
|
211
|
+
return false
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
const PREVIEW_LENGTH = 200
|
|
215
|
+
|
|
216
|
+
export const truncatePreview = (value: string | null | undefined): string | null => {
|
|
217
|
+
if (!value) return null
|
|
218
|
+
if (value.length <= PREVIEW_LENGTH) return value
|
|
219
|
+
return value.slice(0, PREVIEW_LENGTH) + "..."
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
export const AiUsage = Schema.Struct({
|
|
223
|
+
inputTokens: Schema.NullOr(Schema.Number),
|
|
224
|
+
outputTokens: Schema.NullOr(Schema.Number),
|
|
225
|
+
totalTokens: Schema.NullOr(Schema.Number),
|
|
226
|
+
cachedInputTokens: Schema.NullOr(Schema.Number),
|
|
227
|
+
reasoningTokens: Schema.NullOr(Schema.Number),
|
|
228
|
+
}).annotate({ identifier: "AiUsage" })
|
|
229
|
+
|
|
230
|
+
export type AiUsage = typeof AiUsage.Type
|
|
231
|
+
|
|
232
|
+
export const AiCallSummary = Schema.Struct({
|
|
233
|
+
traceId: Schema.String,
|
|
234
|
+
spanId: Schema.String,
|
|
235
|
+
operation: Schema.String.pipe(Schema.annotateKey({ description: "AI operation: streamText, generateText, streamObject, etc." })),
|
|
236
|
+
service: Schema.String,
|
|
237
|
+
functionId: Schema.NullOr(Schema.String).pipe(Schema.annotateKey({ description: "ai.telemetry.functionId" })),
|
|
238
|
+
provider: Schema.NullOr(Schema.String).pipe(Schema.annotateKey({ description: "ai.model.provider (e.g. openai.responses)" })),
|
|
239
|
+
model: Schema.NullOr(Schema.String).pipe(Schema.annotateKey({ description: "ai.model.id (e.g. gpt-5.4)" })),
|
|
240
|
+
status: TraceSpanStatus,
|
|
241
|
+
startedAt: Schema.String.pipe(Schema.annotateKey({ description: "ISO 8601 timestamp" })),
|
|
242
|
+
durationMs: Schema.Number,
|
|
243
|
+
sessionId: Schema.NullOr(Schema.String),
|
|
244
|
+
userId: Schema.NullOr(Schema.String),
|
|
245
|
+
promptPreview: Schema.NullOr(Schema.String).pipe(Schema.annotateKey({ description: "First ~200 chars of prompt content" })),
|
|
246
|
+
responsePreview: Schema.NullOr(Schema.String).pipe(Schema.annotateKey({ description: "First ~200 chars of response text" })),
|
|
247
|
+
finishReason: Schema.NullOr(Schema.String),
|
|
248
|
+
toolCallCount: Schema.Number.pipe(Schema.annotateKey({ description: "Number of tool call child spans" })),
|
|
249
|
+
usage: Schema.NullOr(AiUsage),
|
|
250
|
+
}).annotate({ identifier: "AiCallSummary" })
|
|
251
|
+
|
|
252
|
+
export type AiCallSummary = typeof AiCallSummary.Type
|
|
253
|
+
|
|
254
|
+
export const AiToolCall = Schema.Struct({
|
|
255
|
+
name: Schema.String,
|
|
256
|
+
spanId: Schema.NullOr(Schema.String),
|
|
257
|
+
status: TraceSpanStatus,
|
|
258
|
+
durationMs: Schema.NullOr(Schema.Number),
|
|
259
|
+
}).annotate({ identifier: "AiToolCall" })
|
|
260
|
+
|
|
261
|
+
export type AiToolCall = typeof AiToolCall.Type
|
|
262
|
+
|
|
263
|
+
export const AiCallDetail = Schema.Struct({
|
|
264
|
+
traceId: Schema.String,
|
|
265
|
+
spanId: Schema.String,
|
|
266
|
+
operation: Schema.String,
|
|
267
|
+
service: Schema.String,
|
|
268
|
+
functionId: Schema.NullOr(Schema.String),
|
|
269
|
+
provider: Schema.NullOr(Schema.String),
|
|
270
|
+
model: Schema.NullOr(Schema.String),
|
|
271
|
+
status: TraceSpanStatus,
|
|
272
|
+
startedAt: Schema.String,
|
|
273
|
+
durationMs: Schema.Number,
|
|
274
|
+
sessionId: Schema.NullOr(Schema.String),
|
|
275
|
+
userId: Schema.NullOr(Schema.String),
|
|
276
|
+
finishReason: Schema.NullOr(Schema.String),
|
|
277
|
+
promptMessages: Schema.NullOr(Schema.Unknown).pipe(Schema.annotateKey({ description: "Full parsed ai.prompt.messages or ai.prompt" })),
|
|
278
|
+
responseText: Schema.NullOr(Schema.String).pipe(Schema.annotateKey({ description: "Full ai.response.text" })),
|
|
279
|
+
toolCalls: Schema.Array(AiToolCall),
|
|
280
|
+
toolsAvailable: Schema.NullOr(Schema.Unknown).pipe(Schema.annotateKey({ description: "Full ai.prompt.tools" })),
|
|
281
|
+
providerMetadata: Schema.NullOr(Schema.Unknown),
|
|
282
|
+
usage: Schema.NullOr(AiUsage),
|
|
283
|
+
timing: Schema.Struct({
|
|
284
|
+
msToFirstChunk: Schema.NullOr(Schema.Number),
|
|
285
|
+
msToFinish: Schema.NullOr(Schema.Number),
|
|
286
|
+
avgOutputTokensPerSecond: Schema.NullOr(Schema.Number),
|
|
287
|
+
}),
|
|
288
|
+
logs: Schema.Array(Schema.Unknown).pipe(Schema.annotateKey({ description: "Correlated log records" })),
|
|
289
|
+
}).annotate({ identifier: "AiCallDetail" })
|
|
290
|
+
|
|
291
|
+
export type AiCallDetail = typeof AiCallDetail.Type
|