@frengki0707/google-cloud-clone 1.33.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 (108) hide show
  1. package/LICENSE +203 -0
  2. package/README.md +83 -0
  3. package/lib/auth.d.mts +33 -0
  4. package/lib/auth.d.ts +33 -0
  5. package/lib/auth.js +70 -0
  6. package/lib/auth.js.map +1 -0
  7. package/lib/auth.mjs +45 -0
  8. package/lib/auth.mjs.map +1 -0
  9. package/lib/gcpLogger.d.mts +25 -0
  10. package/lib/gcpLogger.d.ts +25 -0
  11. package/lib/gcpLogger.js +118 -0
  12. package/lib/gcpLogger.js.map +1 -0
  13. package/lib/gcpLogger.mjs +82 -0
  14. package/lib/gcpLogger.mjs.map +1 -0
  15. package/lib/gcpOpenTelemetry.d.mts +59 -0
  16. package/lib/gcpOpenTelemetry.d.ts +59 -0
  17. package/lib/gcpOpenTelemetry.js +374 -0
  18. package/lib/gcpOpenTelemetry.js.map +1 -0
  19. package/lib/gcpOpenTelemetry.mjs +364 -0
  20. package/lib/gcpOpenTelemetry.mjs.map +1 -0
  21. package/lib/index.d.mts +36 -0
  22. package/lib/index.d.ts +36 -0
  23. package/lib/index.js +56 -0
  24. package/lib/index.js.map +1 -0
  25. package/lib/index.mjs +29 -0
  26. package/lib/index.mjs.map +1 -0
  27. package/lib/metrics.d.mts +65 -0
  28. package/lib/metrics.d.ts +65 -0
  29. package/lib/metrics.js +91 -0
  30. package/lib/metrics.js.map +1 -0
  31. package/lib/metrics.mjs +65 -0
  32. package/lib/metrics.mjs.map +1 -0
  33. package/lib/model-armor.d.mts +59 -0
  34. package/lib/model-armor.d.ts +59 -0
  35. package/lib/model-armor.js +205 -0
  36. package/lib/model-armor.js.map +1 -0
  37. package/lib/model-armor.mjs +181 -0
  38. package/lib/model-armor.mjs.map +1 -0
  39. package/lib/telemetry/action.d.mts +27 -0
  40. package/lib/telemetry/action.d.ts +27 -0
  41. package/lib/telemetry/action.js +92 -0
  42. package/lib/telemetry/action.js.map +1 -0
  43. package/lib/telemetry/action.mjs +73 -0
  44. package/lib/telemetry/action.mjs.map +1 -0
  45. package/lib/telemetry/defaults.d.mts +30 -0
  46. package/lib/telemetry/defaults.d.ts +30 -0
  47. package/lib/telemetry/defaults.js +70 -0
  48. package/lib/telemetry/defaults.js.map +1 -0
  49. package/lib/telemetry/defaults.mjs +46 -0
  50. package/lib/telemetry/defaults.mjs.map +1 -0
  51. package/lib/telemetry/engagement.d.mts +35 -0
  52. package/lib/telemetry/engagement.d.ts +35 -0
  53. package/lib/telemetry/engagement.js +106 -0
  54. package/lib/telemetry/engagement.js.map +1 -0
  55. package/lib/telemetry/engagement.mjs +85 -0
  56. package/lib/telemetry/engagement.mjs.map +1 -0
  57. package/lib/telemetry/feature.d.mts +35 -0
  58. package/lib/telemetry/feature.d.ts +35 -0
  59. package/lib/telemetry/feature.js +142 -0
  60. package/lib/telemetry/feature.js.map +1 -0
  61. package/lib/telemetry/feature.mjs +127 -0
  62. package/lib/telemetry/feature.mjs.map +1 -0
  63. package/lib/telemetry/generate.d.mts +53 -0
  64. package/lib/telemetry/generate.d.ts +53 -0
  65. package/lib/telemetry/generate.js +326 -0
  66. package/lib/telemetry/generate.js.map +1 -0
  67. package/lib/telemetry/generate.mjs +314 -0
  68. package/lib/telemetry/generate.mjs.map +1 -0
  69. package/lib/telemetry/path.d.mts +32 -0
  70. package/lib/telemetry/path.d.ts +32 -0
  71. package/lib/telemetry/path.js +91 -0
  72. package/lib/telemetry/path.js.map +1 -0
  73. package/lib/telemetry/path.mjs +78 -0
  74. package/lib/telemetry/path.mjs.map +1 -0
  75. package/lib/types.d.mts +121 -0
  76. package/lib/types.d.ts +121 -0
  77. package/lib/types.js +17 -0
  78. package/lib/types.js.map +1 -0
  79. package/lib/types.mjs +1 -0
  80. package/lib/types.mjs.map +1 -0
  81. package/lib/utils.d.mts +57 -0
  82. package/lib/utils.d.ts +57 -0
  83. package/lib/utils.js +143 -0
  84. package/lib/utils.js.map +1 -0
  85. package/lib/utils.mjs +104 -0
  86. package/lib/utils.mjs.map +1 -0
  87. package/package.json +89 -0
  88. package/src/auth.ts +89 -0
  89. package/src/gcpLogger.ts +124 -0
  90. package/src/gcpOpenTelemetry.ts +485 -0
  91. package/src/index.ts +59 -0
  92. package/src/metrics.ts +122 -0
  93. package/src/model-armor.ts +317 -0
  94. package/src/telemetry/action.ts +106 -0
  95. package/src/telemetry/defaults.ts +72 -0
  96. package/src/telemetry/engagement.ts +120 -0
  97. package/src/telemetry/feature.ts +170 -0
  98. package/src/telemetry/generate.ts +454 -0
  99. package/src/telemetry/path.ts +111 -0
  100. package/src/types.ts +133 -0
  101. package/src/utils.ts +175 -0
  102. package/tests/logs_no_input_output_test.ts +267 -0
  103. package/tests/logs_session_test.ts +219 -0
  104. package/tests/logs_test.ts +633 -0
  105. package/tests/metrics_test.ts +792 -0
  106. package/tests/model_armor_test.ts +336 -0
  107. package/tests/traces_test.ts +380 -0
  108. package/typedoc.json +3 -0
