@pepys/source-telemetry 0.0.0-stage → 0.1.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.
Files changed (32) hide show
  1. package/README.md +35 -2
  2. package/package.json +30 -4
  3. package/src/consumers/products/find-claude-code-model-request.ts +41 -0
  4. package/src/consumers/products/find-codex-model-request.ts +34 -0
  5. package/src/consumers/products/find-codex-turn-cost.ts +21 -0
  6. package/src/consumers/products/find-record-number.ts +101 -0
  7. package/src/consumers/products/parse-fact-type.ts +21 -0
  8. package/src/consumers/products/types.ts +34 -0
  9. package/src/consumers/telemetry-model-request.ts +91 -0
  10. package/src/consumers/telemetry-turn-cost.ts +85 -0
  11. package/src/generated/telemetry-model-request-boundary.ts +368 -0
  12. package/src/generated/telemetry-model-request.sql +35 -0
  13. package/src/generated/telemetry-turn-cost-boundary.ts +361 -0
  14. package/src/generated/telemetry-turn-cost.sql +26 -0
  15. package/src/index.ts +26 -0
  16. package/src/log-record-family.ts +41 -0
  17. package/src/otlp/format-canonical-json.ts +24 -0
  18. package/src/otlp/parse-logs-export.ts +71 -0
  19. package/src/otlp/read-otlp-facts.ts +226 -0
  20. package/src/otlp/receive-otlp-logs.ts +130 -0
  21. package/src/otlp/to-attribute-map.ts +63 -0
  22. package/src/otlp/types.ts +36 -0
  23. package/src/products/claude-code/claude-code-product.ts +45 -0
  24. package/src/products/codex/codex-product.ts +37 -0
  25. package/src/products/define-telemetry-product.ts +7 -0
  26. package/src/products/find-attribute-text.ts +12 -0
  27. package/src/products/products.ts +8 -0
  28. package/src/products/types.ts +21 -0
  29. package/src/queries/session-model-requests-query.ts +55 -0
  30. package/src/telemetry-model-request-consumer.ts +59 -0
  31. package/src/telemetry-plugin.ts +44 -0
  32. package/src/telemetry-turn-cost-consumer.ts +42 -0
