UNPKG

@genkit-ai/google-cloud

Version:

Genkit AI framework plugin for Google Cloud Platform including Firestore trace/state store and deployment helpers for Cloud Functions for Firebase.

314 lines 10.1 kB
import { ValueType } from "@opentelemetry/api"; import { createHash } from "crypto"; import { GENKIT_VERSION } from "genkit"; import { logger } from "genkit/logging"; import { toDisplayPath } from "genkit/tracing"; import { MetricCounter, MetricHistogram, internalMetricNamespaceWrap } from "../metrics.js"; import { createCommonLogAttributes, extractErrorName, extractOuterFeatureNameFromPath, truncate, truncatePath } from "../utils.js"; class GenerateTelemetry { /** * Wraps the declared metrics in a Genkit-specific, internal namespace. */ _N = internalMetricNamespaceWrap.bind(null, "ai"); actionCounter = new MetricCounter(this._N("generate/requests"), { description: "Counts calls to genkit generate actions.", valueType: ValueType.INT }); latencies = new MetricHistogram(this._N("generate/latency"), { description: "Latencies when interacting with a Genkit model.", valueType: ValueType.DOUBLE, unit: "ms" }); inputCharacters = new MetricCounter( this._N("generate/input/characters"), { description: "Counts input characters to any Genkit model.", valueType: ValueType.INT } ); inputTokens = new MetricCounter(this._N("generate/input/tokens"), { description: "Counts input tokens to a Genkit model.", valueType: ValueType.INT }); inputImages = new MetricCounter(this._N("generate/input/images"), { description: "Counts input images to a Genkit model.", valueType: ValueType.INT }); outputCharacters = new MetricCounter( this._N("generate/output/characters"), { description: "Counts output characters from a Genkit model.", valueType: ValueType.INT } ); outputTokens = new MetricCounter(this._N("generate/output/tokens"), { description: "Counts output tokens from a Genkit model.", valueType: ValueType.INT }); thinkingTokens = new MetricCounter( this._N("generate/thinking/tokens"), { description: "Counts thinking tokens from a Genkit model.", valueType: ValueType.INT } ); outputImages = new MetricCounter(this._N("generate/output/images"), { description: "Count output images from a Genkit model.", valueType: ValueType.INT }); tick(span, logInputAndOutput, projectId) { const attributes = span.attributes; const modelName = truncate(attributes["genkit:name"], 1024); const path = attributes["genkit:path"] || ""; const input = "genkit:input" in attributes ? JSON.parse( attributes["genkit:input"] ) : void 0; const output = "genkit:output" in attributes ? JSON.parse( attributes["genkit:output"] ) : void 0; const errName = extractErrorName(span.events); let featureName = truncate( attributes["genkit:metadata:flow:name"] || extractOuterFeatureNameFromPath(path) ); if (!featureName || featureName === "<unknown>") { featureName = "generate"; } const sessionId = attributes["genkit:sessionId"]; const threadName = attributes["genkit:threadName"]; if (input) { this.recordGenerateActionMetrics(modelName, featureName, path, { response: output, errName }); this.recordGenerateActionConfigLogs( span, modelName, featureName, path, input, projectId, sessionId, threadName ); if (logInputAndOutput) { this.recordGenerateActionInputLogs( span, modelName, featureName, path, input, projectId, sessionId, threadName ); } } if (output && logInputAndOutput) { this.recordGenerateActionOutputLogs( span, modelName, featureName, path, output, projectId, sessionId, threadName ); } } recordGenerateActionMetrics(modelName, featureName, path, opts) { this.doRecordGenerateActionMetrics(modelName, opts.response?.usage || {}, { featureName, path, latencyMs: opts.response?.latencyMs, errName: opts.errName, source: "ts", sourceVersion: GENKIT_VERSION }); } recordGenerateActionConfigLogs(span, model, featureName, qualifiedPath, input, projectId, sessionId, threadName) { const path = truncatePath(toDisplayPath(qualifiedPath)); const sharedMetadata = { ...createCommonLogAttributes(span, projectId), model, path, qualifiedPath, featureName, sessionId, threadName }; logger.logStructured(`Config[${path}, ${model}]`, { ...sharedMetadata, maxOutputTokens: input.config?.maxOutputTokens, stopSequences: input.config?.stopSequences, // array source: "ts", sourceVersion: GENKIT_VERSION }); } recordGenerateActionInputLogs(span, model, featureName, qualifiedPath, input, projectId, sessionId, threadName) { const path = truncatePath(toDisplayPath(qualifiedPath)); const sharedMetadata = { ...createCommonLogAttributes(span, projectId), model, path, qualifiedPath, featureName, sessionId, threadName }; const messages = input.messages.length; input.messages.forEach((msg, msgIdx) => { const parts = msg.content.length; msg.content.forEach((part, partIdx) => { const partCounts = this.toPartCounts(partIdx, parts, msgIdx, messages); logger.logStructured(`Input[${path}, ${model}] ${partCounts}`, { ...sharedMetadata, content: this.toPartLogContent(part), role: msg.role, partIndex: partIdx, totalParts: parts, messageIndex: msgIdx, totalMessages: messages }); }); }); } recordGenerateActionOutputLogs(span, model, featureName, qualifiedPath, output, projectId, sessionId, threadName) { const path = truncatePath(toDisplayPath(qualifiedPath)); const sharedMetadata = { ...createCommonLogAttributes(span, projectId), model, path, qualifiedPath, featureName, sessionId, threadName }; const message = output.message || output.candidates?.[0]?.message; if (message?.content) { const parts = message.content.length; message.content.forEach((part, partIdx) => { const partCounts = this.toPartCounts(partIdx, parts, 0, 1); const initial = output.finishMessage ? { finishMessage: truncate(output.finishMessage) } : {}; logger.logStructured(`Output[${path}, ${model}] ${partCounts}`, { ...initial, ...sharedMetadata, content: this.toPartLogContent(part), role: message.role, partIndex: partIdx, totalParts: parts, candidateIndex: 0, totalCandidates: 1, messageIndex: 0, finishReason: output.finishReason }); }); } } toPartCounts(partOrdinal, parts, msgOrdinal, messages) { if (parts > 1 && messages > 1) { return `(part ${this.xOfY(partOrdinal, parts)} in message ${this.xOfY( msgOrdinal, messages )})`; } if (parts > 1) { return `(part ${this.xOfY(partOrdinal, parts)})`; } if (messages > 1) { return `(message ${this.xOfY(msgOrdinal, messages)})`; } return ""; } xOfY(x, y) { return `${x + 1} of ${y}`; } toPartLogContent(part) { if (part.text) { return truncate(part.text); } if (part.data) { return truncate(JSON.stringify(part.data)); } if (part.media) { return this.toPartLogMedia(part); } if (part.toolRequest) { return this.toPartLogToolRequest(part); } if (part.toolResponse) { return this.toPartLogToolResponse(part); } if (part.custom) { return truncate(JSON.stringify(part.custom)); } return "<unknown format>"; } toPartLogMedia(part) { if (part.media.url.startsWith("data:")) { const splitIdx = part.media.url.indexOf("base64,"); if (splitIdx < 0) { return "<unknown media format>"; } const prefix = part.media.url.substring(0, splitIdx + 7); const hashedContent = createHash("sha256").update(part.media.url.substring(splitIdx + 7)).digest("hex"); return `${prefix}<sha256(${hashedContent})>`; } return truncate(part.media.url); } toPartLogToolRequest(part) { const inputText = typeof part.toolRequest.input === "string" ? part.toolRequest.input : JSON.stringify(part.toolRequest.input); return truncate( `Tool request: ${part.toolRequest.name}, ref: ${part.toolRequest.ref}, input: ${inputText}` ); } toPartLogToolResponse(part) { const outputText = typeof part.toolResponse.output === "string" ? part.toolResponse.output : JSON.stringify(part.toolResponse.output); return truncate( `Tool response: ${part.toolResponse.name}, ref: ${part.toolResponse.ref}, output: ${outputText}` ); } /** * Records all metrics associated with performing a GenerateAction. */ doRecordGenerateActionMetrics(modelName, usage, dimensions) { const shared = { modelName, featureName: dimensions.featureName, path: dimensions.path, source: dimensions.source, sourceVersion: dimensions.sourceVersion, status: dimensions.errName ? "failure" : "success" }; this.actionCounter.add(1, { error: dimensions.errName, ...shared }); this.latencies.record(dimensions.latencyMs, shared); this.inputTokens.add(usage.inputTokens, shared); this.inputCharacters.add(usage.inputCharacters, shared); this.inputImages.add(usage.inputImages, shared); this.outputTokens.add(usage.outputTokens, shared); this.outputCharacters.add(usage.outputCharacters, shared); this.outputImages.add(usage.outputImages, shared); this.thinkingTokens.add(usage.thoughtsTokens, shared); } } const generateTelemetry = new GenerateTelemetry(); export { generateTelemetry }; //# sourceMappingURL=generate.mjs.map