@lmnr-ai/lmnr 0.6.1 → 0.6.2
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/dist/cli.js +1 -1
- package/dist/cli.js.map +1 -1
- package/dist/cli.mjs +1 -1
- package/dist/cli.mjs.map +1 -1
- package/dist/index.d.mts +2 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +5 -2
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +5 -2
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
package/dist/cli.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/cli.ts","../package.json","../src/utils.ts"],"sourcesContent":["#!/usr/bin/env node\n\nimport { context, propagation, trace } from \"@opentelemetry/api\";\nimport { ArgumentParser } from \"argparse\";\nimport * as esbuild from \"esbuild\";\nimport * as glob from \"glob\";\n\nimport { version } from \"../package.json\";\nimport { Evaluation } from \"./evaluations\";\nimport { initializeLogger } from \"./utils\";\n\nconst logger = initializeLogger();\n\ndeclare global {\n // eslint-disable-next-line no-var\n var _evaluation: Evaluation<any, any, any> | undefined;\n // eslint-disable-next-line no-var\n var _set_global_evaluation: boolean;\n}\n\nexport function loadModule({\n moduleText,\n}: {\n filename: string;\n moduleText: string;\n}): Evaluation<any, any, any> {\n globalThis._evaluation = undefined;\n globalThis._set_global_evaluation = true;\n\n // it needs \"require\" to be passed in\n // eslint-disable-next-line\n new Function(\"require\", moduleText)(require);\n\n // Return the modified _evals global variable\n return globalThis._evaluation!;\n}\n\nasync function cli() {\n const [, , ...args] = process.argv;\n\n // Use argparse, which is the port of the python library\n const parser = new ArgumentParser({\n prog: \"lmnr\",\n description: \"CLI for Laminar. Use `lmnr <subcommand> --help` for more information.\",\n });\n\n parser.add_argument(\"-v\", \"--version\", { action: \"version\", version });\n\n const subparsers = parser.add_subparsers({\n title: \"subcommands\",\n dest: \"subcommand\",\n });\n\n const parserEval = subparsers.add_parser(\"eval\", {\n description: \"Run an evaluation\",\n help: \"Run an evaluation\",\n });\n\n parserEval.add_argument(\"file\", {\n help: \"A file containing the evaluation to run. If no file is provided, \" +\n \"the evaluation will run all `*.eval.ts|js` files in the `evals` directory.\",\n nargs: \"?\",\n });\n\n parserEval.add_argument(\"--fail-on-error\", {\n help: \"Fail on error. If specified, will fail if encounters a file that cannot be run\",\n action: \"store_true\",\n });\n\n parserEval.set_defaults({\n func: async (args: any) => {\n const files = args.file\n ? [args.file]\n : glob.sync('evals/**/*.eval.{ts,js}');\n\n files.sort();\n\n if (files.length === 0) {\n logger.error(\"No evaluation files found. Please provide a file or \" +\n \"ensure there are eval files that are named like `*.eval.{ts,js}` in\" +\n \"the `evals` directory or its subdirectories.\");\n process.exit(1);\n }\n\n if (!args.file) {\n logger.info(`Located ${files.length} evaluation files in evals/`);\n }\n\n for (const file of files) {\n logger.info(`Loading ${file}...`);\n // TODO: Add various optimizations, e.g. minify, pure, tree shaking, etc.\n const buildOptions = {\n entryPoints: [file],\n outfile: `tmp_out_${file}.js`,\n write: false, // will be loaded in memory as a temp file\n platform: \"node\" as esbuild.Platform,\n bundle: true,\n external: [\"node_modules/*\"],\n };\n\n const result = await esbuild.build(buildOptions);\n\n if (!result.outputFiles) {\n logger.error(\"Error when building: No output files found \" +\n \"it is likely that all eval files are not valid TypeScript or JavaScript files.\");\n if (args.fail_on_error) {\n process.exit(1);\n }\n continue;\n }\n\n const outputFileText = result.outputFiles[0].text;\n\n const evaluation = loadModule({\n filename: args.file,\n moduleText: outputFileText,\n });\n\n if (!evaluation?.run) {\n logger.error(`Evaluation ${file} does not properly call evaluate()`);\n if (args.fail_on_error) {\n process.exit(1);\n }\n continue;\n }\n\n await evaluation.run();\n\n // FIXME: Now every evaluation file creates a new tracer provider.\n // Attempt to re-initialize it in the same process breaks it.\n // For now, we disable all APIs after running each file, but ideally\n // we should keep a global tracer provider that is initialized once\n // here in the CLI.\n context.disable();\n trace.disable();\n propagation.disable();\n }\n },\n });\n\n parser.set_defaults({\n func: () => {\n parser.print_help();\n process.exit(0);\n },\n });\n\n const parsed = parser.parse_args(args);\n await parsed.func(parsed);\n}\n\ncli().catch((err) => {\n logger.error(err);\n process.exit(1);\n});\n","{\n \"name\": \"@lmnr-ai/lmnr\",\n \"version\": \"0.6.1\",\n \"description\": \"TypeScript SDK for Laminar AI\",\n \"main\": \"dist/index.js\",\n \"types\": \"dist/index.d.ts\",\n \"scripts\": {\n \"build\": \"tsup\",\n \"test\": \"tsx --test test/*.test.ts\",\n \"lint\": \"eslint\",\n \"lint:fix\": \"eslint --fix\"\n },\n \"files\": [\n \"dist\",\n \"assets\"\n ],\n \"bin\": {\n \"lmnr\": \"./dist/cli.js\"\n },\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"git+https://github.com/lmnr-ai/lmnr-ts.git\"\n },\n \"keywords\": [\n \"laminar\",\n \"lmnr\",\n \"sdk\",\n \"lmnr.ai\"\n ],\n \"author\": \"founders@lmnr.ai\",\n \"license\": \"Apache-2.0\",\n \"bugs\": {\n \"url\": \"https://github.com/lmnr-ai/lmnr-ts/issues\"\n },\n \"homepage\": \"https://github.com/lmnr-ai/lmnr-ts#README\",\n \"devDependencies\": {\n \"@anthropic-ai/sdk\": \"^0.39.0\",\n \"@aws-sdk/client-bedrock-runtime\": \"^3.806.0\",\n \"@azure/openai\": \"^2.0.0\",\n \"@browserbasehq/stagehand\": \"^1.14.0\",\n \"@eslint/eslintrc\": \"^3.3.1\",\n \"@eslint/js\": \"^9.26.0\",\n \"@google-cloud/aiplatform\": \"^4.1.0\",\n \"@google-cloud/vertexai\": \"^1.10.0\",\n \"@langchain/core\": \"^0.3.55\",\n \"@pinecone-database/pinecone\": \"^5.1.2\",\n \"@playwright/test\": \"^1.52.0\",\n \"@qdrant/js-client-rest\": \"^1.14.0\",\n \"@stylistic/eslint-plugin-js\": \"^4.2.0\",\n \"@types/argparse\": \"^2.0.17\",\n \"@types/cli-progress\": \"^3.11.6\",\n \"@types/node\": \"^22.15.17\",\n \"@types/semver\": \"^7.7.0\",\n \"@types/uuid\": \"^10.0.0\",\n \"chromadb\": \"^2.4.3\",\n \"cohere-ai\": \"^7.17.1\",\n \"eslint-plugin-simple-import-sort\": \"^12.1.1\",\n \"eslint-plugin-unused-imports\": \"^4.1.4\",\n \"langchain\": \"^0.3.24\",\n \"llamaindex\": \"^0.9.19\",\n \"nock\": \"^14.0.4\",\n \"openai\": \"^4.98.0\",\n \"playwright\": \"^1.52.0\",\n \"puppeteer\": \"^24.8.2\",\n \"puppeteer-core\": \"^24.8.2\",\n \"together-ai\": \"^0.14.0\",\n \"tsup\": \"^8.4.0\",\n \"tsx\": \"^4.19.4\",\n \"typescript\": \"^5.8.3\",\n \"typescript-eslint\": \"^8.32.0\"\n },\n \"dependencies\": {\n \"@grpc/grpc-js\": \"^1.13.3\",\n \"@opentelemetry/api\": \"^1.9.0\",\n \"@opentelemetry/context-async-hooks\": \"^1.30.1\",\n \"@opentelemetry/core\": \"^1.30.1\",\n \"@opentelemetry/exporter-trace-otlp-grpc\": \"^0.57.2\",\n \"@opentelemetry/exporter-trace-otlp-proto\": \"^0.57.2\",\n \"@opentelemetry/instrumentation\": \"^0.57.2\",\n \"@opentelemetry/otlp-exporter-base\": \"^0.57.2\",\n \"@opentelemetry/otlp-grpc-exporter-base\": \"^0.57.2\",\n \"@opentelemetry/resources\": \"^1.30.1\",\n \"@opentelemetry/sdk-node\": \"^0.57.2\",\n \"@opentelemetry/sdk-trace-base\": \"^1.30.1\",\n \"@opentelemetry/sdk-trace-base-v2\": \"npm:@opentelemetry/sdk-trace-base@2.0.0\",\n \"@opentelemetry/sdk-trace-node\": \"^1.30.1\",\n \"@opentelemetry/semantic-conventions\": \"^1.33.0\",\n \"@traceloop/ai-semantic-conventions\": \"^0.12.0\",\n \"@traceloop/instrumentation-anthropic\": \"^0.12.0\",\n \"@traceloop/instrumentation-azure\": \"^0.12.0\",\n \"@traceloop/instrumentation-bedrock\": \"^0.12.0\",\n \"@traceloop/instrumentation-chromadb\": \"^0.12.0\",\n \"@traceloop/instrumentation-cohere\": \"^0.12.0\",\n \"@traceloop/instrumentation-langchain\": \"^0.12.0\",\n \"@traceloop/instrumentation-llamaindex\": \"^0.12.0\",\n \"@traceloop/instrumentation-openai\": \"^0.12.0\",\n \"@traceloop/instrumentation-pinecone\": \"^0.12.0\",\n \"@traceloop/instrumentation-qdrant\": \"^0.12.0\",\n \"@traceloop/instrumentation-together\": \"^0.12.1\",\n \"@traceloop/instrumentation-vertexai\": \"^0.12.0\",\n \"argparse\": \"^2.0.1\",\n \"cli-progress\": \"^3.12.0\",\n \"esbuild\": \"^0.25.4\",\n \"glob\": \"^11.0.2\",\n \"pino\": \"^9.6.0\",\n \"pino-pretty\": \"^13.0.0\",\n \"uuid\": \"^11.1.0\"\n }\n}","import { SpanContext, TraceFlags } from '@opentelemetry/api';\nimport pino, { Level } from 'pino';\nimport pinoPretty from 'pino-pretty';\nimport { v4 as uuidv4 } from 'uuid';\n\nimport { LaminarSpanContext } from './types';\n\nexport function initializeLogger(options?: { colorize?: boolean, level?: Level }) {\n const colorize = options?.colorize ?? true;\n const level = options?.level\n ?? (process.env.LMNR_LOG_LEVEL?.toLowerCase()?.trim() as Level)\n ?? 'info';\n\n return pino(pinoPretty({\n colorize,\n minimumLevel: level,\n }));\n}\n\nconst logger = initializeLogger();\n\nexport type StringUUID = `${string}-${string}-${string}-${string}-${string}`;\n\nexport const isStringUUID = (id: string): id is StringUUID =>\n /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/.test(id);\n\nexport const NIL_UUID: StringUUID = '00000000-0000-0000-0000-000000000000';\n\nexport const newUUID = (): StringUUID => {\n // crypto.randomUUID is available in most of the modern browsers and node,\n // but is not available in \"insecure\" contexts, e.g. not https, not localhost\n // so we fallback to uuidv4 in those cases, which is less secure, but works\n // just fine.\n if (typeof crypto !== 'undefined' && typeof crypto.randomUUID === 'function') {\n return crypto.randomUUID();\n } else {\n return uuidv4() as `${string}-${string}-${string}-${string}-${string}`;\n }\n};\n\nexport const otelSpanIdToUUID = (spanId: string): string => {\n let id = spanId.toLowerCase();\n if (id.startsWith('0x')) {\n id = id.slice(2);\n }\n if (id.length !== 16) {\n logger.warn(`Span ID ${spanId} is not 16 hex chars long. ` +\n 'This is not a valid OpenTelemetry span ID.');\n }\n\n if (!/^[0-9a-f]+$/.test(id)) {\n logger.error(`Span ID ${spanId} is not a valid hex string. ` +\n 'Generating a random UUID instead.');\n return newUUID();\n }\n\n return id.padStart(32, '0').replace(/^(.{8})(.{4})(.{4})(.{4})(.{12})$/, '$1-$2-$3-$4-$5');\n};\n\nexport const otelTraceIdToUUID = (traceId: string): string => {\n let id = traceId.toLowerCase();\n if (id.startsWith('0x')) {\n id = id.slice(2);\n }\n if (id.length !== 32) {\n logger.warn(`Trace ID ${traceId} is not 32 hex chars long. ` +\n 'This is not a valid OpenTelemetry trace ID.');\n }\n if (!/^[0-9a-f]+$/.test(id)) {\n logger.error(`Trace ID ${traceId} is not a valid hex string. ` +\n 'Generating a random UUID instead.');\n return newUUID();\n }\n\n return id.replace(/^(.{8})(.{4})(.{4})(.{4})(.{12})$/, '$1-$2-$3-$4-$5');\n};\n\nexport const uuidToOtelTraceId = (uuid: string): string => uuid.replace(/-/g, '');\nexport const uuidToOtelSpanId = (uuid: string): string => uuid.replace(/-/g, '').slice(16);\n\n/**\n * This is a simple implementation of a semaphore to replicate\n * the behavior of the `asyncio.Semaphore` in Python.\n */\nexport class Semaphore {\n /**\n * Number of permits available.\n */\n private _value: number;\n /**\n * List of promises that will be resolved when a permit becomes available.\n */\n private _waiters: ((...args: any[]) => any)[] = [];\n\n constructor(value = 1) {\n if (value < 0) {\n throw new Error(\"Semaphore value must be >= 0\");\n }\n this._value = value;\n this._waiters = [];\n }\n\n async acquire() {\n if (this._value > 0) {\n this._value--;\n return;\n }\n\n // Create a promise that will be resolved when a permit becomes available\n return new Promise(resolve => {\n this._waiters.push(resolve);\n });\n }\n\n release() {\n if (this._waiters.length > 0) {\n // If there are waiters, wake up the first one\n const resolve = this._waiters.shift();\n resolve?.();\n } else {\n this._value++;\n }\n }\n\n // Python-like context manager functionality\n async using<T>(fn: (...args: any[]) => Promise<T>) {\n try {\n await this.acquire();\n return await fn();\n } finally {\n this.release();\n }\n }\n}\n\nexport const tryToOtelSpanContext = (\n spanContext: LaminarSpanContext | Record<string, unknown> | string | SpanContext,\n): SpanContext => {\n if (typeof spanContext === 'string') {\n try {\n const record = JSON.parse(spanContext) as Record<string, unknown>;\n return recordToOtelSpanContext(record);\n } catch (e) {\n throw new Error(`Failed to parse span context ${spanContext}. ` +\n 'The string must be a json representation of a LaminarSpanContext.'\n + `Error: ${e instanceof Error ? e.message : String(e)}`);\n }\n } else if (isRecord(spanContext)) {\n // This covers the `LaminarSpanContext` case too.\n return recordToOtelSpanContext(spanContext);\n } else if (typeof spanContext.traceId === 'string'\n && typeof spanContext.spanId === 'string'\n && spanContext.traceId.length === 32\n && spanContext.spanId.length === 16) {\n logger.warn('The span context is already an OpenTelemetry SpanContext. ' +\n 'Returning it as is. Please use `LaminarSpanContext` objects instead.');\n return spanContext;\n }\n else {\n throw new Error(`Invalid span context ${JSON.stringify(spanContext)}. ` +\n 'Must be a LaminarSpanContext or its json representation.');\n }\n};\n\nconst recordToOtelSpanContext = (record: Record<string, unknown>): SpanContext => {\n if (typeof record.spanId === 'string' && typeof record.traceId === 'string') {\n return {\n spanId: uuidToOtelSpanId(record?.spanId ?? record?.['span_id']),\n traceId: uuidToOtelTraceId(record?.traceId ?? record?.['trace_id']),\n isRemote: record?.isRemote ?? record?.['is_remote'] ?? false,\n traceFlags: record?.traceFlags ?? TraceFlags.SAMPLED,\n } as SpanContext;\n } else {\n throw new Error(`Invalid span context ${JSON.stringify(record)}. ` +\n 'Must be a json representation of a LaminarSpanContext.');\n }\n};\n\nconst isRecord = (value: unknown): value is Record<string, unknown> =>\n typeof value === 'object' && !Array.isArray(value) && value !== null;\n"],"mappings":"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","names":["import_api","pino","pinoPretty","logger","args"]}
|
|
1
|
+
{"version":3,"sources":["../src/cli.ts","../package.json","../src/utils.ts"],"sourcesContent":["#!/usr/bin/env node\n\nimport { context, propagation, trace } from \"@opentelemetry/api\";\nimport { ArgumentParser } from \"argparse\";\nimport * as esbuild from \"esbuild\";\nimport * as glob from \"glob\";\n\nimport { version } from \"../package.json\";\nimport { Evaluation } from \"./evaluations\";\nimport { initializeLogger } from \"./utils\";\n\nconst logger = initializeLogger();\n\ndeclare global {\n // eslint-disable-next-line no-var\n var _evaluation: Evaluation<any, any, any> | undefined;\n // eslint-disable-next-line no-var\n var _set_global_evaluation: boolean;\n}\n\nexport function loadModule({\n moduleText,\n}: {\n filename: string;\n moduleText: string;\n}): Evaluation<any, any, any> {\n globalThis._evaluation = undefined;\n globalThis._set_global_evaluation = true;\n\n // it needs \"require\" to be passed in\n // eslint-disable-next-line\n new Function(\"require\", moduleText)(require);\n\n // Return the modified _evals global variable\n return globalThis._evaluation!;\n}\n\nasync function cli() {\n const [, , ...args] = process.argv;\n\n // Use argparse, which is the port of the python library\n const parser = new ArgumentParser({\n prog: \"lmnr\",\n description: \"CLI for Laminar. Use `lmnr <subcommand> --help` for more information.\",\n });\n\n parser.add_argument(\"-v\", \"--version\", { action: \"version\", version });\n\n const subparsers = parser.add_subparsers({\n title: \"subcommands\",\n dest: \"subcommand\",\n });\n\n const parserEval = subparsers.add_parser(\"eval\", {\n description: \"Run an evaluation\",\n help: \"Run an evaluation\",\n });\n\n parserEval.add_argument(\"file\", {\n help: \"A file containing the evaluation to run. If no file is provided, \" +\n \"the evaluation will run all `*.eval.ts|js` files in the `evals` directory.\",\n nargs: \"?\",\n });\n\n parserEval.add_argument(\"--fail-on-error\", {\n help: \"Fail on error. If specified, will fail if encounters a file that cannot be run\",\n action: \"store_true\",\n });\n\n parserEval.set_defaults({\n func: async (args: any) => {\n const files = args.file\n ? [args.file]\n : glob.sync('evals/**/*.eval.{ts,js}');\n\n files.sort();\n\n if (files.length === 0) {\n logger.error(\"No evaluation files found. Please provide a file or \" +\n \"ensure there are eval files that are named like `*.eval.{ts,js}` in\" +\n \"the `evals` directory or its subdirectories.\");\n process.exit(1);\n }\n\n if (!args.file) {\n logger.info(`Located ${files.length} evaluation files in evals/`);\n }\n\n for (const file of files) {\n logger.info(`Loading ${file}...`);\n // TODO: Add various optimizations, e.g. minify, pure, tree shaking, etc.\n const buildOptions = {\n entryPoints: [file],\n outfile: `tmp_out_${file}.js`,\n write: false, // will be loaded in memory as a temp file\n platform: \"node\" as esbuild.Platform,\n bundle: true,\n external: [\"node_modules/*\"],\n };\n\n const result = await esbuild.build(buildOptions);\n\n if (!result.outputFiles) {\n logger.error(\"Error when building: No output files found \" +\n \"it is likely that all eval files are not valid TypeScript or JavaScript files.\");\n if (args.fail_on_error) {\n process.exit(1);\n }\n continue;\n }\n\n const outputFileText = result.outputFiles[0].text;\n\n const evaluation = loadModule({\n filename: args.file,\n moduleText: outputFileText,\n });\n\n if (!evaluation?.run) {\n logger.error(`Evaluation ${file} does not properly call evaluate()`);\n if (args.fail_on_error) {\n process.exit(1);\n }\n continue;\n }\n\n await evaluation.run();\n\n // FIXME: Now every evaluation file creates a new tracer provider.\n // Attempt to re-initialize it in the same process breaks it.\n // For now, we disable all APIs after running each file, but ideally\n // we should keep a global tracer provider that is initialized once\n // here in the CLI.\n context.disable();\n trace.disable();\n propagation.disable();\n }\n },\n });\n\n parser.set_defaults({\n func: () => {\n parser.print_help();\n process.exit(0);\n },\n });\n\n const parsed = parser.parse_args(args);\n await parsed.func(parsed);\n}\n\ncli().catch((err) => {\n logger.error(err);\n process.exit(1);\n});\n","{\n \"name\": \"@lmnr-ai/lmnr\",\n \"version\": \"0.6.2\",\n \"description\": \"TypeScript SDK for Laminar AI\",\n \"main\": \"dist/index.js\",\n \"types\": \"dist/index.d.ts\",\n \"scripts\": {\n \"build\": \"tsup\",\n \"test\": \"tsx --test test/*.test.ts\",\n \"lint\": \"eslint\",\n \"lint:fix\": \"eslint --fix\"\n },\n \"files\": [\n \"dist\",\n \"assets\"\n ],\n \"bin\": {\n \"lmnr\": \"./dist/cli.js\"\n },\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"git+https://github.com/lmnr-ai/lmnr-ts.git\"\n },\n \"keywords\": [\n \"laminar\",\n \"lmnr\",\n \"sdk\",\n \"lmnr.ai\"\n ],\n \"author\": \"founders@lmnr.ai\",\n \"license\": \"Apache-2.0\",\n \"bugs\": {\n \"url\": \"https://github.com/lmnr-ai/lmnr-ts/issues\"\n },\n \"homepage\": \"https://github.com/lmnr-ai/lmnr-ts#README\",\n \"devDependencies\": {\n \"@anthropic-ai/sdk\": \"^0.39.0\",\n \"@aws-sdk/client-bedrock-runtime\": \"^3.806.0\",\n \"@azure/openai\": \"^2.0.0\",\n \"@browserbasehq/stagehand\": \"^1.14.0\",\n \"@eslint/eslintrc\": \"^3.3.1\",\n \"@eslint/js\": \"^9.26.0\",\n \"@google-cloud/aiplatform\": \"^4.1.0\",\n \"@google-cloud/vertexai\": \"^1.10.0\",\n \"@langchain/core\": \"^0.3.55\",\n \"@pinecone-database/pinecone\": \"^5.1.2\",\n \"@playwright/test\": \"^1.52.0\",\n \"@qdrant/js-client-rest\": \"^1.14.0\",\n \"@stylistic/eslint-plugin-js\": \"^4.2.0\",\n \"@types/argparse\": \"^2.0.17\",\n \"@types/cli-progress\": \"^3.11.6\",\n \"@types/node\": \"^22.15.17\",\n \"@types/semver\": \"^7.7.0\",\n \"@types/uuid\": \"^10.0.0\",\n \"chromadb\": \"^2.4.3\",\n \"cohere-ai\": \"^7.17.1\",\n \"eslint-plugin-simple-import-sort\": \"^12.1.1\",\n \"eslint-plugin-unused-imports\": \"^4.1.4\",\n \"langchain\": \"^0.3.24\",\n \"llamaindex\": \"^0.9.19\",\n \"nock\": \"^14.0.4\",\n \"openai\": \"^4.98.0\",\n \"playwright\": \"^1.52.0\",\n \"puppeteer\": \"^24.8.2\",\n \"puppeteer-core\": \"^24.8.2\",\n \"together-ai\": \"^0.14.0\",\n \"tsup\": \"^8.4.0\",\n \"tsx\": \"^4.19.4\",\n \"typescript\": \"^5.8.3\",\n \"typescript-eslint\": \"^8.32.0\"\n },\n \"dependencies\": {\n \"@grpc/grpc-js\": \"^1.13.3\",\n \"@opentelemetry/api\": \"^1.9.0\",\n \"@opentelemetry/context-async-hooks\": \"^1.30.1\",\n \"@opentelemetry/core\": \"^1.30.1\",\n \"@opentelemetry/exporter-trace-otlp-grpc\": \"^0.57.2\",\n \"@opentelemetry/exporter-trace-otlp-proto\": \"^0.57.2\",\n \"@opentelemetry/instrumentation\": \"^0.57.2\",\n \"@opentelemetry/otlp-exporter-base\": \"^0.57.2\",\n \"@opentelemetry/otlp-grpc-exporter-base\": \"^0.57.2\",\n \"@opentelemetry/resources\": \"^1.30.1\",\n \"@opentelemetry/sdk-node\": \"^0.57.2\",\n \"@opentelemetry/sdk-trace-base\": \"^1.30.1\",\n \"@opentelemetry/sdk-trace-base-v2\": \"npm:@opentelemetry/sdk-trace-base@2.0.0\",\n \"@opentelemetry/sdk-trace-node\": \"^1.30.1\",\n \"@opentelemetry/semantic-conventions\": \"^1.33.0\",\n \"@traceloop/ai-semantic-conventions\": \"^0.12.0\",\n \"@traceloop/instrumentation-anthropic\": \"^0.12.0\",\n \"@traceloop/instrumentation-azure\": \"^0.12.0\",\n \"@traceloop/instrumentation-bedrock\": \"^0.12.0\",\n \"@traceloop/instrumentation-chromadb\": \"^0.12.0\",\n \"@traceloop/instrumentation-cohere\": \"^0.12.0\",\n \"@traceloop/instrumentation-langchain\": \"^0.12.0\",\n \"@traceloop/instrumentation-llamaindex\": \"^0.12.0\",\n \"@traceloop/instrumentation-openai\": \"^0.12.0\",\n \"@traceloop/instrumentation-pinecone\": \"^0.12.0\",\n \"@traceloop/instrumentation-qdrant\": \"^0.12.0\",\n \"@traceloop/instrumentation-together\": \"^0.12.1\",\n \"@traceloop/instrumentation-vertexai\": \"^0.12.0\",\n \"argparse\": \"^2.0.1\",\n \"cli-progress\": \"^3.12.0\",\n \"esbuild\": \"^0.25.4\",\n \"glob\": \"^11.0.2\",\n \"pino\": \"^9.6.0\",\n \"pino-pretty\": \"^13.0.0\",\n \"uuid\": \"^11.1.0\"\n }\n}","import { SpanContext, TraceFlags } from '@opentelemetry/api';\nimport pino, { Level } from 'pino';\nimport pinoPretty from 'pino-pretty';\nimport { v4 as uuidv4 } from 'uuid';\n\nimport { LaminarSpanContext } from './types';\n\nexport function initializeLogger(options?: { colorize?: boolean, level?: Level }) {\n const colorize = options?.colorize ?? true;\n const level = options?.level\n ?? (process.env.LMNR_LOG_LEVEL?.toLowerCase()?.trim() as Level)\n ?? 'info';\n\n return pino(pinoPretty({\n colorize,\n minimumLevel: level,\n }));\n}\n\nconst logger = initializeLogger();\n\nexport type StringUUID = `${string}-${string}-${string}-${string}-${string}`;\n\nexport const isStringUUID = (id: string): id is StringUUID =>\n /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/.test(id);\n\nexport const NIL_UUID: StringUUID = '00000000-0000-0000-0000-000000000000';\n\nexport const newUUID = (): StringUUID => {\n // crypto.randomUUID is available in most of the modern browsers and node,\n // but is not available in \"insecure\" contexts, e.g. not https, not localhost\n // so we fallback to uuidv4 in those cases, which is less secure, but works\n // just fine.\n if (typeof crypto !== 'undefined' && typeof crypto.randomUUID === 'function') {\n return crypto.randomUUID();\n } else {\n return uuidv4() as `${string}-${string}-${string}-${string}-${string}`;\n }\n};\n\nexport const otelSpanIdToUUID = (spanId: string): string => {\n let id = spanId.toLowerCase();\n if (id.startsWith('0x')) {\n id = id.slice(2);\n }\n if (id.length !== 16) {\n logger.warn(`Span ID ${spanId} is not 16 hex chars long. ` +\n 'This is not a valid OpenTelemetry span ID.');\n }\n\n if (!/^[0-9a-f]+$/.test(id)) {\n logger.error(`Span ID ${spanId} is not a valid hex string. ` +\n 'Generating a random UUID instead.');\n return newUUID();\n }\n\n return id.padStart(32, '0').replace(/^(.{8})(.{4})(.{4})(.{4})(.{12})$/, '$1-$2-$3-$4-$5');\n};\n\nexport const otelTraceIdToUUID = (traceId: string): string => {\n let id = traceId.toLowerCase();\n if (id.startsWith('0x')) {\n id = id.slice(2);\n }\n if (id.length !== 32) {\n logger.warn(`Trace ID ${traceId} is not 32 hex chars long. ` +\n 'This is not a valid OpenTelemetry trace ID.');\n }\n if (!/^[0-9a-f]+$/.test(id)) {\n logger.error(`Trace ID ${traceId} is not a valid hex string. ` +\n 'Generating a random UUID instead.');\n return newUUID();\n }\n\n return id.replace(/^(.{8})(.{4})(.{4})(.{4})(.{12})$/, '$1-$2-$3-$4-$5');\n};\n\nexport const uuidToOtelTraceId = (uuid: string): string => uuid.replace(/-/g, '');\nexport const uuidToOtelSpanId = (uuid: string): string => uuid.replace(/-/g, '').slice(16);\n\n/**\n * This is a simple implementation of a semaphore to replicate\n * the behavior of the `asyncio.Semaphore` in Python.\n */\nexport class Semaphore {\n /**\n * Number of permits available.\n */\n private _value: number;\n /**\n * List of promises that will be resolved when a permit becomes available.\n */\n private _waiters: ((...args: any[]) => any)[] = [];\n\n constructor(value = 1) {\n if (value < 0) {\n throw new Error(\"Semaphore value must be >= 0\");\n }\n this._value = value;\n this._waiters = [];\n }\n\n async acquire() {\n if (this._value > 0) {\n this._value--;\n return;\n }\n\n // Create a promise that will be resolved when a permit becomes available\n return new Promise(resolve => {\n this._waiters.push(resolve);\n });\n }\n\n release() {\n if (this._waiters.length > 0) {\n // If there are waiters, wake up the first one\n const resolve = this._waiters.shift();\n resolve?.();\n } else {\n this._value++;\n }\n }\n\n // Python-like context manager functionality\n async using<T>(fn: (...args: any[]) => Promise<T>) {\n try {\n await this.acquire();\n return await fn();\n } finally {\n this.release();\n }\n }\n}\n\nexport const tryToOtelSpanContext = (\n spanContext: LaminarSpanContext | Record<string, unknown> | string | SpanContext,\n): SpanContext => {\n if (typeof spanContext === 'string') {\n try {\n const record = JSON.parse(spanContext) as Record<string, unknown>;\n return recordToOtelSpanContext(record);\n } catch (e) {\n throw new Error(`Failed to parse span context ${spanContext}. ` +\n 'The string must be a json representation of a LaminarSpanContext.'\n + `Error: ${e instanceof Error ? e.message : String(e)}`);\n }\n } else if (isRecord(spanContext)) {\n // This covers the `LaminarSpanContext` case too.\n return recordToOtelSpanContext(spanContext);\n } else if (typeof spanContext.traceId === 'string'\n && typeof spanContext.spanId === 'string'\n && spanContext.traceId.length === 32\n && spanContext.spanId.length === 16) {\n logger.warn('The span context is already an OpenTelemetry SpanContext. ' +\n 'Returning it as is. Please use `LaminarSpanContext` objects instead.');\n return spanContext;\n }\n else {\n throw new Error(`Invalid span context ${JSON.stringify(spanContext)}. ` +\n 'Must be a LaminarSpanContext or its json representation.');\n }\n};\n\nconst recordToOtelSpanContext = (record: Record<string, unknown>): SpanContext => {\n if (typeof record.spanId === 'string' && typeof record.traceId === 'string') {\n return {\n spanId: uuidToOtelSpanId(record?.spanId ?? record?.['span_id']),\n traceId: uuidToOtelTraceId(record?.traceId ?? record?.['trace_id']),\n isRemote: record?.isRemote ?? record?.['is_remote'] ?? false,\n traceFlags: record?.traceFlags ?? TraceFlags.SAMPLED,\n } as SpanContext;\n } else {\n throw new Error(`Invalid span context ${JSON.stringify(record)}. ` +\n 'Must be a json representation of a LaminarSpanContext.');\n }\n};\n\nconst isRecord = (value: unknown): value is Record<string, unknown> =>\n typeof value === 'object' && !Array.isArray(value) && value !== null;\n"],"mappings":"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","names":["import_api","pino","pinoPretty","logger","args"]}
|
package/dist/cli.mjs
CHANGED
package/dist/cli.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/cli.ts","../package.json","../src/utils.ts"],"sourcesContent":["#!/usr/bin/env node\n\nimport { context, propagation, trace } from \"@opentelemetry/api\";\nimport { ArgumentParser } from \"argparse\";\nimport * as esbuild from \"esbuild\";\nimport * as glob from \"glob\";\n\nimport { version } from \"../package.json\";\nimport { Evaluation } from \"./evaluations\";\nimport { initializeLogger } from \"./utils\";\n\nconst logger = initializeLogger();\n\ndeclare global {\n // eslint-disable-next-line no-var\n var _evaluation: Evaluation<any, any, any> | undefined;\n // eslint-disable-next-line no-var\n var _set_global_evaluation: boolean;\n}\n\nexport function loadModule({\n moduleText,\n}: {\n filename: string;\n moduleText: string;\n}): Evaluation<any, any, any> {\n globalThis._evaluation = undefined;\n globalThis._set_global_evaluation = true;\n\n // it needs \"require\" to be passed in\n // eslint-disable-next-line\n new Function(\"require\", moduleText)(require);\n\n // Return the modified _evals global variable\n return globalThis._evaluation!;\n}\n\nasync function cli() {\n const [, , ...args] = process.argv;\n\n // Use argparse, which is the port of the python library\n const parser = new ArgumentParser({\n prog: \"lmnr\",\n description: \"CLI for Laminar. Use `lmnr <subcommand> --help` for more information.\",\n });\n\n parser.add_argument(\"-v\", \"--version\", { action: \"version\", version });\n\n const subparsers = parser.add_subparsers({\n title: \"subcommands\",\n dest: \"subcommand\",\n });\n\n const parserEval = subparsers.add_parser(\"eval\", {\n description: \"Run an evaluation\",\n help: \"Run an evaluation\",\n });\n\n parserEval.add_argument(\"file\", {\n help: \"A file containing the evaluation to run. If no file is provided, \" +\n \"the evaluation will run all `*.eval.ts|js` files in the `evals` directory.\",\n nargs: \"?\",\n });\n\n parserEval.add_argument(\"--fail-on-error\", {\n help: \"Fail on error. If specified, will fail if encounters a file that cannot be run\",\n action: \"store_true\",\n });\n\n parserEval.set_defaults({\n func: async (args: any) => {\n const files = args.file\n ? [args.file]\n : glob.sync('evals/**/*.eval.{ts,js}');\n\n files.sort();\n\n if (files.length === 0) {\n logger.error(\"No evaluation files found. Please provide a file or \" +\n \"ensure there are eval files that are named like `*.eval.{ts,js}` in\" +\n \"the `evals` directory or its subdirectories.\");\n process.exit(1);\n }\n\n if (!args.file) {\n logger.info(`Located ${files.length} evaluation files in evals/`);\n }\n\n for (const file of files) {\n logger.info(`Loading ${file}...`);\n // TODO: Add various optimizations, e.g. minify, pure, tree shaking, etc.\n const buildOptions = {\n entryPoints: [file],\n outfile: `tmp_out_${file}.js`,\n write: false, // will be loaded in memory as a temp file\n platform: \"node\" as esbuild.Platform,\n bundle: true,\n external: [\"node_modules/*\"],\n };\n\n const result = await esbuild.build(buildOptions);\n\n if (!result.outputFiles) {\n logger.error(\"Error when building: No output files found \" +\n \"it is likely that all eval files are not valid TypeScript or JavaScript files.\");\n if (args.fail_on_error) {\n process.exit(1);\n }\n continue;\n }\n\n const outputFileText = result.outputFiles[0].text;\n\n const evaluation = loadModule({\n filename: args.file,\n moduleText: outputFileText,\n });\n\n if (!evaluation?.run) {\n logger.error(`Evaluation ${file} does not properly call evaluate()`);\n if (args.fail_on_error) {\n process.exit(1);\n }\n continue;\n }\n\n await evaluation.run();\n\n // FIXME: Now every evaluation file creates a new tracer provider.\n // Attempt to re-initialize it in the same process breaks it.\n // For now, we disable all APIs after running each file, but ideally\n // we should keep a global tracer provider that is initialized once\n // here in the CLI.\n context.disable();\n trace.disable();\n propagation.disable();\n }\n },\n });\n\n parser.set_defaults({\n func: () => {\n parser.print_help();\n process.exit(0);\n },\n });\n\n const parsed = parser.parse_args(args);\n await parsed.func(parsed);\n}\n\ncli().catch((err) => {\n logger.error(err);\n process.exit(1);\n});\n","{\n \"name\": \"@lmnr-ai/lmnr\",\n \"version\": \"0.6.1\",\n \"description\": \"TypeScript SDK for Laminar AI\",\n \"main\": \"dist/index.js\",\n \"types\": \"dist/index.d.ts\",\n \"scripts\": {\n \"build\": \"tsup\",\n \"test\": \"tsx --test test/*.test.ts\",\n \"lint\": \"eslint\",\n \"lint:fix\": \"eslint --fix\"\n },\n \"files\": [\n \"dist\",\n \"assets\"\n ],\n \"bin\": {\n \"lmnr\": \"./dist/cli.js\"\n },\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"git+https://github.com/lmnr-ai/lmnr-ts.git\"\n },\n \"keywords\": [\n \"laminar\",\n \"lmnr\",\n \"sdk\",\n \"lmnr.ai\"\n ],\n \"author\": \"founders@lmnr.ai\",\n \"license\": \"Apache-2.0\",\n \"bugs\": {\n \"url\": \"https://github.com/lmnr-ai/lmnr-ts/issues\"\n },\n \"homepage\": \"https://github.com/lmnr-ai/lmnr-ts#README\",\n \"devDependencies\": {\n \"@anthropic-ai/sdk\": \"^0.39.0\",\n \"@aws-sdk/client-bedrock-runtime\": \"^3.806.0\",\n \"@azure/openai\": \"^2.0.0\",\n \"@browserbasehq/stagehand\": \"^1.14.0\",\n \"@eslint/eslintrc\": \"^3.3.1\",\n \"@eslint/js\": \"^9.26.0\",\n \"@google-cloud/aiplatform\": \"^4.1.0\",\n \"@google-cloud/vertexai\": \"^1.10.0\",\n \"@langchain/core\": \"^0.3.55\",\n \"@pinecone-database/pinecone\": \"^5.1.2\",\n \"@playwright/test\": \"^1.52.0\",\n \"@qdrant/js-client-rest\": \"^1.14.0\",\n \"@stylistic/eslint-plugin-js\": \"^4.2.0\",\n \"@types/argparse\": \"^2.0.17\",\n \"@types/cli-progress\": \"^3.11.6\",\n \"@types/node\": \"^22.15.17\",\n \"@types/semver\": \"^7.7.0\",\n \"@types/uuid\": \"^10.0.0\",\n \"chromadb\": \"^2.4.3\",\n \"cohere-ai\": \"^7.17.1\",\n \"eslint-plugin-simple-import-sort\": \"^12.1.1\",\n \"eslint-plugin-unused-imports\": \"^4.1.4\",\n \"langchain\": \"^0.3.24\",\n \"llamaindex\": \"^0.9.19\",\n \"nock\": \"^14.0.4\",\n \"openai\": \"^4.98.0\",\n \"playwright\": \"^1.52.0\",\n \"puppeteer\": \"^24.8.2\",\n \"puppeteer-core\": \"^24.8.2\",\n \"together-ai\": \"^0.14.0\",\n \"tsup\": \"^8.4.0\",\n \"tsx\": \"^4.19.4\",\n \"typescript\": \"^5.8.3\",\n \"typescript-eslint\": \"^8.32.0\"\n },\n \"dependencies\": {\n \"@grpc/grpc-js\": \"^1.13.3\",\n \"@opentelemetry/api\": \"^1.9.0\",\n \"@opentelemetry/context-async-hooks\": \"^1.30.1\",\n \"@opentelemetry/core\": \"^1.30.1\",\n \"@opentelemetry/exporter-trace-otlp-grpc\": \"^0.57.2\",\n \"@opentelemetry/exporter-trace-otlp-proto\": \"^0.57.2\",\n \"@opentelemetry/instrumentation\": \"^0.57.2\",\n \"@opentelemetry/otlp-exporter-base\": \"^0.57.2\",\n \"@opentelemetry/otlp-grpc-exporter-base\": \"^0.57.2\",\n \"@opentelemetry/resources\": \"^1.30.1\",\n \"@opentelemetry/sdk-node\": \"^0.57.2\",\n \"@opentelemetry/sdk-trace-base\": \"^1.30.1\",\n \"@opentelemetry/sdk-trace-base-v2\": \"npm:@opentelemetry/sdk-trace-base@2.0.0\",\n \"@opentelemetry/sdk-trace-node\": \"^1.30.1\",\n \"@opentelemetry/semantic-conventions\": \"^1.33.0\",\n \"@traceloop/ai-semantic-conventions\": \"^0.12.0\",\n \"@traceloop/instrumentation-anthropic\": \"^0.12.0\",\n \"@traceloop/instrumentation-azure\": \"^0.12.0\",\n \"@traceloop/instrumentation-bedrock\": \"^0.12.0\",\n \"@traceloop/instrumentation-chromadb\": \"^0.12.0\",\n \"@traceloop/instrumentation-cohere\": \"^0.12.0\",\n \"@traceloop/instrumentation-langchain\": \"^0.12.0\",\n \"@traceloop/instrumentation-llamaindex\": \"^0.12.0\",\n \"@traceloop/instrumentation-openai\": \"^0.12.0\",\n \"@traceloop/instrumentation-pinecone\": \"^0.12.0\",\n \"@traceloop/instrumentation-qdrant\": \"^0.12.0\",\n \"@traceloop/instrumentation-together\": \"^0.12.1\",\n \"@traceloop/instrumentation-vertexai\": \"^0.12.0\",\n \"argparse\": \"^2.0.1\",\n \"cli-progress\": \"^3.12.0\",\n \"esbuild\": \"^0.25.4\",\n \"glob\": \"^11.0.2\",\n \"pino\": \"^9.6.0\",\n \"pino-pretty\": \"^13.0.0\",\n \"uuid\": \"^11.1.0\"\n }\n}","import { SpanContext, TraceFlags } from '@opentelemetry/api';\nimport pino, { Level } from 'pino';\nimport pinoPretty from 'pino-pretty';\nimport { v4 as uuidv4 } from 'uuid';\n\nimport { LaminarSpanContext } from './types';\n\nexport function initializeLogger(options?: { colorize?: boolean, level?: Level }) {\n const colorize = options?.colorize ?? true;\n const level = options?.level\n ?? (process.env.LMNR_LOG_LEVEL?.toLowerCase()?.trim() as Level)\n ?? 'info';\n\n return pino(pinoPretty({\n colorize,\n minimumLevel: level,\n }));\n}\n\nconst logger = initializeLogger();\n\nexport type StringUUID = `${string}-${string}-${string}-${string}-${string}`;\n\nexport const isStringUUID = (id: string): id is StringUUID =>\n /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/.test(id);\n\nexport const NIL_UUID: StringUUID = '00000000-0000-0000-0000-000000000000';\n\nexport const newUUID = (): StringUUID => {\n // crypto.randomUUID is available in most of the modern browsers and node,\n // but is not available in \"insecure\" contexts, e.g. not https, not localhost\n // so we fallback to uuidv4 in those cases, which is less secure, but works\n // just fine.\n if (typeof crypto !== 'undefined' && typeof crypto.randomUUID === 'function') {\n return crypto.randomUUID();\n } else {\n return uuidv4() as `${string}-${string}-${string}-${string}-${string}`;\n }\n};\n\nexport const otelSpanIdToUUID = (spanId: string): string => {\n let id = spanId.toLowerCase();\n if (id.startsWith('0x')) {\n id = id.slice(2);\n }\n if (id.length !== 16) {\n logger.warn(`Span ID ${spanId} is not 16 hex chars long. ` +\n 'This is not a valid OpenTelemetry span ID.');\n }\n\n if (!/^[0-9a-f]+$/.test(id)) {\n logger.error(`Span ID ${spanId} is not a valid hex string. ` +\n 'Generating a random UUID instead.');\n return newUUID();\n }\n\n return id.padStart(32, '0').replace(/^(.{8})(.{4})(.{4})(.{4})(.{12})$/, '$1-$2-$3-$4-$5');\n};\n\nexport const otelTraceIdToUUID = (traceId: string): string => {\n let id = traceId.toLowerCase();\n if (id.startsWith('0x')) {\n id = id.slice(2);\n }\n if (id.length !== 32) {\n logger.warn(`Trace ID ${traceId} is not 32 hex chars long. ` +\n 'This is not a valid OpenTelemetry trace ID.');\n }\n if (!/^[0-9a-f]+$/.test(id)) {\n logger.error(`Trace ID ${traceId} is not a valid hex string. ` +\n 'Generating a random UUID instead.');\n return newUUID();\n }\n\n return id.replace(/^(.{8})(.{4})(.{4})(.{4})(.{12})$/, '$1-$2-$3-$4-$5');\n};\n\nexport const uuidToOtelTraceId = (uuid: string): string => uuid.replace(/-/g, '');\nexport const uuidToOtelSpanId = (uuid: string): string => uuid.replace(/-/g, '').slice(16);\n\n/**\n * This is a simple implementation of a semaphore to replicate\n * the behavior of the `asyncio.Semaphore` in Python.\n */\nexport class Semaphore {\n /**\n * Number of permits available.\n */\n private _value: number;\n /**\n * List of promises that will be resolved when a permit becomes available.\n */\n private _waiters: ((...args: any[]) => any)[] = [];\n\n constructor(value = 1) {\n if (value < 0) {\n throw new Error(\"Semaphore value must be >= 0\");\n }\n this._value = value;\n this._waiters = [];\n }\n\n async acquire() {\n if (this._value > 0) {\n this._value--;\n return;\n }\n\n // Create a promise that will be resolved when a permit becomes available\n return new Promise(resolve => {\n this._waiters.push(resolve);\n });\n }\n\n release() {\n if (this._waiters.length > 0) {\n // If there are waiters, wake up the first one\n const resolve = this._waiters.shift();\n resolve?.();\n } else {\n this._value++;\n }\n }\n\n // Python-like context manager functionality\n async using<T>(fn: (...args: any[]) => Promise<T>) {\n try {\n await this.acquire();\n return await fn();\n } finally {\n this.release();\n }\n }\n}\n\nexport const tryToOtelSpanContext = (\n spanContext: LaminarSpanContext | Record<string, unknown> | string | SpanContext,\n): SpanContext => {\n if (typeof spanContext === 'string') {\n try {\n const record = JSON.parse(spanContext) as Record<string, unknown>;\n return recordToOtelSpanContext(record);\n } catch (e) {\n throw new Error(`Failed to parse span context ${spanContext}. ` +\n 'The string must be a json representation of a LaminarSpanContext.'\n + `Error: ${e instanceof Error ? e.message : String(e)}`);\n }\n } else if (isRecord(spanContext)) {\n // This covers the `LaminarSpanContext` case too.\n return recordToOtelSpanContext(spanContext);\n } else if (typeof spanContext.traceId === 'string'\n && typeof spanContext.spanId === 'string'\n && spanContext.traceId.length === 32\n && spanContext.spanId.length === 16) {\n logger.warn('The span context is already an OpenTelemetry SpanContext. ' +\n 'Returning it as is. Please use `LaminarSpanContext` objects instead.');\n return spanContext;\n }\n else {\n throw new Error(`Invalid span context ${JSON.stringify(spanContext)}. ` +\n 'Must be a LaminarSpanContext or its json representation.');\n }\n};\n\nconst recordToOtelSpanContext = (record: Record<string, unknown>): SpanContext => {\n if (typeof record.spanId === 'string' && typeof record.traceId === 'string') {\n return {\n spanId: uuidToOtelSpanId(record?.spanId ?? record?.['span_id']),\n traceId: uuidToOtelTraceId(record?.traceId ?? record?.['trace_id']),\n isRemote: record?.isRemote ?? record?.['is_remote'] ?? false,\n traceFlags: record?.traceFlags ?? TraceFlags.SAMPLED,\n } as SpanContext;\n } else {\n throw new Error(`Invalid span context ${JSON.stringify(record)}. ` +\n 'Must be a json representation of a LaminarSpanContext.');\n }\n};\n\nconst isRecord = (value: unknown): value is Record<string, unknown> =>\n typeof value === 'object' && !Array.isArray(value) && value !== null;\n"],"mappings":"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","names":["logger","args"]}
|
|
1
|
+
{"version":3,"sources":["../src/cli.ts","../package.json","../src/utils.ts"],"sourcesContent":["#!/usr/bin/env node\n\nimport { context, propagation, trace } from \"@opentelemetry/api\";\nimport { ArgumentParser } from \"argparse\";\nimport * as esbuild from \"esbuild\";\nimport * as glob from \"glob\";\n\nimport { version } from \"../package.json\";\nimport { Evaluation } from \"./evaluations\";\nimport { initializeLogger } from \"./utils\";\n\nconst logger = initializeLogger();\n\ndeclare global {\n // eslint-disable-next-line no-var\n var _evaluation: Evaluation<any, any, any> | undefined;\n // eslint-disable-next-line no-var\n var _set_global_evaluation: boolean;\n}\n\nexport function loadModule({\n moduleText,\n}: {\n filename: string;\n moduleText: string;\n}): Evaluation<any, any, any> {\n globalThis._evaluation = undefined;\n globalThis._set_global_evaluation = true;\n\n // it needs \"require\" to be passed in\n // eslint-disable-next-line\n new Function(\"require\", moduleText)(require);\n\n // Return the modified _evals global variable\n return globalThis._evaluation!;\n}\n\nasync function cli() {\n const [, , ...args] = process.argv;\n\n // Use argparse, which is the port of the python library\n const parser = new ArgumentParser({\n prog: \"lmnr\",\n description: \"CLI for Laminar. Use `lmnr <subcommand> --help` for more information.\",\n });\n\n parser.add_argument(\"-v\", \"--version\", { action: \"version\", version });\n\n const subparsers = parser.add_subparsers({\n title: \"subcommands\",\n dest: \"subcommand\",\n });\n\n const parserEval = subparsers.add_parser(\"eval\", {\n description: \"Run an evaluation\",\n help: \"Run an evaluation\",\n });\n\n parserEval.add_argument(\"file\", {\n help: \"A file containing the evaluation to run. If no file is provided, \" +\n \"the evaluation will run all `*.eval.ts|js` files in the `evals` directory.\",\n nargs: \"?\",\n });\n\n parserEval.add_argument(\"--fail-on-error\", {\n help: \"Fail on error. If specified, will fail if encounters a file that cannot be run\",\n action: \"store_true\",\n });\n\n parserEval.set_defaults({\n func: async (args: any) => {\n const files = args.file\n ? [args.file]\n : glob.sync('evals/**/*.eval.{ts,js}');\n\n files.sort();\n\n if (files.length === 0) {\n logger.error(\"No evaluation files found. Please provide a file or \" +\n \"ensure there are eval files that are named like `*.eval.{ts,js}` in\" +\n \"the `evals` directory or its subdirectories.\");\n process.exit(1);\n }\n\n if (!args.file) {\n logger.info(`Located ${files.length} evaluation files in evals/`);\n }\n\n for (const file of files) {\n logger.info(`Loading ${file}...`);\n // TODO: Add various optimizations, e.g. minify, pure, tree shaking, etc.\n const buildOptions = {\n entryPoints: [file],\n outfile: `tmp_out_${file}.js`,\n write: false, // will be loaded in memory as a temp file\n platform: \"node\" as esbuild.Platform,\n bundle: true,\n external: [\"node_modules/*\"],\n };\n\n const result = await esbuild.build(buildOptions);\n\n if (!result.outputFiles) {\n logger.error(\"Error when building: No output files found \" +\n \"it is likely that all eval files are not valid TypeScript or JavaScript files.\");\n if (args.fail_on_error) {\n process.exit(1);\n }\n continue;\n }\n\n const outputFileText = result.outputFiles[0].text;\n\n const evaluation = loadModule({\n filename: args.file,\n moduleText: outputFileText,\n });\n\n if (!evaluation?.run) {\n logger.error(`Evaluation ${file} does not properly call evaluate()`);\n if (args.fail_on_error) {\n process.exit(1);\n }\n continue;\n }\n\n await evaluation.run();\n\n // FIXME: Now every evaluation file creates a new tracer provider.\n // Attempt to re-initialize it in the same process breaks it.\n // For now, we disable all APIs after running each file, but ideally\n // we should keep a global tracer provider that is initialized once\n // here in the CLI.\n context.disable();\n trace.disable();\n propagation.disable();\n }\n },\n });\n\n parser.set_defaults({\n func: () => {\n parser.print_help();\n process.exit(0);\n },\n });\n\n const parsed = parser.parse_args(args);\n await parsed.func(parsed);\n}\n\ncli().catch((err) => {\n logger.error(err);\n process.exit(1);\n});\n","{\n \"name\": \"@lmnr-ai/lmnr\",\n \"version\": \"0.6.2\",\n \"description\": \"TypeScript SDK for Laminar AI\",\n \"main\": \"dist/index.js\",\n \"types\": \"dist/index.d.ts\",\n \"scripts\": {\n \"build\": \"tsup\",\n \"test\": \"tsx --test test/*.test.ts\",\n \"lint\": \"eslint\",\n \"lint:fix\": \"eslint --fix\"\n },\n \"files\": [\n \"dist\",\n \"assets\"\n ],\n \"bin\": {\n \"lmnr\": \"./dist/cli.js\"\n },\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"git+https://github.com/lmnr-ai/lmnr-ts.git\"\n },\n \"keywords\": [\n \"laminar\",\n \"lmnr\",\n \"sdk\",\n \"lmnr.ai\"\n ],\n \"author\": \"founders@lmnr.ai\",\n \"license\": \"Apache-2.0\",\n \"bugs\": {\n \"url\": \"https://github.com/lmnr-ai/lmnr-ts/issues\"\n },\n \"homepage\": \"https://github.com/lmnr-ai/lmnr-ts#README\",\n \"devDependencies\": {\n \"@anthropic-ai/sdk\": \"^0.39.0\",\n \"@aws-sdk/client-bedrock-runtime\": \"^3.806.0\",\n \"@azure/openai\": \"^2.0.0\",\n \"@browserbasehq/stagehand\": \"^1.14.0\",\n \"@eslint/eslintrc\": \"^3.3.1\",\n \"@eslint/js\": \"^9.26.0\",\n \"@google-cloud/aiplatform\": \"^4.1.0\",\n \"@google-cloud/vertexai\": \"^1.10.0\",\n \"@langchain/core\": \"^0.3.55\",\n \"@pinecone-database/pinecone\": \"^5.1.2\",\n \"@playwright/test\": \"^1.52.0\",\n \"@qdrant/js-client-rest\": \"^1.14.0\",\n \"@stylistic/eslint-plugin-js\": \"^4.2.0\",\n \"@types/argparse\": \"^2.0.17\",\n \"@types/cli-progress\": \"^3.11.6\",\n \"@types/node\": \"^22.15.17\",\n \"@types/semver\": \"^7.7.0\",\n \"@types/uuid\": \"^10.0.0\",\n \"chromadb\": \"^2.4.3\",\n \"cohere-ai\": \"^7.17.1\",\n \"eslint-plugin-simple-import-sort\": \"^12.1.1\",\n \"eslint-plugin-unused-imports\": \"^4.1.4\",\n \"langchain\": \"^0.3.24\",\n \"llamaindex\": \"^0.9.19\",\n \"nock\": \"^14.0.4\",\n \"openai\": \"^4.98.0\",\n \"playwright\": \"^1.52.0\",\n \"puppeteer\": \"^24.8.2\",\n \"puppeteer-core\": \"^24.8.2\",\n \"together-ai\": \"^0.14.0\",\n \"tsup\": \"^8.4.0\",\n \"tsx\": \"^4.19.4\",\n \"typescript\": \"^5.8.3\",\n \"typescript-eslint\": \"^8.32.0\"\n },\n \"dependencies\": {\n \"@grpc/grpc-js\": \"^1.13.3\",\n \"@opentelemetry/api\": \"^1.9.0\",\n \"@opentelemetry/context-async-hooks\": \"^1.30.1\",\n \"@opentelemetry/core\": \"^1.30.1\",\n \"@opentelemetry/exporter-trace-otlp-grpc\": \"^0.57.2\",\n \"@opentelemetry/exporter-trace-otlp-proto\": \"^0.57.2\",\n \"@opentelemetry/instrumentation\": \"^0.57.2\",\n \"@opentelemetry/otlp-exporter-base\": \"^0.57.2\",\n \"@opentelemetry/otlp-grpc-exporter-base\": \"^0.57.2\",\n \"@opentelemetry/resources\": \"^1.30.1\",\n \"@opentelemetry/sdk-node\": \"^0.57.2\",\n \"@opentelemetry/sdk-trace-base\": \"^1.30.1\",\n \"@opentelemetry/sdk-trace-base-v2\": \"npm:@opentelemetry/sdk-trace-base@2.0.0\",\n \"@opentelemetry/sdk-trace-node\": \"^1.30.1\",\n \"@opentelemetry/semantic-conventions\": \"^1.33.0\",\n \"@traceloop/ai-semantic-conventions\": \"^0.12.0\",\n \"@traceloop/instrumentation-anthropic\": \"^0.12.0\",\n \"@traceloop/instrumentation-azure\": \"^0.12.0\",\n \"@traceloop/instrumentation-bedrock\": \"^0.12.0\",\n \"@traceloop/instrumentation-chromadb\": \"^0.12.0\",\n \"@traceloop/instrumentation-cohere\": \"^0.12.0\",\n \"@traceloop/instrumentation-langchain\": \"^0.12.0\",\n \"@traceloop/instrumentation-llamaindex\": \"^0.12.0\",\n \"@traceloop/instrumentation-openai\": \"^0.12.0\",\n \"@traceloop/instrumentation-pinecone\": \"^0.12.0\",\n \"@traceloop/instrumentation-qdrant\": \"^0.12.0\",\n \"@traceloop/instrumentation-together\": \"^0.12.1\",\n \"@traceloop/instrumentation-vertexai\": \"^0.12.0\",\n \"argparse\": \"^2.0.1\",\n \"cli-progress\": \"^3.12.0\",\n \"esbuild\": \"^0.25.4\",\n \"glob\": \"^11.0.2\",\n \"pino\": \"^9.6.0\",\n \"pino-pretty\": \"^13.0.0\",\n \"uuid\": \"^11.1.0\"\n }\n}","import { SpanContext, TraceFlags } from '@opentelemetry/api';\nimport pino, { Level } from 'pino';\nimport pinoPretty from 'pino-pretty';\nimport { v4 as uuidv4 } from 'uuid';\n\nimport { LaminarSpanContext } from './types';\n\nexport function initializeLogger(options?: { colorize?: boolean, level?: Level }) {\n const colorize = options?.colorize ?? true;\n const level = options?.level\n ?? (process.env.LMNR_LOG_LEVEL?.toLowerCase()?.trim() as Level)\n ?? 'info';\n\n return pino(pinoPretty({\n colorize,\n minimumLevel: level,\n }));\n}\n\nconst logger = initializeLogger();\n\nexport type StringUUID = `${string}-${string}-${string}-${string}-${string}`;\n\nexport const isStringUUID = (id: string): id is StringUUID =>\n /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/.test(id);\n\nexport const NIL_UUID: StringUUID = '00000000-0000-0000-0000-000000000000';\n\nexport const newUUID = (): StringUUID => {\n // crypto.randomUUID is available in most of the modern browsers and node,\n // but is not available in \"insecure\" contexts, e.g. not https, not localhost\n // so we fallback to uuidv4 in those cases, which is less secure, but works\n // just fine.\n if (typeof crypto !== 'undefined' && typeof crypto.randomUUID === 'function') {\n return crypto.randomUUID();\n } else {\n return uuidv4() as `${string}-${string}-${string}-${string}-${string}`;\n }\n};\n\nexport const otelSpanIdToUUID = (spanId: string): string => {\n let id = spanId.toLowerCase();\n if (id.startsWith('0x')) {\n id = id.slice(2);\n }\n if (id.length !== 16) {\n logger.warn(`Span ID ${spanId} is not 16 hex chars long. ` +\n 'This is not a valid OpenTelemetry span ID.');\n }\n\n if (!/^[0-9a-f]+$/.test(id)) {\n logger.error(`Span ID ${spanId} is not a valid hex string. ` +\n 'Generating a random UUID instead.');\n return newUUID();\n }\n\n return id.padStart(32, '0').replace(/^(.{8})(.{4})(.{4})(.{4})(.{12})$/, '$1-$2-$3-$4-$5');\n};\n\nexport const otelTraceIdToUUID = (traceId: string): string => {\n let id = traceId.toLowerCase();\n if (id.startsWith('0x')) {\n id = id.slice(2);\n }\n if (id.length !== 32) {\n logger.warn(`Trace ID ${traceId} is not 32 hex chars long. ` +\n 'This is not a valid OpenTelemetry trace ID.');\n }\n if (!/^[0-9a-f]+$/.test(id)) {\n logger.error(`Trace ID ${traceId} is not a valid hex string. ` +\n 'Generating a random UUID instead.');\n return newUUID();\n }\n\n return id.replace(/^(.{8})(.{4})(.{4})(.{4})(.{12})$/, '$1-$2-$3-$4-$5');\n};\n\nexport const uuidToOtelTraceId = (uuid: string): string => uuid.replace(/-/g, '');\nexport const uuidToOtelSpanId = (uuid: string): string => uuid.replace(/-/g, '').slice(16);\n\n/**\n * This is a simple implementation of a semaphore to replicate\n * the behavior of the `asyncio.Semaphore` in Python.\n */\nexport class Semaphore {\n /**\n * Number of permits available.\n */\n private _value: number;\n /**\n * List of promises that will be resolved when a permit becomes available.\n */\n private _waiters: ((...args: any[]) => any)[] = [];\n\n constructor(value = 1) {\n if (value < 0) {\n throw new Error(\"Semaphore value must be >= 0\");\n }\n this._value = value;\n this._waiters = [];\n }\n\n async acquire() {\n if (this._value > 0) {\n this._value--;\n return;\n }\n\n // Create a promise that will be resolved when a permit becomes available\n return new Promise(resolve => {\n this._waiters.push(resolve);\n });\n }\n\n release() {\n if (this._waiters.length > 0) {\n // If there are waiters, wake up the first one\n const resolve = this._waiters.shift();\n resolve?.();\n } else {\n this._value++;\n }\n }\n\n // Python-like context manager functionality\n async using<T>(fn: (...args: any[]) => Promise<T>) {\n try {\n await this.acquire();\n return await fn();\n } finally {\n this.release();\n }\n }\n}\n\nexport const tryToOtelSpanContext = (\n spanContext: LaminarSpanContext | Record<string, unknown> | string | SpanContext,\n): SpanContext => {\n if (typeof spanContext === 'string') {\n try {\n const record = JSON.parse(spanContext) as Record<string, unknown>;\n return recordToOtelSpanContext(record);\n } catch (e) {\n throw new Error(`Failed to parse span context ${spanContext}. ` +\n 'The string must be a json representation of a LaminarSpanContext.'\n + `Error: ${e instanceof Error ? e.message : String(e)}`);\n }\n } else if (isRecord(spanContext)) {\n // This covers the `LaminarSpanContext` case too.\n return recordToOtelSpanContext(spanContext);\n } else if (typeof spanContext.traceId === 'string'\n && typeof spanContext.spanId === 'string'\n && spanContext.traceId.length === 32\n && spanContext.spanId.length === 16) {\n logger.warn('The span context is already an OpenTelemetry SpanContext. ' +\n 'Returning it as is. Please use `LaminarSpanContext` objects instead.');\n return spanContext;\n }\n else {\n throw new Error(`Invalid span context ${JSON.stringify(spanContext)}. ` +\n 'Must be a LaminarSpanContext or its json representation.');\n }\n};\n\nconst recordToOtelSpanContext = (record: Record<string, unknown>): SpanContext => {\n if (typeof record.spanId === 'string' && typeof record.traceId === 'string') {\n return {\n spanId: uuidToOtelSpanId(record?.spanId ?? record?.['span_id']),\n traceId: uuidToOtelTraceId(record?.traceId ?? record?.['trace_id']),\n isRemote: record?.isRemote ?? record?.['is_remote'] ?? false,\n traceFlags: record?.traceFlags ?? TraceFlags.SAMPLED,\n } as SpanContext;\n } else {\n throw new Error(`Invalid span context ${JSON.stringify(record)}. ` +\n 'Must be a json representation of a LaminarSpanContext.');\n }\n};\n\nconst isRecord = (value: unknown): value is Record<string, unknown> =>\n typeof value === 'object' && !Array.isArray(value) && value !== null;\n"],"mappings":"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","names":["logger","args"]}
|
package/dist/index.d.mts
CHANGED
|
@@ -264,6 +264,8 @@ declare class Laminar {
|
|
|
264
264
|
* Next.js place Laminar initialize in `instrumentation.ts`, and then patch
|
|
265
265
|
* the modules in server components or API routes.
|
|
266
266
|
*
|
|
267
|
+
* Make sure to call this after {@link Laminar.initialize()}.
|
|
268
|
+
*
|
|
267
269
|
* @param {InitializeOptions["instrumentModules"]} modules - Record of modules to instrument.
|
|
268
270
|
*/
|
|
269
271
|
static patch(modules: InitializeOptions["instrumentModules"]): void;
|
package/dist/index.d.ts
CHANGED
|
@@ -264,6 +264,8 @@ declare class Laminar {
|
|
|
264
264
|
* Next.js place Laminar initialize in `instrumentation.ts`, and then patch
|
|
265
265
|
* the modules in server components or API routes.
|
|
266
266
|
*
|
|
267
|
+
* Make sure to call this after {@link Laminar.initialize()}.
|
|
268
|
+
*
|
|
267
269
|
* @param {InitializeOptions["instrumentModules"]} modules - Record of modules to instrument.
|
|
268
270
|
*/
|
|
269
271
|
static patch(modules: InitializeOptions["instrumentModules"]): void;
|
package/dist/index.js
CHANGED
|
@@ -393,7 +393,7 @@ var AgentResource = class extends BaseResource {
|
|
|
393
393
|
};
|
|
394
394
|
|
|
395
395
|
// package.json
|
|
396
|
-
var version = "0.6.
|
|
396
|
+
var version = "0.6.2";
|
|
397
397
|
|
|
398
398
|
// src/version.ts
|
|
399
399
|
var getLangVersion = () => {
|
|
@@ -812,11 +812,14 @@ var Laminar = class {
|
|
|
812
812
|
* Next.js place Laminar initialize in `instrumentation.ts`, and then patch
|
|
813
813
|
* the modules in server components or API routes.
|
|
814
814
|
*
|
|
815
|
+
* Make sure to call this after {@link Laminar.initialize()}.
|
|
816
|
+
*
|
|
815
817
|
* @param {InitializeOptions["instrumentModules"]} modules - Record of modules to instrument.
|
|
816
818
|
*/
|
|
817
819
|
static patch(modules) {
|
|
818
820
|
if (!this.isInitialized) {
|
|
819
|
-
|
|
821
|
+
logger3.warn("Laminar must be initialized before patching modules. Skipping patch.");
|
|
822
|
+
return;
|
|
820
823
|
}
|
|
821
824
|
if (!modules || Object.keys(modules).length === 0) {
|
|
822
825
|
throw new Error("Pass at least one module to patch");
|