@@ -0,0 +1,368 @@
1
+ // Generated by `pepys build`. Do not edit.
2
+
3
+ declare const idTag: unique symbol;
4
+ declare const kindTag: unique symbol;
5
+
6
+ export type EntityID = string & { readonly [idTag]: 'EntityID' };
7
+
8
+ type KindedID<Kind extends string> = EntityID & { readonly [kindTag]: Kind };
9
+
10
+ export type TelemetryModelRequestID = KindedID<'telemetry:model_request'>;
11
+
12
+ export function toTelemetryModelRequestID(id: string): TelemetryModelRequestID {
13
+ const text = 'telemetry:model_request:' + id;
14
+
15
+ if (!isTelemetryModelRequestID(text)) {
16
+ throw new Error('an ID of the kind telemetry:model_request is never empty');
17
+ }
18
+
19
+ return text;
20
+ }
21
+
22
+ export function parseTelemetryModelRequestID(text: string): TelemetryModelRequestID | undefined {
23
+ return isTelemetryModelRequestID(text) ? text : undefined;
24
+ }
25
+
26
+ function isTelemetryModelRequestID(text: string): text is TelemetryModelRequestID {
27
+ return text.length > 'telemetry:model_request:'.length && text.startsWith('telemetry:model_request:');
28
+ }
29
+
30
+ export type TelemetrySessionID = KindedID<'telemetry:session'>;
31
+
32
+ export function toTelemetrySessionID(id: string): TelemetrySessionID {
33
+ const text = 'telemetry:session:' + id;
34
+
35
+ if (!isTelemetrySessionID(text)) {
36
+ throw new Error('an ID of the kind telemetry:session is never empty');
37
+ }
38
+
39
+ return text;
40
+ }
41
+
42
+ export function parseTelemetrySessionID(text: string): TelemetrySessionID | undefined {
43
+ return isTelemetrySessionID(text) ? text : undefined;
44
+ }
45
+
46
+ function isTelemetrySessionID(text: string): text is TelemetrySessionID {
47
+ return text.length > 'telemetry:session:'.length && text.startsWith('telemetry:session:');
48
+ }
49
+
50
+ export interface TelemetryModelRequestModel {
51
+ input: string;
52
+ }
53
+
54
+ export type TelemetryModelRequestMessage = { kind: 'apply'; input: string } | { kind: 'reset' };
55
+
56
+ export function initTelemetryModelRequestModel(): TelemetryModelRequestModel {
57
+ return { input: '' };
58
+ }
59
+
60
+ export function updateTelemetryModelRequestModel(model: TelemetryModelRequestModel, message: TelemetryModelRequestMessage): TelemetryModelRequestModel {
61
+ return message.kind === 'reset' ? initTelemetryModelRequestModel() : { ...model, input: message.input };
62
+ }
63
+
64
+ export interface TelemetryPayload {
65
+ resource: TelemetryPayloadResource;
66
+ scope: TelemetryPayloadScope;
67
+ logRecord: TelemetryPayloadLogRecord;
68
+ origin: TelemetryPayloadOrigin;
69
+ }
70
+
71
+ export interface TelemetryPayloadResource {
72
+ attributes: TelemetryPayloadResourceAttributesItem[];
73
+ }
74
+
75
+ export interface TelemetryPayloadResourceAttributesItem {
76
+ key: string;
77
+ value: unknown;
78
+ }
79
+
80
+ export interface TelemetryPayloadScope {
81
+ name: string;
82
+ version?: string;
83
+ }
84
+
85
+ export interface TelemetryPayloadLogRecord {
86
+ attributes?: TelemetryPayloadLogRecordAttributesItem[];
87
+ }
88
+
89
+ export interface TelemetryPayloadLogRecordAttributesItem {
90
+ key: string;
91
+ value: unknown;
92
+ }
93
+
94
+ export interface TelemetryPayloadOrigin {
95
+ issuer: string;
96
+ subject: string;
97
+ }
98
+
99
+ export type TelemetryModelRequestFact =
100
+ | { id: string; type: 'telemetry.claude_code.api_request' | 'telemetry.claude_code.api_error' | 'telemetry.claude_code.api_refusal' | 'telemetry.claude_code.user_prompt' | 'telemetry.claude_code.assistant_response' | 'telemetry.claude_code.tool_result' | 'telemetry.claude_code.tool_decision' | 'telemetry.claude_code.unlisted' | 'telemetry.codex.sse_event' | 'telemetry.codex.api_request' | 'telemetry.codex.turn_cost' | 'telemetry.codex.conversation_starts' | 'telemetry.codex.user_prompt' | 'telemetry.codex.tool_decision' | 'telemetry.codex.tool_result' | 'telemetry.codex.unlisted'; entity: string; at: string; payload: TelemetryPayload };
101
+
102
+ export function parseTelemetryModelRequestFact(factJSON: string): TelemetryModelRequestFact | undefined {
103
+ const parsed: unknown = JSON.parse(factJSON);
104
+
105
+ return decodeTelemetryModelRequestFact(parsed);
106
+ }
107
+
108
+ function decodeTelemetryModelRequestFact(value: unknown): TelemetryModelRequestFact | undefined {
109
+ if (typeof value !== 'object' || value === null || Array.isArray(value)) {
110
+ return undefined;
111
+ }
112
+
113
+ const id = 'id' in value ? value.id : undefined;
114
+ const type = 'type' in value ? value.type : undefined;
115
+ const entity = 'entity' in value ? value.entity : undefined;
116
+ const at = 'at' in value ? value.at : undefined;
117
+ const payload = 'payload' in value ? value.payload : undefined;
118
+
119
+ if (typeof id !== 'string' || typeof entity !== 'string' || typeof at !== 'string') {
120
+ return undefined;
121
+ }
122
+
123
+ if (type === 'telemetry.claude_code.api_request' || type === 'telemetry.claude_code.api_error' || type === 'telemetry.claude_code.api_refusal' || type === 'telemetry.claude_code.user_prompt' || type === 'telemetry.claude_code.assistant_response' || type === 'telemetry.claude_code.tool_result' || type === 'telemetry.claude_code.tool_decision' || type === 'telemetry.claude_code.unlisted' || type === 'telemetry.codex.sse_event' || type === 'telemetry.codex.api_request' || type === 'telemetry.codex.turn_cost' || type === 'telemetry.codex.conversation_starts' || type === 'telemetry.codex.user_prompt' || type === 'telemetry.codex.tool_decision' || type === 'telemetry.codex.tool_result' || type === 'telemetry.codex.unlisted') {
124
+ const decoded = decodeTelemetryPayload(payload);
125
+
126
+ if (decoded === undefined) {
127
+ return undefined;
128
+ }
129
+
130
+ return { id, type, entity, at, payload: decoded };
131
+ }
132
+
133
+ return undefined;
134
+ }
135
+
136
+ function decodeTelemetryPayload(value: unknown): TelemetryPayload | undefined {
137
+ if (typeof value !== 'object' || value === null || Array.isArray(value)) {
138
+ return undefined;
139
+ }
140
+
141
+ const raw0 = 'resource' in value ? value.resource : undefined;
142
+ const field0 = decodeTelemetryPayloadResource(raw0);
143
+
144
+ if (field0 === undefined) {
145
+ return undefined;
146
+ }
147
+
148
+ const raw1 = 'scope' in value ? value.scope : undefined;
149
+ const field1 = decodeTelemetryPayloadScope(raw1);
150
+
151
+ if (field1 === undefined) {
152
+ return undefined;
153
+ }
154
+
155
+ const raw2 = 'logRecord' in value ? value.logRecord : undefined;
156
+ const field2 = decodeTelemetryPayloadLogRecord(raw2);
157
+
158
+ if (field2 === undefined) {
159
+ return undefined;
160
+ }
161
+
162
+ const raw3 = 'origin' in value ? value.origin : undefined;
163
+ const field3 = decodeTelemetryPayloadOrigin(raw3);
164
+
165
+ if (field3 === undefined) {
166
+ return undefined;
167
+ }
168
+
169
+ return {
170
+ resource: field0,
171
+ scope: field1,
172
+ logRecord: field2,
173
+ origin: field3,
174
+ };
175
+ }
176
+
177
+ function decodeTelemetryPayloadResource(value: unknown): TelemetryPayloadResource | undefined {
178
+ if (typeof value !== 'object' || value === null || Array.isArray(value)) {
179
+ return undefined;
180
+ }
181
+
182
+ const raw0 = 'attributes' in value ? value.attributes : undefined;
183
+ const field0 = decodeTelemetryPayloadResourceAttributes(raw0);
184
+
185
+ if (field0 === undefined) {
186
+ return undefined;
187
+ }
188
+
189
+ return {
190
+ attributes: field0,
191
+ };
192
+ }
193
+
194
+ function decodeTelemetryPayloadResourceAttributes(value: unknown): TelemetryPayloadResourceAttributesItem[] | undefined {
195
+ if (!Array.isArray(value)) {
196
+ return undefined;
197
+ }
198
+
199
+ const items: TelemetryPayloadResourceAttributesItem[] = [];
200
+
201
+ for (let index = 0; index < value.length; index += 1) {
202
+ const element: unknown = value[index];
203
+ const item = decodeTelemetryPayloadResourceAttributesItem(element);
204
+
205
+ if (item === undefined) {
206
+ return undefined;
207
+ }
208
+
209
+ items.push(item);
210
+ }
211
+
212
+ return items;
213
+ }
214
+
215
+ function decodeTelemetryPayloadResourceAttributesItem(value: unknown): TelemetryPayloadResourceAttributesItem | undefined {
216
+ if (typeof value !== 'object' || value === null || Array.isArray(value)) {
217
+ return undefined;
218
+ }
219
+
220
+ const raw0 = 'key' in value ? value.key : undefined;
221
+ const field0 = typeof raw0 === 'string' ? raw0 : undefined;
222
+
223
+ if (field0 === undefined) {
224
+ return undefined;
225
+ }
226
+
227
+ const field1 = 'value' in value ? value.value : undefined;
228
+
229
+ return {
230
+ key: field0,
231
+ value: field1,
232
+ };
233
+ }
234
+
235
+ function decodeTelemetryPayloadScope(value: unknown): TelemetryPayloadScope | undefined {
236
+ if (typeof value !== 'object' || value === null || Array.isArray(value)) {
237
+ return undefined;
238
+ }
239
+
240
+ const raw0 = 'name' in value ? value.name : undefined;
241
+ const field0 = typeof raw0 === 'string' ? raw0 : undefined;
242
+
243
+ if (field0 === undefined) {
244
+ return undefined;
245
+ }
246
+
247
+ const raw1 = 'version' in value ? value.version : undefined;
248
+ const field1 = raw1 === undefined ? undefined : typeof raw1 === 'string' ? raw1 : undefined;
249
+
250
+ if (raw1 !== undefined && field1 === undefined) {
251
+ return undefined;
252
+ }
253
+
254
+ return {
255
+ name: field0,
256
+ ...(field1 === undefined ? {} : { version: field1 }),
257
+ };
258
+ }
259
+
260
+ function decodeTelemetryPayloadLogRecord(value: unknown): TelemetryPayloadLogRecord | undefined {
261
+ if (typeof value !== 'object' || value === null || Array.isArray(value)) {
262
+ return undefined;
263
+ }
264
+
265
+ const raw0 = 'attributes' in value ? value.attributes : undefined;
266
+ const field0 = raw0 === undefined ? undefined : decodeTelemetryPayloadLogRecordAttributes(raw0);
267
+
268
+ if (raw0 !== undefined && field0 === undefined) {
269
+ return undefined;
270
+ }
271
+
272
+ return {
273
+ ...(field0 === undefined ? {} : { attributes: field0 }),
274
+ };
275
+ }
276
+
277
+ function decodeTelemetryPayloadLogRecordAttributes(value: unknown): TelemetryPayloadLogRecordAttributesItem[] | undefined {
278
+ if (!Array.isArray(value)) {
279
+ return undefined;
280
+ }
281
+
282
+ const items: TelemetryPayloadLogRecordAttributesItem[] = [];
283
+
284
+ for (let index = 0; index < value.length; index += 1) {
285
+ const element: unknown = value[index];
286
+ const item = decodeTelemetryPayloadLogRecordAttributesItem(element);
287
+
288
+ if (item === undefined) {
289
+ return undefined;
290
+ }
291
+
292
+ items.push(item);
293
+ }
294
+
295
+ return items;
296
+ }
297
+
298
+ function decodeTelemetryPayloadLogRecordAttributesItem(value: unknown): TelemetryPayloadLogRecordAttributesItem | undefined {
299
+ if (typeof value !== 'object' || value === null || Array.isArray(value)) {
300
+ return undefined;
301
+ }
302
+
303
+ const raw0 = 'key' in value ? value.key : undefined;
304
+ const field0 = typeof raw0 === 'string' ? raw0 : undefined;
305
+
306
+ if (field0 === undefined) {
307
+ return undefined;
308
+ }
309
+
310
+ const field1 = 'value' in value ? value.value : undefined;
311
+
312
+ return {
313
+ key: field0,
314
+ value: field1,
315
+ };
316
+ }
317
+
318
+ function decodeTelemetryPayloadOrigin(value: unknown): TelemetryPayloadOrigin | undefined {
319
+ if (typeof value !== 'object' || value === null || Array.isArray(value)) {
320
+ return undefined;
321
+ }
322
+
323
+ const raw0 = 'issuer' in value ? value.issuer : undefined;
324
+ const field0 = typeof raw0 === 'string' ? raw0 : undefined;
325
+
326
+ if (field0 === undefined) {
327
+ return undefined;
328
+ }
329
+
330
+ const raw1 = 'subject' in value ? value.subject : undefined;
331
+ const field1 = typeof raw1 === 'string' ? raw1 : undefined;
332
+
333
+ if (field1 === undefined) {
334
+ return undefined;
335
+ }
336
+
337
+ return {
338
+ issuer: field0,
339
+ subject: field1,
340
+ };
341
+ }
342
+
343
+ export interface TelemetryModelRequestRow {
344
+ id: TelemetryModelRequestID;
345
+ session_id: EntityID;
346
+ agent_session: string | null;
347
+ product: string;
348
+ model: string;
349
+ input_tokens: number;
350
+ cache_read_tokens: number;
351
+ cache_creation_tokens: number;
352
+ output_tokens: number;
353
+ cost_usd: number | null;
354
+ cost_basis: string;
355
+ duration_ms: number | null;
356
+ agent: string | null;
357
+ origin_issuer: string;
358
+ origin_subject: string;
359
+ at: string;
360
+ }
361
+
362
+ export interface TelemetryModelRequestKey {
363
+ id: TelemetryModelRequestID;
364
+ }
365
+
366
+ export type TelemetryModelRequestOperation =
367
+ | { upsert: { telemetry_model_request: TelemetryModelRequestRow } }
368
+ | { delete: { telemetry_model_request: TelemetryModelRequestKey } };
@@ -0,0 +1,35 @@
1
+ -- Generated by `pepys build`. Do not edit.
2
+
3
+ CREATE TABLE "telemetry_model_request" (
4
+ "id" TEXT NOT NULL,
5
+ "session_id" TEXT NOT NULL,
6
+ "agent_session" TEXT,
7
+ "product" TEXT NOT NULL,
8
+ "model" TEXT NOT NULL,
9
+ "input_tokens" INTEGER NOT NULL,
10
+ "cache_read_tokens" INTEGER NOT NULL,
11
+ "cache_creation_tokens" INTEGER NOT NULL,
12
+ "output_tokens" INTEGER NOT NULL,
13
+ "cost_usd" REAL,
14
+ "cost_basis" TEXT NOT NULL,
15
+ "duration_ms" INTEGER,
16
+ "agent" TEXT,
17
+ "origin_issuer" TEXT NOT NULL,
18
+ "origin_subject" TEXT NOT NULL,
19
+ "at" TEXT NOT NULL,
20
+ PRIMARY KEY ("id")
21
+ );
22
+
23
+ CREATE INDEX "telemetry_model_request_session_id_idx" ON "telemetry_model_request" ("session_id");
24
+
25
+ CREATE INDEX "telemetry_model_request_at_idx" ON "telemetry_model_request" ("at");
26
+
27
+ CREATE VIEW "entity" AS
28
+ SELECT
29
+ "id" AS "id",
30
+ 'telemetry:model_request' AS "kind",
31
+ "model" AS "title",
32
+ NULL AS "url",
33
+ NULL AS "actor",
34
+ "at" AS "updated_at"
35
+ FROM "telemetry_model_request";