Skip to content

Commit

Permalink
feat(js): truncate input/output to align with GCP limits (#1670)
Browse files Browse the repository at this point in the history
Truncate the input and output logs, as well as model names, paths, errors, stacks, and other bits to ensure that the logs size remains within the GCP limits.
  • Loading branch information
kmandrika authored Jan 29, 2025
1 parent 51ceb79 commit 94ea90c
Show file tree
Hide file tree
Showing 7 changed files with 268 additions and 37 deletions.
8 changes: 5 additions & 3 deletions js/plugins/google-cloud/src/telemetry/action.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ import {
createCommonLogAttributes,
extractErrorName,
extractOuterFeatureNameFromPath,
truncate,
truncatePath,
} from '../utils.js';

class ActionTelemetry implements Telemetry {
Expand Down Expand Up @@ -79,8 +81,8 @@ class ActionTelemetry implements Telemetry {
}

if (subtype === 'tool' && logInputAndOutput) {
const input = attributes['genkit:input'] as string;
const output = attributes['genkit:output'] as string;
const input = truncate(attributes['genkit:input'] as string);
const output = truncate(attributes['genkit:output'] as string);
const sessionId = attributes['genkit:sessionId'] as string;
const threadName = attributes['genkit:threadName'] as string;

Expand Down Expand Up @@ -159,7 +161,7 @@ class ActionTelemetry implements Telemetry {
sessionId?: string,
threadName?: string
) {
const path = toDisplayPath(qualifiedPath);
const path = truncatePath(toDisplayPath(qualifiedPath));
const sharedMetadata = {
...createCommonLogAttributes(span, projectId),
path,
Expand Down
6 changes: 4 additions & 2 deletions js/plugins/google-cloud/src/telemetry/engagement.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ import {
Telemetry,
internalMetricNamespaceWrap,
} from '../metrics.js';
import { createCommonLogAttributes } from '../utils.js';
import { createCommonLogAttributes, truncate } from '../utils.js';

class EngagementTelemetry implements Telemetry {
/**
Expand Down Expand Up @@ -81,7 +81,9 @@ class EngagementTelemetry implements Telemetry {
feedbackValue: attributes['genkit:metadata:feedbackValue'],
};
if (attributes['genkit:metadata:textFeedback']) {
metadata['textFeedback'] = attributes['genkit:metadata:textFeedback'];
metadata['textFeedback'] = truncate(
attributes['genkit:metadata:textFeedback'] as string
);
}
logger.logStructured(`UserFeedback[${name}]`, metadata);
}
Expand Down
13 changes: 9 additions & 4 deletions js/plugins/google-cloud/src/telemetry/feature.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,12 @@ import {
Telemetry,
internalMetricNamespaceWrap,
} from '../metrics.js';
import { createCommonLogAttributes, extractErrorName } from '../utils.js';
import {
createCommonLogAttributes,
extractErrorName,
truncate,
truncatePath,
} from '../utils.js';

class FeaturesTelemetry implements Telemetry {
/**
Expand Down Expand Up @@ -77,8 +82,8 @@ class FeaturesTelemetry implements Telemetry {
}

if (logInputAndOutput) {
const input = attributes['genkit:input'] as string;
const output = attributes['genkit:output'] as string;
const input = truncate(attributes['genkit:input'] as string);
const output = truncate(attributes['genkit:output'] as string);
const sessionId = attributes['genkit:sessionId'] as string;
const threadName = attributes['genkit:threadName'] as string;

Expand Down Expand Up @@ -146,7 +151,7 @@ class FeaturesTelemetry implements Telemetry {
sessionId?: string,
threadName?: string
) {
const path = toDisplayPath(qualifiedPath);
const path = truncatePath(toDisplayPath(qualifiedPath));
const sharedMetadata = {
...createCommonLogAttributes(span, projectId),
path,
Expand Down
42 changes: 19 additions & 23 deletions js/plugins/google-cloud/src/telemetry/generate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ import {
createCommonLogAttributes,
extractErrorName,
extractOuterFeatureNameFromPath,
truncate,
truncatePath,
} from '../utils.js';

type SharedDimensions = {
Expand All @@ -59,9 +61,6 @@ class GenerateTelemetry implements Telemetry {
*/
private _N = internalMetricNamespaceWrap.bind(null, 'ai');

/** The maximum length (in characters) of a logged prompt message. */
private MAX_LOG_CONTENT_CHARS = 128_000;

private actionCounter = new MetricCounter(this._N('generate/requests'), {
description: 'Counts calls to genkit generate actions.',
valueType: ValueType.INT,
Expand Down Expand Up @@ -136,7 +135,7 @@ class GenerateTelemetry implements Telemetry {
projectId?: string
): void {
const attributes = span.attributes;
const modelName = attributes['genkit:name'] as string;
const modelName = truncate(attributes['genkit:name'] as string, 1024);
const path = (attributes['genkit:path'] as string) || '';
const input =
'genkit:input' in attributes
Expand All @@ -152,8 +151,10 @@ class GenerateTelemetry implements Telemetry {
: undefined;

const errName = extractErrorName(span.events);
let featureName = (attributes['genkit:metadata:flow:name'] ||
extractOuterFeatureNameFromPath(path)) as string;
let featureName = truncate(
(attributes['genkit:metadata:flow:name'] ||
extractOuterFeatureNameFromPath(path)) as string
);
if (!featureName || featureName === '<unknown>') {
featureName = 'generate';
}
Expand All @@ -162,7 +163,7 @@ class GenerateTelemetry implements Telemetry {
const threadName = attributes['genkit:threadName'] as string;

if (input) {
this.recordGenerateActionMetrics(modelName, featureName, path, input, {
this.recordGenerateActionMetrics(modelName, featureName, path, {
response: output,
errName,
});
Expand Down Expand Up @@ -209,7 +210,6 @@ class GenerateTelemetry implements Telemetry {
modelName: string,
featureName: string,
path: string,
input: GenerateRequestData,
opts: {
response?: GenerateResponseData;
errName?: string;
Expand All @@ -235,7 +235,7 @@ class GenerateTelemetry implements Telemetry {
sessionId?: string,
threadName?: string
) {
const path = toDisplayPath(qualifiedPath);
const path = truncatePath(toDisplayPath(qualifiedPath));
const sharedMetadata = {
...createCommonLogAttributes(span, projectId),
model,
Expand All @@ -251,7 +251,7 @@ class GenerateTelemetry implements Telemetry {
topK: input.config?.topK,
topP: input.config?.topP,
maxOutputTokens: input.config?.maxOutputTokens,
stopSequences: input.config?.stopSequences,
stopSequences: truncate(input.config?.stopSequences, 1024),
source: 'ts',
sourceVersion: GENKIT_VERSION,
});
Expand All @@ -267,7 +267,7 @@ class GenerateTelemetry implements Telemetry {
sessionId?: string,
threadName?: string
) {
const path = toDisplayPath(qualifiedPath);
const path = truncatePath(toDisplayPath(qualifiedPath));
const sharedMetadata = {
...createCommonLogAttributes(span, projectId),
model,
Expand Down Expand Up @@ -305,7 +305,7 @@ class GenerateTelemetry implements Telemetry {
sessionId?: string,
threadName?: string
) {
const path = toDisplayPath(qualifiedPath);
const path = truncatePath(toDisplayPath(qualifiedPath));
const sharedMetadata = {
...createCommonLogAttributes(span, projectId),
model,
Expand All @@ -321,7 +321,7 @@ class GenerateTelemetry implements Telemetry {
message.content.forEach((part, partIdx) => {
const partCounts = this.toPartCounts(partIdx, parts, 0, 1);
const initial = output.finishMessage
? { finishMessage: this.toPartLogText(output.finishMessage) }
? { finishMessage: truncate(output.finishMessage) }
: {};
logger.logStructured(`Output[${path}, ${model}] ${partCounts}`, {
...initial,
Expand Down Expand Up @@ -364,10 +364,10 @@ class GenerateTelemetry implements Telemetry {

private toPartLogContent(part: Part): string {
if (part.text) {
return this.toPartLogText(part.text);
return truncate(part.text);
}
if (part.data) {
return this.toPartLogText(JSON.stringify(part.data));
return truncate(JSON.stringify(part.data));
}
if (part.media) {
return this.toPartLogMedia(part);
Expand All @@ -379,15 +379,11 @@ class GenerateTelemetry implements Telemetry {
return this.toPartLogToolResponse(part);
}
if (part.custom) {
return this.toPartLogText(JSON.stringify(part.custom));
return truncate(JSON.stringify(part.custom));
}
return '<unknown format>';
}

private toPartLogText(text: string): string {
return text.substring(0, this.MAX_LOG_CONTENT_CHARS);
}

private toPartLogMedia(part: MediaPart): string {
if (part.media.url.startsWith('data:')) {
const splitIdx = part.media.url.indexOf('base64,');
Expand All @@ -400,15 +396,15 @@ class GenerateTelemetry implements Telemetry {
.digest('hex');
return `${prefix}<sha256(${hashedContent})>`;
}
return this.toPartLogText(part.media.url);
return truncate(part.media.url);
}

private toPartLogToolRequest(part: ToolRequestPart): string {
const inputText =
typeof part.toolRequest.input === 'string'
? part.toolRequest.input
: JSON.stringify(part.toolRequest.input);
return this.toPartLogText(
return truncate(
`Tool request: ${part.toolRequest.name}, ref: ${part.toolRequest.ref}, input: ${inputText}`
);
}
Expand All @@ -418,7 +414,7 @@ class GenerateTelemetry implements Telemetry {
typeof part.toolResponse.output === 'string'
? part.toolResponse.output
: JSON.stringify(part.toolResponse.output);
return this.toPartLogText(
return truncate(
`Tool response: ${part.toolResponse.name}, ref: ${part.toolResponse.ref}, output: ${outputText}`
);
}
Expand Down
5 changes: 3 additions & 2 deletions js/plugins/google-cloud/src/telemetry/path.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import {
extractErrorMessage,
extractErrorName,
extractErrorStack,
truncatePath,
} from '../utils.js';

class PathsTelemetry implements Telemetry {
Expand Down Expand Up @@ -123,7 +124,7 @@ class PathsTelemetry implements Telemetry {
sessionId?: string,
threadName?: string
) {
const displayPath = toDisplayPath(path);
const displayPath = truncatePath(toDisplayPath(path));
logger.logStructuredError(`Error[${displayPath}, ${errorName}]`, {
...createCommonLogAttributes(span, projectId),
path: displayPath,
Expand Down Expand Up @@ -207,7 +208,7 @@ class PathsTelemetry implements Telemetry {
flowName: featureName,
sessionId,
threadName,
paths: flowPaths.map((p) => toDisplayPath(p.path)),
paths: flowPaths.map((p) => truncatePath(toDisplayPath(p.path))),
});

flowPaths.forEach((p) => this.writePathMetric(featureName, p));
Expand Down
28 changes: 25 additions & 3 deletions js/plugins/google-cloud/src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,17 @@ import { TraceFlags } from '@opentelemetry/api';
import { ReadableSpan, TimedEvent } from '@opentelemetry/sdk-trace-base';
import { resolveCurrentPrincipal } from './auth.js';

/**
* The maximum length (in characters) of a logged input or output.
* This limit exists to align the logs with GCP logging size limits.
* */
const MAX_LOG_CONTENT_CHARS = 128_000;

/**
* The maximum length (in characters) of a flow path.
*/
const MAX_PATH_CHARS = 4096;

export function extractOuterFlowNameFromPath(path: string) {
if (!path || path === '<unknown>') {
return '<unknown>';
Expand All @@ -27,6 +38,17 @@ export function extractOuterFlowNameFromPath(path: string) {
return flowName ? flowName[1] : '<unknown>';
}

export function truncate(
text: string,
limit: number = MAX_LOG_CONTENT_CHARS
): string {
return text ? text.substring(0, limit) : text;
}

export function truncatePath(path: string) {
return truncate(path, MAX_PATH_CHARS);
}

/**
* Extract first feature name from a path
* e.g. for /{myFlow,t:flow}/{myStep,t:flowStep}/{googleai/gemini-pro,t:action,s:model}
Expand All @@ -47,7 +69,7 @@ export function extractErrorName(events: TimedEvent[]): string | undefined {
.map((event) => {
const attributes = event.attributes;
return attributes
? (attributes['exception.type'] as string)
? truncate(attributes['exception.type'] as string, 1024)
: '<unknown>';
})
.at(0);
Expand All @@ -59,7 +81,7 @@ export function extractErrorMessage(events: TimedEvent[]): string | undefined {
.map((event) => {
const attributes = event.attributes;
return attributes
? (attributes['exception.message'] as string)
? truncate(attributes['exception.message'] as string, 4096)
: '<unknown>';
})
.at(0);
Expand All @@ -71,7 +93,7 @@ export function extractErrorStack(events: TimedEvent[]): string | undefined {
.map((event) => {
const attributes = event.attributes;
return attributes
? (attributes['exception.stacktrace'] as string)
? truncate(attributes['exception.stacktrace'] as string, 32_768)
: '<unknown>';
})
.at(0);
Expand Down
Loading

0 comments on commit 94ea90c

Please sign in to comment.