@@ -0,0 +1,170 @@
1
+ /**
2
+ * Copyright 2024 Google LLC
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ */
16
+
17
+ import { ValueType } from '@opentelemetry/api';
18
+ import { hrTimeDuration, hrTimeToMilliseconds } from '@opentelemetry/core';
19
+ import type { ReadableSpan } from '@opentelemetry/sdk-trace-base';
20
+ import { GENKIT_VERSION, GenkitError } from 'genkit';
21
+ import { logger } from 'genkit/logging';
22
+ import { toDisplayPath } from 'genkit/tracing';
23
+ import {
24
+ MetricCounter,
25
+ MetricHistogram,
26
+ internalMetricNamespaceWrap,
27
+ type Telemetry,
28
+ } from '../metrics.js';
29
+ import {
30
+ createCommonLogAttributes,
31
+ extractErrorName,
32
+ truncate,
33
+ truncatePath,
34
+ } from '../utils.js';
35
+
36
+ class FeaturesTelemetry implements Telemetry {
37
+ /**
38
+ * Wraps the declared metrics in a Genkit-specific, internal namespace.
39
+ */
40
+ private _N = internalMetricNamespaceWrap.bind(null, 'feature');
41
+
42
+ private featureCounter = new MetricCounter(this._N('requests'), {
43
+ description: 'Counts calls to genkit features.',
44
+ valueType: ValueType.INT,
45
+ });
46
+
47
+ private featureLatencies = new MetricHistogram(this._N('latency'), {
48
+ description: 'Latencies when calling Genkit features.',
49
+ valueType: ValueType.DOUBLE,
50
+ unit: 'ms',
51
+ });
52
+
53
+ tick(
54
+ span: ReadableSpan,
55
+ logInputAndOutput: boolean,
56
+ projectId?: string
57
+ ): void {
58
+ const attributes = span.attributes;
59
+ const name = attributes['genkit:name'] as string;
60
+ const path = attributes['genkit:path'] as string;
61
+ const latencyMs = hrTimeToMilliseconds(
62
+ hrTimeDuration(span.startTime, span.endTime)
63
+ );
64
+ const isRoot = attributes['genkit:isRoot'] as boolean;
65
+ if (!isRoot) {
66
+ throw new GenkitError({
67
+ status: 'FAILED_PRECONDITION',
68
+ message: 'FeatureTelemetry tick called with non-root span.',
69
+ });
70
+ }
71
+ const state = attributes['genkit:state'] as string;
72
+
73
+ if (state === 'success') {
74
+ this.writeFeatureSuccess(name, latencyMs);
75
+ } else if (state === 'error') {
76
+ const errorName = extractErrorName(span.events) || '<unknown>';
77
+ this.writeFeatureFailure(name, latencyMs, errorName);
78
+ } else {
79
+ logger.warn(`Unknown state; ${state}`);
80
+ return;
81
+ }
82
+
83
+ if (logInputAndOutput) {
84
+ const input = truncate(attributes['genkit:input'] as string);
85
+ const output = truncate(attributes['genkit:output'] as string);
86
+ const sessionId = attributes['genkit:sessionId'] as string;
87
+ const threadName = attributes['genkit:threadName'] as string;
88
+
89
+ if (input) {
90
+ this.writeLog(
91
+ span,
92
+ 'Input',
93
+ name,
94
+ path,
95
+ input,
96
+ projectId,
97
+ sessionId,
98
+ threadName
99
+ );
100
+ }
101
+ if (output) {
102
+ this.writeLog(
103
+ span,
104
+ 'Output',
105
+ name,
106
+ path,
107
+ output,
108
+ projectId,
109
+ sessionId,
110
+ threadName
111
+ );
112
+ }
113
+ }
114
+ }
115
+
116
+ private writeFeatureSuccess(featureName: string, latencyMs: number) {
117
+ const dimensions = {
118
+ name: featureName,
119
+ status: 'success',
120
+ source: 'ts',
121
+ sourceVersion: GENKIT_VERSION,
122
+ };
123
+ this.featureCounter.add(1, dimensions);
124
+ this.featureLatencies.record(latencyMs, dimensions);
125
+ }
126
+
127
+ private writeFeatureFailure(
128
+ featureName: string,
129
+ latencyMs: number,
130
+ errorName: string
131
+ ) {
132
+ const dimensions = {
133
+ name: featureName,
134
+ status: 'failure',
135
+ source: 'ts',
136
+ sourceVersion: GENKIT_VERSION,
137
+ error: errorName,
138
+ };
139
+ this.featureCounter.add(1, dimensions);
140
+ this.featureLatencies.record(latencyMs, dimensions);
141
+ }
142
+
143
+ private writeLog(
144
+ span: ReadableSpan,
145
+ tag: string,
146
+ featureName: string,
147
+ qualifiedPath: string,
148
+ content: string,
149
+ projectId?: string,
150
+ sessionId?: string,
151
+ threadName?: string
152
+ ) {
153
+ const path = truncatePath(toDisplayPath(qualifiedPath));
154
+ const sharedMetadata = {
155
+ ...createCommonLogAttributes(span, projectId),
156
+ path,
157
+ qualifiedPath,
158
+ featureName,
159
+ sessionId,
160
+ threadName,
161
+ };
162
+ logger.logStructured(`${tag}[${path}, ${featureName}]`, {
163
+ ...sharedMetadata,
164
+ content,
165
+ });
166
+ }
167
+ }
168
+
169
+ const featuresTelemetry = new FeaturesTelemetry();
170
+ export { featuresTelemetry };
@@ -0,0 +1,454 @@
1
+ /**
2
+ * Copyright 2024 Google LLC
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ */
16
+
17
+ import { ValueType } from '@opentelemetry/api';
18
+ import type { ReadableSpan } from '@opentelemetry/sdk-trace-base';
19
+ import { createHash } from 'crypto';
20
+ import {
21
+ GENKIT_VERSION,
22
+ type GenerateRequestData,
23
+ type GenerateResponseData,
24
+ type GenerationUsage,
25
+ type MediaPart,
26
+ type Part,
27
+ type ToolRequestPart,
28
+ type ToolResponsePart,
29
+ } from 'genkit';
30
+ import { logger } from 'genkit/logging';
31
+ import { toDisplayPath } from 'genkit/tracing';
32
+ import {
33
+ MetricCounter,
34
+ MetricHistogram,
35
+ internalMetricNamespaceWrap,
36
+ type Telemetry,
37
+ } from '../metrics.js';
38
+ import {
39
+ createCommonLogAttributes,
40
+ extractErrorName,
41
+ extractOuterFeatureNameFromPath,
42
+ truncate,
43
+ truncatePath,
44
+ } from '../utils.js';
45
+
46
+ type SharedDimensions = {
47
+ modelName?: string;
48
+ featureName?: string;
49
+ path?: string;
50
+ status?: string;
51
+ source?: string;
52
+ sourceVersion?: string;
53
+ };
54
+
55
+ class GenerateTelemetry implements Telemetry {
56
+ /**
57
+ * Wraps the declared metrics in a Genkit-specific, internal namespace.
58
+ */
59
+ private _N = internalMetricNamespaceWrap.bind(null, 'ai');
60
+
61
+ private actionCounter = new MetricCounter(this._N('generate/requests'), {
62
+ description: 'Counts calls to genkit generate actions.',
63
+ valueType: ValueType.INT,
64
+ });
65
+
66
+ private latencies = new MetricHistogram(this._N('generate/latency'), {
67
+ description: 'Latencies when interacting with a Genkit model.',
68
+ valueType: ValueType.DOUBLE,
69
+ unit: 'ms',
70
+ });
71
+
72
+ private inputCharacters = new MetricCounter(
73
+ this._N('generate/input/characters'),
74
+ {
75
+ description: 'Counts input characters to any Genkit model.',
76
+ valueType: ValueType.INT,
77
+ }
78
+ );
79
+
80
+ private inputTokens = new MetricCounter(this._N('generate/input/tokens'), {
81
+ description: 'Counts input tokens to a Genkit model.',
82
+ valueType: ValueType.INT,
83
+ });
84
+
85
+ private inputImages = new MetricCounter(this._N('generate/input/images'), {
86
+ description: 'Counts input images to a Genkit model.',
87
+ valueType: ValueType.INT,
88
+ });
89
+
90
+ private outputCharacters = new MetricCounter(
91
+ this._N('generate/output/characters'),
92
+ {
93
+ description: 'Counts output characters from a Genkit model.',
94
+ valueType: ValueType.INT,
95
+ }
96
+ );
97
+
98
+ private outputTokens = new MetricCounter(this._N('generate/output/tokens'), {
99
+ description: 'Counts output tokens from a Genkit model.',
100
+ valueType: ValueType.INT,
101
+ });
102
+
103
+ private thinkingTokens = new MetricCounter(
104
+ this._N('generate/thinking/tokens'),
105
+ {
106
+ description: 'Counts thinking tokens from a Genkit model.',
107
+ valueType: ValueType.INT,
108
+ }
109
+ );
110
+
111
+ private outputImages = new MetricCounter(this._N('generate/output/images'), {
112
+ description: 'Count output images from a Genkit model.',
113
+ valueType: ValueType.INT,
114
+ });
115
+
116
+ tick(
117
+ span: ReadableSpan,
118
+ logInputAndOutput: boolean,
119
+ projectId?: string
120
+ ): void {
121
+ const attributes = span.attributes;
122
+ const modelName = truncate(attributes['genkit:name'] as string, 1024);
123
+ const path = (attributes['genkit:path'] as string) || '';
124
+ const input =
125
+ 'genkit:input' in attributes
126
+ ? (JSON.parse(
127
+ attributes['genkit:input']! as string
128
+ ) as GenerateRequestData)
129
+ : undefined;
130
+ const output =
131
+ 'genkit:output' in attributes
132
+ ? (JSON.parse(
133
+ attributes['genkit:output']! as string
134
+ ) as GenerateResponseData)
135
+ : undefined;
136
+
137
+ const errName = extractErrorName(span.events);
138
+ let featureName = truncate(
139
+ (attributes['genkit:metadata:flow:name'] ||
140
+ extractOuterFeatureNameFromPath(path)) as string
141
+ );
142
+ if (!featureName || featureName === '<unknown>') {
143
+ featureName = 'generate';
144
+ }
145
+
146
+ const sessionId = attributes['genkit:sessionId'] as string;
147
+ const threadName = attributes['genkit:threadName'] as string;
148
+
149
+ if (input) {
150
+ this.recordGenerateActionMetrics(modelName, featureName, path, {
151
+ response: output,
152
+ errName,
153
+ });
154
+ this.recordGenerateActionConfigLogs(
155
+ span,
156
+ modelName,
157
+ featureName,
158
+ path,
159
+ input,
160
+ projectId,
161
+ sessionId,
162
+ threadName
163
+ );
164
+
165
+ if (logInputAndOutput) {
166
+ this.recordGenerateActionInputLogs(
167
+ span,
168
+ modelName,
169
+ featureName,
170
+ path,
171
+ input,
172
+ projectId,
173
+ sessionId,
174
+ threadName
175
+ );
176
+ }
177
+ }
178
+
179
+ if (output && logInputAndOutput) {
180
+ this.recordGenerateActionOutputLogs(
181
+ span,
182
+ modelName,
183
+ featureName,
184
+ path,
185
+ output,
186
+ projectId,
187
+ sessionId,
188
+ threadName
189
+ );
190
+ }
191
+ }
192
+
193
+ private recordGenerateActionMetrics(
194
+ modelName: string,
195
+ featureName: string,
196
+ path: string,
197
+ opts: {
198
+ response?: GenerateResponseData;
199
+ errName?: string;
200
+ }
201
+ ) {
202
+ this.doRecordGenerateActionMetrics(modelName, opts.response?.usage || {}, {
203
+ featureName,
204
+ path,
205
+ latencyMs: opts.response?.latencyMs,
206
+ errName: opts.errName,
207
+ source: 'ts',
208
+ sourceVersion: GENKIT_VERSION,
209
+ });
210
+ }
211
+
212
+ private recordGenerateActionConfigLogs(
213
+ span: ReadableSpan,
214
+ model: string,
215
+ featureName: string,
216
+ qualifiedPath: string,
217
+ input: GenerateRequestData,
218
+ projectId?: string,
219
+ sessionId?: string,
220
+ threadName?: string
221
+ ) {
222
+ const path = truncatePath(toDisplayPath(qualifiedPath));
223
+ const sharedMetadata = {
224
+ ...createCommonLogAttributes(span, projectId),
225
+ model,
226
+ path,
227
+ qualifiedPath,
228
+ featureName,
229
+ sessionId,
230
+ threadName,
231
+ };
232
+ logger.logStructured(`Config[${path}, ${model}]`, {
233
+ ...sharedMetadata,
234
+ maxOutputTokens: input.config?.maxOutputTokens,
235
+ stopSequences: input.config?.stopSequences, // array
236
+ source: 'ts',
237
+ sourceVersion: GENKIT_VERSION,
238
+ });
239
+ }
240
+
241
+ private recordGenerateActionInputLogs(
242
+ span: ReadableSpan,
243
+ model: string,
244
+ featureName: string,
245
+ qualifiedPath: string,
246
+ input: GenerateRequestData,
247
+ projectId?: string,
248
+ sessionId?: string,
249
+ threadName?: string
250
+ ) {
251
+ const path = truncatePath(toDisplayPath(qualifiedPath));
252
+ const sharedMetadata = {
253
+ ...createCommonLogAttributes(span, projectId),
254
+ model,
255
+ path,
256
+ qualifiedPath,
257
+ featureName,
258
+ sessionId,
259
+ threadName,
260
+ };
261
+
262
+ const messages = input.messages.length;
263
+ input.messages.forEach((msg, msgIdx) => {
264
+ const parts = msg.content.length;
265
+ msg.content.forEach((part, partIdx) => {
266
+ const partCounts = this.toPartCounts(partIdx, parts, msgIdx, messages);
267
+ logger.logStructured(`Input[${path}, ${model}] ${partCounts}`, {
268
+ ...sharedMetadata,
269
+ content: this.toPartLogContent(part),
270
+ role: msg.role,
271
+ partIndex: partIdx,
272
+ totalParts: parts,
273
+ messageIndex: msgIdx,
274
+ totalMessages: messages,
275
+ });
276
+ });
277
+ });
278
+ }
279
+
280
+ private recordGenerateActionOutputLogs(
281
+ span: ReadableSpan,
282
+ model: string,
283
+ featureName: string,
284
+ qualifiedPath: string,
285
+ output: GenerateResponseData,
286
+ projectId?: string,
287
+ sessionId?: string,
288
+ threadName?: string
289
+ ) {
290
+ const path = truncatePath(toDisplayPath(qualifiedPath));
291
+ const sharedMetadata = {
292
+ ...createCommonLogAttributes(span, projectId),
293
+ model,
294
+ path,
295
+ qualifiedPath,
296
+ featureName,
297
+ sessionId,
298
+ threadName,
299
+ };
300
+ const message = output.message || output.candidates?.[0]?.message!;
301
+
302
+ if (message?.content) {
303
+ const parts = message.content.length;
304
+ message.content.forEach((part, partIdx) => {
305
+ const partCounts = this.toPartCounts(partIdx, parts, 0, 1);
306
+ const initial = output.finishMessage
307
+ ? { finishMessage: truncate(output.finishMessage) }
308
+ : {};
309
+ logger.logStructured(`Output[${path}, ${model}] ${partCounts}`, {
310
+ ...initial,
311
+ ...sharedMetadata,
312
+ content: this.toPartLogContent(part),
313
+ role: message.role,
314
+ partIndex: partIdx,
315
+ totalParts: parts,
316
+ candidateIndex: 0,
317
+ totalCandidates: 1,
318
+ messageIndex: 0,
319
+ finishReason: output.finishReason,
320
+ });
321
+ });
322
+ }
323
+ }
324
+
325
+ private toPartCounts(
326
+ partOrdinal: number,
327
+ parts: number,
328
+ msgOrdinal: number,
329
+ messages: number
330
+ ): string {
331
+ if (parts > 1 && messages > 1) {
332
+ return `(part ${this.xOfY(partOrdinal, parts)} in message ${this.xOfY(
333
+ msgOrdinal,
334
+ messages
335
+ )})`;
336
+ }
337
+ if (parts > 1) {
338
+ return `(part ${this.xOfY(partOrdinal, parts)})`;
339
+ }
340
+ if (messages > 1) {
341
+ return `(message ${this.xOfY(msgOrdinal, messages)})`;
342
+ }
343
+ return '';
344
+ }
345
+
346
+ private xOfY(x: number, y: number): string {
347
+ return `${x + 1} of ${y}`;
348
+ }
349
+
350
+ private toPartLogContent(part: Part): string {
351
+ if (part.text) {
352
+ return truncate(part.text);
353
+ }
354
+ if (part.data) {
355
+ return truncate(JSON.stringify(part.data));
356
+ }
357
+ if (part.media) {
358
+ return this.toPartLogMedia(part);
359
+ }
360
+ if (part.toolRequest) {
361
+ return this.toPartLogToolRequest(part);
362
+ }
363
+ if (part.toolResponse) {
364
+ return this.toPartLogToolResponse(part);
365
+ }
366
+ if (part.custom) {
367
+ return truncate(JSON.stringify(part.custom));
368
+ }
369
+ return '<unknown format>';
370
+ }
371
+
372
+ private toPartLogMedia(part: MediaPart): string {
373
+ if (part.media.url.startsWith('data:')) {
374
+ const splitIdx = part.media.url.indexOf('base64,');
375
+ if (splitIdx < 0) {
376
+ return '<unknown media format>';
377
+ }
378
+ const prefix = part.media.url.substring(0, splitIdx + 7);
379
+ const hashedContent = createHash('sha256')
380
+ .update(part.media.url.substring(splitIdx + 7))
381
+ .digest('hex');
382
+ return `${prefix}<sha256(${hashedContent})>`;
383
+ }
384
+ return truncate(part.media.url);
385
+ }
386
+
387
+ private toPartLogToolRequest(part: ToolRequestPart): string {
388
+ const inputText =
389
+ typeof part.toolRequest.input === 'string'
390
+ ? part.toolRequest.input
391
+ : JSON.stringify(part.toolRequest.input);
392
+ return truncate(
393
+ `Tool request: ${part.toolRequest.name}, ref: ${part.toolRequest.ref}, input: ${inputText}`
394
+ );
395
+ }
396
+
397
+ private toPartLogToolResponse(part: ToolResponsePart): string {
398
+ const outputText =
399
+ typeof part.toolResponse.output === 'string'
400
+ ? part.toolResponse.output
401
+ : JSON.stringify(part.toolResponse.output);
402
+ return truncate(
403
+ `Tool response: ${part.toolResponse.name}, ref: ${part.toolResponse.ref}, output: ${outputText}`
404
+ );
405
+ }
406
+
407
+ /**
408
+ * Records all metrics associated with performing a GenerateAction.
409
+ */
410
+ private doRecordGenerateActionMetrics(
411
+ modelName: string,
412
+ usage: GenerationUsage,
413
+ dimensions: {
414
+ featureName?: string;
415
+ path?: string;
416
+ latencyMs?: number;
417
+ errName?: string;
418
+ source?: string;
419
+ sourceVersion: string;
420
+ }
421
+ ) {
422
+ const shared: SharedDimensions = {
423
+ modelName: modelName,
424
+ featureName: dimensions.featureName,
425
+ path: dimensions.path,
426
+ source: dimensions.source,
427
+ sourceVersion: dimensions.sourceVersion,
428
+ status: dimensions.errName ? 'failure' : 'success',
429
+ };
430
+
431
+ this.actionCounter.add(1, {
432
+ error: dimensions.errName,
433
+ ...shared,
434
+ });
435
+
436
+ this.latencies.record(dimensions.latencyMs, shared);
437
+
438
+ // inputs
439
+ this.inputTokens.add(usage.inputTokens, shared);
440
+ this.inputCharacters.add(usage.inputCharacters, shared);
441
+ this.inputImages.add(usage.inputImages, shared);
442
+
443
+ // outputs
444
+ this.outputTokens.add(usage.outputTokens, shared);
445
+ this.outputCharacters.add(usage.outputCharacters, shared);
446
+ this.outputImages.add(usage.outputImages, shared);
447
+
448
+ // thoughts
449
+ this.thinkingTokens.add(usage.thoughtsTokens, shared);
450
+ }
451
+ }
452
+
453
+ const generateTelemetry = new GenerateTelemetry();
454
+ export { generateTelemetry };