UNPKG

@genkit-ai/core

Version:

Genkit AI framework core libraries.

1 lines 7.89 kB
{"version":3,"sources":["../../src/tracing/localFileTraceStore.ts"],"sourcesContent":["/**\n * Copyright 2024 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { Mutex } from 'async-mutex';\nimport crypto from 'crypto';\nimport fs from 'fs';\nimport os from 'os';\nimport path from 'path';\nimport { logger } from '../logging.js';\nimport {\n TraceData,\n TraceDataSchema,\n TraceQuery,\n TraceQueryResponse,\n TraceStore,\n} from './types.js';\n\n/**\n * Implementation of trace store that persists traces on local disk.\n */\nexport class LocalFileTraceStore implements TraceStore {\n private readonly storeRoot;\n private mutexes: Record<string, Mutex> = {};\n private filters: Record<string, string>;\n\n static defaultFilters: Record<string, string> = {\n // Prevent prompt rendering from spamming local trace store\n 'genkit:metadata:subtype': 'prompt',\n };\n\n constructor(filters = LocalFileTraceStore.defaultFilters) {\n const rootHash = crypto\n .createHash('md5')\n .update(require?.main?.filename || 'unknown')\n .digest('hex');\n this.storeRoot = path.resolve(os.tmpdir(), `.genkit/${rootHash}/traces`);\n fs.mkdirSync(this.storeRoot, { recursive: true });\n logger.info(\n `Initialized local file trace store at root: ${this.storeRoot}`\n );\n this.filters = filters;\n }\n\n async load(id: string): Promise<TraceData | undefined> {\n const filePath = path.resolve(this.storeRoot, `${id}`);\n if (!fs.existsSync(filePath)) {\n return undefined;\n }\n const data = fs.readFileSync(filePath, 'utf8');\n const parsed = JSON.parse(data);\n // For backwards compatibility, new field.\n if (!parsed.traceId) {\n parsed.traceId = id;\n }\n return TraceDataSchema.parse(parsed);\n }\n\n getMutex(id: string): Mutex {\n if (!this.mutexes[id]) {\n this.mutexes[id] = new Mutex();\n }\n return this.mutexes[id];\n }\n\n async save(id: string, rawTrace: TraceData): Promise<void> {\n let trace = this.filter(rawTrace);\n if (Object.keys(trace.spans).length === 0) {\n return;\n }\n const mutex = this.getMutex(id);\n await mutex.waitForUnlock();\n const release = await mutex.acquire();\n try {\n const existing = await this.load(id);\n if (existing) {\n Object.keys(trace.spans).forEach(\n (spanId) => (existing.spans[spanId] = trace.spans[spanId])\n );\n existing.displayName = trace.displayName;\n existing.startTime = trace.startTime;\n existing.endTime = trace.endTime;\n trace = existing;\n }\n fs.writeFileSync(\n path.resolve(this.storeRoot, `${id}`),\n JSON.stringify(trace)\n );\n } finally {\n release();\n }\n }\n\n async list(query?: TraceQuery): Promise<TraceQueryResponse> {\n const files = fs.readdirSync(this.storeRoot);\n files.sort((a, b) => {\n return (\n fs.statSync(path.resolve(this.storeRoot, `${b}`)).mtime.getTime() -\n fs.statSync(path.resolve(this.storeRoot, `${a}`)).mtime.getTime()\n );\n });\n const startFrom = query?.continuationToken\n ? parseInt(query?.continuationToken)\n : 0;\n const stopAt = startFrom + (query?.limit || 10);\n const traces = files.slice(startFrom, stopAt).map((id) => {\n const filePath = path.resolve(this.storeRoot, `${id}`);\n const data = fs.readFileSync(filePath, 'utf8');\n const parsed = JSON.parse(data);\n // For backwards compatibility, new field.\n if (!parsed.traceId) {\n parsed.traceId = id;\n }\n return TraceDataSchema.parse(parsed);\n });\n return {\n traces,\n continuationToken: files.length > stopAt ? stopAt.toString() : undefined,\n };\n }\n\n private filter(trace: TraceData): TraceData {\n // Delete any spans that match the filter criteria\n Object.keys(trace.spans).forEach((spanId) => {\n const span = trace.spans[spanId];\n Object.keys(this.filters).forEach((f) => {\n if (span.attributes[f] === this.filters[f]) {\n delete trace.spans[spanId];\n }\n });\n });\n // Delete the root wrapper if it's the only span left\n if (Object.keys(trace.spans).length === 1) {\n Object.keys(trace.spans).forEach((spanId) => {\n const span = trace.spans[spanId];\n if (span.attributes['genkit:name'] === 'dev-run-action-wrapper') {\n delete trace.spans[spanId];\n }\n });\n }\n return trace;\n }\n}\n"],"mappings":"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","names":[]}