google-logging-utils-internal 1.1.3
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/package/LICENSE +203 -0
- package/package/README.md +83 -0
- package/package/lib/auth.d.mts +33 -0
- package/package/lib/auth.d.ts +33 -0
- package/package/lib/auth.js +70 -0
- package/package/lib/auth.js.map +1 -0
- package/package/lib/auth.mjs +45 -0
- package/package/lib/auth.mjs.map +1 -0
- package/package/lib/gcpLogger.d.mts +25 -0
- package/package/lib/gcpLogger.d.ts +25 -0
- package/package/lib/gcpLogger.js +118 -0
- package/package/lib/gcpLogger.js.map +1 -0
- package/package/lib/gcpLogger.mjs +82 -0
- package/package/lib/gcpLogger.mjs.map +1 -0
- package/package/lib/gcpOpenTelemetry.d.mts +59 -0
- package/package/lib/gcpOpenTelemetry.d.ts +59 -0
- package/package/lib/gcpOpenTelemetry.js +374 -0
- package/package/lib/gcpOpenTelemetry.js.map +1 -0
- package/package/lib/gcpOpenTelemetry.mjs +364 -0
- package/package/lib/gcpOpenTelemetry.mjs.map +1 -0
- package/package/lib/index.d.mts +36 -0
- package/package/lib/index.d.ts +36 -0
- package/package/lib/index.js +56 -0
- package/package/lib/index.js.map +1 -0
- package/package/lib/index.mjs +29 -0
- package/package/lib/index.mjs.map +1 -0
- package/package/lib/metrics.d.mts +65 -0
- package/package/lib/metrics.d.ts +65 -0
- package/package/lib/metrics.js +91 -0
- package/package/lib/metrics.js.map +1 -0
- package/package/lib/metrics.mjs +65 -0
- package/package/lib/metrics.mjs.map +1 -0
- package/package/lib/model-armor.d.mts +59 -0
- package/package/lib/model-armor.d.ts +59 -0
- package/package/lib/model-armor.js +205 -0
- package/package/lib/model-armor.js.map +1 -0
- package/package/lib/model-armor.mjs +181 -0
- package/package/lib/model-armor.mjs.map +1 -0
- package/package/lib/telemetry/action.d.mts +27 -0
- package/package/lib/telemetry/action.d.ts +27 -0
- package/package/lib/telemetry/action.js +92 -0
- package/package/lib/telemetry/action.js.map +1 -0
- package/package/lib/telemetry/action.mjs +73 -0
- package/package/lib/telemetry/action.mjs.map +1 -0
- package/package/lib/telemetry/defaults.d.mts +30 -0
- package/package/lib/telemetry/defaults.d.ts +30 -0
- package/package/lib/telemetry/defaults.js +70 -0
- package/package/lib/telemetry/defaults.js.map +1 -0
- package/package/lib/telemetry/defaults.mjs +46 -0
- package/package/lib/telemetry/defaults.mjs.map +1 -0
- package/package/lib/telemetry/engagement.d.mts +35 -0
- package/package/lib/telemetry/engagement.d.ts +35 -0
- package/package/lib/telemetry/engagement.js +106 -0
- package/package/lib/telemetry/engagement.js.map +1 -0
- package/package/lib/telemetry/engagement.mjs +85 -0
- package/package/lib/telemetry/engagement.mjs.map +1 -0
- package/package/lib/telemetry/feature.d.mts +35 -0
- package/package/lib/telemetry/feature.d.ts +35 -0
- package/package/lib/telemetry/feature.js +142 -0
- package/package/lib/telemetry/feature.js.map +1 -0
- package/package/lib/telemetry/feature.mjs +127 -0
- package/package/lib/telemetry/feature.mjs.map +1 -0
- package/package/lib/telemetry/generate.d.mts +53 -0
- package/package/lib/telemetry/generate.d.ts +53 -0
- package/package/lib/telemetry/generate.js +326 -0
- package/package/lib/telemetry/generate.js.map +1 -0
- package/package/lib/telemetry/generate.mjs +314 -0
- package/package/lib/telemetry/generate.mjs.map +1 -0
- package/package/lib/telemetry/path.d.mts +32 -0
- package/package/lib/telemetry/path.d.ts +32 -0
- package/package/lib/telemetry/path.js +91 -0
- package/package/lib/telemetry/path.js.map +1 -0
- package/package/lib/telemetry/path.mjs +78 -0
- package/package/lib/telemetry/path.mjs.map +1 -0
- package/package/lib/types.d.mts +121 -0
- package/package/lib/types.d.ts +121 -0
- package/package/lib/types.js +17 -0
- package/package/lib/types.js.map +1 -0
- package/package/lib/types.mjs +1 -0
- package/package/lib/types.mjs.map +1 -0
- package/package/lib/utils.d.mts +57 -0
- package/package/lib/utils.d.ts +57 -0
- package/package/lib/utils.js +143 -0
- package/package/lib/utils.js.map +1 -0
- package/package/lib/utils.mjs +104 -0
- package/package/lib/utils.mjs.map +1 -0
- package/package/package.json +90 -0
- package/package/src/auth.ts +89 -0
- package/package/src/gcpLogger.ts +124 -0
- package/package/src/gcpOpenTelemetry.ts +485 -0
- package/package/src/index.ts +59 -0
- package/package/src/metrics.ts +122 -0
- package/package/src/model-armor.ts +317 -0
- package/package/src/telemetry/action.ts +106 -0
- package/package/src/telemetry/defaults.ts +72 -0
- package/package/src/telemetry/engagement.ts +120 -0
- package/package/src/telemetry/feature.ts +170 -0
- package/package/src/telemetry/generate.ts +454 -0
- package/package/src/telemetry/path.ts +111 -0
- package/package/src/types.ts +133 -0
- package/package/src/utils.ts +175 -0
- package/package/tests/logs_no_input_output_test.ts +267 -0
- package/package/tests/logs_session_test.ts +219 -0
- package/package/tests/logs_test.ts +633 -0
- package/package/tests/metrics_test.ts +792 -0
- package/package/tests/model_armor_test.ts +336 -0
- package/package/tests/traces_test.ts +380 -0
- package/package/typedoc.json +3 -0
- package/package.json +10 -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 };
|