observability-toolkit 1.8.5 → 2.1.0
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/README.md +167 -281
- package/dist/__tests__/find-constant-dedup.test.d.ts +11 -0
- package/dist/__tests__/find-constant-dedup.test.d.ts.map +1 -0
- package/dist/__tests__/find-constant-dedup.test.js +132 -0
- package/dist/__tests__/find-constant-dedup.test.js.map +1 -0
- package/dist/backends/backend-schemas.d.ts +309 -0
- package/dist/backends/backend-schemas.d.ts.map +1 -0
- package/dist/backends/backend-schemas.js +215 -0
- package/dist/backends/backend-schemas.js.map +1 -0
- package/dist/backends/cloud.d.ts +46 -0
- package/dist/backends/cloud.d.ts.map +1 -0
- package/dist/backends/cloud.js +520 -0
- package/dist/backends/cloud.js.map +1 -0
- package/dist/backends/cloud.test.d.ts +2 -0
- package/dist/backends/cloud.test.d.ts.map +1 -0
- package/dist/backends/cloud.test.js +436 -0
- package/dist/backends/cloud.test.js.map +1 -0
- package/dist/backends/index.d.ts +672 -236
- package/dist/backends/index.d.ts.map +1 -1
- package/dist/backends/index.js +334 -0
- package/dist/backends/index.js.map +1 -1
- package/dist/backends/index.test.js +606 -31
- package/dist/backends/index.test.js.map +1 -1
- package/dist/backends/local-jsonl-boolean-search.test.js +8 -7
- package/dist/backends/local-jsonl-boolean-search.test.js.map +1 -1
- package/dist/backends/local-jsonl-cache.test.js +33 -31
- package/dist/backends/local-jsonl-cache.test.js.map +1 -1
- package/dist/backends/local-jsonl-circuit-breaker.test.js +9 -7
- package/dist/backends/local-jsonl-circuit-breaker.test.js.map +1 -1
- package/dist/backends/local-jsonl-export.test.js +73 -58
- package/dist/backends/local-jsonl-export.test.js.map +1 -1
- package/dist/backends/local-jsonl-index.test.js +52 -50
- package/dist/backends/local-jsonl-index.test.js.map +1 -1
- package/dist/backends/local-jsonl-logs.test.js +47 -31
- package/dist/backends/local-jsonl-logs.test.js.map +1 -1
- package/dist/backends/local-jsonl-metrics.test.js +85 -82
- package/dist/backends/local-jsonl-metrics.test.js.map +1 -1
- package/dist/backends/local-jsonl-otlp-unwrap.test.d.ts +2 -0
- package/dist/backends/local-jsonl-otlp-unwrap.test.d.ts.map +1 -0
- package/dist/backends/local-jsonl-otlp-unwrap.test.js +602 -0
- package/dist/backends/local-jsonl-otlp-unwrap.test.js.map +1 -0
- package/dist/backends/local-jsonl-traces.test.js +161 -147
- package/dist/backends/local-jsonl-traces.test.js.map +1 -1
- package/dist/backends/local-jsonl.d.ts +64 -5
- package/dist/backends/local-jsonl.d.ts.map +1 -1
- package/dist/backends/local-jsonl.js +1821 -612
- package/dist/backends/local-jsonl.js.map +1 -1
- package/dist/backends/shared.d.ts +9 -0
- package/dist/backends/shared.d.ts.map +1 -0
- package/dist/backends/shared.js +9 -0
- package/dist/backends/shared.js.map +1 -0
- package/dist/generated/opentelemetry/proto/collector/logs/v1/logs_service_pb.d.ts +40 -0
- package/dist/generated/opentelemetry/proto/collector/logs/v1/logs_service_pb.d.ts.map +1 -0
- package/dist/generated/opentelemetry/proto/collector/logs/v1/logs_service_pb.js +27 -0
- package/dist/generated/opentelemetry/proto/collector/logs/v1/logs_service_pb.js.map +1 -0
- package/dist/generated/opentelemetry/proto/collector/metrics/v1/metrics_service_pb.d.ts +106 -0
- package/dist/generated/opentelemetry/proto/collector/metrics/v1/metrics_service_pb.d.ts.map +1 -0
- package/dist/generated/opentelemetry/proto/collector/metrics/v1/metrics_service_pb.js +43 -0
- package/dist/generated/opentelemetry/proto/collector/metrics/v1/metrics_service_pb.js.map +1 -0
- package/dist/generated/opentelemetry/proto/collector/profiles/v1development/profiles_service_pb.d.ts +111 -0
- package/dist/generated/opentelemetry/proto/collector/profiles/v1development/profiles_service_pb.d.ts.map +1 -0
- package/dist/generated/opentelemetry/proto/collector/profiles/v1development/profiles_service_pb.js +42 -0
- package/dist/generated/opentelemetry/proto/collector/profiles/v1development/profiles_service_pb.js.map +1 -0
- package/dist/generated/opentelemetry/proto/collector/trace/v1/trace_service_pb.d.ts +106 -0
- package/dist/generated/opentelemetry/proto/collector/trace/v1/trace_service_pb.d.ts.map +1 -0
- package/dist/generated/opentelemetry/proto/collector/trace/v1/trace_service_pb.js +43 -0
- package/dist/generated/opentelemetry/proto/collector/trace/v1/trace_service_pb.js.map +1 -0
- package/dist/generated/opentelemetry/proto/common/v1/common_pb.d.ts +243 -0
- package/dist/generated/opentelemetry/proto/common/v1/common_pb.d.ts.map +1 -0
- package/dist/generated/opentelemetry/proto/common/v1/common_pb.js +49 -0
- package/dist/generated/opentelemetry/proto/common/v1/common_pb.js.map +1 -0
- package/dist/generated/opentelemetry/proto/logs/v1/logs_pb.d.ts +90 -0
- package/dist/generated/opentelemetry/proto/logs/v1/logs_pb.d.ts.map +1 -0
- package/dist/generated/opentelemetry/proto/logs/v1/logs_pb.js +66 -0
- package/dist/generated/opentelemetry/proto/logs/v1/logs_pb.js.map +1 -0
- package/dist/generated/opentelemetry/proto/metrics/v1/metrics_pb.d.ts +1134 -0
- package/dist/generated/opentelemetry/proto/metrics/v1/metrics_pb.d.ts.map +1 -0
- package/dist/generated/opentelemetry/proto/metrics/v1/metrics_pb.js +223 -0
- package/dist/generated/opentelemetry/proto/metrics/v1/metrics_pb.js.map +1 -0
- package/dist/generated/opentelemetry/proto/profiles/v1development/profiles_pb.d.ts +678 -0
- package/dist/generated/opentelemetry/proto/profiles/v1development/profiles_pb.d.ts.map +1 -0
- package/dist/generated/opentelemetry/proto/profiles/v1development/profiles_pb.js +107 -0
- package/dist/generated/opentelemetry/proto/profiles/v1development/profiles_pb.js.map +1 -0
- package/dist/generated/opentelemetry/proto/resource/v1/resource_pb.d.ts +46 -0
- package/dist/generated/opentelemetry/proto/resource/v1/resource_pb.d.ts.map +1 -0
- package/dist/generated/opentelemetry/proto/resource/v1/resource_pb.js +25 -0
- package/dist/generated/opentelemetry/proto/resource/v1/resource_pb.js.map +1 -0
- package/dist/generated/opentelemetry/proto/trace/v1/trace_pb.d.ts +569 -0
- package/dist/generated/opentelemetry/proto/trace/v1/trace_pb.d.ts.map +1 -0
- package/dist/generated/opentelemetry/proto/trace/v1/trace_pb.js +195 -0
- package/dist/generated/opentelemetry/proto/trace/v1/trace_pb.js.map +1 -0
- package/dist/lib/agent-judge/agent-as-judge.d.ts +157 -0
- package/dist/lib/agent-judge/agent-as-judge.d.ts.map +1 -0
- package/dist/lib/agent-judge/agent-as-judge.js +137 -0
- package/dist/lib/agent-judge/agent-as-judge.js.map +1 -0
- package/dist/lib/agent-judge/agent-as-judge.test.d.ts +5 -0
- package/dist/lib/agent-judge/agent-as-judge.test.d.ts.map +1 -0
- package/dist/lib/agent-judge/agent-as-judge.test.js +839 -0
- package/dist/lib/agent-judge/agent-as-judge.test.js.map +1 -0
- package/dist/lib/agent-judge/agent-eval-metrics.d.ts +293 -0
- package/dist/lib/agent-judge/agent-eval-metrics.d.ts.map +1 -0
- package/dist/lib/agent-judge/agent-eval-metrics.js +715 -0
- package/dist/lib/agent-judge/agent-eval-metrics.js.map +1 -0
- package/dist/lib/agent-judge/agent-eval-metrics.test.d.ts +5 -0
- package/dist/lib/agent-judge/agent-eval-metrics.test.d.ts.map +1 -0
- package/dist/lib/agent-judge/agent-eval-metrics.test.js +676 -0
- package/dist/lib/agent-judge/agent-eval-metrics.test.js.map +1 -0
- package/dist/lib/agent-judge/agent-judge-classes.d.ts +95 -0
- package/dist/lib/agent-judge/agent-judge-classes.d.ts.map +1 -0
- package/dist/lib/agent-judge/agent-judge-classes.js +222 -0
- package/dist/lib/agent-judge/agent-judge-classes.js.map +1 -0
- package/dist/lib/agent-judge/agent-judge-classes.test.d.ts +6 -0
- package/dist/lib/agent-judge/agent-judge-classes.test.d.ts.map +1 -0
- package/dist/lib/agent-judge/agent-judge-classes.test.js +271 -0
- package/dist/lib/agent-judge/agent-judge-classes.test.js.map +1 -0
- package/dist/lib/agent-judge/agent-judge-consensus.d.ts +58 -0
- package/dist/lib/agent-judge/agent-judge-consensus.d.ts.map +1 -0
- package/dist/lib/agent-judge/agent-judge-consensus.js +149 -0
- package/dist/lib/agent-judge/agent-judge-consensus.js.map +1 -0
- package/dist/lib/agent-judge/agent-judge-consensus.test.d.ts +2 -0
- package/dist/lib/agent-judge/agent-judge-consensus.test.d.ts.map +1 -0
- package/dist/lib/agent-judge/agent-judge-consensus.test.js +170 -0
- package/dist/lib/agent-judge/agent-judge-consensus.test.js.map +1 -0
- package/dist/lib/agent-judge/agent-judge-verification.d.ts +89 -0
- package/dist/lib/agent-judge/agent-judge-verification.d.ts.map +1 -0
- package/dist/lib/agent-judge/agent-judge-verification.js +235 -0
- package/dist/lib/agent-judge/agent-judge-verification.js.map +1 -0
- package/dist/lib/agent-judge/agent-judge-verification.test.d.ts +5 -0
- package/dist/lib/agent-judge/agent-judge-verification.test.d.ts.map +1 -0
- package/dist/lib/agent-judge/agent-judge-verification.test.js +399 -0
- package/dist/lib/agent-judge/agent-judge-verification.test.js.map +1 -0
- package/dist/lib/audit/agent-auditor-scoring.d.ts +167 -0
- package/dist/lib/audit/agent-auditor-scoring.d.ts.map +1 -0
- package/dist/lib/audit/agent-auditor-scoring.js +338 -0
- package/dist/lib/audit/agent-auditor-scoring.js.map +1 -0
- package/dist/lib/audit/agent-auditor-scoring.test.d.ts +2 -0
- package/dist/lib/audit/agent-auditor-scoring.test.d.ts.map +1 -0
- package/dist/lib/audit/agent-auditor-scoring.test.js +576 -0
- package/dist/lib/audit/agent-auditor-scoring.test.js.map +1 -0
- package/dist/lib/audit/audit-record.d.ts +139 -0
- package/dist/lib/audit/audit-record.d.ts.map +1 -0
- package/dist/lib/audit/audit-record.js +288 -0
- package/dist/lib/audit/audit-record.js.map +1 -0
- package/dist/lib/audit/audit-record.test.d.ts +5 -0
- package/dist/lib/audit/audit-record.test.d.ts.map +1 -0
- package/dist/lib/audit/audit-record.test.js +258 -0
- package/dist/lib/audit/audit-record.test.js.map +1 -0
- package/dist/lib/audit/audit-scoring-constants.d.ts +57 -0
- package/dist/lib/audit/audit-scoring-constants.d.ts.map +1 -0
- package/dist/lib/audit/audit-scoring-constants.js +59 -0
- package/dist/lib/audit/audit-scoring-constants.js.map +1 -0
- package/dist/lib/audit/compliance-report.d.ts +125 -0
- package/dist/lib/audit/compliance-report.d.ts.map +1 -0
- package/dist/lib/audit/compliance-report.js +205 -0
- package/dist/lib/audit/compliance-report.js.map +1 -0
- package/dist/lib/audit/compliance-report.test.d.ts +5 -0
- package/dist/lib/audit/compliance-report.test.d.ts.map +1 -0
- package/dist/lib/audit/compliance-report.test.js +290 -0
- package/dist/lib/audit/compliance-report.test.js.map +1 -0
- package/dist/lib/audit/retention-guard.d.ts +41 -0
- package/dist/lib/audit/retention-guard.d.ts.map +1 -0
- package/dist/lib/audit/retention-guard.js +103 -0
- package/dist/lib/audit/retention-guard.js.map +1 -0
- package/dist/lib/audit/retention-guard.test.d.ts +5 -0
- package/dist/lib/audit/retention-guard.test.d.ts.map +1 -0
- package/dist/lib/audit/retention-guard.test.js +109 -0
- package/dist/lib/audit/retention-guard.test.js.map +1 -0
- package/dist/lib/audit/skill-auditor-scoring.d.ts +69 -0
- package/dist/lib/audit/skill-auditor-scoring.d.ts.map +1 -0
- package/dist/lib/audit/skill-auditor-scoring.js +149 -0
- package/dist/lib/audit/skill-auditor-scoring.js.map +1 -0
- package/dist/lib/audit/skill-auditor-scoring.test.d.ts +2 -0
- package/dist/lib/audit/skill-auditor-scoring.test.d.ts.map +1 -0
- package/dist/lib/audit/skill-auditor-scoring.test.js +369 -0
- package/dist/lib/audit/skill-auditor-scoring.test.js.map +1 -0
- package/dist/lib/audit/verification-events.d.ts +119 -0
- package/dist/lib/audit/verification-events.d.ts.map +1 -0
- package/dist/lib/audit/verification-events.js +175 -0
- package/dist/lib/audit/verification-events.js.map +1 -0
- package/dist/lib/audit/verification-events.test.d.ts +5 -0
- package/dist/lib/audit/verification-events.test.d.ts.map +1 -0
- package/dist/lib/audit/verification-events.test.js +197 -0
- package/dist/lib/audit/verification-events.test.js.map +1 -0
- package/dist/lib/core/constants-models.d.ts +90 -0
- package/dist/lib/core/constants-models.d.ts.map +1 -0
- package/dist/lib/core/constants-models.js +208 -0
- package/dist/lib/core/constants-models.js.map +1 -0
- package/dist/lib/core/constants-otel.d.ts +68 -0
- package/dist/lib/core/constants-otel.d.ts.map +1 -0
- package/dist/lib/core/constants-otel.js +128 -0
- package/dist/lib/core/constants-otel.js.map +1 -0
- package/dist/lib/core/constants-symlink.test.d.ts.map +1 -0
- package/dist/lib/{constants-symlink.test.js → core/constants-symlink.test.js} +25 -24
- package/dist/lib/core/constants-symlink.test.js.map +1 -0
- package/dist/lib/core/constants-telemetry.d.ts +21 -0
- package/dist/lib/core/constants-telemetry.d.ts.map +1 -0
- package/dist/lib/core/constants-telemetry.js +162 -0
- package/dist/lib/core/constants-telemetry.js.map +1 -0
- package/dist/lib/core/constants.d.ts +152 -0
- package/dist/lib/core/constants.d.ts.map +1 -0
- package/dist/lib/core/constants.js +223 -0
- package/dist/lib/core/constants.js.map +1 -0
- package/dist/lib/core/constants.test.d.ts.map +1 -0
- package/dist/lib/{constants.test.js → core/constants.test.js} +198 -82
- package/dist/lib/core/constants.test.js.map +1 -0
- package/dist/lib/core/doc-sync.test.d.ts +9 -0
- package/dist/lib/core/doc-sync.test.d.ts.map +1 -0
- package/dist/lib/core/doc-sync.test.js +159 -0
- package/dist/lib/core/doc-sync.test.js.map +1 -0
- package/dist/lib/core/edge-cases.test.d.ts.map +1 -0
- package/dist/lib/{edge-cases.test.js → core/edge-cases.test.js} +76 -73
- package/dist/lib/core/edge-cases.test.js.map +1 -0
- package/dist/lib/{file-utils.d.ts → core/file-utils.d.ts} +63 -8
- package/dist/lib/core/file-utils.d.ts.map +1 -0
- package/dist/lib/{file-utils.js → core/file-utils.js} +186 -93
- package/dist/lib/core/file-utils.js.map +1 -0
- package/dist/lib/core/file-utils.test-constants.d.ts +38 -0
- package/dist/lib/core/file-utils.test-constants.d.ts.map +1 -0
- package/dist/lib/core/file-utils.test-constants.js +40 -0
- package/dist/lib/core/file-utils.test-constants.js.map +1 -0
- package/dist/lib/core/file-utils.test.d.ts.map +1 -0
- package/dist/lib/{file-utils.test.js → core/file-utils.test.js} +240 -214
- package/dist/lib/core/file-utils.test.js.map +1 -0
- package/dist/lib/{input-validator.d.ts → core/input-validator.d.ts} +30 -20
- package/dist/lib/core/input-validator.d.ts.map +1 -0
- package/dist/lib/core/input-validator.fuzz.test.d.ts.map +1 -0
- package/dist/lib/{input-validator.fuzz.test.js → core/input-validator.fuzz.test.js} +41 -29
- package/dist/lib/core/input-validator.fuzz.test.js.map +1 -0
- package/dist/lib/{input-validator.js → core/input-validator.js} +83 -39
- package/dist/lib/core/input-validator.js.map +1 -0
- package/dist/lib/core/input-validator.test.d.ts.map +1 -0
- package/dist/lib/{input-validator.test.js → core/input-validator.test.js} +95 -45
- package/dist/lib/core/input-validator.test.js.map +1 -0
- package/dist/lib/{logger.d.ts → core/logger.d.ts} +4 -18
- package/dist/lib/core/logger.d.ts.map +1 -0
- package/dist/lib/core/logger.js +104 -0
- package/dist/lib/core/logger.js.map +1 -0
- package/dist/lib/core/logger.test.d.ts.map +1 -0
- package/dist/lib/core/logger.test.js.map +1 -0
- package/dist/lib/core/schema-types.d.ts +37 -0
- package/dist/lib/core/schema-types.d.ts.map +1 -0
- package/dist/lib/core/schema-types.js +29 -0
- package/dist/lib/core/schema-types.js.map +1 -0
- package/dist/lib/{server-utils.d.ts → core/server-utils.d.ts} +11 -1
- package/dist/lib/core/server-utils.d.ts.map +1 -0
- package/dist/lib/{server-utils.js → core/server-utils.js} +25 -5
- package/dist/lib/core/server-utils.js.map +1 -0
- package/dist/lib/core/shared-schemas.d.ts +301 -0
- package/dist/lib/core/shared-schemas.d.ts.map +1 -0
- package/dist/lib/core/shared-schemas.js +222 -0
- package/dist/lib/core/shared-schemas.js.map +1 -0
- package/dist/lib/core/shared-schemas.test.d.ts.map +1 -0
- package/dist/lib/{shared-schemas.test.js → core/shared-schemas.test.js} +48 -18
- package/dist/lib/core/shared-schemas.test.js.map +1 -0
- package/dist/lib/core/units.d.ts +67 -0
- package/dist/lib/core/units.d.ts.map +1 -0
- package/dist/lib/core/units.js +88 -0
- package/dist/lib/core/units.js.map +1 -0
- package/dist/lib/cost/cost-estimation.d.ts +264 -0
- package/dist/lib/cost/cost-estimation.d.ts.map +1 -0
- package/dist/lib/cost/cost-estimation.js +541 -0
- package/dist/lib/cost/cost-estimation.js.map +1 -0
- package/dist/lib/cost/cost-estimation.test.d.ts +5 -0
- package/dist/lib/cost/cost-estimation.test.d.ts.map +1 -0
- package/dist/lib/cost/cost-estimation.test.js +701 -0
- package/dist/lib/cost/cost-estimation.test.js.map +1 -0
- package/dist/lib/cost/pricing-cache.d.ts +59 -0
- package/dist/lib/cost/pricing-cache.d.ts.map +1 -0
- package/dist/lib/cost/pricing-cache.js +120 -0
- package/dist/lib/cost/pricing-cache.js.map +1 -0
- package/dist/lib/cost/pricing-cache.test.d.ts +5 -0
- package/dist/lib/cost/pricing-cache.test.d.ts.map +1 -0
- package/dist/lib/cost/pricing-cache.test.js +176 -0
- package/dist/lib/cost/pricing-cache.test.js.map +1 -0
- package/dist/lib/dashboard-file-utils.d.ts +35 -0
- package/dist/lib/dashboard-file-utils.d.ts.map +1 -0
- package/dist/lib/dashboard-file-utils.js +94 -0
- package/dist/lib/dashboard-file-utils.js.map +1 -0
- package/dist/lib/{error-sanitizer.d.ts → errors/error-sanitizer.d.ts} +5 -0
- package/dist/lib/errors/error-sanitizer.d.ts.map +1 -0
- package/dist/lib/{error-sanitizer.js → errors/error-sanitizer.js} +8 -6
- package/dist/lib/errors/error-sanitizer.js.map +1 -0
- package/dist/lib/errors/error-sanitizer.test.d.ts.map +1 -0
- package/dist/lib/{error-sanitizer.test.js → errors/error-sanitizer.test.js} +17 -11
- package/dist/lib/errors/error-sanitizer.test.js.map +1 -0
- package/dist/lib/{error-types.d.ts → errors/error-types.d.ts} +5 -0
- package/dist/lib/errors/error-types.d.ts.map +1 -0
- package/dist/lib/{error-types.js → errors/error-types.js} +34 -1
- package/dist/lib/errors/error-types.js.map +1 -0
- package/dist/lib/errors/error-types.test.d.ts.map +1 -0
- package/dist/lib/{error-types.test.js → errors/error-types.test.js} +51 -1
- package/dist/lib/errors/error-types.test.js.map +1 -0
- package/dist/lib/errors/query-sanitizer.d.ts.map +1 -0
- package/dist/lib/{query-sanitizer.js → errors/query-sanitizer.js} +9 -1
- package/dist/lib/errors/query-sanitizer.js.map +1 -0
- package/dist/lib/errors/query-sanitizer.test.d.ts.map +1 -0
- package/dist/lib/{query-sanitizer.test.js → errors/query-sanitizer.test.js} +9 -6
- package/dist/lib/errors/query-sanitizer.test.js.map +1 -0
- package/dist/lib/exports/confident-export.d.ts +105 -0
- package/dist/lib/exports/confident-export.d.ts.map +1 -0
- package/dist/lib/exports/confident-export.js +385 -0
- package/dist/lib/exports/confident-export.js.map +1 -0
- package/dist/lib/exports/confident-export.test.d.ts +7 -0
- package/dist/lib/exports/confident-export.test.d.ts.map +1 -0
- package/dist/lib/exports/confident-export.test.js +848 -0
- package/dist/lib/exports/confident-export.test.js.map +1 -0
- package/dist/lib/exports/datadog-export.d.ts +200 -0
- package/dist/lib/exports/datadog-export.d.ts.map +1 -0
- package/dist/lib/exports/datadog-export.js +488 -0
- package/dist/lib/exports/datadog-export.js.map +1 -0
- package/dist/lib/exports/datadog-export.test.d.ts +2 -0
- package/dist/lib/exports/datadog-export.test.d.ts.map +1 -0
- package/dist/lib/exports/datadog-export.test.js +890 -0
- package/dist/lib/exports/datadog-export.test.js.map +1 -0
- package/dist/lib/exports/export-config-schemas.d.ts +67 -0
- package/dist/lib/exports/export-config-schemas.d.ts.map +1 -0
- package/dist/lib/exports/export-config-schemas.js +120 -0
- package/dist/lib/exports/export-config-schemas.js.map +1 -0
- package/dist/lib/exports/export-config-schemas.test.d.ts +8 -0
- package/dist/lib/exports/export-config-schemas.test.d.ts.map +1 -0
- package/dist/lib/exports/export-config-schemas.test.js +503 -0
- package/dist/lib/exports/export-config-schemas.test.js.map +1 -0
- package/dist/lib/exports/export-utils.d.ts +127 -0
- package/dist/lib/exports/export-utils.d.ts.map +1 -0
- package/dist/lib/exports/export-utils.js +303 -0
- package/dist/lib/exports/export-utils.js.map +1 -0
- package/dist/lib/exports/export-utils.test.d.ts +5 -0
- package/dist/lib/exports/export-utils.test.d.ts.map +1 -0
- package/dist/lib/exports/export-utils.test.js +344 -0
- package/dist/lib/exports/export-utils.test.js.map +1 -0
- package/dist/lib/exports/langfuse-export.d.ts +129 -0
- package/dist/lib/exports/langfuse-export.d.ts.map +1 -0
- package/dist/lib/exports/langfuse-export.js +370 -0
- package/dist/lib/exports/langfuse-export.js.map +1 -0
- package/dist/lib/exports/langfuse-export.test.d.ts +7 -0
- package/dist/lib/exports/langfuse-export.test.d.ts.map +1 -0
- package/dist/lib/exports/langfuse-export.test.js +1020 -0
- package/dist/lib/exports/langfuse-export.test.js.map +1 -0
- package/dist/lib/{otlp-export.d.ts → exports/otlp-export.d.ts} +3 -2
- package/dist/lib/exports/otlp-export.d.ts.map +1 -0
- package/dist/lib/{otlp-export.js → exports/otlp-export.js} +51 -36
- package/dist/lib/exports/otlp-export.js.map +1 -0
- package/dist/lib/exports/otlp-format-converter.d.ts +70 -0
- package/dist/lib/exports/otlp-format-converter.d.ts.map +1 -0
- package/dist/lib/exports/otlp-format-converter.js +401 -0
- package/dist/lib/exports/otlp-format-converter.js.map +1 -0
- package/dist/lib/exports/otlp-proto-encode.d.ts +53 -0
- package/dist/lib/exports/otlp-proto-encode.d.ts.map +1 -0
- package/dist/lib/exports/otlp-proto-encode.js +165 -0
- package/dist/lib/exports/otlp-proto-encode.js.map +1 -0
- package/dist/lib/exports/otlp-proto-encode.test.d.ts +7 -0
- package/dist/lib/exports/otlp-proto-encode.test.d.ts.map +1 -0
- package/dist/lib/exports/otlp-proto-encode.test.js +997 -0
- package/dist/lib/exports/otlp-proto-encode.test.js.map +1 -0
- package/dist/lib/exports/phoenix-export.d.ts +119 -0
- package/dist/lib/exports/phoenix-export.d.ts.map +1 -0
- package/dist/lib/exports/phoenix-export.js +448 -0
- package/dist/lib/exports/phoenix-export.js.map +1 -0
- package/dist/lib/exports/phoenix-export.test.d.ts +11 -0
- package/dist/lib/exports/phoenix-export.test.d.ts.map +1 -0
- package/dist/lib/exports/phoenix-export.test.js +816 -0
- package/dist/lib/exports/phoenix-export.test.js.map +1 -0
- package/dist/lib/index.d.ts +16 -0
- package/dist/lib/index.d.ts.map +1 -0
- package/dist/lib/index.js +31 -0
- package/dist/lib/index.js.map +1 -0
- package/dist/lib/judge/evaluation-hooks-schemas.d.ts +186 -0
- package/dist/lib/judge/evaluation-hooks-schemas.d.ts.map +1 -0
- package/dist/lib/judge/evaluation-hooks-schemas.js +125 -0
- package/dist/lib/judge/evaluation-hooks-schemas.js.map +1 -0
- package/dist/lib/judge/evaluation-hooks.d.ts +88 -0
- package/dist/lib/judge/evaluation-hooks.d.ts.map +1 -0
- package/dist/lib/judge/evaluation-hooks.js +658 -0
- package/dist/lib/judge/evaluation-hooks.js.map +1 -0
- package/dist/lib/judge/evaluation-hooks.test.d.ts +8 -0
- package/dist/lib/judge/evaluation-hooks.test.d.ts.map +1 -0
- package/dist/lib/judge/evaluation-hooks.test.js +934 -0
- package/dist/lib/judge/evaluation-hooks.test.js.map +1 -0
- package/dist/lib/judge/llm-as-judge.d.ts +138 -0
- package/dist/lib/judge/llm-as-judge.d.ts.map +1 -0
- package/dist/lib/judge/llm-as-judge.js +103 -0
- package/dist/lib/judge/llm-as-judge.js.map +1 -0
- package/dist/lib/judge/llm-as-judge.test.d.ts +2 -0
- package/dist/lib/judge/llm-as-judge.test.d.ts.map +1 -0
- package/dist/lib/judge/llm-as-judge.test.js +2179 -0
- package/dist/lib/judge/llm-as-judge.test.js.map +1 -0
- package/dist/lib/judge/llm-judge-bias.d.ts +44 -0
- package/dist/lib/judge/llm-judge-bias.d.ts.map +1 -0
- package/dist/lib/judge/llm-judge-bias.js +130 -0
- package/dist/lib/judge/llm-judge-bias.js.map +1 -0
- package/dist/lib/judge/llm-judge-bias.test.d.ts +2 -0
- package/dist/lib/judge/llm-judge-bias.test.d.ts.map +1 -0
- package/dist/lib/judge/llm-judge-bias.test.js +380 -0
- package/dist/lib/judge/llm-judge-bias.test.js.map +1 -0
- package/dist/lib/judge/llm-judge-code.d.ts +99 -0
- package/dist/lib/judge/llm-judge-code.d.ts.map +1 -0
- package/dist/lib/judge/llm-judge-code.js +261 -0
- package/dist/lib/judge/llm-judge-code.js.map +1 -0
- package/dist/lib/judge/llm-judge-code.test.d.ts +2 -0
- package/dist/lib/judge/llm-judge-code.test.d.ts.map +1 -0
- package/dist/lib/judge/llm-judge-code.test.js +981 -0
- package/dist/lib/judge/llm-judge-code.test.js.map +1 -0
- package/dist/lib/judge/llm-judge-config.d.ts +241 -0
- package/dist/lib/judge/llm-judge-config.d.ts.map +1 -0
- package/dist/lib/judge/llm-judge-config.js +390 -0
- package/dist/lib/judge/llm-judge-config.js.map +1 -0
- package/dist/lib/judge/llm-judge-config.test.d.ts +5 -0
- package/dist/lib/judge/llm-judge-config.test.d.ts.map +1 -0
- package/dist/lib/judge/llm-judge-config.test.js +392 -0
- package/dist/lib/judge/llm-judge-config.test.js.map +1 -0
- package/dist/lib/judge/llm-judge-constants.d.ts +111 -0
- package/dist/lib/judge/llm-judge-constants.d.ts.map +1 -0
- package/dist/lib/judge/llm-judge-constants.js +150 -0
- package/dist/lib/judge/llm-judge-constants.js.map +1 -0
- package/dist/lib/judge/llm-judge-dag.d.ts +57 -0
- package/dist/lib/judge/llm-judge-dag.d.ts.map +1 -0
- package/dist/lib/judge/llm-judge-dag.js +217 -0
- package/dist/lib/judge/llm-judge-dag.js.map +1 -0
- package/dist/lib/judge/llm-judge-dag.test.d.ts +8 -0
- package/dist/lib/judge/llm-judge-dag.test.d.ts.map +1 -0
- package/dist/lib/judge/llm-judge-dag.test.js +973 -0
- package/dist/lib/judge/llm-judge-dag.test.js.map +1 -0
- package/dist/lib/judge/llm-judge-domain.d.ts +42 -0
- package/dist/lib/judge/llm-judge-domain.d.ts.map +1 -0
- package/dist/lib/judge/llm-judge-domain.js +167 -0
- package/dist/lib/judge/llm-judge-domain.js.map +1 -0
- package/dist/lib/judge/llm-judge-domain.test.d.ts +6 -0
- package/dist/lib/judge/llm-judge-domain.test.d.ts.map +1 -0
- package/dist/lib/judge/llm-judge-domain.test.js +337 -0
- package/dist/lib/judge/llm-judge-domain.test.js.map +1 -0
- package/dist/lib/judge/llm-judge-geval.d.ts +42 -0
- package/dist/lib/judge/llm-judge-geval.d.ts.map +1 -0
- package/dist/lib/judge/llm-judge-geval.js +213 -0
- package/dist/lib/judge/llm-judge-geval.js.map +1 -0
- package/dist/lib/judge/llm-judge-geval.test.d.ts +2 -0
- package/dist/lib/judge/llm-judge-geval.test.d.ts.map +1 -0
- package/dist/lib/judge/llm-judge-geval.test.js +556 -0
- package/dist/lib/judge/llm-judge-geval.test.js.map +1 -0
- package/dist/lib/judge/llm-judge-otel.test.d.ts +9 -0
- package/dist/lib/judge/llm-judge-otel.test.d.ts.map +1 -0
- package/dist/lib/judge/llm-judge-otel.test.js +91 -0
- package/dist/lib/judge/llm-judge-otel.test.js.map +1 -0
- package/dist/lib/judge/llm-judge-qag.d.ts +38 -0
- package/dist/lib/judge/llm-judge-qag.d.ts.map +1 -0
- package/dist/lib/judge/llm-judge-qag.js +205 -0
- package/dist/lib/judge/llm-judge-qag.js.map +1 -0
- package/dist/lib/judge/llm-judge-qag.test.d.ts +2 -0
- package/dist/lib/judge/llm-judge-qag.test.d.ts.map +1 -0
- package/dist/lib/judge/llm-judge-qag.test.js +386 -0
- package/dist/lib/judge/llm-judge-qag.test.js.map +1 -0
- package/dist/lib/judge/llm-judge-resilience.d.ts +74 -0
- package/dist/lib/judge/llm-judge-resilience.d.ts.map +1 -0
- package/dist/lib/judge/llm-judge-resilience.js +146 -0
- package/dist/lib/judge/llm-judge-resilience.js.map +1 -0
- package/dist/lib/judge/llm-judge-resilience.test.d.ts +2 -0
- package/dist/lib/judge/llm-judge-resilience.test.d.ts.map +1 -0
- package/dist/lib/judge/llm-judge-resilience.test.js +353 -0
- package/dist/lib/judge/llm-judge-resilience.test.js.map +1 -0
- package/dist/lib/judge/llm-judge-security.d.ts +106 -0
- package/dist/lib/judge/llm-judge-security.d.ts.map +1 -0
- package/dist/lib/judge/llm-judge-security.js +314 -0
- package/dist/lib/judge/llm-judge-security.js.map +1 -0
- package/dist/lib/judge/llm-judge-security.test.d.ts +2 -0
- package/dist/lib/judge/llm-judge-security.test.d.ts.map +1 -0
- package/dist/lib/judge/llm-judge-security.test.js +1011 -0
- package/dist/lib/judge/llm-judge-security.test.js.map +1 -0
- package/dist/lib/observability/context-accumulator.d.ts +32 -0
- package/dist/lib/observability/context-accumulator.d.ts.map +1 -0
- package/dist/lib/observability/context-accumulator.js +87 -0
- package/dist/lib/observability/context-accumulator.js.map +1 -0
- package/dist/lib/observability/evaluation-events.d.ts +35 -0
- package/dist/lib/observability/evaluation-events.d.ts.map +1 -0
- package/dist/lib/observability/evaluation-events.js +90 -0
- package/dist/lib/observability/evaluation-events.js.map +1 -0
- package/dist/lib/observability/file-span-exporter.d.ts +17 -0
- package/dist/lib/observability/file-span-exporter.d.ts.map +1 -0
- package/dist/lib/observability/file-span-exporter.js +49 -0
- package/dist/lib/observability/file-span-exporter.js.map +1 -0
- package/dist/lib/observability/histogram-bucket-constants.d.ts +25 -0
- package/dist/lib/observability/histogram-bucket-constants.d.ts.map +1 -0
- package/dist/lib/observability/histogram-bucket-constants.js +60 -0
- package/dist/lib/observability/histogram-bucket-constants.js.map +1 -0
- package/dist/lib/observability/histogram.d.ts +112 -0
- package/dist/lib/observability/histogram.d.ts.map +1 -0
- package/dist/lib/observability/histogram.js +170 -0
- package/dist/lib/observability/histogram.js.map +1 -0
- package/dist/lib/observability/histogram.test.d.ts +5 -0
- package/dist/lib/observability/histogram.test.d.ts.map +1 -0
- package/dist/lib/observability/histogram.test.js +385 -0
- package/dist/lib/observability/histogram.test.js.map +1 -0
- package/dist/lib/observability/indexer.d.ts +114 -0
- package/dist/lib/observability/indexer.d.ts.map +1 -0
- package/dist/lib/{indexer.js → observability/indexer.js} +65 -16
- package/dist/lib/observability/indexer.js.map +1 -0
- package/dist/lib/observability/indexer.test.d.ts.map +1 -0
- package/dist/lib/{indexer.test.js → observability/indexer.test.js} +94 -77
- package/dist/lib/observability/indexer.test.js.map +1 -0
- package/dist/lib/observability/instrumentation-eval.test.d.ts +5 -0
- package/dist/lib/observability/instrumentation-eval.test.d.ts.map +1 -0
- package/dist/lib/observability/instrumentation-eval.test.js +63 -0
- package/dist/lib/observability/instrumentation-eval.test.js.map +1 -0
- package/dist/lib/observability/instrumentation-init-errors.test.d.ts +13 -0
- package/dist/lib/observability/instrumentation-init-errors.test.d.ts.map +1 -0
- package/dist/lib/observability/instrumentation-init-errors.test.js +194 -0
- package/dist/lib/observability/instrumentation-init-errors.test.js.map +1 -0
- package/dist/lib/observability/instrumentation-retry-timeout.test.d.ts +15 -0
- package/dist/lib/observability/instrumentation-retry-timeout.test.d.ts.map +1 -0
- package/dist/lib/observability/instrumentation-retry-timeout.test.js +188 -0
- package/dist/lib/observability/instrumentation-retry-timeout.test.js.map +1 -0
- package/dist/lib/observability/instrumentation-set-otel.test.d.ts +5 -0
- package/dist/lib/observability/instrumentation-set-otel.test.d.ts.map +1 -0
- package/dist/lib/observability/instrumentation-set-otel.test.js +59 -0
- package/dist/lib/observability/instrumentation-set-otel.test.js.map +1 -0
- package/dist/lib/observability/instrumentation.d.ts +158 -0
- package/dist/lib/observability/instrumentation.d.ts.map +1 -0
- package/dist/lib/observability/instrumentation.integration.test.d.ts +2 -0
- package/dist/lib/observability/instrumentation.integration.test.d.ts.map +1 -0
- package/dist/lib/observability/instrumentation.integration.test.js +590 -0
- package/dist/lib/observability/instrumentation.integration.test.js.map +1 -0
- package/dist/lib/observability/instrumentation.js +512 -0
- package/dist/lib/observability/instrumentation.js.map +1 -0
- package/dist/lib/observability/instrumentation.test.d.ts +2 -0
- package/dist/lib/observability/instrumentation.test.d.ts.map +1 -0
- package/dist/lib/observability/instrumentation.test.js +822 -0
- package/dist/lib/observability/instrumentation.test.js.map +1 -0
- package/dist/lib/observability/mcp-semconv-constants.d.ts +98 -0
- package/dist/lib/observability/mcp-semconv-constants.d.ts.map +1 -0
- package/dist/lib/observability/mcp-semconv-constants.js +102 -0
- package/dist/lib/observability/mcp-semconv-constants.js.map +1 -0
- package/dist/lib/observability/mcp-semconv.d.ts +37 -0
- package/dist/lib/observability/mcp-semconv.d.ts.map +1 -0
- package/dist/lib/observability/mcp-semconv.js +87 -0
- package/dist/lib/observability/mcp-semconv.js.map +1 -0
- package/dist/lib/observability/mcp-semconv.test.d.ts +2 -0
- package/dist/lib/observability/mcp-semconv.test.d.ts.map +1 -0
- package/dist/lib/observability/mcp-semconv.test.js +168 -0
- package/dist/lib/observability/mcp-semconv.test.js.map +1 -0
- package/dist/lib/observability/metrics.d.ts +100 -0
- package/dist/lib/observability/metrics.d.ts.map +1 -0
- package/dist/lib/observability/metrics.js +429 -0
- package/dist/lib/observability/metrics.js.map +1 -0
- package/dist/lib/observability/metrics.test.d.ts +5 -0
- package/dist/lib/observability/metrics.test.d.ts.map +1 -0
- package/dist/lib/observability/metrics.test.js +191 -0
- package/dist/lib/observability/metrics.test.js.map +1 -0
- package/dist/lib/observability/observability-test-constants.d.ts +34 -0
- package/dist/lib/observability/observability-test-constants.d.ts.map +1 -0
- package/dist/lib/observability/observability-test-constants.js +55 -0
- package/dist/lib/observability/observability-test-constants.js.map +1 -0
- package/dist/lib/observability/opentelemetry-resources.test.d.ts +2 -0
- package/dist/lib/observability/opentelemetry-resources.test.d.ts.map +1 -0
- package/dist/lib/observability/opentelemetry-resources.test.js +19 -0
- package/dist/lib/observability/opentelemetry-resources.test.js.map +1 -0
- package/dist/lib/observability/parse-stats.d.ts +119 -0
- package/dist/lib/observability/parse-stats.d.ts.map +1 -0
- package/dist/lib/observability/parse-stats.js +207 -0
- package/dist/lib/observability/parse-stats.js.map +1 -0
- package/dist/lib/observability/parse-stats.test.d.ts +5 -0
- package/dist/lib/observability/parse-stats.test.d.ts.map +1 -0
- package/dist/lib/observability/parse-stats.test.js +287 -0
- package/dist/lib/observability/parse-stats.test.js.map +1 -0
- package/dist/lib/observability/render-trace-tree.d.ts +31 -0
- package/dist/lib/observability/render-trace-tree.d.ts.map +1 -0
- package/dist/lib/observability/render-trace-tree.js +95 -0
- package/dist/lib/observability/render-trace-tree.js.map +1 -0
- package/dist/lib/observability/render-trace-tree.test.d.ts +5 -0
- package/dist/lib/observability/render-trace-tree.test.d.ts.map +1 -0
- package/dist/lib/observability/render-trace-tree.test.js +97 -0
- package/dist/lib/observability/render-trace-tree.test.js.map +1 -0
- package/dist/lib/observability/span-attributes.d.ts +27 -0
- package/dist/lib/observability/span-attributes.d.ts.map +1 -0
- package/dist/lib/observability/span-attributes.js +85 -0
- package/dist/lib/observability/span-attributes.js.map +1 -0
- package/dist/lib/observability/trace-anomaly-detector.d.ts +23 -0
- package/dist/lib/observability/trace-anomaly-detector.d.ts.map +1 -0
- package/dist/lib/observability/trace-anomaly-detector.js +211 -0
- package/dist/lib/observability/trace-anomaly-detector.js.map +1 -0
- package/dist/lib/observability/trace-anomaly-detector.test.d.ts +5 -0
- package/dist/lib/observability/trace-anomaly-detector.test.d.ts.map +1 -0
- package/dist/lib/observability/trace-anomaly-detector.test.js +224 -0
- package/dist/lib/observability/trace-anomaly-detector.test.js.map +1 -0
- package/dist/lib/observability/trace-anomaly-schemas.d.ts +189 -0
- package/dist/lib/observability/trace-anomaly-schemas.d.ts.map +1 -0
- package/dist/lib/observability/trace-anomaly-schemas.js +167 -0
- package/dist/lib/observability/trace-anomaly-schemas.js.map +1 -0
- package/dist/lib/privacy/content-redaction.d.ts +141 -0
- package/dist/lib/privacy/content-redaction.d.ts.map +1 -0
- package/dist/lib/privacy/content-redaction.js +210 -0
- package/dist/lib/privacy/content-redaction.js.map +1 -0
- package/dist/lib/privacy/content-redaction.test.d.ts +2 -0
- package/dist/lib/privacy/content-redaction.test.d.ts.map +1 -0
- package/dist/lib/privacy/content-redaction.test.js +302 -0
- package/dist/lib/privacy/content-redaction.test.js.map +1 -0
- package/dist/lib/quality/bucket-utils.d.ts +17 -0
- package/dist/lib/quality/bucket-utils.d.ts.map +1 -0
- package/dist/lib/quality/bucket-utils.js +31 -0
- package/dist/lib/quality/bucket-utils.js.map +1 -0
- package/dist/lib/quality/bucket-utils.test.d.ts +2 -0
- package/dist/lib/quality/bucket-utils.test.d.ts.map +1 -0
- package/dist/lib/quality/bucket-utils.test.js +42 -0
- package/dist/lib/quality/bucket-utils.test.js.map +1 -0
- package/dist/lib/quality/qfe-backtest-detail.test.d.ts +5 -0
- package/dist/lib/quality/qfe-backtest-detail.test.d.ts.map +1 -0
- package/dist/lib/quality/qfe-backtest-detail.test.js +179 -0
- package/dist/lib/quality/qfe-backtest-detail.test.js.map +1 -0
- package/dist/lib/quality/qfe-calibration-paths.test.d.ts +5 -0
- package/dist/lib/quality/qfe-calibration-paths.test.d.ts.map +1 -0
- package/dist/lib/quality/qfe-calibration-paths.test.js +203 -0
- package/dist/lib/quality/qfe-calibration-paths.test.js.map +1 -0
- package/dist/lib/quality/qfe-correlation-helpers.test.d.ts +6 -0
- package/dist/lib/quality/qfe-correlation-helpers.test.d.ts.map +1 -0
- package/dist/lib/quality/qfe-correlation-helpers.test.js +143 -0
- package/dist/lib/quality/qfe-correlation-helpers.test.js.map +1 -0
- package/dist/lib/quality/qfe-cqi-paths.test.d.ts +6 -0
- package/dist/lib/quality/qfe-cqi-paths.test.d.ts.map +1 -0
- package/dist/lib/quality/qfe-cqi-paths.test.js +231 -0
- package/dist/lib/quality/qfe-cqi-paths.test.js.map +1 -0
- package/dist/lib/quality/qfe-critic-internals.test.d.ts +6 -0
- package/dist/lib/quality/qfe-critic-internals.test.d.ts.map +1 -0
- package/dist/lib/quality/qfe-critic-internals.test.js +191 -0
- package/dist/lib/quality/qfe-critic-internals.test.js.map +1 -0
- package/dist/lib/quality/qfe-derived-paths.test.d.ts +2 -0
- package/dist/lib/quality/qfe-derived-paths.test.d.ts.map +1 -0
- package/dist/lib/quality/qfe-derived-paths.test.js +372 -0
- package/dist/lib/quality/qfe-derived-paths.test.js.map +1 -0
- package/dist/lib/quality/qfe-dynamics-paths.test.d.ts +8 -0
- package/dist/lib/quality/qfe-dynamics-paths.test.d.ts.map +1 -0
- package/dist/lib/quality/qfe-dynamics-paths.test.js +223 -0
- package/dist/lib/quality/qfe-dynamics-paths.test.js.map +1 -0
- package/dist/lib/quality/qfe-granger-internals.test.d.ts +6 -0
- package/dist/lib/quality/qfe-granger-internals.test.d.ts.map +1 -0
- package/dist/lib/quality/qfe-granger-internals.test.js +158 -0
- package/dist/lib/quality/qfe-granger-internals.test.js.map +1 -0
- package/dist/lib/quality/qfe-label-normalize.test.d.ts +7 -0
- package/dist/lib/quality/qfe-label-normalize.test.d.ts.map +1 -0
- package/dist/lib/quality/qfe-label-normalize.test.js +332 -0
- package/dist/lib/quality/qfe-label-normalize.test.js.map +1 -0
- package/dist/lib/quality/qfe-ordinal-edge.test.d.ts +6 -0
- package/dist/lib/quality/qfe-ordinal-edge.test.d.ts.map +1 -0
- package/dist/lib/quality/qfe-ordinal-edge.test.js +98 -0
- package/dist/lib/quality/qfe-ordinal-edge.test.js.map +1 -0
- package/dist/lib/quality/qfe-roles-detail.test.d.ts +5 -0
- package/dist/lib/quality/qfe-roles-detail.test.d.ts.map +1 -0
- package/dist/lib/quality/qfe-roles-detail.test.js +115 -0
- package/dist/lib/quality/qfe-roles-detail.test.js.map +1 -0
- package/dist/lib/quality/qfe-rolling-detail.test.d.ts +7 -0
- package/dist/lib/quality/qfe-rolling-detail.test.d.ts.map +1 -0
- package/dist/lib/quality/qfe-rolling-detail.test.js +249 -0
- package/dist/lib/quality/qfe-rolling-detail.test.js.map +1 -0
- package/dist/lib/quality/qfe-stats-internals.test.d.ts +7 -0
- package/dist/lib/quality/qfe-stats-internals.test.d.ts.map +1 -0
- package/dist/lib/quality/qfe-stats-internals.test.js +143 -0
- package/dist/lib/quality/qfe-stats-internals.test.js.map +1 -0
- package/dist/lib/quality/qfe-streaming.test.d.ts +5 -0
- package/dist/lib/quality/qfe-streaming.test.d.ts.map +1 -0
- package/dist/lib/quality/qfe-streaming.test.js +239 -0
- package/dist/lib/quality/qfe-streaming.test.js.map +1 -0
- package/dist/lib/quality/qfe-sweep-detail.test.d.ts +6 -0
- package/dist/lib/quality/qfe-sweep-detail.test.d.ts.map +1 -0
- package/dist/lib/quality/qfe-sweep-detail.test.js +291 -0
- package/dist/lib/quality/qfe-sweep-detail.test.js.map +1 -0
- package/dist/lib/quality/quality-alerts.d.ts +23 -0
- package/dist/lib/quality/quality-alerts.d.ts.map +1 -0
- package/dist/lib/quality/quality-alerts.js +89 -0
- package/dist/lib/quality/quality-alerts.js.map +1 -0
- package/dist/lib/quality/quality-alerts.test.d.ts +2 -0
- package/dist/lib/quality/quality-alerts.test.d.ts.map +1 -0
- package/dist/lib/quality/quality-alerts.test.js +86 -0
- package/dist/lib/quality/quality-alerts.test.js.map +1 -0
- package/dist/lib/quality/quality-constants.d.ts +294 -0
- package/dist/lib/quality/quality-constants.d.ts.map +1 -0
- package/dist/lib/quality/quality-constants.js +335 -0
- package/dist/lib/quality/quality-constants.js.map +1 -0
- package/dist/lib/quality/quality-feature-engineering.d.ts +1071 -0
- package/dist/lib/quality/quality-feature-engineering.d.ts.map +1 -0
- package/dist/lib/quality/quality-feature-engineering.js +2076 -0
- package/dist/lib/quality/quality-feature-engineering.js.map +1 -0
- package/dist/lib/quality/quality-feature-engineering.test.d.ts +5 -0
- package/dist/lib/quality/quality-feature-engineering.test.d.ts.map +1 -0
- package/dist/lib/quality/quality-feature-engineering.test.js +2908 -0
- package/dist/lib/quality/quality-feature-engineering.test.js.map +1 -0
- package/dist/lib/quality/quality-metrics.d.ts +943 -0
- package/dist/lib/quality/quality-metrics.d.ts.map +1 -0
- package/dist/lib/quality/quality-metrics.js +1151 -0
- package/dist/lib/quality/quality-metrics.js.map +1 -0
- package/dist/lib/quality/quality-metrics.test.d.ts +5 -0
- package/dist/lib/quality/quality-metrics.test.d.ts.map +1 -0
- package/dist/lib/quality/quality-metrics.test.js +2766 -0
- package/dist/lib/quality/quality-metrics.test.js.map +1 -0
- package/dist/lib/quality/quality-multi-agent.d.ts +106 -0
- package/dist/lib/quality/quality-multi-agent.d.ts.map +1 -0
- package/dist/lib/quality/quality-multi-agent.js +124 -0
- package/dist/lib/quality/quality-multi-agent.js.map +1 -0
- package/dist/lib/quality/quality-multi-agent.test.d.ts +6 -0
- package/dist/lib/quality/quality-multi-agent.test.d.ts.map +1 -0
- package/dist/lib/quality/quality-multi-agent.test.js +163 -0
- package/dist/lib/quality/quality-multi-agent.test.js.map +1 -0
- package/dist/lib/quality/quality-sla.d.ts +35 -0
- package/dist/lib/quality/quality-sla.d.ts.map +1 -0
- package/dist/lib/quality/quality-sla.js +62 -0
- package/dist/lib/quality/quality-sla.js.map +1 -0
- package/dist/lib/quality/quality-sla.test.d.ts +5 -0
- package/dist/lib/quality/quality-sla.test.d.ts.map +1 -0
- package/dist/lib/quality/quality-sla.test.js +144 -0
- package/dist/lib/quality/quality-sla.test.js.map +1 -0
- package/dist/lib/quality/quality-test-constants.d.ts +23 -0
- package/dist/lib/quality/quality-test-constants.d.ts.map +1 -0
- package/dist/lib/quality/quality-test-constants.js +25 -0
- package/dist/lib/quality/quality-test-constants.js.map +1 -0
- package/dist/lib/quality/quality-trends.d.ts +101 -0
- package/dist/lib/quality/quality-trends.d.ts.map +1 -0
- package/dist/lib/quality/quality-trends.js +299 -0
- package/dist/lib/quality/quality-trends.js.map +1 -0
- package/dist/lib/quality/quality-trends.test.d.ts +6 -0
- package/dist/lib/quality/quality-trends.test.d.ts.map +1 -0
- package/dist/lib/quality/quality-trends.test.js +377 -0
- package/dist/lib/quality/quality-trends.test.js.map +1 -0
- package/dist/lib/quality/quality-views.d.ts +966 -0
- package/dist/lib/quality/quality-views.d.ts.map +1 -0
- package/dist/lib/quality/quality-views.js +367 -0
- package/dist/lib/quality/quality-views.js.map +1 -0
- package/dist/lib/quality/quality-views.test.d.ts +6 -0
- package/dist/lib/quality/quality-views.test.d.ts.map +1 -0
- package/dist/lib/quality/quality-views.test.js +262 -0
- package/dist/lib/quality/quality-views.test.js.map +1 -0
- package/dist/lib/quality/quality-visualization.d.ts +112 -0
- package/dist/lib/quality/quality-visualization.d.ts.map +1 -0
- package/dist/lib/quality/quality-visualization.js +136 -0
- package/dist/lib/quality/quality-visualization.js.map +1 -0
- package/dist/lib/quality/quality-visualization.test.d.ts +5 -0
- package/dist/lib/quality/quality-visualization.test.d.ts.map +1 -0
- package/dist/lib/quality/quality-visualization.test.js +189 -0
- package/dist/lib/quality/quality-visualization.test.js.map +1 -0
- package/dist/lib/resilience/cache.d.ts +56 -0
- package/dist/lib/resilience/cache.d.ts.map +1 -0
- package/dist/lib/resilience/cache.js +96 -0
- package/dist/lib/resilience/cache.js.map +1 -0
- package/dist/lib/resilience/cache.test.d.ts.map +1 -0
- package/dist/lib/{cache.test.js → resilience/cache.test.js} +21 -20
- package/dist/lib/resilience/cache.test.js.map +1 -0
- package/dist/lib/resilience/circuit-breaker.d.ts +147 -0
- package/dist/lib/resilience/circuit-breaker.d.ts.map +1 -0
- package/dist/lib/resilience/circuit-breaker.js +251 -0
- package/dist/lib/resilience/circuit-breaker.js.map +1 -0
- package/dist/lib/resilience/circuit-breaker.test.d.ts.map +1 -0
- package/dist/lib/{circuit-breaker.test.js → resilience/circuit-breaker.test.js} +29 -26
- package/dist/lib/resilience/circuit-breaker.test.js.map +1 -0
- package/dist/lib/{toon-encoder.d.ts → resilience/toon-encoder.d.ts} +6 -1
- package/dist/lib/resilience/toon-encoder.d.ts.map +1 -0
- package/dist/lib/{toon-encoder.js → resilience/toon-encoder.js} +7 -2
- package/dist/lib/resilience/toon-encoder.js.map +1 -0
- package/dist/lib/resilience/toon-encoder.test.d.ts.map +1 -0
- package/dist/lib/{toon-encoder.test.js → resilience/toon-encoder.test.js} +7 -6
- package/dist/lib/resilience/toon-encoder.test.js.map +1 -0
- package/dist/lib/testing/mock-llm-builder.d.ts +139 -0
- package/dist/lib/testing/mock-llm-builder.d.ts.map +1 -0
- package/dist/lib/testing/mock-llm-builder.js +254 -0
- package/dist/lib/testing/mock-llm-builder.js.map +1 -0
- package/dist/lib/testing/mock-llm-builder.test.d.ts +5 -0
- package/dist/lib/testing/mock-llm-builder.test.d.ts.map +1 -0
- package/dist/lib/testing/mock-llm-builder.test.js +304 -0
- package/dist/lib/testing/mock-llm-builder.test.js.map +1 -0
- package/dist/lib/validation/api-schemas.d.ts +705 -0
- package/dist/lib/validation/api-schemas.d.ts.map +1 -0
- package/dist/lib/validation/api-schemas.js +351 -0
- package/dist/lib/validation/api-schemas.js.map +1 -0
- package/dist/lib/validation/api-schemas.test.d.ts +5 -0
- package/dist/lib/validation/api-schemas.test.d.ts.map +1 -0
- package/dist/lib/validation/api-schemas.test.js +427 -0
- package/dist/lib/validation/api-schemas.test.js.map +1 -0
- package/dist/lib/validation/dashboard-schemas.d.ts +203 -0
- package/dist/lib/validation/dashboard-schemas.d.ts.map +1 -0
- package/dist/lib/validation/dashboard-schemas.js +186 -0
- package/dist/lib/validation/dashboard-schemas.js.map +1 -0
- package/dist/lib/validation/dashboard-schemas.test.d.ts +5 -0
- package/dist/lib/validation/dashboard-schemas.test.d.ts.map +1 -0
- package/dist/lib/validation/dashboard-schemas.test.js +353 -0
- package/dist/lib/validation/dashboard-schemas.test.js.map +1 -0
- package/dist/server.d.ts +7 -1
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +172 -102
- package/dist/server.js.map +1 -1
- package/dist/server.test.js +102 -95
- package/dist/server.test.js.map +1 -1
- package/dist/test-helpers/assertions.d.ts +6 -0
- package/dist/test-helpers/assertions.d.ts.map +1 -0
- package/dist/test-helpers/assertions.js +11 -0
- package/dist/test-helpers/assertions.js.map +1 -0
- package/dist/test-helpers/env-utils.d.ts +0 -64
- package/dist/test-helpers/env-utils.d.ts.map +1 -1
- package/dist/test-helpers/env-utils.js +0 -100
- package/dist/test-helpers/env-utils.js.map +1 -1
- package/dist/test-helpers/fuzz-generators.d.ts.map +1 -1
- package/dist/test-helpers/fuzz-generators.js +62 -22
- package/dist/test-helpers/fuzz-generators.js.map +1 -1
- package/dist/test-helpers/index.d.ts +3 -2
- package/dist/test-helpers/index.d.ts.map +1 -1
- package/dist/test-helpers/index.js +4 -2
- package/dist/test-helpers/index.js.map +1 -1
- package/dist/test-helpers/memfs-utils.test.js +81 -76
- package/dist/test-helpers/memfs-utils.test.js.map +1 -1
- package/dist/test-helpers/mock-backends.d.ts +19 -17
- package/dist/test-helpers/mock-backends.d.ts.map +1 -1
- package/dist/test-helpers/mock-backends.js +16 -4
- package/dist/test-helpers/mock-backends.js.map +1 -1
- package/dist/test-helpers/mock-backends.test.js +43 -112
- package/dist/test-helpers/mock-backends.test.js.map +1 -1
- package/dist/test-helpers/race-condition-helpers.d.ts.map +1 -1
- package/dist/test-helpers/race-condition-helpers.js +3 -2
- package/dist/test-helpers/race-condition-helpers.js.map +1 -1
- package/dist/test-helpers/schema-validators.d.ts +2 -2
- package/dist/test-helpers/schema-validators.d.ts.map +1 -1
- package/dist/test-helpers/schema-validators.js +35 -31
- package/dist/test-helpers/schema-validators.js.map +1 -1
- package/dist/test-helpers/test-constants.d.ts +74 -0
- package/dist/test-helpers/test-constants.d.ts.map +1 -0
- package/dist/test-helpers/test-constants.js +78 -0
- package/dist/test-helpers/test-constants.js.map +1 -0
- package/dist/test-helpers/test-data-builders.d.ts +25 -7
- package/dist/test-helpers/test-data-builders.d.ts.map +1 -1
- package/dist/test-helpers/test-data-builders.js +32 -9
- package/dist/test-helpers/test-data-builders.js.map +1 -1
- package/dist/test-helpers/test-data-builders.test.js +116 -107
- package/dist/test-helpers/test-data-builders.test.js.map +1 -1
- package/dist/test-helpers/tool-validators.d.ts +1 -1
- package/dist/test-helpers/tool-validators.d.ts.map +1 -1
- package/dist/test-helpers/tool-validators.js +10 -10
- package/dist/test-helpers/tool-validators.js.map +1 -1
- package/dist/tools/audit-trail.d.ts +170 -0
- package/dist/tools/audit-trail.d.ts.map +1 -0
- package/dist/tools/audit-trail.js +109 -0
- package/dist/tools/audit-trail.js.map +1 -0
- package/dist/tools/audit-trail.test.d.ts +5 -0
- package/dist/tools/audit-trail.test.d.ts.map +1 -0
- package/dist/tools/audit-trail.test.js +122 -0
- package/dist/tools/audit-trail.test.js.map +1 -0
- package/dist/tools/context-stats.d.ts +6 -20
- package/dist/tools/context-stats.d.ts.map +1 -1
- package/dist/tools/context-stats.js +106 -90
- package/dist/tools/context-stats.js.map +1 -1
- package/dist/tools/context-stats.test.js +109 -60
- package/dist/tools/context-stats.test.js.map +1 -1
- package/dist/tools/detect-trace-anomalies.d.ts +123 -0
- package/dist/tools/detect-trace-anomalies.d.ts.map +1 -0
- package/dist/tools/detect-trace-anomalies.js +66 -0
- package/dist/tools/detect-trace-anomalies.js.map +1 -0
- package/dist/tools/estimate-cost.d.ts +77 -0
- package/dist/tools/estimate-cost.d.ts.map +1 -0
- package/dist/tools/estimate-cost.js +104 -0
- package/dist/tools/estimate-cost.js.map +1 -0
- package/dist/tools/estimate-cost.test.d.ts +5 -0
- package/dist/tools/estimate-cost.test.d.ts.map +1 -0
- package/dist/tools/estimate-cost.test.js +343 -0
- package/dist/tools/estimate-cost.test.js.map +1 -0
- package/dist/tools/export-base.d.ts +77 -0
- package/dist/tools/export-base.d.ts.map +1 -0
- package/dist/tools/export-base.js +150 -0
- package/dist/tools/export-base.js.map +1 -0
- package/dist/tools/export-base.test.d.ts +18 -0
- package/dist/tools/export-base.test.d.ts.map +1 -0
- package/dist/tools/export-base.test.js +220 -0
- package/dist/tools/export-base.test.js.map +1 -0
- package/dist/tools/export-confident.d.ts +149 -0
- package/dist/tools/export-confident.d.ts.map +1 -0
- package/dist/tools/export-confident.js +36 -0
- package/dist/tools/export-confident.js.map +1 -0
- package/dist/tools/export-confident.test.d.ts +7 -0
- package/dist/tools/export-confident.test.d.ts.map +1 -0
- package/dist/tools/export-confident.test.js +336 -0
- package/dist/tools/export-confident.test.js.map +1 -0
- package/dist/tools/export-datadog.d.ts +121 -0
- package/dist/tools/export-datadog.d.ts.map +1 -0
- package/dist/tools/export-datadog.js +158 -0
- package/dist/tools/export-datadog.js.map +1 -0
- package/dist/tools/export-datadog.test.d.ts +8 -0
- package/dist/tools/export-datadog.test.d.ts.map +1 -0
- package/dist/tools/export-datadog.test.js +376 -0
- package/dist/tools/export-datadog.test.js.map +1 -0
- package/dist/tools/export-jaeger.d.ts +100 -0
- package/dist/tools/export-jaeger.d.ts.map +1 -0
- package/dist/tools/export-jaeger.js +154 -0
- package/dist/tools/export-jaeger.js.map +1 -0
- package/dist/tools/export-jaeger.test.d.ts +2 -0
- package/dist/tools/export-jaeger.test.d.ts.map +1 -0
- package/dist/tools/export-jaeger.test.js +113 -0
- package/dist/tools/export-jaeger.test.js.map +1 -0
- package/dist/tools/export-langfuse.d.ts +135 -0
- package/dist/tools/export-langfuse.d.ts.map +1 -0
- package/dist/tools/export-langfuse.js +33 -0
- package/dist/tools/export-langfuse.js.map +1 -0
- package/dist/tools/export-langfuse.test.d.ts +7 -0
- package/dist/tools/export-langfuse.test.d.ts.map +1 -0
- package/dist/tools/export-langfuse.test.js +292 -0
- package/dist/tools/export-langfuse.test.js.map +1 -0
- package/dist/tools/export-phoenix.d.ts +170 -0
- package/dist/tools/export-phoenix.d.ts.map +1 -0
- package/dist/tools/export-phoenix.js +47 -0
- package/dist/tools/export-phoenix.js.map +1 -0
- package/dist/tools/export-phoenix.test.d.ts +7 -0
- package/dist/tools/export-phoenix.test.d.ts.map +1 -0
- package/dist/tools/export-phoenix.test.js +317 -0
- package/dist/tools/export-phoenix.test.js.map +1 -0
- package/dist/tools/get-trace-url.d.ts +2 -10
- package/dist/tools/get-trace-url.d.ts.map +1 -1
- package/dist/tools/get-trace-url.js +5 -8
- package/dist/tools/get-trace-url.js.map +1 -1
- package/dist/tools/get-trace-url.test.js +81 -399
- package/dist/tools/get-trace-url.test.js.map +1 -1
- package/dist/tools/hallucination-detection.d.ts +203 -0
- package/dist/tools/hallucination-detection.d.ts.map +1 -0
- package/dist/tools/hallucination-detection.js +189 -0
- package/dist/tools/hallucination-detection.js.map +1 -0
- package/dist/tools/hallucination-detection.test.d.ts +5 -0
- package/dist/tools/hallucination-detection.test.d.ts.map +1 -0
- package/dist/tools/hallucination-detection.test.js +529 -0
- package/dist/tools/hallucination-detection.test.js.map +1 -0
- package/dist/tools/health-check.d.ts +35 -16
- package/dist/tools/health-check.d.ts.map +1 -1
- package/dist/tools/health-check.js +101 -85
- package/dist/tools/health-check.js.map +1 -1
- package/dist/tools/health-check.test.js +72 -165
- package/dist/tools/health-check.test.js.map +1 -1
- package/dist/tools/index.d.ts +19 -0
- package/dist/tools/index.d.ts.map +1 -1
- package/dist/tools/index.js +19 -0
- package/dist/tools/index.js.map +1 -1
- package/dist/tools/ingest-constants.d.ts +8 -0
- package/dist/tools/ingest-constants.d.ts.map +1 -0
- package/dist/tools/ingest-constants.js +8 -0
- package/dist/tools/ingest-constants.js.map +1 -0
- package/dist/tools/ingest-spans.d.ts +45 -0
- package/dist/tools/ingest-spans.d.ts.map +1 -0
- package/dist/tools/ingest-spans.js +129 -0
- package/dist/tools/ingest-spans.js.map +1 -0
- package/dist/tools/ingest-spans.test.d.ts +5 -0
- package/dist/tools/ingest-spans.test.d.ts.map +1 -0
- package/dist/tools/ingest-spans.test.js +250 -0
- package/dist/tools/ingest-spans.test.js.map +1 -0
- package/dist/tools/ingest-traces.d.ts +76 -0
- package/dist/tools/ingest-traces.d.ts.map +1 -0
- package/dist/tools/ingest-traces.js +164 -0
- package/dist/tools/ingest-traces.js.map +1 -0
- package/dist/tools/ingest-traces.test.d.ts +5 -0
- package/dist/tools/ingest-traces.test.d.ts.map +1 -0
- package/dist/tools/ingest-traces.test.js +483 -0
- package/dist/tools/ingest-traces.test.js.map +1 -0
- package/dist/tools/inject-evaluations.d.ts +254 -0
- package/dist/tools/inject-evaluations.d.ts.map +1 -0
- package/dist/tools/inject-evaluations.js +133 -0
- package/dist/tools/inject-evaluations.js.map +1 -0
- package/dist/tools/inject-evaluations.test.d.ts +5 -0
- package/dist/tools/inject-evaluations.test.d.ts.map +1 -0
- package/dist/tools/inject-evaluations.test.js +371 -0
- package/dist/tools/inject-evaluations.test.js.map +1 -0
- package/dist/tools/manage-datasets.d.ts +850 -0
- package/dist/tools/manage-datasets.d.ts.map +1 -0
- package/dist/tools/manage-datasets.js +139 -0
- package/dist/tools/manage-datasets.js.map +1 -0
- package/dist/tools/manage-datasets.test.d.ts +5 -0
- package/dist/tools/manage-datasets.test.d.ts.map +1 -0
- package/dist/tools/manage-datasets.test.js +430 -0
- package/dist/tools/manage-datasets.test.js.map +1 -0
- package/dist/tools/multi-agent-coordination.d.ts +178 -0
- package/dist/tools/multi-agent-coordination.d.ts.map +1 -0
- package/dist/tools/multi-agent-coordination.js +270 -0
- package/dist/tools/multi-agent-coordination.js.map +1 -0
- package/dist/tools/multi-agent-coordination.test.d.ts +5 -0
- package/dist/tools/multi-agent-coordination.test.d.ts.map +1 -0
- package/dist/tools/multi-agent-coordination.test.js +530 -0
- package/dist/tools/multi-agent-coordination.test.js.map +1 -0
- package/dist/tools/query-evaluations.d.ts +154 -91
- package/dist/tools/query-evaluations.d.ts.map +1 -1
- package/dist/tools/query-evaluations.js +206 -169
- package/dist/tools/query-evaluations.js.map +1 -1
- package/dist/tools/query-evaluations.test.js +386 -391
- package/dist/tools/query-evaluations.test.js.map +1 -1
- package/dist/tools/query-llm-events.d.ts +100 -75
- package/dist/tools/query-llm-events.d.ts.map +1 -1
- package/dist/tools/query-llm-events.js +106 -80
- package/dist/tools/query-llm-events.js.map +1 -1
- package/dist/tools/query-llm-events.test.js +183 -346
- package/dist/tools/query-llm-events.test.js.map +1 -1
- package/dist/tools/query-logs.d.ts +45 -58
- package/dist/tools/query-logs.d.ts.map +1 -1
- package/dist/tools/query-logs.js +54 -101
- package/dist/tools/query-logs.js.map +1 -1
- package/dist/tools/query-logs.test.js +118 -314
- package/dist/tools/query-logs.test.js.map +1 -1
- package/dist/tools/query-metric-histograms.d.ts +112 -0
- package/dist/tools/query-metric-histograms.d.ts.map +1 -0
- package/dist/tools/query-metric-histograms.js +69 -0
- package/dist/tools/query-metric-histograms.js.map +1 -0
- package/dist/tools/query-metric-histograms.test.d.ts +5 -0
- package/dist/tools/query-metric-histograms.test.d.ts.map +1 -0
- package/dist/tools/query-metric-histograms.test.js +209 -0
- package/dist/tools/query-metric-histograms.test.js.map +1 -0
- package/dist/tools/query-metrics.d.ts +159 -60
- package/dist/tools/query-metrics.d.ts.map +1 -1
- package/dist/tools/query-metrics.js +133 -111
- package/dist/tools/query-metrics.js.map +1 -1
- package/dist/tools/query-metrics.test.js +314 -389
- package/dist/tools/query-metrics.test.js.map +1 -1
- package/dist/tools/query-regressions.d.ts +76 -0
- package/dist/tools/query-regressions.d.ts.map +1 -0
- package/dist/tools/query-regressions.js +122 -0
- package/dist/tools/query-regressions.js.map +1 -0
- package/dist/tools/query-regressions.test.d.ts +8 -0
- package/dist/tools/query-regressions.test.d.ts.map +1 -0
- package/dist/tools/query-regressions.test.js +129 -0
- package/dist/tools/query-regressions.test.js.map +1 -0
- package/dist/tools/query-traces.d.ts +103 -71
- package/dist/tools/query-traces.d.ts.map +1 -1
- package/dist/tools/query-traces.js +75 -106
- package/dist/tools/query-traces.js.map +1 -1
- package/dist/tools/query-traces.test.js +140 -846
- package/dist/tools/query-traces.test.js.map +1 -1
- package/dist/tools/query-verifications.d.ts +123 -0
- package/dist/tools/query-verifications.d.ts.map +1 -0
- package/dist/tools/query-verifications.js +102 -0
- package/dist/tools/query-verifications.js.map +1 -0
- package/dist/tools/query-verifications.test.d.ts +5 -0
- package/dist/tools/query-verifications.test.d.ts.map +1 -0
- package/dist/tools/query-verifications.test.js +163 -0
- package/dist/tools/query-verifications.test.js.map +1 -0
- package/dist/tools/routing-telemetry.d.ts +168 -0
- package/dist/tools/routing-telemetry.d.ts.map +1 -0
- package/dist/tools/routing-telemetry.js +267 -0
- package/dist/tools/routing-telemetry.js.map +1 -0
- package/dist/tools/routing-telemetry.test.d.ts +5 -0
- package/dist/tools/routing-telemetry.test.d.ts.map +1 -0
- package/dist/tools/routing-telemetry.test.js +747 -0
- package/dist/tools/routing-telemetry.test.js.map +1 -0
- package/dist/tools/setup-claudeignore.d.ts +4 -32
- package/dist/tools/setup-claudeignore.d.ts.map +1 -1
- package/dist/tools/setup-claudeignore.js +18 -22
- package/dist/tools/setup-claudeignore.js.map +1 -1
- package/dist/tools/setup-claudeignore.test.js +50 -49
- package/dist/tools/setup-claudeignore.test.js.map +1 -1
- package/dist/tools/token-budget.d.ts +170 -0
- package/dist/tools/token-budget.d.ts.map +1 -0
- package/dist/tools/token-budget.js +219 -0
- package/dist/tools/token-budget.js.map +1 -0
- package/dist/tools/token-budget.test.d.ts +5 -0
- package/dist/tools/token-budget.test.d.ts.map +1 -0
- package/dist/tools/token-budget.test.js +293 -0
- package/dist/tools/token-budget.test.js.map +1 -0
- package/package.json +76 -6
- package/dist/backends/local-jsonl.test.d.ts +0 -2
- package/dist/backends/local-jsonl.test.d.ts.map +0 -1
- package/dist/backends/local-jsonl.test.js +0 -4651
- package/dist/backends/local-jsonl.test.js.map +0 -1
- package/dist/backends/signoz-api-circuit-breaker.test.d.ts +0 -6
- package/dist/backends/signoz-api-circuit-breaker.test.d.ts.map +0 -1
- package/dist/backends/signoz-api-circuit-breaker.test.js +0 -548
- package/dist/backends/signoz-api-circuit-breaker.test.js.map +0 -1
- package/dist/backends/signoz-api-rate-limiter.test.d.ts +0 -6
- package/dist/backends/signoz-api-rate-limiter.test.d.ts.map +0 -1
- package/dist/backends/signoz-api-rate-limiter.test.js +0 -389
- package/dist/backends/signoz-api-rate-limiter.test.js.map +0 -1
- package/dist/backends/signoz-api-ssrf.test.d.ts +0 -6
- package/dist/backends/signoz-api-ssrf.test.d.ts.map +0 -1
- package/dist/backends/signoz-api-ssrf.test.js +0 -216
- package/dist/backends/signoz-api-ssrf.test.js.map +0 -1
- package/dist/backends/signoz-api-test-helpers.d.ts +0 -80
- package/dist/backends/signoz-api-test-helpers.d.ts.map +0 -1
- package/dist/backends/signoz-api-test-helpers.js +0 -79
- package/dist/backends/signoz-api-test-helpers.js.map +0 -1
- package/dist/backends/signoz-api.d.ts +0 -95
- package/dist/backends/signoz-api.d.ts.map +0 -1
- package/dist/backends/signoz-api.integration.test.d.ts +0 -8
- package/dist/backends/signoz-api.integration.test.d.ts.map +0 -1
- package/dist/backends/signoz-api.integration.test.js +0 -137
- package/dist/backends/signoz-api.integration.test.js.map +0 -1
- package/dist/backends/signoz-api.js +0 -1016
- package/dist/backends/signoz-api.js.map +0 -1
- package/dist/backends/signoz-api.test.d.ts +0 -11
- package/dist/backends/signoz-api.test.d.ts.map +0 -1
- package/dist/backends/signoz-api.test.js +0 -831
- package/dist/backends/signoz-api.test.js.map +0 -1
- package/dist/lib/cache.d.ts +0 -77
- package/dist/lib/cache.d.ts.map +0 -1
- package/dist/lib/cache.js +0 -119
- package/dist/lib/cache.js.map +0 -1
- package/dist/lib/cache.test.d.ts.map +0 -1
- package/dist/lib/cache.test.js.map +0 -1
- package/dist/lib/circuit-breaker.d.ts +0 -83
- package/dist/lib/circuit-breaker.d.ts.map +0 -1
- package/dist/lib/circuit-breaker.js +0 -125
- package/dist/lib/circuit-breaker.js.map +0 -1
- package/dist/lib/circuit-breaker.test.d.ts.map +0 -1
- package/dist/lib/circuit-breaker.test.js.map +0 -1
- package/dist/lib/constants-symlink.test.d.ts.map +0 -1
- package/dist/lib/constants-symlink.test.js.map +0 -1
- package/dist/lib/constants.d.ts +0 -108
- package/dist/lib/constants.d.ts.map +0 -1
- package/dist/lib/constants.js +0 -350
- package/dist/lib/constants.js.map +0 -1
- package/dist/lib/constants.test.d.ts.map +0 -1
- package/dist/lib/constants.test.js.map +0 -1
- package/dist/lib/edge-cases.test.d.ts.map +0 -1
- package/dist/lib/edge-cases.test.js.map +0 -1
- package/dist/lib/error-sanitizer.d.ts.map +0 -1
- package/dist/lib/error-sanitizer.js.map +0 -1
- package/dist/lib/error-sanitizer.test.d.ts.map +0 -1
- package/dist/lib/error-sanitizer.test.js.map +0 -1
- package/dist/lib/error-types.d.ts.map +0 -1
- package/dist/lib/error-types.js.map +0 -1
- package/dist/lib/error-types.test.d.ts.map +0 -1
- package/dist/lib/error-types.test.js.map +0 -1
- package/dist/lib/file-utils.d.ts.map +0 -1
- package/dist/lib/file-utils.js.map +0 -1
- package/dist/lib/file-utils.test.d.ts.map +0 -1
- package/dist/lib/file-utils.test.js.map +0 -1
- package/dist/lib/indexer.d.ts +0 -96
- package/dist/lib/indexer.d.ts.map +0 -1
- package/dist/lib/indexer.js.map +0 -1
- package/dist/lib/indexer.test.d.ts.map +0 -1
- package/dist/lib/indexer.test.js.map +0 -1
- package/dist/lib/input-validator.d.ts.map +0 -1
- package/dist/lib/input-validator.fuzz.test.d.ts.map +0 -1
- package/dist/lib/input-validator.fuzz.test.js.map +0 -1
- package/dist/lib/input-validator.js.map +0 -1
- package/dist/lib/input-validator.test.d.ts.map +0 -1
- package/dist/lib/input-validator.test.js.map +0 -1
- package/dist/lib/logger.d.ts.map +0 -1
- package/dist/lib/logger.js +0 -81
- package/dist/lib/logger.js.map +0 -1
- package/dist/lib/logger.test.d.ts.map +0 -1
- package/dist/lib/logger.test.js.map +0 -1
- package/dist/lib/otlp-export.d.ts.map +0 -1
- package/dist/lib/otlp-export.js.map +0 -1
- package/dist/lib/query-sanitizer.d.ts.map +0 -1
- package/dist/lib/query-sanitizer.js.map +0 -1
- package/dist/lib/query-sanitizer.test.d.ts.map +0 -1
- package/dist/lib/query-sanitizer.test.js.map +0 -1
- package/dist/lib/server-utils.d.ts.map +0 -1
- package/dist/lib/server-utils.js.map +0 -1
- package/dist/lib/shared-schemas.d.ts +0 -81
- package/dist/lib/shared-schemas.d.ts.map +0 -1
- package/dist/lib/shared-schemas.js +0 -80
- package/dist/lib/shared-schemas.js.map +0 -1
- package/dist/lib/shared-schemas.test.d.ts.map +0 -1
- package/dist/lib/shared-schemas.test.js.map +0 -1
- package/dist/lib/toon-encoder.d.ts.map +0 -1
- package/dist/lib/toon-encoder.js.map +0 -1
- package/dist/lib/toon-encoder.test.d.ts.map +0 -1
- package/dist/lib/toon-encoder.test.js.map +0 -1
- package/dist/tools/signoz.integration.test.d.ts +0 -8
- package/dist/tools/signoz.integration.test.d.ts.map +0 -1
- package/dist/tools/signoz.integration.test.js +0 -141
- package/dist/tools/signoz.integration.test.js.map +0 -1
- /package/dist/lib/{constants-symlink.test.d.ts → core/constants-symlink.test.d.ts} +0 -0
- /package/dist/lib/{constants.test.d.ts → core/constants.test.d.ts} +0 -0
- /package/dist/lib/{edge-cases.test.d.ts → core/edge-cases.test.d.ts} +0 -0
- /package/dist/lib/{file-utils.test.d.ts → core/file-utils.test.d.ts} +0 -0
- /package/dist/lib/{input-validator.fuzz.test.d.ts → core/input-validator.fuzz.test.d.ts} +0 -0
- /package/dist/lib/{input-validator.test.d.ts → core/input-validator.test.d.ts} +0 -0
- /package/dist/lib/{logger.test.d.ts → core/logger.test.d.ts} +0 -0
- /package/dist/lib/{logger.test.js → core/logger.test.js} +0 -0
- /package/dist/lib/{shared-schemas.test.d.ts → core/shared-schemas.test.d.ts} +0 -0
- /package/dist/lib/{error-sanitizer.test.d.ts → errors/error-sanitizer.test.d.ts} +0 -0
- /package/dist/lib/{error-types.test.d.ts → errors/error-types.test.d.ts} +0 -0
- /package/dist/lib/{query-sanitizer.d.ts → errors/query-sanitizer.d.ts} +0 -0
- /package/dist/lib/{query-sanitizer.test.d.ts → errors/query-sanitizer.test.d.ts} +0 -0
- /package/dist/lib/{indexer.test.d.ts → observability/indexer.test.d.ts} +0 -0
- /package/dist/lib/{cache.test.d.ts → resilience/cache.test.d.ts} +0 -0
- /package/dist/lib/{circuit-breaker.test.d.ts → resilience/circuit-breaker.test.d.ts} +0 -0
- /package/dist/lib/{toon-encoder.test.d.ts → resilience/toon-encoder.test.d.ts} +0 -0
|
@@ -1,24 +1,38 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Local JSONL backend for reading telemetry data from ~/.claude/telemetry/
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
4
|
+
* Supports two JSONL line formats:
|
|
5
|
+
* - **Flat**: one span/log/metric per line with embedded resource (original hooks format)
|
|
6
|
+
* - **OTLP envelope**: one ResourceSpans/ResourceLogs/ResourceMetrics envelope per line
|
|
7
|
+
* (as produced by the L5 batch migration script or future OTLP writers)
|
|
8
|
+
*
|
|
9
|
+
* Format detection is automatic via the unwrap adapters (A4 items 1-6).
|
|
6
10
|
*/
|
|
7
11
|
import { join } from 'path';
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
13
|
-
import {
|
|
14
|
-
import {
|
|
15
|
-
import {
|
|
12
|
+
import { datasetRecordSchema, datasetTraceRecordSchema, datasetRunRecordSchema, rawEvaluatorTypeSchema, } from '../lib/validation/api-schemas.js';
|
|
13
|
+
import { homedir } from 'os';
|
|
14
|
+
import { GENAI_EVALUATION_ATTRIBUTES, GENAI_RESPONSE_ATTRIBUTES, GENAI_AGENT_ATTRIBUTES, AGENT_JUDGE_ATTRIBUTES, AGGREGATION_TEMPORALITY, } from './index.js';
|
|
15
|
+
import { convertToOTLPTraces, convertToOTLPLogs, convertToOTLPMetrics, } from '../lib/exports/otlp-export.js';
|
|
16
|
+
import { DEFAULT_QUERY_RESULT_LIMIT, DATASET_TRACE_TRUNCATION_LIMIT, MAX_STEP_SCORES, MAX_TOOL_VERIFICATIONS, HOOKS_TELEMETRY_DIR, WORST_FILES_LIMIT, getOtelLogSeverityNumber, getSpanKind, getStatusCodeName, getTelemetryDirectories, normalizeOtelLogSeverity } from '../lib/core/constants.js';
|
|
17
|
+
import { ONE_MILLION, NANOSECONDS_PER_SECOND, NANOSECONDS_PER_SECOND_BIGINT, TIME_MS, PERCENTILE, PERCENT_MULTIPLIER, } from '../lib/core/units.js';
|
|
18
|
+
import { listFiles, streamJsonl, parseDateFromFilename, getDateString, paginateResults, hasReachedLimit, } from '../lib/core/file-utils.js';
|
|
19
|
+
import { QueryCache, makeCacheKey, CACHE_TTL_MS, DEFAULT_CACHE_SIZE } from '../lib/resilience/cache.js';
|
|
20
|
+
import { recordQueryDuration, recordCacheHit } from '../lib/observability/metrics.js';
|
|
21
|
+
import { Histogram } from '../lib/observability/histogram.js';
|
|
22
|
+
import { getIndexPath, readIndex, isIndexStale, queryIndex, readLinesByNumber, } from '../lib/observability/indexer.js';
|
|
23
|
+
import { sanitizePath } from '../lib/errors/error-sanitizer.js';
|
|
24
|
+
import { CircuitBreaker } from '../lib/resilience/circuit-breaker.js';
|
|
25
|
+
import { withSpan } from '../lib/observability/instrumentation.js';
|
|
16
26
|
import { existsSync } from 'fs';
|
|
27
|
+
import { appendFile, mkdir, readFile, writeFile, rename } from 'fs/promises';
|
|
28
|
+
import { randomUUID } from 'crypto';
|
|
29
|
+
import { ParseStatsTracker } from '../lib/observability/parse-stats.js';
|
|
30
|
+
import { createLogger } from '../lib/core/logger.js';
|
|
31
|
+
const queryLog = createLogger('query');
|
|
32
|
+
const configLog = createLogger('config');
|
|
17
33
|
function startTiming() {
|
|
18
34
|
const start = performance.now();
|
|
19
|
-
return {
|
|
20
|
-
end: () => performance.now() - start
|
|
21
|
-
};
|
|
35
|
+
return { end: () => performance.now() - start };
|
|
22
36
|
}
|
|
23
37
|
const SLOW_QUERY_THRESHOLD_MS = 500;
|
|
24
38
|
/**
|
|
@@ -29,29 +43,39 @@ const regexCache = new Map();
|
|
|
29
43
|
const MAX_CACHED_REGEX_PATTERNS = 100;
|
|
30
44
|
/**
|
|
31
45
|
* Get a cached regex or compile and cache a new one.
|
|
46
|
+
* Accepts an optional pre-compiled RegExp to seed the cache on first miss (CR-L3).
|
|
32
47
|
* Returns null for invalid patterns (logs warning).
|
|
33
48
|
*/
|
|
34
|
-
function getCachedRegex(pattern) {
|
|
49
|
+
function getCachedRegex(pattern, precompiled) {
|
|
35
50
|
const cached = regexCache.get(pattern);
|
|
36
51
|
if (cached) {
|
|
52
|
+
// Move to end of Map for proper LRU ordering
|
|
53
|
+
regexCache.delete(pattern);
|
|
54
|
+
regexCache.set(pattern, cached);
|
|
37
55
|
return cached;
|
|
38
56
|
}
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
const firstKey = regexCache.keys().next().value;
|
|
44
|
-
if (firstKey !== undefined) {
|
|
45
|
-
regexCache.delete(firstKey);
|
|
46
|
-
}
|
|
57
|
+
// Use pre-compiled regex if provided (avoids double-compilation when validator returns RegExp)
|
|
58
|
+
const regex = precompiled ?? (() => {
|
|
59
|
+
try {
|
|
60
|
+
return new RegExp(pattern);
|
|
47
61
|
}
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
62
|
+
catch {
|
|
63
|
+
const truncatedPattern = pattern.length > PERCENTILE.P50 ? pattern.slice(0, PERCENTILE.P50) + '...' : pattern;
|
|
64
|
+
configLog.warn(`Invalid regex pattern: ${truncatedPattern.replace(/[\n\r]/g, ' ')}`);
|
|
65
|
+
return null;
|
|
66
|
+
}
|
|
67
|
+
})();
|
|
68
|
+
if (!regex)
|
|
53
69
|
return null;
|
|
70
|
+
// LRU eviction: remove oldest entry if at capacity
|
|
71
|
+
if (regexCache.size >= MAX_CACHED_REGEX_PATTERNS) {
|
|
72
|
+
const firstKey = regexCache.keys().next().value;
|
|
73
|
+
if (firstKey !== undefined) {
|
|
74
|
+
regexCache.delete(firstKey);
|
|
75
|
+
}
|
|
54
76
|
}
|
|
77
|
+
regexCache.set(pattern, regex);
|
|
78
|
+
return regex;
|
|
55
79
|
}
|
|
56
80
|
/**
|
|
57
81
|
* Insert item into a sorted array, maintaining sort order and max size.
|
|
@@ -132,11 +156,11 @@ function parseTimeBucket(bucket) {
|
|
|
132
156
|
const unit = match[2];
|
|
133
157
|
switch (unit) {
|
|
134
158
|
case 'm':
|
|
135
|
-
return value *
|
|
159
|
+
return value * TIME_MS.MINUTE; // minutes to ms
|
|
136
160
|
case 'h':
|
|
137
|
-
return value *
|
|
161
|
+
return value * TIME_MS.HOUR; // hours to ms
|
|
138
162
|
case 'd':
|
|
139
|
-
return value *
|
|
163
|
+
return value * TIME_MS.DAY; // days to ms
|
|
140
164
|
default:
|
|
141
165
|
return null;
|
|
142
166
|
}
|
|
@@ -148,6 +172,325 @@ function floorToBucket(timestamp, bucketMs) {
|
|
|
148
172
|
const ts = new Date(timestamp).getTime();
|
|
149
173
|
return Math.floor(ts / bucketMs) * bucketMs;
|
|
150
174
|
}
|
|
175
|
+
// ---------------------------------------------------------------------------
|
|
176
|
+
// OTLP envelope unwrap adapters (A4 items 1-6)
|
|
177
|
+
//
|
|
178
|
+
// When the L5 batch migration script converts flat JSONL to OTLP format,
|
|
179
|
+
// each JSONL line becomes an OTLP envelope wrapping one or more records.
|
|
180
|
+
// These adapters detect envelope lines and unwrap them to Flat* shapes
|
|
181
|
+
// so the existing normalize* functions can process them unchanged.
|
|
182
|
+
// ---------------------------------------------------------------------------
|
|
183
|
+
/** Reserved keys that must not be set on plain objects to prevent prototype pollution */
|
|
184
|
+
const RESERVED_KEYS = new Set(['__proto__', 'constructor', 'prototype']);
|
|
185
|
+
/** Convert OTLP KeyValue array to flat attributes record (null-prototype to prevent pollution) */
|
|
186
|
+
function fromOTLPAttributes(kvs) {
|
|
187
|
+
if (!kvs)
|
|
188
|
+
return Object.create(null);
|
|
189
|
+
const out = Object.create(null);
|
|
190
|
+
for (const kv of kvs) {
|
|
191
|
+
if (RESERVED_KEYS.has(kv.key))
|
|
192
|
+
continue;
|
|
193
|
+
out[kv.key] = fromOTLPValue(kv.value);
|
|
194
|
+
}
|
|
195
|
+
return out;
|
|
196
|
+
}
|
|
197
|
+
/** Convert a single OTLP AnyValue to a JS primitive/object */
|
|
198
|
+
function fromOTLPValue(v) {
|
|
199
|
+
if (!v)
|
|
200
|
+
return undefined;
|
|
201
|
+
if (v.stringValue !== undefined)
|
|
202
|
+
return v.stringValue;
|
|
203
|
+
if (v.intValue !== undefined) {
|
|
204
|
+
const n = Number(v.intValue);
|
|
205
|
+
return Number.isFinite(n) ? n : undefined;
|
|
206
|
+
}
|
|
207
|
+
if (v.doubleValue !== undefined)
|
|
208
|
+
return v.doubleValue;
|
|
209
|
+
if (v.boolValue !== undefined)
|
|
210
|
+
return v.boolValue;
|
|
211
|
+
if (v.arrayValue)
|
|
212
|
+
return v.arrayValue.values.map(fromOTLPValue);
|
|
213
|
+
if (v.kvlistValue)
|
|
214
|
+
return fromOTLPAttributes(v.kvlistValue.values);
|
|
215
|
+
return undefined;
|
|
216
|
+
}
|
|
217
|
+
/** Convert OTLP nanosecond string to [seconds, nanoseconds] tuple. Returns undefined on invalid input. */
|
|
218
|
+
function nanoStrToTuple(ns) {
|
|
219
|
+
if (!ns)
|
|
220
|
+
return undefined;
|
|
221
|
+
try {
|
|
222
|
+
const n = BigInt(ns);
|
|
223
|
+
return [
|
|
224
|
+
Number(n / NANOSECONDS_PER_SECOND_BIGINT),
|
|
225
|
+
Number(n % NANOSECONDS_PER_SECOND_BIGINT),
|
|
226
|
+
];
|
|
227
|
+
}
|
|
228
|
+
catch {
|
|
229
|
+
return undefined;
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
/** Convert OTLP nanosecond string to epoch milliseconds. Returns undefined on invalid input. */
|
|
233
|
+
function nanoStrToMs(ns) {
|
|
234
|
+
if (!ns)
|
|
235
|
+
return undefined;
|
|
236
|
+
try {
|
|
237
|
+
return Number(BigInt(ns) / BigInt(ONE_MILLION));
|
|
238
|
+
}
|
|
239
|
+
catch {
|
|
240
|
+
return undefined;
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
/** Convert OTLP nanosecond string to ISO-8601 timestamp. Returns undefined on invalid input. */
|
|
244
|
+
function nanoStrToISO(ns) {
|
|
245
|
+
const ms = nanoStrToMs(ns);
|
|
246
|
+
return ms !== undefined ? new Date(ms).toISOString() : undefined;
|
|
247
|
+
}
|
|
248
|
+
/** Extract numeric value from OTLP data point (asDouble preferred, asInt fallback with precision guard). */
|
|
249
|
+
function dataPointValue(dp) {
|
|
250
|
+
if (dp.asDouble !== undefined) {
|
|
251
|
+
const d = Number(dp.asDouble);
|
|
252
|
+
return Number.isFinite(d) ? d : 0;
|
|
253
|
+
}
|
|
254
|
+
const n = Number(dp.asInt ?? '0');
|
|
255
|
+
return Number.isFinite(n) ? n : 0;
|
|
256
|
+
}
|
|
257
|
+
/**
|
|
258
|
+
* Extract service.name and service.version from OTLP resource attributes.
|
|
259
|
+
* Only these two fields are mapped; all other resource attributes (host.name,
|
|
260
|
+
* process.pid, telemetry.sdk.*, etc.) are intentionally dropped because flat
|
|
261
|
+
* records carry them in span-level attributes and the normalize* functions
|
|
262
|
+
* already handle that shape.
|
|
263
|
+
*/
|
|
264
|
+
function extractResource(attrs) {
|
|
265
|
+
if (!attrs)
|
|
266
|
+
return undefined;
|
|
267
|
+
let serviceName;
|
|
268
|
+
let serviceVersion;
|
|
269
|
+
for (const kv of attrs) {
|
|
270
|
+
if (kv.key === 'service.name' && kv.value?.stringValue)
|
|
271
|
+
serviceName = kv.value.stringValue;
|
|
272
|
+
if (kv.key === 'service.version' && kv.value?.stringValue)
|
|
273
|
+
serviceVersion = kv.value.stringValue;
|
|
274
|
+
if (serviceName && serviceVersion)
|
|
275
|
+
break;
|
|
276
|
+
}
|
|
277
|
+
return (serviceName || serviceVersion) ? { serviceName, serviceVersion } : undefined;
|
|
278
|
+
}
|
|
279
|
+
/**
|
|
280
|
+
* Detect and unwrap an OTLP trace envelope line to FlatSpan[].
|
|
281
|
+
* Returns null if the record is not an OTLP envelope (i.e., it's a flat span).
|
|
282
|
+
*/
|
|
283
|
+
function unwrapOTLPSpans(record) {
|
|
284
|
+
if (!record || typeof record !== 'object' || !('resourceSpans' in record))
|
|
285
|
+
return null;
|
|
286
|
+
const envelope = record;
|
|
287
|
+
const results = [];
|
|
288
|
+
for (const rs of envelope.resourceSpans) {
|
|
289
|
+
const resource = extractResource(rs.resource?.attributes);
|
|
290
|
+
for (const ss of rs.scopeSpans ?? []) {
|
|
291
|
+
const instrumentationScope = ss.scope
|
|
292
|
+
? { name: ss.scope.name, version: ss.scope.version }
|
|
293
|
+
: undefined;
|
|
294
|
+
for (const span of ss.spans ?? []) {
|
|
295
|
+
const attrs = fromOTLPAttributes(span.attributes);
|
|
296
|
+
// Merge resource attributes into span attributes (service.name, etc.)
|
|
297
|
+
if (resource?.serviceName)
|
|
298
|
+
attrs['service.name'] = resource.serviceName;
|
|
299
|
+
if (resource?.serviceVersion)
|
|
300
|
+
attrs['service.version'] = resource.serviceVersion;
|
|
301
|
+
results.push({
|
|
302
|
+
traceId: span.traceId,
|
|
303
|
+
spanId: span.spanId,
|
|
304
|
+
parentSpanId: span.parentSpanId,
|
|
305
|
+
name: span.name,
|
|
306
|
+
kind: span.kind,
|
|
307
|
+
startTime: nanoStrToTuple(span.startTimeUnixNano),
|
|
308
|
+
endTime: nanoStrToTuple(span.endTimeUnixNano),
|
|
309
|
+
status: span.status,
|
|
310
|
+
attributes: attrs,
|
|
311
|
+
events: span.events?.map(e => ({
|
|
312
|
+
name: e.name,
|
|
313
|
+
timestamp: nanoStrToMs(e.timeUnixNano),
|
|
314
|
+
attributes: fromOTLPAttributes(e.attributes),
|
|
315
|
+
})),
|
|
316
|
+
links: span.links?.map(l => ({
|
|
317
|
+
context: { traceId: l.traceId, spanId: l.spanId },
|
|
318
|
+
attributes: fromOTLPAttributes(l.attributes),
|
|
319
|
+
})),
|
|
320
|
+
resource,
|
|
321
|
+
instrumentationScope,
|
|
322
|
+
});
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
return results;
|
|
327
|
+
}
|
|
328
|
+
/**
|
|
329
|
+
* Detect and unwrap an OTLP log envelope line to FlatLog[].
|
|
330
|
+
* Returns null if the record is not an OTLP envelope.
|
|
331
|
+
*/
|
|
332
|
+
function unwrapOTLPLogs(record) {
|
|
333
|
+
if (!record || typeof record !== 'object' || !('resourceLogs' in record))
|
|
334
|
+
return null;
|
|
335
|
+
const envelope = record;
|
|
336
|
+
const results = [];
|
|
337
|
+
for (const rl of envelope.resourceLogs) {
|
|
338
|
+
const resource = extractResource(rl.resource?.attributes);
|
|
339
|
+
for (const sl of rl.scopeLogs ?? []) {
|
|
340
|
+
const instrumentationScope = sl.scope
|
|
341
|
+
? { name: sl.scope.name, version: sl.scope.version }
|
|
342
|
+
: undefined;
|
|
343
|
+
for (const log of sl.logRecords ?? []) {
|
|
344
|
+
const attrs = fromOTLPAttributes(log.attributes);
|
|
345
|
+
const bodyValue = log.body;
|
|
346
|
+
results.push({
|
|
347
|
+
timestamp: nanoStrToISO(log.timeUnixNano) ?? '',
|
|
348
|
+
severityText: log.severityText,
|
|
349
|
+
body: bodyValue?.stringValue ?? '',
|
|
350
|
+
traceId: log.traceId,
|
|
351
|
+
spanId: log.spanId,
|
|
352
|
+
attributes: attrs,
|
|
353
|
+
resource: resource ? { serviceName: resource.serviceName } : undefined,
|
|
354
|
+
instrumentationScope,
|
|
355
|
+
});
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
return results;
|
|
360
|
+
}
|
|
361
|
+
/**
|
|
362
|
+
* Detect and unwrap an OTLP metric envelope line to FlatMetric[].
|
|
363
|
+
* Returns null if the record is not an OTLP envelope.
|
|
364
|
+
*/
|
|
365
|
+
function unwrapOTLPMetrics(record) {
|
|
366
|
+
if (!record || typeof record !== 'object' || !('resourceMetrics' in record))
|
|
367
|
+
return null;
|
|
368
|
+
const envelope = record;
|
|
369
|
+
const results = [];
|
|
370
|
+
for (const rm of envelope.resourceMetrics) {
|
|
371
|
+
const resource = extractResource(rm.resource?.attributes);
|
|
372
|
+
for (const sm of rm.scopeMetrics ?? []) {
|
|
373
|
+
for (const metric of sm.metrics ?? []) {
|
|
374
|
+
const name = metric.name;
|
|
375
|
+
const unit = metric.unit;
|
|
376
|
+
// Determine metric type and extract data points
|
|
377
|
+
const gauge = metric.gauge;
|
|
378
|
+
const sum = metric.sum;
|
|
379
|
+
const histogram = metric.histogram;
|
|
380
|
+
if (histogram?.dataPoints) {
|
|
381
|
+
for (const dp of histogram.dataPoints) {
|
|
382
|
+
const attrs = fromOTLPAttributes(dp.attributes);
|
|
383
|
+
const bucketCounts = dp.bucketCounts ?? [];
|
|
384
|
+
const explicitBounds = dp.explicitBounds ?? [];
|
|
385
|
+
// Convert OTLP per-bucket counts + explicitBounds to cumulative {le, count}
|
|
386
|
+
let cumulative = 0;
|
|
387
|
+
const buckets = [];
|
|
388
|
+
for (let i = 0; i < bucketCounts.length; i++) {
|
|
389
|
+
cumulative += Number(bucketCounts[i]);
|
|
390
|
+
buckets.push({ le: i < explicitBounds.length ? explicitBounds[i] : Infinity, count: cumulative });
|
|
391
|
+
}
|
|
392
|
+
const histSum = dp.sum ?? 0;
|
|
393
|
+
results.push({
|
|
394
|
+
timestamp: nanoStrToISO(dp.timeUnixNano) ?? '',
|
|
395
|
+
name,
|
|
396
|
+
value: histSum,
|
|
397
|
+
type: 'histogram',
|
|
398
|
+
unit,
|
|
399
|
+
attributes: attrs,
|
|
400
|
+
resource: resource ? { serviceName: resource.serviceName } : undefined,
|
|
401
|
+
histogram: { buckets, sum: histSum, count: Number(dp.count ?? 0) },
|
|
402
|
+
aggregationTemporality: histogram.aggregationTemporality,
|
|
403
|
+
});
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
else if (sum?.dataPoints) {
|
|
407
|
+
for (const dp of sum.dataPoints) {
|
|
408
|
+
const attrs = fromOTLPAttributes(dp.attributes);
|
|
409
|
+
results.push({
|
|
410
|
+
timestamp: nanoStrToISO(dp.timeUnixNano) ?? '',
|
|
411
|
+
name,
|
|
412
|
+
value: dataPointValue(dp),
|
|
413
|
+
type: 'counter',
|
|
414
|
+
unit,
|
|
415
|
+
attributes: attrs,
|
|
416
|
+
resource: resource ? { serviceName: resource.serviceName } : undefined,
|
|
417
|
+
aggregationTemporality: sum.aggregationTemporality,
|
|
418
|
+
});
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
else if (gauge?.dataPoints) {
|
|
422
|
+
for (const dp of gauge.dataPoints) {
|
|
423
|
+
const attrs = fromOTLPAttributes(dp.attributes);
|
|
424
|
+
results.push({
|
|
425
|
+
timestamp: nanoStrToISO(dp.timeUnixNano) ?? '',
|
|
426
|
+
name,
|
|
427
|
+
value: dataPointValue(dp),
|
|
428
|
+
type: 'gauge',
|
|
429
|
+
unit,
|
|
430
|
+
attributes: attrs,
|
|
431
|
+
resource: resource ? { serviceName: resource.serviceName } : undefined,
|
|
432
|
+
});
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
return results;
|
|
439
|
+
}
|
|
440
|
+
/**
|
|
441
|
+
* Detect and unwrap an OTLP trace envelope containing evaluation events.
|
|
442
|
+
* Evaluations in OTLP are span events with name "gen_ai.evaluation.result".
|
|
443
|
+
* Returns null if the record is not an OTLP envelope.
|
|
444
|
+
*/
|
|
445
|
+
function unwrapOTLPEvaluations(record) {
|
|
446
|
+
if (!record || typeof record !== 'object' || !('resourceSpans' in record))
|
|
447
|
+
return null;
|
|
448
|
+
const envelope = record;
|
|
449
|
+
const results = [];
|
|
450
|
+
for (const rs of envelope.resourceSpans) {
|
|
451
|
+
for (const ss of rs.scopeSpans ?? []) {
|
|
452
|
+
for (const span of ss.spans ?? []) {
|
|
453
|
+
for (const event of span.events ?? []) {
|
|
454
|
+
if (event.name !== 'gen_ai.evaluation.result')
|
|
455
|
+
continue;
|
|
456
|
+
const attrs = fromOTLPAttributes(event.attributes);
|
|
457
|
+
results.push({
|
|
458
|
+
timestamp: nanoStrToISO(event.timeUnixNano) ?? '',
|
|
459
|
+
name: event.name,
|
|
460
|
+
attributes: attrs,
|
|
461
|
+
traceId: span.traceId,
|
|
462
|
+
spanId: span.spanId,
|
|
463
|
+
});
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
// Return null (not []) when envelope contains no evaluation events,
|
|
469
|
+
// so flattenEvaluationRecord falls through to the passthrough path.
|
|
470
|
+
// This prevents a flat record that coincidentally has a 'resourceSpans'
|
|
471
|
+
// key from being silently swallowed.
|
|
472
|
+
return results.length > 0 ? results : null;
|
|
473
|
+
}
|
|
474
|
+
/** Unwrap-or-passthrough for trace records. OTLP envelopes yield multiple spans; flat records yield one. */
|
|
475
|
+
function flattenSpanRecord(raw) {
|
|
476
|
+
const unwrapped = unwrapOTLPSpans(raw);
|
|
477
|
+
return unwrapped ?? [raw];
|
|
478
|
+
}
|
|
479
|
+
/** Unwrap-or-passthrough for log records */
|
|
480
|
+
function flattenLogRecord(raw) {
|
|
481
|
+
const unwrapped = unwrapOTLPLogs(raw);
|
|
482
|
+
return unwrapped ?? [raw];
|
|
483
|
+
}
|
|
484
|
+
/** Unwrap-or-passthrough for metric records */
|
|
485
|
+
function flattenMetricRecord(raw) {
|
|
486
|
+
const unwrapped = unwrapOTLPMetrics(raw);
|
|
487
|
+
return unwrapped ?? [raw];
|
|
488
|
+
}
|
|
489
|
+
/** Unwrap-or-passthrough for evaluation records */
|
|
490
|
+
function flattenEvaluationRecord(raw) {
|
|
491
|
+
const unwrapped = unwrapOTLPEvaluations(raw);
|
|
492
|
+
return unwrapped ?? [raw];
|
|
493
|
+
}
|
|
151
494
|
/**
|
|
152
495
|
* Extract a field from an object using dot notation path.
|
|
153
496
|
* Returns undefined if the path doesn't exist or the object is not traversable.
|
|
@@ -156,7 +499,7 @@ function extractField(obj, path) {
|
|
|
156
499
|
const parts = path.split('.');
|
|
157
500
|
let current = obj;
|
|
158
501
|
for (const part of parts) {
|
|
159
|
-
if (current
|
|
502
|
+
if (current === null || typeof current !== 'object')
|
|
160
503
|
return undefined;
|
|
161
504
|
current = current[part];
|
|
162
505
|
}
|
|
@@ -201,21 +544,95 @@ function extractValidNumber(value) {
|
|
|
201
544
|
return undefined;
|
|
202
545
|
return Number.isFinite(value) ? value : undefined;
|
|
203
546
|
}
|
|
204
|
-
/** Valid evaluator types per our schema */
|
|
205
|
-
const VALID_EVALUATOR_TYPES = ['llm', 'human', 'rule', 'classifier'];
|
|
206
547
|
/**
|
|
207
|
-
* Extract and validate evaluatorType
|
|
548
|
+
* Extract and validate evaluatorType using schema-based normalization (NARROW-11).
|
|
208
549
|
*/
|
|
209
550
|
function extractValidEvaluatorType(value) {
|
|
210
551
|
if (typeof value !== 'string')
|
|
211
552
|
return undefined;
|
|
212
|
-
const
|
|
213
|
-
return
|
|
553
|
+
const result = rawEvaluatorTypeSchema.safeParse(value);
|
|
554
|
+
return result.success ? result.data : undefined;
|
|
214
555
|
}
|
|
215
556
|
/**
|
|
216
557
|
* Convert flat evaluation to normalized EvaluationResult
|
|
217
558
|
* Includes runtime validation for data integrity (P0-2 fix)
|
|
218
559
|
*/
|
|
560
|
+
/**
|
|
561
|
+
* Safely extract StepScore array from raw attribute value.
|
|
562
|
+
* Returns undefined if not a valid array or if parsing fails.
|
|
563
|
+
*/
|
|
564
|
+
function extractStepScores(value) {
|
|
565
|
+
if (!value)
|
|
566
|
+
return undefined;
|
|
567
|
+
// If it's a string, try to parse as JSON
|
|
568
|
+
let arr;
|
|
569
|
+
if (typeof value === 'string') {
|
|
570
|
+
try {
|
|
571
|
+
arr = JSON.parse(value);
|
|
572
|
+
if (!Array.isArray(arr))
|
|
573
|
+
return undefined;
|
|
574
|
+
}
|
|
575
|
+
catch {
|
|
576
|
+
return undefined;
|
|
577
|
+
}
|
|
578
|
+
}
|
|
579
|
+
else if (Array.isArray(value)) {
|
|
580
|
+
arr = value;
|
|
581
|
+
}
|
|
582
|
+
else {
|
|
583
|
+
return undefined;
|
|
584
|
+
}
|
|
585
|
+
// Validate each element and filter invalid entries
|
|
586
|
+
return arr
|
|
587
|
+
.filter((item) => {
|
|
588
|
+
if (!item || typeof item !== 'object')
|
|
589
|
+
return false;
|
|
590
|
+
const obj = item;
|
|
591
|
+
return ((typeof obj.step === 'string' || typeof obj.step === 'number') &&
|
|
592
|
+
typeof obj.score === 'number' &&
|
|
593
|
+
Number.isFinite(obj.score));
|
|
594
|
+
})
|
|
595
|
+
.slice(0, MAX_STEP_SCORES); // Respect MAX_STEP_SCORES limit
|
|
596
|
+
}
|
|
597
|
+
/**
|
|
598
|
+
* Safely extract ToolVerification array from raw attribute value.
|
|
599
|
+
* Returns undefined if not a valid array or if parsing fails.
|
|
600
|
+
*/
|
|
601
|
+
function extractToolVerifications(value) {
|
|
602
|
+
if (!value)
|
|
603
|
+
return undefined;
|
|
604
|
+
// If it's a string, try to parse as JSON
|
|
605
|
+
let arr;
|
|
606
|
+
if (typeof value === 'string') {
|
|
607
|
+
try {
|
|
608
|
+
arr = JSON.parse(value);
|
|
609
|
+
if (!Array.isArray(arr))
|
|
610
|
+
return undefined;
|
|
611
|
+
}
|
|
612
|
+
catch {
|
|
613
|
+
return undefined;
|
|
614
|
+
}
|
|
615
|
+
}
|
|
616
|
+
else if (Array.isArray(value)) {
|
|
617
|
+
arr = value;
|
|
618
|
+
}
|
|
619
|
+
else {
|
|
620
|
+
return undefined;
|
|
621
|
+
}
|
|
622
|
+
// Validate each element and filter invalid entries
|
|
623
|
+
return arr
|
|
624
|
+
.filter((item) => {
|
|
625
|
+
if (!item || typeof item !== 'object')
|
|
626
|
+
return false;
|
|
627
|
+
const obj = item;
|
|
628
|
+
return (typeof obj.toolName === 'string' &&
|
|
629
|
+
typeof obj.toolCorrect === 'boolean' &&
|
|
630
|
+
typeof obj.argsCorrect === 'boolean' &&
|
|
631
|
+
typeof obj.score === 'number' &&
|
|
632
|
+
Number.isFinite(obj.score));
|
|
633
|
+
})
|
|
634
|
+
.slice(0, MAX_TOOL_VERIFICATIONS);
|
|
635
|
+
}
|
|
219
636
|
function normalizeEvaluation(raw) {
|
|
220
637
|
const attrs = raw.attributes;
|
|
221
638
|
// evaluationName is required per OTel spec - must be non-empty string
|
|
@@ -235,24 +652,18 @@ function normalizeEvaluation(raw) {
|
|
|
235
652
|
// Evaluator fields (custom extension)
|
|
236
653
|
evaluator: extractValidString(attrs?.[GENAI_EVALUATION_ATTRIBUTES.EVALUATOR]),
|
|
237
654
|
evaluatorType: extractValidEvaluatorType(attrs?.[GENAI_EVALUATION_ATTRIBUTES.EVALUATOR_TYPE]),
|
|
238
|
-
responseId: extractValidString(attrs?.[
|
|
655
|
+
responseId: extractValidString(attrs?.[GENAI_RESPONSE_ATTRIBUTES.ID]),
|
|
239
656
|
traceId: raw.traceId,
|
|
240
657
|
spanId: raw.spanId,
|
|
241
658
|
sessionId: extractValidString(attrs?.['session.id']),
|
|
659
|
+
// Agent-as-Judge fields (Section 10.7)
|
|
660
|
+
agentId: extractValidString(attrs?.[GENAI_AGENT_ATTRIBUTES.AGENT_ID]),
|
|
661
|
+
agentName: extractValidString(attrs?.[GENAI_AGENT_ATTRIBUTES.AGENT_NAME]),
|
|
662
|
+
stepScores: extractStepScores(attrs?.[AGENT_JUDGE_ATTRIBUTES.STEP_SCORES]),
|
|
663
|
+
toolVerifications: extractToolVerifications(attrs?.[AGENT_JUDGE_ATTRIBUTES.TOOL_VERIFICATIONS]),
|
|
664
|
+
trajectoryLength: extractValidNumber(attrs?.[AGENT_JUDGE_ATTRIBUTES.TRAJECTORY_LENGTH]),
|
|
242
665
|
};
|
|
243
666
|
}
|
|
244
|
-
/**
|
|
245
|
-
* OTel-compliant severity number mapping
|
|
246
|
-
* https://opentelemetry.io/docs/specs/otel/logs/data-model/#field-severitynumber
|
|
247
|
-
*/
|
|
248
|
-
const SEVERITY_MAP = {
|
|
249
|
-
TRACE: 1,
|
|
250
|
-
DEBUG: 5,
|
|
251
|
-
INFO: 9,
|
|
252
|
-
WARN: 13,
|
|
253
|
-
ERROR: 17,
|
|
254
|
-
FATAL: 21,
|
|
255
|
-
};
|
|
256
667
|
/**
|
|
257
668
|
* Convert flat span to normalized TraceSpan
|
|
258
669
|
*/
|
|
@@ -261,18 +672,18 @@ function normalizeSpan(raw) {
|
|
|
261
672
|
return null;
|
|
262
673
|
// Convert [seconds, nanoseconds] to nanoseconds
|
|
263
674
|
const startTimeUnixNano = raw.startTime
|
|
264
|
-
? raw.startTime[0] *
|
|
675
|
+
? raw.startTime[0] * NANOSECONDS_PER_SECOND + raw.startTime[1]
|
|
265
676
|
: 0;
|
|
266
677
|
const endTimeUnixNano = raw.endTime
|
|
267
|
-
? raw.endTime[0] *
|
|
678
|
+
? raw.endTime[0] * NANOSECONDS_PER_SECOND + raw.endTime[1]
|
|
268
679
|
: undefined;
|
|
269
680
|
// Calculate duration in ms
|
|
270
681
|
let durationMs;
|
|
271
682
|
if (raw.duration) {
|
|
272
|
-
durationMs = (raw.duration[0] *
|
|
683
|
+
durationMs = (raw.duration[0] * TIME_MS.SECOND) + (raw.duration[1] / ONE_MILLION);
|
|
273
684
|
}
|
|
274
685
|
else if (endTimeUnixNano) {
|
|
275
|
-
durationMs = (endTimeUnixNano - startTimeUnixNano) /
|
|
686
|
+
durationMs = (endTimeUnixNano - startTimeUnixNano) / ONE_MILLION;
|
|
276
687
|
}
|
|
277
688
|
// Add service name to attributes if present in resource
|
|
278
689
|
const attributes = { ...raw.attributes };
|
|
@@ -324,14 +735,15 @@ function normalizeLog(raw, extractFields) {
|
|
|
324
735
|
// Handle timestamp: either ISO string or [seconds, nanoseconds] array
|
|
325
736
|
let timestamp;
|
|
326
737
|
if (Array.isArray(raw.timestamp)) {
|
|
327
|
-
const ms = raw.timestamp[0] *
|
|
738
|
+
const ms = raw.timestamp[0] * TIME_MS.SECOND + Math.floor(raw.timestamp[1] / ONE_MILLION);
|
|
328
739
|
timestamp = new Date(ms).toISOString();
|
|
329
740
|
}
|
|
330
741
|
else {
|
|
331
742
|
timestamp = raw.timestamp;
|
|
332
743
|
}
|
|
333
|
-
const
|
|
334
|
-
const
|
|
744
|
+
const rawSeverity = raw.severityText || raw.severity || 'INFO';
|
|
745
|
+
const severity = normalizeOtelLogSeverity(rawSeverity) ?? rawSeverity;
|
|
746
|
+
const severityNumber = getOtelLogSeverityNumber(severity);
|
|
335
747
|
const body = raw.body || '';
|
|
336
748
|
// Extract fields from JSON body if requested
|
|
337
749
|
let extractedFields;
|
|
@@ -368,11 +780,11 @@ function normalizeAggregationTemporality(value) {
|
|
|
368
780
|
}
|
|
369
781
|
// Handle numeric values (OTel spec)
|
|
370
782
|
switch (value) {
|
|
371
|
-
case
|
|
783
|
+
case AGGREGATION_TEMPORALITY.DELTA:
|
|
372
784
|
return 'DELTA';
|
|
373
|
-
case
|
|
785
|
+
case AGGREGATION_TEMPORALITY.CUMULATIVE:
|
|
374
786
|
return 'CUMULATIVE';
|
|
375
|
-
case
|
|
787
|
+
case AGGREGATION_TEMPORALITY.UNSPECIFIED:
|
|
376
788
|
default:
|
|
377
789
|
return 'UNSPECIFIED';
|
|
378
790
|
}
|
|
@@ -386,7 +798,7 @@ function normalizeExemplarTimestamp(ts, metricTimestamp) {
|
|
|
386
798
|
if (typeof ts === 'string')
|
|
387
799
|
return ts;
|
|
388
800
|
// Convert [seconds, nanoseconds] to ISO string
|
|
389
|
-
const ms = ts[0] *
|
|
801
|
+
const ms = ts[0] * TIME_MS.SECOND + Math.floor(ts[1] / ONE_MILLION);
|
|
390
802
|
return new Date(ms).toISOString();
|
|
391
803
|
}
|
|
392
804
|
/**
|
|
@@ -434,10 +846,12 @@ function normalizeMetric(raw) {
|
|
|
434
846
|
};
|
|
435
847
|
}
|
|
436
848
|
/**
|
|
437
|
-
* Get files within date range
|
|
849
|
+
* Get files within date range.
|
|
850
|
+
* allowedBases constrains which directories listFiles may access (CR-H2).
|
|
851
|
+
* Callers should pass the telemetryDir as a base so test-provided directories are allowed.
|
|
438
852
|
*/
|
|
439
|
-
function getFilesInRange(dir, pattern, startDate, endDate) {
|
|
440
|
-
const files = listFiles(dir, pattern);
|
|
853
|
+
function getFilesInRange(dir, pattern, startDate, endDate, allowedBases) {
|
|
854
|
+
const files = listFiles(dir, pattern, allowedBases);
|
|
441
855
|
if (!startDate && !endDate) {
|
|
442
856
|
// Default to today only
|
|
443
857
|
const today = getDateString();
|
|
@@ -457,20 +871,47 @@ function getFilesInRange(dir, pattern, startDate, endDate) {
|
|
|
457
871
|
return true;
|
|
458
872
|
});
|
|
459
873
|
}
|
|
874
|
+
function encodeCursor(payload) {
|
|
875
|
+
return Buffer.from(JSON.stringify(payload)).toString('base64url');
|
|
876
|
+
}
|
|
877
|
+
function decodeCursor(cursor) {
|
|
878
|
+
try {
|
|
879
|
+
return JSON.parse(Buffer.from(cursor, 'base64url').toString());
|
|
880
|
+
}
|
|
881
|
+
catch {
|
|
882
|
+
return null;
|
|
883
|
+
}
|
|
884
|
+
}
|
|
460
885
|
export class LocalJsonlBackend {
|
|
461
886
|
name = 'local-jsonl';
|
|
462
887
|
telemetryDir;
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
888
|
+
/** Security: directories listFiles is allowed to access (CR-H2) */
|
|
889
|
+
allowedBases;
|
|
890
|
+
traceCache = new QueryCache(DEFAULT_CACHE_SIZE, CACHE_TTL_MS, 'traces');
|
|
891
|
+
logCache = new QueryCache(DEFAULT_CACHE_SIZE, CACHE_TTL_MS, 'logs');
|
|
892
|
+
metricCache = new QueryCache(DEFAULT_CACHE_SIZE, CACHE_TTL_MS, 'metrics');
|
|
893
|
+
llmEventCache = new QueryCache(DEFAULT_CACHE_SIZE, CACHE_TTL_MS, 'llmEvents');
|
|
894
|
+
evaluationCache = new QueryCache(DEFAULT_CACHE_SIZE, CACHE_TTL_MS, 'evaluations');
|
|
468
895
|
useIndexes;
|
|
469
896
|
circuitBreaker;
|
|
897
|
+
parseStatsTracker;
|
|
898
|
+
/** Count of trace lines that expanded to multiple spans (OTLP envelopes), across both indexed and full-scan paths */
|
|
899
|
+
otlpTraceExpansions = 0;
|
|
900
|
+
/** Histograms for tracking query latency per type */
|
|
901
|
+
queryHistograms = {
|
|
902
|
+
traces: new Histogram(),
|
|
903
|
+
logs: new Histogram(),
|
|
904
|
+
metrics: new Histogram(),
|
|
905
|
+
llmEvents: new Histogram(),
|
|
906
|
+
evaluations: new Histogram(),
|
|
907
|
+
};
|
|
470
908
|
constructor(telemetryDir, useIndexes = true) {
|
|
471
|
-
this.telemetryDir = telemetryDir ||
|
|
909
|
+
this.telemetryDir = telemetryDir || HOOKS_TELEMETRY_DIR;
|
|
472
910
|
this.useIndexes = useIndexes;
|
|
473
911
|
this.circuitBreaker = new CircuitBreaker({ name: 'local-file-io' });
|
|
912
|
+
this.parseStatsTracker = new ParseStatsTracker();
|
|
913
|
+
// Allow telemetryDir itself so custom dirs (including test temp dirs) are permitted (CR-H2)
|
|
914
|
+
this.allowedBases = [homedir(), process.cwd(), this.telemetryDir];
|
|
474
915
|
}
|
|
475
916
|
/**
|
|
476
917
|
* Get circuit breaker state (for health check and testing)
|
|
@@ -506,6 +947,45 @@ export class LocalJsonlBackend {
|
|
|
506
947
|
evaluations: this.evaluationCache.getStats(),
|
|
507
948
|
};
|
|
508
949
|
}
|
|
950
|
+
/**
|
|
951
|
+
* Get parse statistics for JSONL file processing.
|
|
952
|
+
* Returns aggregate stats including success rate and worst files.
|
|
953
|
+
*/
|
|
954
|
+
getParseStats() {
|
|
955
|
+
return {
|
|
956
|
+
...this.parseStatsTracker.getAggregateStats(),
|
|
957
|
+
otlpTraceExpansions: this.otlpTraceExpansions,
|
|
958
|
+
};
|
|
959
|
+
}
|
|
960
|
+
/**
|
|
961
|
+
* Reset parse statistics (useful for testing)
|
|
962
|
+
*/
|
|
963
|
+
resetParseStats() {
|
|
964
|
+
this.parseStatsTracker.reset();
|
|
965
|
+
this.otlpTraceExpansions = 0;
|
|
966
|
+
}
|
|
967
|
+
/**
|
|
968
|
+
* Get query latency statistics per query type
|
|
969
|
+
*/
|
|
970
|
+
getQueryStats() {
|
|
971
|
+
return {
|
|
972
|
+
traces: this.queryHistograms.traces.getStats(),
|
|
973
|
+
logs: this.queryHistograms.logs.getStats(),
|
|
974
|
+
metrics: this.queryHistograms.metrics.getStats(),
|
|
975
|
+
llmEvents: this.queryHistograms.llmEvents.getStats(),
|
|
976
|
+
evaluations: this.queryHistograms.evaluations.getStats(),
|
|
977
|
+
};
|
|
978
|
+
}
|
|
979
|
+
/**
|
|
980
|
+
* Reset query latency histograms (useful for testing)
|
|
981
|
+
*/
|
|
982
|
+
resetQueryStats() {
|
|
983
|
+
this.queryHistograms.traces.reset();
|
|
984
|
+
this.queryHistograms.logs.reset();
|
|
985
|
+
this.queryHistograms.metrics.reset();
|
|
986
|
+
this.queryHistograms.llmEvents.reset();
|
|
987
|
+
this.queryHistograms.evaluations.reset();
|
|
988
|
+
}
|
|
509
989
|
/**
|
|
510
990
|
* Try to use an index for a file, returning matching line numbers or null if full scan needed
|
|
511
991
|
*/
|
|
@@ -521,365 +1001,474 @@ export class LocalJsonlBackend {
|
|
|
521
1001
|
return queryIndex(index, indexOptions);
|
|
522
1002
|
}
|
|
523
1003
|
async queryTraces(options) {
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
1004
|
+
// P2 fix: Defer attribute allocation until span is actually created
|
|
1005
|
+
return withSpan('obs_toolkit.query.traces', () => ({
|
|
1006
|
+
'obs_toolkit.query.type': 'traces',
|
|
1007
|
+
'obs_toolkit.query.backend': 'local',
|
|
1008
|
+
'obs_toolkit.query.limit': options.limit ?? DEFAULT_QUERY_RESULT_LIMIT,
|
|
1009
|
+
'obs_toolkit.query.start_date': options.startDate,
|
|
1010
|
+
'obs_toolkit.query.end_date': options.endDate,
|
|
1011
|
+
}), async (span) => {
|
|
1012
|
+
const timer = startTiming();
|
|
1013
|
+
const logTiming = () => {
|
|
1014
|
+
const durationMs = timer.end();
|
|
1015
|
+
recordQueryDuration('traces', durationMs, 'local');
|
|
1016
|
+
this.queryHistograms.traces.observe(durationMs);
|
|
1017
|
+
if (durationMs > SLOW_QUERY_THRESHOLD_MS) {
|
|
1018
|
+
queryLog.warn(`Slow query: queryTraces took ${durationMs.toFixed(1)}ms`, { durationMs });
|
|
1019
|
+
}
|
|
1020
|
+
};
|
|
1021
|
+
// Check circuit breaker - fail fast if open
|
|
1022
|
+
if (!this.circuitBreaker.canRequest()) {
|
|
1023
|
+
logTiming();
|
|
1024
|
+
if (span)
|
|
1025
|
+
span.setAttribute('obs_toolkit.query.result_count', 0);
|
|
1026
|
+
return [];
|
|
529
1027
|
}
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
logTiming();
|
|
541
|
-
return cached;
|
|
542
|
-
}
|
|
543
|
-
const files = getFilesInRange(this.telemetryDir, /traces-\d{4}-\d{2}-\d{2}\.jsonl(\.gz)?$/, options.startDate, options.endDate);
|
|
544
|
-
const results = [];
|
|
545
|
-
const limit = options.limit || 100;
|
|
546
|
-
const offset = options.offset || 0;
|
|
547
|
-
// Use cached regex to avoid recompilation for frequently-used patterns
|
|
548
|
-
const spanNameRegex = options.spanNameRegex
|
|
549
|
-
? getCachedRegex(options.spanNameRegex)
|
|
550
|
-
: null;
|
|
551
|
-
// Build index query options for indexable filters
|
|
552
|
-
const indexOptions = {
|
|
553
|
-
traceId: options.traceId,
|
|
554
|
-
spanName: options.spanName,
|
|
555
|
-
serviceName: options.serviceName,
|
|
556
|
-
};
|
|
557
|
-
// Helper to apply non-indexable filters to a span
|
|
558
|
-
const applyFilters = (span) => {
|
|
559
|
-
// Regex filter (not indexable)
|
|
560
|
-
if (spanNameRegex && !spanNameRegex.test(span.name))
|
|
561
|
-
return false;
|
|
562
|
-
if (options.excludeSpanName && span.name.includes(options.excludeSpanName))
|
|
563
|
-
return false;
|
|
564
|
-
if (options.minDurationMs && (span.durationMs || 0) < options.minDurationMs)
|
|
565
|
-
return false;
|
|
566
|
-
if (options.maxDurationMs && (span.durationMs || Infinity) > options.maxDurationMs)
|
|
567
|
-
return false;
|
|
568
|
-
// Apply attribute filter
|
|
569
|
-
if (options.attributeFilter) {
|
|
570
|
-
for (const [key, value] of Object.entries(options.attributeFilter)) {
|
|
571
|
-
if (span.attributes?.[key] !== value)
|
|
572
|
-
return false;
|
|
1028
|
+
// Check cache first
|
|
1029
|
+
const cacheKey = makeCacheKey('traces', options);
|
|
1030
|
+
const cached = this.traceCache.get(cacheKey);
|
|
1031
|
+
if (cached) {
|
|
1032
|
+
logTiming();
|
|
1033
|
+
// M3 fix: Record cache hit for metrics and span event
|
|
1034
|
+
recordCacheHit('traces');
|
|
1035
|
+
if (span) {
|
|
1036
|
+
span.addEvent('cache_hit');
|
|
1037
|
+
span.setAttribute('obs_toolkit.query.result_count', cached.length);
|
|
573
1038
|
}
|
|
1039
|
+
return cached;
|
|
574
1040
|
}
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
1041
|
+
const files = getFilesInRange(this.telemetryDir, /traces-\d{4}-\d{2}-\d{2}\.jsonl(\.gz)?$/, options.startDate, options.endDate, this.allowedBases);
|
|
1042
|
+
const results = [];
|
|
1043
|
+
const limit = options.limit || DEFAULT_QUERY_RESULT_LIMIT;
|
|
1044
|
+
const offset = options.offset || 0;
|
|
1045
|
+
// Use cached regex to avoid recompilation for frequently-used patterns
|
|
1046
|
+
const spanNameRegex = options.spanNameRegex
|
|
1047
|
+
? getCachedRegex(options.spanNameRegex)
|
|
1048
|
+
: null;
|
|
1049
|
+
// Build index query options for indexable filters
|
|
1050
|
+
const indexOptions = {
|
|
1051
|
+
traceId: options.traceId,
|
|
1052
|
+
spanName: options.spanName,
|
|
1053
|
+
serviceName: options.serviceName,
|
|
1054
|
+
};
|
|
1055
|
+
// Helper to apply non-indexable filters to a span
|
|
1056
|
+
const applyFilters = (s) => {
|
|
1057
|
+
// Regex filter (not indexable)
|
|
1058
|
+
if (spanNameRegex && !spanNameRegex.test(s.name))
|
|
1059
|
+
return false;
|
|
1060
|
+
if (options.excludeSpanName && s.name.includes(options.excludeSpanName))
|
|
1061
|
+
return false;
|
|
1062
|
+
if (options.minDurationMs && (s.durationMs || 0) < options.minDurationMs)
|
|
1063
|
+
return false;
|
|
1064
|
+
if (options.maxDurationMs && (s.durationMs || Infinity) > options.maxDurationMs)
|
|
1065
|
+
return false;
|
|
1066
|
+
// Apply attribute filter
|
|
1067
|
+
if (options.attributeFilter) {
|
|
1068
|
+
for (const [key, value] of Object.entries(options.attributeFilter)) {
|
|
1069
|
+
if (s.attributes?.[key] !== value)
|
|
1070
|
+
return false;
|
|
1071
|
+
}
|
|
1072
|
+
}
|
|
1073
|
+
// Apply attributeExists filter
|
|
1074
|
+
if (options.attributeExists) {
|
|
1075
|
+
for (const key of options.attributeExists) {
|
|
1076
|
+
if (s.attributes?.[key] === undefined)
|
|
1077
|
+
return false;
|
|
1078
|
+
}
|
|
1079
|
+
}
|
|
1080
|
+
// Apply attributeNotExists filter
|
|
1081
|
+
if (options.attributeNotExists) {
|
|
1082
|
+
for (const key of options.attributeNotExists) {
|
|
1083
|
+
if (s.attributes?.[key] !== undefined)
|
|
1084
|
+
return false;
|
|
1085
|
+
}
|
|
1086
|
+
}
|
|
1087
|
+
// Apply numeric filter conditions
|
|
1088
|
+
if (options.numericFilter && options.numericFilter.length > 0) {
|
|
1089
|
+
if (!applyNumericFilters(s.attributes, options.numericFilter))
|
|
579
1090
|
return false;
|
|
580
1091
|
}
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
1092
|
+
// OTel GenAI agent/tool filters
|
|
1093
|
+
if (options.agentId && s.attributes?.['gen_ai.agent.id'] !== options.agentId)
|
|
1094
|
+
return false;
|
|
1095
|
+
if (options.agentName && s.attributes?.['gen_ai.agent.name'] !== options.agentName)
|
|
1096
|
+
return false;
|
|
1097
|
+
if (options.agentDescription) {
|
|
1098
|
+
const desc = s.attributes?.['gen_ai.agent.description'];
|
|
1099
|
+
if (typeof desc !== 'string' || !desc.toLowerCase().includes(options.agentDescription.toLowerCase()))
|
|
586
1100
|
return false;
|
|
587
1101
|
}
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
1102
|
+
if (options.agentVersion) {
|
|
1103
|
+
const ver = s.attributes?.['gen_ai.agent.version'];
|
|
1104
|
+
if (typeof ver !== 'string' || !ver.toLowerCase().includes(options.agentVersion.toLowerCase()))
|
|
1105
|
+
return false;
|
|
1106
|
+
}
|
|
1107
|
+
if (options.toolName && s.attributes?.['gen_ai.tool.name'] !== options.toolName)
|
|
592
1108
|
return false;
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
if (options.toolType && span.attributes?.['gen_ai.tool.type'] !== options.toolType)
|
|
604
|
-
return false;
|
|
605
|
-
if (options.operationName && span.attributes?.['gen_ai.operation.name'] !== options.operationName)
|
|
606
|
-
return false;
|
|
607
|
-
return true;
|
|
608
|
-
};
|
|
609
|
-
try {
|
|
610
|
-
for (const file of files) {
|
|
611
|
-
// Try to use index for pre-filtering
|
|
612
|
-
const matchingLines = this.tryUseIndex(file, 'traces', indexOptions);
|
|
613
|
-
if (matchingLines !== null) {
|
|
614
|
-
// Use indexed query - read only matching lines
|
|
615
|
-
const rawRecords = await readLinesByNumber(file, matchingLines);
|
|
616
|
-
for (const raw of rawRecords) {
|
|
617
|
-
const span = normalizeSpan(raw);
|
|
618
|
-
if (!span)
|
|
619
|
-
continue;
|
|
620
|
-
if (!applyFilters(span))
|
|
621
|
-
continue;
|
|
622
|
-
results.push(span);
|
|
623
|
-
if (hasReachedLimit(results.length, offset, limit)) {
|
|
624
|
-
const paginated = paginateResults(results, offset, limit);
|
|
625
|
-
this.traceCache.set(cacheKey, paginated);
|
|
626
|
-
this.circuitBreaker.recordSuccess();
|
|
627
|
-
logTiming();
|
|
628
|
-
return paginated;
|
|
629
|
-
}
|
|
630
|
-
}
|
|
1109
|
+
if (options.toolCallId && s.attributes?.['gen_ai.tool.call.id'] !== options.toolCallId)
|
|
1110
|
+
return false;
|
|
1111
|
+
if (options.toolType && s.attributes?.['gen_ai.tool.type'] !== options.toolType)
|
|
1112
|
+
return false;
|
|
1113
|
+
if (options.operationName && s.attributes?.['gen_ai.operation.name'] !== options.operationName)
|
|
1114
|
+
return false;
|
|
1115
|
+
if (options.outputType) {
|
|
1116
|
+
const ot = s.attributes?.['gen_ai.output.type'];
|
|
1117
|
+
if (typeof ot !== 'string' || ot.toLowerCase() !== options.outputType.toLowerCase())
|
|
1118
|
+
return false;
|
|
631
1119
|
}
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
1120
|
+
return true;
|
|
1121
|
+
};
|
|
1122
|
+
try {
|
|
1123
|
+
for (const file of files) {
|
|
1124
|
+
// Try to use index for pre-filtering
|
|
1125
|
+
const matchingLines = this.tryUseIndex(file, 'traces', indexOptions);
|
|
1126
|
+
if (matchingLines !== null) {
|
|
1127
|
+
// Use indexed query - read only matching lines
|
|
1128
|
+
// Note: index was built against flat records. For OTLP envelope lines,
|
|
1129
|
+
// multi-span envelopes may have only one traceId indexed per line.
|
|
1130
|
+
// The full-scan fallback path handles these correctly.
|
|
1131
|
+
const rawRecords = await readLinesByNumber(file, matchingLines);
|
|
1132
|
+
for (const rawLine of rawRecords) {
|
|
1133
|
+
const expanded = flattenSpanRecord(rawLine);
|
|
1134
|
+
if (expanded.length > 1)
|
|
1135
|
+
this.otlpTraceExpansions++;
|
|
1136
|
+
for (const raw of expanded) {
|
|
1137
|
+
const traceSpan = normalizeSpan(raw);
|
|
1138
|
+
if (!traceSpan)
|
|
1139
|
+
continue;
|
|
1140
|
+
if (!applyFilters(traceSpan))
|
|
1141
|
+
continue;
|
|
1142
|
+
results.push(traceSpan);
|
|
1143
|
+
if (hasReachedLimit(results.length, offset, limit)) {
|
|
1144
|
+
const paginated = paginateResults(results, offset, limit);
|
|
1145
|
+
this.traceCache.set(cacheKey, paginated);
|
|
1146
|
+
this.circuitBreaker.recordSuccess();
|
|
1147
|
+
logTiming();
|
|
1148
|
+
if (span)
|
|
1149
|
+
span.setAttribute('obs_toolkit.query.result_count', paginated.length);
|
|
1150
|
+
return paginated;
|
|
1151
|
+
}
|
|
1152
|
+
}
|
|
647
1153
|
}
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
const
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
1154
|
+
}
|
|
1155
|
+
else {
|
|
1156
|
+
// Fall back to full file scan
|
|
1157
|
+
for await (const rawLine of streamJsonl(file, { statsTracker: this.parseStatsTracker })) {
|
|
1158
|
+
const expanded = flattenSpanRecord(rawLine);
|
|
1159
|
+
if (expanded.length > 1)
|
|
1160
|
+
this.otlpTraceExpansions++;
|
|
1161
|
+
for (const raw of expanded) {
|
|
1162
|
+
const traceSpan = normalizeSpan(raw);
|
|
1163
|
+
if (!traceSpan)
|
|
1164
|
+
continue;
|
|
1165
|
+
// Apply indexable filters (since no index was used)
|
|
1166
|
+
if (options.traceId && traceSpan.traceId !== options.traceId)
|
|
1167
|
+
continue;
|
|
1168
|
+
if (options.spanName && !traceSpan.name.includes(options.spanName))
|
|
1169
|
+
continue;
|
|
1170
|
+
if (options.serviceName) {
|
|
1171
|
+
const svc = traceSpan.attributes?.['service.name'];
|
|
1172
|
+
if (svc !== options.serviceName)
|
|
1173
|
+
continue;
|
|
1174
|
+
}
|
|
1175
|
+
if (!applyFilters(traceSpan))
|
|
1176
|
+
continue;
|
|
1177
|
+
results.push(traceSpan);
|
|
1178
|
+
if (hasReachedLimit(results.length, offset, limit)) {
|
|
1179
|
+
const paginated = paginateResults(results, offset, limit);
|
|
1180
|
+
this.traceCache.set(cacheKey, paginated);
|
|
1181
|
+
this.circuitBreaker.recordSuccess();
|
|
1182
|
+
logTiming();
|
|
1183
|
+
if (span)
|
|
1184
|
+
span.setAttribute('obs_toolkit.query.result_count', paginated.length);
|
|
1185
|
+
return paginated;
|
|
1186
|
+
}
|
|
1187
|
+
}
|
|
657
1188
|
}
|
|
658
1189
|
}
|
|
659
1190
|
}
|
|
1191
|
+
const paginated = paginateResults(results, offset, limit);
|
|
1192
|
+
this.traceCache.set(cacheKey, paginated);
|
|
1193
|
+
this.circuitBreaker.recordSuccess();
|
|
1194
|
+
logTiming();
|
|
1195
|
+
if (span)
|
|
1196
|
+
span.setAttribute('obs_toolkit.query.result_count', paginated.length);
|
|
1197
|
+
return paginated;
|
|
660
1198
|
}
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
}
|
|
667
|
-
catch (error) {
|
|
668
|
-
this.circuitBreaker.recordFailure();
|
|
669
|
-
logTiming();
|
|
670
|
-
throw error;
|
|
671
|
-
}
|
|
1199
|
+
catch (error) {
|
|
1200
|
+
this.circuitBreaker.recordFailure();
|
|
1201
|
+
logTiming();
|
|
1202
|
+
throw error;
|
|
1203
|
+
}
|
|
1204
|
+
});
|
|
672
1205
|
}
|
|
673
1206
|
async queryLogs(options) {
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
logTiming()
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
if (cached) {
|
|
690
|
-
logTiming();
|
|
691
|
-
return cached;
|
|
692
|
-
}
|
|
693
|
-
const files = getFilesInRange(this.telemetryDir, /logs-\d{4}-\d{2}-\d{2}\.jsonl(\.gz)?$/, options.startDate, options.endDate);
|
|
694
|
-
const results = [];
|
|
695
|
-
const limit = options.limit || 100;
|
|
696
|
-
const offset = options.offset || 0;
|
|
697
|
-
// Build index query options for indexable filters
|
|
698
|
-
const indexOptions = {
|
|
699
|
-
traceId: options.traceId,
|
|
700
|
-
severity: options.severity,
|
|
701
|
-
};
|
|
702
|
-
// Helper to apply non-indexable filters to a log
|
|
703
|
-
const applyFilters = (log) => {
|
|
704
|
-
if (options.search && !log.body.toLowerCase().includes(options.search.toLowerCase()))
|
|
705
|
-
return false;
|
|
706
|
-
// Apply boolean search with multiple terms
|
|
707
|
-
if (options.searchTerms && options.searchTerms.length > 0) {
|
|
708
|
-
const bodyLower = log.body.toLowerCase();
|
|
709
|
-
const operator = options.searchOperator || 'AND';
|
|
710
|
-
if (operator === 'AND') {
|
|
711
|
-
const allMatch = options.searchTerms.every(term => bodyLower.includes(term.toLowerCase()));
|
|
712
|
-
if (!allMatch)
|
|
713
|
-
return false;
|
|
714
|
-
}
|
|
715
|
-
else {
|
|
716
|
-
const anyMatch = options.searchTerms.some(term => bodyLower.includes(term.toLowerCase()));
|
|
717
|
-
if (!anyMatch)
|
|
718
|
-
return false;
|
|
1207
|
+
// P2 fix: Defer attribute allocation until span is actually created
|
|
1208
|
+
return withSpan('obs_toolkit.query.logs', () => ({
|
|
1209
|
+
'obs_toolkit.query.type': 'logs',
|
|
1210
|
+
'obs_toolkit.query.backend': 'local',
|
|
1211
|
+
'obs_toolkit.query.limit': options.limit ?? DEFAULT_QUERY_RESULT_LIMIT,
|
|
1212
|
+
'obs_toolkit.query.start_date': options.startDate,
|
|
1213
|
+
'obs_toolkit.query.end_date': options.endDate,
|
|
1214
|
+
}), async (span) => {
|
|
1215
|
+
const timer = startTiming();
|
|
1216
|
+
const logTiming = () => {
|
|
1217
|
+
const durationMs = timer.end();
|
|
1218
|
+
recordQueryDuration('logs', durationMs, 'local');
|
|
1219
|
+
this.queryHistograms.logs.observe(durationMs);
|
|
1220
|
+
if (durationMs > SLOW_QUERY_THRESHOLD_MS) {
|
|
1221
|
+
queryLog.warn(`Slow query: queryLogs took ${durationMs.toFixed(1)}ms`, { durationMs });
|
|
719
1222
|
}
|
|
1223
|
+
};
|
|
1224
|
+
// Check circuit breaker - fail fast if open
|
|
1225
|
+
if (!this.circuitBreaker.canRequest()) {
|
|
1226
|
+
logTiming();
|
|
1227
|
+
if (span)
|
|
1228
|
+
span.setAttribute('obs_toolkit.query.result_count', 0);
|
|
1229
|
+
return [];
|
|
720
1230
|
}
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
if (
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
1231
|
+
// Check cache first
|
|
1232
|
+
const cacheKey = makeCacheKey('logs', options);
|
|
1233
|
+
const cached = this.logCache.get(cacheKey);
|
|
1234
|
+
if (cached) {
|
|
1235
|
+
logTiming();
|
|
1236
|
+
// M3 fix: Record cache hit for metrics and span event
|
|
1237
|
+
recordCacheHit('logs');
|
|
1238
|
+
if (span) {
|
|
1239
|
+
span.addEvent('cache_hit');
|
|
1240
|
+
span.setAttribute('obs_toolkit.query.result_count', cached.length);
|
|
728
1241
|
}
|
|
1242
|
+
return cached;
|
|
729
1243
|
}
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
1244
|
+
const files = getFilesInRange(this.telemetryDir, /logs-\d{4}-\d{2}-\d{2}\.jsonl(\.gz)?$/, options.startDate, options.endDate, this.allowedBases);
|
|
1245
|
+
const results = [];
|
|
1246
|
+
const limit = options.limit || DEFAULT_QUERY_RESULT_LIMIT;
|
|
1247
|
+
const offset = options.offset || 0;
|
|
1248
|
+
// Build index query options for indexable filters
|
|
1249
|
+
const indexOptions = {
|
|
1250
|
+
traceId: options.traceId,
|
|
1251
|
+
severity: options.severity,
|
|
1252
|
+
};
|
|
1253
|
+
// Helper to apply non-indexable filters to a log
|
|
1254
|
+
const applyFilters = (logRecord) => {
|
|
1255
|
+
if (options.search && !logRecord.body.toLowerCase().includes(options.search.toLowerCase()))
|
|
1256
|
+
return false;
|
|
1257
|
+
// Apply boolean search with multiple terms
|
|
1258
|
+
if (options.searchTerms && options.searchTerms.length > 0) {
|
|
1259
|
+
const bodyLower = logRecord.body.toLowerCase();
|
|
1260
|
+
const operator = options.searchOperator || 'AND';
|
|
1261
|
+
if (operator === 'AND') {
|
|
1262
|
+
const allMatch = options.searchTerms.every(term => bodyLower.includes(term.toLowerCase()));
|
|
1263
|
+
if (!allMatch)
|
|
1264
|
+
return false;
|
|
1265
|
+
}
|
|
1266
|
+
else {
|
|
1267
|
+
const anyMatch = options.searchTerms.some(term => bodyLower.includes(term.toLowerCase()));
|
|
1268
|
+
if (!anyMatch)
|
|
1269
|
+
return false;
|
|
1270
|
+
}
|
|
735
1271
|
}
|
|
736
|
-
|
|
737
|
-
// Apply numeric filter conditions
|
|
738
|
-
if (options.numericFilter && options.numericFilter.length > 0) {
|
|
739
|
-
if (!applyNumericFilters(log.attributes, options.numericFilter))
|
|
1272
|
+
if (options.excludeSearch && logRecord.body.toLowerCase().includes(options.excludeSearch.toLowerCase()))
|
|
740
1273
|
return false;
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
const
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
if (!log)
|
|
754
|
-
continue;
|
|
755
|
-
if (!applyFilters(log))
|
|
756
|
-
continue;
|
|
757
|
-
results.push(log);
|
|
758
|
-
if (hasReachedLimit(results.length, offset, limit)) {
|
|
759
|
-
const paginated = paginateResults(results, offset, limit);
|
|
760
|
-
this.logCache.set(cacheKey, paginated);
|
|
761
|
-
this.circuitBreaker.recordSuccess();
|
|
762
|
-
logTiming();
|
|
763
|
-
return paginated;
|
|
764
|
-
}
|
|
1274
|
+
// Apply attributeExists filter
|
|
1275
|
+
if (options.attributeExists) {
|
|
1276
|
+
for (const key of options.attributeExists) {
|
|
1277
|
+
if (logRecord.attributes?.[key] === undefined)
|
|
1278
|
+
return false;
|
|
1279
|
+
}
|
|
1280
|
+
}
|
|
1281
|
+
// Apply attributeNotExists filter
|
|
1282
|
+
if (options.attributeNotExists) {
|
|
1283
|
+
for (const key of options.attributeNotExists) {
|
|
1284
|
+
if (logRecord.attributes?.[key] !== undefined)
|
|
1285
|
+
return false;
|
|
765
1286
|
}
|
|
766
1287
|
}
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
const
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
1288
|
+
// Apply numeric filter conditions
|
|
1289
|
+
if (options.numericFilter && options.numericFilter.length > 0) {
|
|
1290
|
+
if (!applyNumericFilters(logRecord.attributes, options.numericFilter))
|
|
1291
|
+
return false;
|
|
1292
|
+
}
|
|
1293
|
+
return true;
|
|
1294
|
+
};
|
|
1295
|
+
try {
|
|
1296
|
+
for (const file of files) {
|
|
1297
|
+
// Try to use index for pre-filtering
|
|
1298
|
+
const matchingLines = this.tryUseIndex(file, 'logs', indexOptions);
|
|
1299
|
+
if (matchingLines !== null) {
|
|
1300
|
+
// Use indexed query - read only matching lines
|
|
1301
|
+
const rawRecords = await readLinesByNumber(file, matchingLines);
|
|
1302
|
+
for (const rawLine of rawRecords) {
|
|
1303
|
+
for (const raw of flattenLogRecord(rawLine)) {
|
|
1304
|
+
const logRecord = normalizeLog(raw, options.extractFields);
|
|
1305
|
+
if (!logRecord)
|
|
1306
|
+
continue;
|
|
1307
|
+
if (!applyFilters(logRecord))
|
|
1308
|
+
continue;
|
|
1309
|
+
results.push(logRecord);
|
|
1310
|
+
if (hasReachedLimit(results.length, offset, limit)) {
|
|
1311
|
+
const paginated = paginateResults(results, offset, limit);
|
|
1312
|
+
this.logCache.set(cacheKey, paginated);
|
|
1313
|
+
this.circuitBreaker.recordSuccess();
|
|
1314
|
+
logTiming();
|
|
1315
|
+
if (span)
|
|
1316
|
+
span.setAttribute('obs_toolkit.query.result_count', paginated.length);
|
|
1317
|
+
return paginated;
|
|
1318
|
+
}
|
|
1319
|
+
}
|
|
1320
|
+
}
|
|
1321
|
+
}
|
|
1322
|
+
else {
|
|
1323
|
+
// Fall back to full file scan
|
|
1324
|
+
for await (const rawLine of streamJsonl(file, { statsTracker: this.parseStatsTracker })) {
|
|
1325
|
+
for (const raw of flattenLogRecord(rawLine)) {
|
|
1326
|
+
const logRecord = normalizeLog(raw, options.extractFields);
|
|
1327
|
+
if (!logRecord)
|
|
1328
|
+
continue;
|
|
1329
|
+
// Apply indexable filters (since no index was used)
|
|
1330
|
+
if (options.severity && logRecord.severity !== options.severity)
|
|
1331
|
+
continue;
|
|
1332
|
+
if (options.traceId && logRecord.traceId !== options.traceId)
|
|
1333
|
+
continue;
|
|
1334
|
+
if (!applyFilters(logRecord))
|
|
1335
|
+
continue;
|
|
1336
|
+
results.push(logRecord);
|
|
1337
|
+
if (hasReachedLimit(results.length, offset, limit)) {
|
|
1338
|
+
const paginated = paginateResults(results, offset, limit);
|
|
1339
|
+
this.logCache.set(cacheKey, paginated);
|
|
1340
|
+
this.circuitBreaker.recordSuccess();
|
|
1341
|
+
logTiming();
|
|
1342
|
+
if (span)
|
|
1343
|
+
span.setAttribute('obs_toolkit.query.result_count', paginated.length);
|
|
1344
|
+
return paginated;
|
|
1345
|
+
}
|
|
1346
|
+
}
|
|
787
1347
|
}
|
|
788
1348
|
}
|
|
789
1349
|
}
|
|
1350
|
+
const paginated = paginateResults(results, offset, limit);
|
|
1351
|
+
this.logCache.set(cacheKey, paginated);
|
|
1352
|
+
this.circuitBreaker.recordSuccess();
|
|
1353
|
+
logTiming();
|
|
1354
|
+
if (span)
|
|
1355
|
+
span.setAttribute('obs_toolkit.query.result_count', paginated.length);
|
|
1356
|
+
return paginated;
|
|
790
1357
|
}
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
}
|
|
797
|
-
catch (error) {
|
|
798
|
-
this.circuitBreaker.recordFailure();
|
|
799
|
-
logTiming();
|
|
800
|
-
throw error;
|
|
801
|
-
}
|
|
1358
|
+
catch (error) {
|
|
1359
|
+
this.circuitBreaker.recordFailure();
|
|
1360
|
+
logTiming();
|
|
1361
|
+
throw error;
|
|
1362
|
+
}
|
|
1363
|
+
});
|
|
802
1364
|
}
|
|
803
1365
|
async queryMetrics(options) {
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
1366
|
+
// P2 fix: Defer attribute allocation until span is actually created
|
|
1367
|
+
return withSpan('obs_toolkit.query.metrics', () => ({
|
|
1368
|
+
'obs_toolkit.query.type': 'metrics',
|
|
1369
|
+
'obs_toolkit.query.backend': 'local',
|
|
1370
|
+
'obs_toolkit.query.limit': options.limit ?? DEFAULT_QUERY_RESULT_LIMIT,
|
|
1371
|
+
'obs_toolkit.query.start_date': options.startDate,
|
|
1372
|
+
'obs_toolkit.query.end_date': options.endDate,
|
|
1373
|
+
}), async (span) => {
|
|
1374
|
+
const timer = startTiming();
|
|
1375
|
+
const logTiming = () => {
|
|
1376
|
+
const durationMs = timer.end();
|
|
1377
|
+
recordQueryDuration('metrics', durationMs, 'local');
|
|
1378
|
+
this.queryHistograms.metrics.observe(durationMs);
|
|
1379
|
+
if (durationMs > SLOW_QUERY_THRESHOLD_MS) {
|
|
1380
|
+
queryLog.warn(`Slow query: queryMetrics took ${durationMs.toFixed(1)}ms`, { durationMs });
|
|
1381
|
+
}
|
|
1382
|
+
};
|
|
1383
|
+
// Check circuit breaker - fail fast if open
|
|
1384
|
+
if (!this.circuitBreaker.canRequest()) {
|
|
1385
|
+
logTiming();
|
|
1386
|
+
if (span)
|
|
1387
|
+
span.setAttribute('obs_toolkit.query.result_count', 0);
|
|
1388
|
+
return [];
|
|
809
1389
|
}
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
1390
|
+
// Check cache first
|
|
1391
|
+
const cacheKey = makeCacheKey('metrics', options);
|
|
1392
|
+
const cached = this.metricCache.get(cacheKey);
|
|
1393
|
+
if (cached) {
|
|
1394
|
+
logTiming();
|
|
1395
|
+
// M3 fix: Record cache hit for metrics and span event
|
|
1396
|
+
recordCacheHit('metrics');
|
|
1397
|
+
if (span) {
|
|
1398
|
+
span.addEvent('cache_hit');
|
|
1399
|
+
span.setAttribute('obs_toolkit.query.result_count', cached.length);
|
|
1400
|
+
}
|
|
1401
|
+
return cached;
|
|
1402
|
+
}
|
|
1403
|
+
const files = getFilesInRange(this.telemetryDir, /metrics-\d{4}-\d{2}-\d{2}\.jsonl(\.gz)?$/, options.startDate, options.endDate, this.allowedBases);
|
|
1404
|
+
const results = [];
|
|
1405
|
+
const limit = options.limit || DEFAULT_QUERY_RESULT_LIMIT;
|
|
1406
|
+
const offset = options.offset || 0;
|
|
1407
|
+
// Build index query options for indexable filters
|
|
1408
|
+
const indexOptions = {
|
|
1409
|
+
metricName: options.metricName,
|
|
1410
|
+
};
|
|
1411
|
+
try {
|
|
1412
|
+
outer: for (const file of files) {
|
|
1413
|
+
// Try to use index for pre-filtering
|
|
1414
|
+
const matchingLines = this.tryUseIndex(file, 'metrics', indexOptions);
|
|
1415
|
+
if (matchingLines !== null) {
|
|
1416
|
+
// Use indexed query - read only matching lines
|
|
1417
|
+
const rawRecords = await readLinesByNumber(file, matchingLines);
|
|
1418
|
+
for (const rawLine of rawRecords) {
|
|
1419
|
+
for (const raw of flattenMetricRecord(rawLine)) {
|
|
1420
|
+
const point = normalizeMetric(raw);
|
|
1421
|
+
if (!point)
|
|
1422
|
+
continue;
|
|
1423
|
+
results.push(point);
|
|
1424
|
+
if (hasReachedLimit(results.length, offset, limit)) {
|
|
1425
|
+
break outer;
|
|
1426
|
+
}
|
|
1427
|
+
}
|
|
845
1428
|
}
|
|
846
1429
|
}
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
1430
|
+
else {
|
|
1431
|
+
// Fall back to full file scan
|
|
1432
|
+
for await (const rawLine of streamJsonl(file, { statsTracker: this.parseStatsTracker })) {
|
|
1433
|
+
for (const raw of flattenMetricRecord(rawLine)) {
|
|
1434
|
+
const point = normalizeMetric(raw);
|
|
1435
|
+
if (!point)
|
|
1436
|
+
continue;
|
|
1437
|
+
// Apply filters (since no index was used)
|
|
1438
|
+
if (options.metricName && !point.name.includes(options.metricName))
|
|
1439
|
+
continue;
|
|
1440
|
+
results.push(point);
|
|
1441
|
+
if (hasReachedLimit(results.length, offset, limit)) {
|
|
1442
|
+
break outer;
|
|
1443
|
+
}
|
|
1444
|
+
}
|
|
860
1445
|
}
|
|
861
1446
|
}
|
|
862
1447
|
}
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
1448
|
+
// Apply aggregation if requested
|
|
1449
|
+
if (options.aggregation && results.length > 0) {
|
|
1450
|
+
const aggregated = this.aggregate(results, options.aggregation, options.groupBy, options.timeBucket);
|
|
1451
|
+
this.metricCache.set(cacheKey, aggregated);
|
|
1452
|
+
this.circuitBreaker.recordSuccess();
|
|
1453
|
+
logTiming();
|
|
1454
|
+
if (span)
|
|
1455
|
+
span.setAttribute('obs_toolkit.query.result_count', aggregated.length);
|
|
1456
|
+
return aggregated;
|
|
1457
|
+
}
|
|
1458
|
+
const paginated = paginateResults(results, offset, limit);
|
|
1459
|
+
this.metricCache.set(cacheKey, paginated);
|
|
868
1460
|
this.circuitBreaker.recordSuccess();
|
|
869
1461
|
logTiming();
|
|
870
|
-
|
|
1462
|
+
if (span)
|
|
1463
|
+
span.setAttribute('obs_toolkit.query.result_count', paginated.length);
|
|
1464
|
+
return paginated;
|
|
871
1465
|
}
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
}
|
|
878
|
-
catch (error) {
|
|
879
|
-
this.circuitBreaker.recordFailure();
|
|
880
|
-
logTiming();
|
|
881
|
-
throw error;
|
|
882
|
-
}
|
|
1466
|
+
catch (error) {
|
|
1467
|
+
this.circuitBreaker.recordFailure();
|
|
1468
|
+
logTiming();
|
|
1469
|
+
throw error;
|
|
1470
|
+
}
|
|
1471
|
+
});
|
|
883
1472
|
}
|
|
884
1473
|
aggregate(points, aggregation, groupBy, timeBucket) {
|
|
885
1474
|
// Parse time bucket if provided
|
|
@@ -926,13 +1515,13 @@ export class LocalJsonlBackend {
|
|
|
926
1515
|
value = values.length;
|
|
927
1516
|
break;
|
|
928
1517
|
case 'p50':
|
|
929
|
-
value = this.calculatePercentile(values,
|
|
1518
|
+
value = this.calculatePercentile(values, PERCENTILE.P50);
|
|
930
1519
|
break;
|
|
931
1520
|
case 'p95':
|
|
932
|
-
value = this.calculatePercentile(values,
|
|
1521
|
+
value = this.calculatePercentile(values, PERCENTILE.P95);
|
|
933
1522
|
break;
|
|
934
1523
|
case 'p99':
|
|
935
|
-
value = this.calculatePercentile(values,
|
|
1524
|
+
value = this.calculatePercentile(values, PERCENTILE.P99);
|
|
936
1525
|
break;
|
|
937
1526
|
case 'rate':
|
|
938
1527
|
value = this.calculateRate(group);
|
|
@@ -962,7 +1551,7 @@ export class LocalJsonlBackend {
|
|
|
962
1551
|
if (values.length === 0)
|
|
963
1552
|
return 0;
|
|
964
1553
|
const sorted = [...values].sort((a, b) => a - b);
|
|
965
|
-
const index = Math.ceil((percentile /
|
|
1554
|
+
const index = Math.ceil((percentile / PERCENT_MULTIPLIER) * sorted.length) - 1;
|
|
966
1555
|
return sorted[Math.max(0, index)];
|
|
967
1556
|
}
|
|
968
1557
|
/**
|
|
@@ -971,7 +1560,8 @@ export class LocalJsonlBackend {
|
|
|
971
1560
|
* Edge cases: single value returns 0, same timestamp returns 0
|
|
972
1561
|
*/
|
|
973
1562
|
calculateRate(group) {
|
|
974
|
-
|
|
1563
|
+
const MIN_POINTS_FOR_RATE = 2;
|
|
1564
|
+
if (group.length < MIN_POINTS_FOR_RATE)
|
|
975
1565
|
return 0;
|
|
976
1566
|
// Sort by timestamp
|
|
977
1567
|
const sorted = [...group].sort((a, b) => new Date(a.timestamp).getTime() - new Date(b.timestamp).getTime());
|
|
@@ -983,250 +1573,665 @@ export class LocalJsonlBackend {
|
|
|
983
1573
|
// Avoid division by zero when timestamps are the same
|
|
984
1574
|
if (durationMs === 0)
|
|
985
1575
|
return 0;
|
|
986
|
-
const durationSeconds = durationMs /
|
|
1576
|
+
const durationSeconds = durationMs / TIME_MS.SECOND;
|
|
987
1577
|
return (lastPoint.value - firstPoint.value) / durationSeconds;
|
|
988
1578
|
}
|
|
989
1579
|
async queryLLMEvents(options) {
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
1580
|
+
// P2 fix: Defer attribute allocation until span is actually created
|
|
1581
|
+
return withSpan('obs_toolkit.query.llm_events', () => ({
|
|
1582
|
+
'obs_toolkit.query.type': 'llm_events',
|
|
1583
|
+
'obs_toolkit.query.backend': 'local',
|
|
1584
|
+
'obs_toolkit.query.limit': options.limit ?? DEFAULT_QUERY_RESULT_LIMIT,
|
|
1585
|
+
'obs_toolkit.query.start_date': options.startDate,
|
|
1586
|
+
'obs_toolkit.query.end_date': options.endDate,
|
|
1587
|
+
}), async (span) => {
|
|
1588
|
+
const timer = startTiming();
|
|
1589
|
+
const logTiming = () => {
|
|
1590
|
+
const durationMs = timer.end();
|
|
1591
|
+
recordQueryDuration('llm_events', durationMs, 'local');
|
|
1592
|
+
this.queryHistograms.llmEvents.observe(durationMs);
|
|
1593
|
+
if (durationMs > SLOW_QUERY_THRESHOLD_MS) {
|
|
1594
|
+
queryLog.warn(`Slow query: queryLLMEvents took ${durationMs.toFixed(1)}ms`, { durationMs });
|
|
1595
|
+
}
|
|
1596
|
+
};
|
|
1597
|
+
// Check circuit breaker - fail fast if open
|
|
1598
|
+
if (!this.circuitBreaker.canRequest()) {
|
|
1599
|
+
logTiming();
|
|
1600
|
+
if (span)
|
|
1601
|
+
span.setAttribute('obs_toolkit.query.result_count', 0);
|
|
1602
|
+
return [];
|
|
995
1603
|
}
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
}
|
|
1009
|
-
const files = getFilesInRange(this.telemetryDir, /llm-events-\d{4}-\d{2}-\d{2}\.jsonl(\.gz)?$/, options.startDate, options.endDate);
|
|
1010
|
-
const results = [];
|
|
1011
|
-
const limit = options.limit || 100;
|
|
1012
|
-
const offset = options.offset || 0;
|
|
1013
|
-
// Build index query options - eventName maps to spanName in index
|
|
1014
|
-
const indexOptions = {
|
|
1015
|
-
spanName: options.eventName,
|
|
1016
|
-
};
|
|
1017
|
-
// Helper to apply non-indexable filters to an event
|
|
1018
|
-
const applyFilters = (event) => {
|
|
1019
|
-
if (options.model) {
|
|
1020
|
-
const model = event.attributes?.['gen_ai.request.model'] || event.attributes?.['model'];
|
|
1021
|
-
if (model !== options.model)
|
|
1022
|
-
return false;
|
|
1604
|
+
// Check cache first
|
|
1605
|
+
const cacheKey = makeCacheKey('llm-events', options);
|
|
1606
|
+
const cached = this.llmEventCache.get(cacheKey);
|
|
1607
|
+
if (cached) {
|
|
1608
|
+
logTiming();
|
|
1609
|
+
// M3 fix: Record cache hit for metrics and span event
|
|
1610
|
+
recordCacheHit('llmEvents');
|
|
1611
|
+
if (span) {
|
|
1612
|
+
span.addEvent('cache_hit');
|
|
1613
|
+
span.setAttribute('obs_toolkit.query.result_count', cached.length);
|
|
1614
|
+
}
|
|
1615
|
+
return cached;
|
|
1023
1616
|
}
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1617
|
+
const files = getFilesInRange(this.telemetryDir, /llm-events-\d{4}-\d{2}-\d{2}\.jsonl(\.gz)?$/, options.startDate, options.endDate, this.allowedBases);
|
|
1618
|
+
const results = [];
|
|
1619
|
+
const limit = options.limit || DEFAULT_QUERY_RESULT_LIMIT;
|
|
1620
|
+
const offset = options.offset || 0;
|
|
1621
|
+
// Build index query options - eventName maps to spanName in index
|
|
1622
|
+
const indexOptions = {
|
|
1623
|
+
spanName: options.eventName,
|
|
1624
|
+
};
|
|
1625
|
+
// Helper to apply non-indexable filters to an event
|
|
1626
|
+
const applyFilters = (event) => {
|
|
1627
|
+
if (options.model) {
|
|
1628
|
+
const model = event.attributes?.['gen_ai.request.model'] || event.attributes?.['model'];
|
|
1629
|
+
if (model !== options.model)
|
|
1630
|
+
return false;
|
|
1631
|
+
}
|
|
1632
|
+
if (options.provider) {
|
|
1633
|
+
// OTel GenAI compliant: gen_ai.provider.name -> gen_ai.system -> provider
|
|
1634
|
+
const provider = event.attributes?.['gen_ai.provider.name'] ||
|
|
1635
|
+
event.attributes?.['gen_ai.system'] ||
|
|
1636
|
+
event.attributes?.['provider'];
|
|
1637
|
+
if (provider !== options.provider)
|
|
1638
|
+
return false;
|
|
1639
|
+
}
|
|
1640
|
+
if (options.operationName) {
|
|
1641
|
+
const opName = event.attributes?.['gen_ai.operation.name'];
|
|
1642
|
+
if (opName !== options.operationName)
|
|
1643
|
+
return false;
|
|
1644
|
+
}
|
|
1645
|
+
if (options.conversationId) {
|
|
1646
|
+
const convId = event.attributes?.['gen_ai.conversation.id'];
|
|
1647
|
+
if (convId !== options.conversationId)
|
|
1648
|
+
return false;
|
|
1649
|
+
}
|
|
1650
|
+
if (options.search) {
|
|
1651
|
+
const searchLower = options.search.toLowerCase();
|
|
1652
|
+
const attrStr = JSON.stringify(event.attributes).toLowerCase();
|
|
1653
|
+
if (!attrStr.includes(searchLower) && !event.name.toLowerCase().includes(searchLower))
|
|
1654
|
+
return false;
|
|
1655
|
+
}
|
|
1656
|
+
return true;
|
|
1657
|
+
};
|
|
1658
|
+
try {
|
|
1659
|
+
for (const file of files) {
|
|
1660
|
+
// Try to use index for pre-filtering
|
|
1661
|
+
const matchingLines = this.tryUseIndex(file, 'llm-events', indexOptions);
|
|
1662
|
+
if (matchingLines !== null) {
|
|
1663
|
+
// Use indexed query - read only matching lines
|
|
1664
|
+
const rawRecords = await readLinesByNumber(file, matchingLines);
|
|
1665
|
+
for (const event of rawRecords) {
|
|
1666
|
+
if (!event.timestamp || !event.name)
|
|
1667
|
+
continue;
|
|
1668
|
+
if (!applyFilters(event))
|
|
1669
|
+
continue;
|
|
1670
|
+
results.push({
|
|
1671
|
+
timestamp: event.timestamp,
|
|
1672
|
+
name: event.name,
|
|
1673
|
+
attributes: event.attributes,
|
|
1674
|
+
});
|
|
1675
|
+
if (hasReachedLimit(results.length, offset, limit)) {
|
|
1676
|
+
const paginated = paginateResults(results, offset, limit);
|
|
1677
|
+
this.llmEventCache.set(cacheKey, paginated);
|
|
1678
|
+
this.circuitBreaker.recordSuccess();
|
|
1679
|
+
logTiming();
|
|
1680
|
+
if (span)
|
|
1681
|
+
span.setAttribute('obs_toolkit.query.result_count', paginated.length);
|
|
1682
|
+
return paginated;
|
|
1683
|
+
}
|
|
1684
|
+
}
|
|
1685
|
+
}
|
|
1686
|
+
else {
|
|
1687
|
+
// Fall back to full file scan
|
|
1688
|
+
for await (const event of streamJsonl(file, { statsTracker: this.parseStatsTracker })) {
|
|
1689
|
+
if (!event.timestamp || !event.name)
|
|
1690
|
+
continue;
|
|
1691
|
+
// Apply indexable filters (since no index was used)
|
|
1692
|
+
if (options.eventName && !event.name.includes(options.eventName))
|
|
1693
|
+
continue;
|
|
1694
|
+
if (!applyFilters(event))
|
|
1695
|
+
continue;
|
|
1696
|
+
results.push({
|
|
1697
|
+
timestamp: event.timestamp,
|
|
1698
|
+
name: event.name,
|
|
1699
|
+
attributes: event.attributes,
|
|
1700
|
+
});
|
|
1701
|
+
if (hasReachedLimit(results.length, offset, limit)) {
|
|
1702
|
+
const paginated = paginateResults(results, offset, limit);
|
|
1703
|
+
this.llmEventCache.set(cacheKey, paginated);
|
|
1704
|
+
this.circuitBreaker.recordSuccess();
|
|
1705
|
+
logTiming();
|
|
1706
|
+
if (span)
|
|
1707
|
+
span.setAttribute('obs_toolkit.query.result_count', paginated.length);
|
|
1708
|
+
return paginated;
|
|
1709
|
+
}
|
|
1710
|
+
}
|
|
1711
|
+
}
|
|
1712
|
+
}
|
|
1713
|
+
const paginated = paginateResults(results, offset, limit);
|
|
1714
|
+
this.llmEventCache.set(cacheKey, paginated);
|
|
1715
|
+
this.circuitBreaker.recordSuccess();
|
|
1716
|
+
logTiming();
|
|
1717
|
+
if (span)
|
|
1718
|
+
span.setAttribute('obs_toolkit.query.result_count', paginated.length);
|
|
1719
|
+
return paginated;
|
|
1031
1720
|
}
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1721
|
+
catch (error) {
|
|
1722
|
+
this.circuitBreaker.recordFailure();
|
|
1723
|
+
logTiming();
|
|
1724
|
+
throw error;
|
|
1036
1725
|
}
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1726
|
+
});
|
|
1727
|
+
}
|
|
1728
|
+
async queryEvaluations(options) {
|
|
1729
|
+
// P2 fix: Defer attribute allocation until span is actually created
|
|
1730
|
+
return withSpan('obs_toolkit.query.evaluations', () => ({
|
|
1731
|
+
'obs_toolkit.query.type': 'evaluations',
|
|
1732
|
+
'obs_toolkit.query.backend': 'local',
|
|
1733
|
+
'obs_toolkit.query.limit': options.limit ?? DEFAULT_QUERY_RESULT_LIMIT,
|
|
1734
|
+
'obs_toolkit.query.start_date': options.startDate,
|
|
1735
|
+
'obs_toolkit.query.end_date': options.endDate,
|
|
1736
|
+
}), async (span) => {
|
|
1737
|
+
const timer = startTiming();
|
|
1738
|
+
const logTiming = () => {
|
|
1739
|
+
const durationMs = timer.end();
|
|
1740
|
+
recordQueryDuration('evaluations', durationMs, 'local');
|
|
1741
|
+
this.queryHistograms.evaluations.observe(durationMs);
|
|
1742
|
+
if (durationMs > SLOW_QUERY_THRESHOLD_MS) {
|
|
1743
|
+
queryLog.warn(`Slow query: queryEvaluations took ${durationMs.toFixed(1)}ms`, { durationMs });
|
|
1744
|
+
}
|
|
1745
|
+
};
|
|
1746
|
+
// Check circuit breaker - fail fast if open
|
|
1747
|
+
if (!this.circuitBreaker.canRequest()) {
|
|
1748
|
+
logTiming();
|
|
1749
|
+
if (span)
|
|
1750
|
+
span.setAttribute('obs_toolkit.query.result_count', 0);
|
|
1751
|
+
return [];
|
|
1041
1752
|
}
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1753
|
+
// Check cache first
|
|
1754
|
+
const cacheKey = makeCacheKey('evaluations', options);
|
|
1755
|
+
const cached = this.evaluationCache.get(cacheKey);
|
|
1756
|
+
if (cached) {
|
|
1757
|
+
logTiming();
|
|
1758
|
+
// M3 fix: Record cache hit for metrics and span event
|
|
1759
|
+
recordCacheHit('evaluations');
|
|
1760
|
+
if (span) {
|
|
1761
|
+
span.addEvent('cache_hit');
|
|
1762
|
+
span.setAttribute('obs_toolkit.query.result_count', cached.length);
|
|
1763
|
+
}
|
|
1764
|
+
return cached;
|
|
1047
1765
|
}
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1766
|
+
const files = getFilesInRange(this.telemetryDir, /evaluations-\d{4}-\d{2}-\d{2}\.jsonl(\.gz)?$/, options.startDate, options.endDate, this.allowedBases);
|
|
1767
|
+
const results = [];
|
|
1768
|
+
const limit = options.limit || DEFAULT_QUERY_RESULT_LIMIT;
|
|
1769
|
+
const offset = options.offset || 0;
|
|
1770
|
+
// Build index query options for indexable filters
|
|
1771
|
+
const indexOptions = {
|
|
1772
|
+
traceId: options.traceId,
|
|
1773
|
+
evaluationName: options.evaluationName,
|
|
1774
|
+
scoreLabel: options.scoreLabel,
|
|
1775
|
+
responseId: options.responseId,
|
|
1776
|
+
evaluator: options.evaluator,
|
|
1777
|
+
};
|
|
1778
|
+
// Pre-load dataset trace IDs when filtering by datasetId
|
|
1779
|
+
let datasetTraceIds;
|
|
1780
|
+
if (options.datasetId) {
|
|
1781
|
+
const allTraces = await this.readDatasetTraces();
|
|
1782
|
+
datasetTraceIds = new Set(allTraces.filter(t => t.datasetId === options.datasetId).map(t => t.traceId));
|
|
1783
|
+
}
|
|
1784
|
+
// Helper to apply non-indexable filters to an evaluation
|
|
1785
|
+
const applyFilters = (evaluation) => {
|
|
1786
|
+
// Dataset membership filter
|
|
1787
|
+
if (datasetTraceIds && (!evaluation.traceId || !datasetTraceIds.has(evaluation.traceId))) {
|
|
1788
|
+
return false;
|
|
1789
|
+
}
|
|
1790
|
+
// Score range filters - only apply when evaluation HAS a scoreValue (P1-1 fix)
|
|
1791
|
+
// Evaluations with only scoreLabel (qualitative) should pass through
|
|
1792
|
+
if (options.scoreMin !== undefined && evaluation.scoreValue !== undefined) {
|
|
1793
|
+
if (evaluation.scoreValue < options.scoreMin)
|
|
1794
|
+
return false;
|
|
1795
|
+
}
|
|
1796
|
+
if (options.scoreMax !== undefined && evaluation.scoreValue !== undefined) {
|
|
1797
|
+
if (evaluation.scoreValue > options.scoreMax)
|
|
1798
|
+
return false;
|
|
1799
|
+
}
|
|
1800
|
+
// Session filter
|
|
1801
|
+
if (options.sessionId && evaluation.sessionId !== options.sessionId) {
|
|
1802
|
+
return false;
|
|
1803
|
+
}
|
|
1804
|
+
// Evaluator type filter (not indexed, so always applied here)
|
|
1805
|
+
if (options.evaluatorType && evaluation.evaluatorType !== options.evaluatorType) {
|
|
1806
|
+
return false;
|
|
1807
|
+
}
|
|
1808
|
+
// Agent-as-Judge filters (Section 10.7)
|
|
1809
|
+
if (options.agentId && evaluation.agentId !== options.agentId) {
|
|
1810
|
+
return false;
|
|
1811
|
+
}
|
|
1812
|
+
if (options.agentName && evaluation.agentName !== options.agentName) {
|
|
1813
|
+
return false;
|
|
1814
|
+
}
|
|
1815
|
+
return true;
|
|
1816
|
+
};
|
|
1817
|
+
try {
|
|
1818
|
+
for (const file of files) {
|
|
1819
|
+
// Try to use index for pre-filtering
|
|
1820
|
+
const matchingLines = this.tryUseIndex(file, 'evaluations', indexOptions);
|
|
1821
|
+
if (matchingLines !== null) {
|
|
1822
|
+
// Use indexed query - read only matching lines
|
|
1823
|
+
const rawRecords = await readLinesByNumber(file, matchingLines);
|
|
1824
|
+
for (const rawLine of rawRecords) {
|
|
1825
|
+
for (const raw of flattenEvaluationRecord(rawLine)) {
|
|
1826
|
+
const evaluation = normalizeEvaluation(raw);
|
|
1827
|
+
if (!evaluation)
|
|
1828
|
+
continue;
|
|
1829
|
+
if (!applyFilters(evaluation))
|
|
1830
|
+
continue;
|
|
1831
|
+
results.push(evaluation);
|
|
1832
|
+
if (hasReachedLimit(results.length, offset, limit)) {
|
|
1833
|
+
const paginated = paginateResults(results, offset, limit);
|
|
1834
|
+
this.evaluationCache.set(cacheKey, paginated);
|
|
1835
|
+
this.circuitBreaker.recordSuccess();
|
|
1836
|
+
logTiming();
|
|
1837
|
+
if (span)
|
|
1838
|
+
span.setAttribute('obs_toolkit.query.result_count', paginated.length);
|
|
1839
|
+
return paginated;
|
|
1840
|
+
}
|
|
1841
|
+
}
|
|
1073
1842
|
}
|
|
1074
1843
|
}
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1844
|
+
else {
|
|
1845
|
+
// Fall back to full file scan
|
|
1846
|
+
for await (const rawLine of streamJsonl(file, { statsTracker: this.parseStatsTracker })) {
|
|
1847
|
+
for (const raw of flattenEvaluationRecord(rawLine)) {
|
|
1848
|
+
const evaluation = normalizeEvaluation(raw);
|
|
1849
|
+
if (!evaluation)
|
|
1850
|
+
continue;
|
|
1851
|
+
// Apply indexable filters (since no index was used)
|
|
1852
|
+
if (options.traceId && evaluation.traceId !== options.traceId)
|
|
1853
|
+
continue;
|
|
1854
|
+
if (options.evaluationName && !evaluation.evaluationName.includes(options.evaluationName))
|
|
1855
|
+
continue;
|
|
1856
|
+
if (options.scoreLabel && evaluation.scoreLabel !== options.scoreLabel)
|
|
1857
|
+
continue;
|
|
1858
|
+
if (options.responseId && evaluation.responseId !== options.responseId)
|
|
1859
|
+
continue;
|
|
1860
|
+
if (options.evaluator && evaluation.evaluator !== options.evaluator)
|
|
1861
|
+
continue;
|
|
1862
|
+
if (!applyFilters(evaluation))
|
|
1863
|
+
continue;
|
|
1864
|
+
results.push(evaluation);
|
|
1865
|
+
if (hasReachedLimit(results.length, offset, limit)) {
|
|
1866
|
+
const paginated = paginateResults(results, offset, limit);
|
|
1867
|
+
this.evaluationCache.set(cacheKey, paginated);
|
|
1868
|
+
this.circuitBreaker.recordSuccess();
|
|
1869
|
+
logTiming();
|
|
1870
|
+
if (span)
|
|
1871
|
+
span.setAttribute('obs_toolkit.query.result_count', paginated.length);
|
|
1872
|
+
return paginated;
|
|
1873
|
+
}
|
|
1874
|
+
}
|
|
1097
1875
|
}
|
|
1098
1876
|
}
|
|
1099
1877
|
}
|
|
1878
|
+
const paginated = paginateResults(results, offset, limit);
|
|
1879
|
+
this.evaluationCache.set(cacheKey, paginated);
|
|
1880
|
+
this.circuitBreaker.recordSuccess();
|
|
1881
|
+
logTiming();
|
|
1882
|
+
if (span)
|
|
1883
|
+
span.setAttribute('obs_toolkit.query.result_count', paginated.length);
|
|
1884
|
+
return paginated;
|
|
1885
|
+
}
|
|
1886
|
+
catch (error) {
|
|
1887
|
+
this.circuitBreaker.recordFailure();
|
|
1888
|
+
logTiming();
|
|
1889
|
+
throw error;
|
|
1890
|
+
}
|
|
1891
|
+
});
|
|
1892
|
+
}
|
|
1893
|
+
// --- Dataset Management ---
|
|
1894
|
+
get datasetsFile() { return join(this.telemetryDir, 'datasets.jsonl'); }
|
|
1895
|
+
get datasetTracesFile() { return join(this.telemetryDir, 'dataset-traces.jsonl'); }
|
|
1896
|
+
get datasetRunsFile() { return join(this.telemetryDir, 'dataset-runs.jsonl'); }
|
|
1897
|
+
/** Promise-chain mutex — serializes dataset mutations to prevent race conditions */
|
|
1898
|
+
_datasetMutex = Promise.resolve();
|
|
1899
|
+
_withDatasetLock(fn) {
|
|
1900
|
+
const prev = this._datasetMutex;
|
|
1901
|
+
let resolve;
|
|
1902
|
+
this._datasetMutex = new Promise(r => { resolve = r; });
|
|
1903
|
+
return prev.then(fn).finally(() => resolve());
|
|
1904
|
+
}
|
|
1905
|
+
async readDatasets() {
|
|
1906
|
+
try {
|
|
1907
|
+
const content = await readFile(this.datasetsFile, 'utf-8');
|
|
1908
|
+
const results = [];
|
|
1909
|
+
for (const line of content.trim().split('\n').filter(Boolean)) {
|
|
1910
|
+
const parsed = JSON.parse(line);
|
|
1911
|
+
const result = datasetRecordSchema.safeParse(parsed);
|
|
1912
|
+
if (result.success) {
|
|
1913
|
+
results.push(result.data);
|
|
1914
|
+
}
|
|
1100
1915
|
}
|
|
1101
|
-
|
|
1102
|
-
this.llmEventCache.set(cacheKey, paginated);
|
|
1103
|
-
this.circuitBreaker.recordSuccess();
|
|
1104
|
-
logTiming();
|
|
1105
|
-
return paginated;
|
|
1916
|
+
return results;
|
|
1106
1917
|
}
|
|
1107
|
-
catch
|
|
1108
|
-
|
|
1109
|
-
logTiming();
|
|
1110
|
-
throw error;
|
|
1918
|
+
catch {
|
|
1919
|
+
return [];
|
|
1111
1920
|
}
|
|
1112
1921
|
}
|
|
1113
|
-
async
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
const
|
|
1117
|
-
|
|
1118
|
-
|
|
1922
|
+
async readDatasetTraces() {
|
|
1923
|
+
try {
|
|
1924
|
+
const content = await readFile(this.datasetTracesFile, 'utf-8');
|
|
1925
|
+
const results = [];
|
|
1926
|
+
for (const line of content.trim().split('\n').filter(Boolean)) {
|
|
1927
|
+
const parsed = JSON.parse(line);
|
|
1928
|
+
const result = datasetTraceRecordSchema.safeParse(parsed);
|
|
1929
|
+
if (result.success) {
|
|
1930
|
+
results.push(result.data);
|
|
1931
|
+
}
|
|
1119
1932
|
}
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
logTiming();
|
|
1933
|
+
return results;
|
|
1934
|
+
}
|
|
1935
|
+
catch {
|
|
1124
1936
|
return [];
|
|
1125
1937
|
}
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1938
|
+
}
|
|
1939
|
+
async appendDataset(record) {
|
|
1940
|
+
await mkdir(this.telemetryDir, { recursive: true });
|
|
1941
|
+
await appendFile(this.datasetsFile, JSON.stringify(record) + '\n');
|
|
1942
|
+
}
|
|
1943
|
+
async appendDatasetTraces(records) {
|
|
1944
|
+
if (records.length === 0)
|
|
1945
|
+
return;
|
|
1946
|
+
await mkdir(this.telemetryDir, { recursive: true });
|
|
1947
|
+
const lines = records.map(r => JSON.stringify(r)).join('\n') + '\n';
|
|
1948
|
+
await appendFile(this.datasetTracesFile, lines);
|
|
1949
|
+
}
|
|
1950
|
+
/** Atomic rewrite: write to temp file then rename (POSIX atomic on same volume) */
|
|
1951
|
+
async atomicWriteFile(filePath, content) {
|
|
1952
|
+
await mkdir(this.telemetryDir, { recursive: true });
|
|
1953
|
+
const tmp = `${filePath}.${Date.now()}.tmp`;
|
|
1954
|
+
await writeFile(tmp, content);
|
|
1955
|
+
await rename(tmp, filePath);
|
|
1956
|
+
}
|
|
1957
|
+
async rewriteDatasetTraces(records) {
|
|
1958
|
+
const lines = records.map(r => JSON.stringify(r)).join('\n') + (records.length ? '\n' : '');
|
|
1959
|
+
await this.atomicWriteFile(this.datasetTracesFile, lines);
|
|
1960
|
+
}
|
|
1961
|
+
async readDatasetRuns() {
|
|
1962
|
+
try {
|
|
1963
|
+
const content = await readFile(this.datasetRunsFile, 'utf-8');
|
|
1964
|
+
const results = [];
|
|
1965
|
+
for (const line of content.trim().split('\n').filter(Boolean)) {
|
|
1966
|
+
const parsed = JSON.parse(line);
|
|
1967
|
+
const result = datasetRunRecordSchema.safeParse(parsed);
|
|
1968
|
+
if (result.success) {
|
|
1969
|
+
results.push(result.data);
|
|
1970
|
+
}
|
|
1971
|
+
}
|
|
1972
|
+
return results;
|
|
1132
1973
|
}
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1974
|
+
catch {
|
|
1975
|
+
return [];
|
|
1976
|
+
}
|
|
1977
|
+
}
|
|
1978
|
+
async appendDatasetRun(record) {
|
|
1979
|
+
await mkdir(this.telemetryDir, { recursive: true });
|
|
1980
|
+
await appendFile(this.datasetRunsFile, JSON.stringify(record) + '\n');
|
|
1981
|
+
}
|
|
1982
|
+
latestVersion(allVersions, datasetId) {
|
|
1983
|
+
return allVersions
|
|
1984
|
+
.filter(d => d.id === datasetId)
|
|
1985
|
+
.sort((a, b) => (b.version ?? 0) - (a.version ?? 0))[0];
|
|
1986
|
+
}
|
|
1987
|
+
async manageDatasets(options) {
|
|
1988
|
+
const MUTATING = new Set(['create', 'delete', 'add_traces', 'remove_traces', 'promote']);
|
|
1989
|
+
if (MUTATING.has(options.action)) {
|
|
1990
|
+
return this._withDatasetLock(() => this._manageDatasetsInner(options));
|
|
1991
|
+
}
|
|
1992
|
+
return this._manageDatasetsInner(options);
|
|
1993
|
+
}
|
|
1994
|
+
async _manageDatasetsInner(options) {
|
|
1995
|
+
const now = new Date().toISOString();
|
|
1996
|
+
switch (options.action) {
|
|
1997
|
+
case 'create': {
|
|
1998
|
+
const id = randomUUID();
|
|
1999
|
+
const traceIds = [...new Set(options.traceIds ?? [])];
|
|
2000
|
+
const record = {
|
|
2001
|
+
id,
|
|
2002
|
+
name: options.name,
|
|
2003
|
+
description: options.description ?? null,
|
|
2004
|
+
createdAt: now,
|
|
2005
|
+
frozen: options.frozen ?? 0,
|
|
2006
|
+
traceCount: traceIds.length,
|
|
2007
|
+
evaluationNames: options.evaluationNames ?? [],
|
|
2008
|
+
dateRangeStart: null,
|
|
2009
|
+
dateRangeEnd: null,
|
|
2010
|
+
selectionCriteria: options.selectionCriteria ?? null,
|
|
2011
|
+
version: 1,
|
|
2012
|
+
parentVersion: null,
|
|
2013
|
+
versionCreatedAt: now,
|
|
2014
|
+
versionReason: 'created',
|
|
2015
|
+
fieldMapping: options.fieldMapping,
|
|
2016
|
+
traceSnapshot: traceIds,
|
|
2017
|
+
};
|
|
2018
|
+
await this.appendDataset(record);
|
|
2019
|
+
const traceRecords = traceIds.map(traceId => ({ datasetId: id, traceId, addedAt: now }));
|
|
2020
|
+
await this.appendDatasetTraces(traceRecords);
|
|
2021
|
+
return { action: 'create', dataset: record };
|
|
1152
2022
|
}
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
2023
|
+
case 'list': {
|
|
2024
|
+
const all = await this.readDatasets();
|
|
2025
|
+
const byId = new Map();
|
|
2026
|
+
for (const d of all) {
|
|
2027
|
+
const existing = byId.get(d.id);
|
|
2028
|
+
if (!existing || (d.version ?? 0) > (existing.version ?? 0)) {
|
|
2029
|
+
byId.set(d.id, d);
|
|
2030
|
+
}
|
|
2031
|
+
}
|
|
2032
|
+
let datasets = [...byId.values()];
|
|
2033
|
+
if (options.filterFrozen !== undefined) {
|
|
2034
|
+
datasets = datasets.filter(d => d.frozen === options.filterFrozen);
|
|
2035
|
+
}
|
|
2036
|
+
// Sort by createdAt descending, then id for stable ordering
|
|
2037
|
+
datasets.sort((a, b) => b.createdAt.localeCompare(a.createdAt) || a.id.localeCompare(b.id));
|
|
2038
|
+
// Apply cursor
|
|
2039
|
+
if (options.cursor) {
|
|
2040
|
+
const decoded = decodeCursor(options.cursor);
|
|
2041
|
+
if (decoded?.createdAt && decoded.id) {
|
|
2042
|
+
const cursorAt = decoded.createdAt;
|
|
2043
|
+
const cursorId = decoded.id;
|
|
2044
|
+
const idx = datasets.findIndex(d => d.createdAt < cursorAt ||
|
|
2045
|
+
(d.createdAt === cursorAt && d.id > cursorId));
|
|
2046
|
+
datasets = idx >= 0 ? datasets.slice(idx) : [];
|
|
2047
|
+
}
|
|
2048
|
+
}
|
|
2049
|
+
const limit = options.limit ?? DEFAULT_QUERY_RESULT_LIMIT;
|
|
2050
|
+
const hasMore = datasets.length > limit;
|
|
2051
|
+
const page = datasets.slice(0, limit);
|
|
2052
|
+
const last = page[page.length - 1];
|
|
2053
|
+
return {
|
|
2054
|
+
action: 'list',
|
|
2055
|
+
datasets: page,
|
|
2056
|
+
count: page.length,
|
|
2057
|
+
hasMore,
|
|
2058
|
+
...(hasMore && last && { nextCursor: encodeCursor({ createdAt: last.createdAt, id: last.id }) }),
|
|
2059
|
+
};
|
|
1156
2060
|
}
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
2061
|
+
case 'get': {
|
|
2062
|
+
const all = await this.readDatasets();
|
|
2063
|
+
const dataset = this.latestVersion(all, options.datasetId);
|
|
2064
|
+
if (!dataset)
|
|
2065
|
+
throw new Error(`Dataset not found: ${options.datasetId}`);
|
|
2066
|
+
const allTraces = await this.readDatasetTraces();
|
|
2067
|
+
const traces = allTraces.filter(t => t.datasetId === options.datasetId);
|
|
2068
|
+
const truncated = traces.length > DATASET_TRACE_TRUNCATION_LIMIT;
|
|
2069
|
+
return {
|
|
2070
|
+
action: 'get',
|
|
2071
|
+
dataset,
|
|
2072
|
+
traces: truncated ? traces.slice(0, DATASET_TRACE_TRUNCATION_LIMIT) : traces,
|
|
2073
|
+
traceCount: traces.length,
|
|
2074
|
+
truncated,
|
|
2075
|
+
};
|
|
1160
2076
|
}
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
2077
|
+
case 'delete': {
|
|
2078
|
+
const all = await this.readDatasets();
|
|
2079
|
+
const remaining = all.filter(d => d.id !== options.datasetId);
|
|
2080
|
+
const deleted = remaining.length < all.length;
|
|
2081
|
+
const lines = remaining.map(r => JSON.stringify(r)).join('\n') + (remaining.length ? '\n' : '');
|
|
2082
|
+
await this.atomicWriteFile(this.datasetsFile, lines);
|
|
2083
|
+
const allTraces = await this.readDatasetTraces();
|
|
2084
|
+
const remainingTraces = allTraces.filter(t => t.datasetId !== options.datasetId);
|
|
2085
|
+
await this.rewriteDatasetTraces(remainingTraces);
|
|
2086
|
+
return { action: 'delete', deleted, id: options.datasetId };
|
|
1164
2087
|
}
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
2088
|
+
case 'add_traces': {
|
|
2089
|
+
const all = await this.readDatasets();
|
|
2090
|
+
const current = this.latestVersion(all, options.datasetId);
|
|
2091
|
+
if (!current)
|
|
2092
|
+
throw new Error(`Dataset not found: ${options.datasetId}`);
|
|
2093
|
+
if (current.frozen === 1)
|
|
2094
|
+
throw new Error('Cannot modify a frozen dataset');
|
|
2095
|
+
const existingTraces = (await this.readDatasetTraces())
|
|
2096
|
+
.filter(t => t.datasetId === options.datasetId);
|
|
2097
|
+
const existingIds = new Set(existingTraces.map(t => t.traceId));
|
|
2098
|
+
const newTraceIds = options.traceIds.filter(id => !existingIds.has(id));
|
|
2099
|
+
const allTraceIds = [...existingIds, ...newTraceIds];
|
|
2100
|
+
const newVersion = {
|
|
2101
|
+
...current,
|
|
2102
|
+
version: (current.version ?? 1) + 1,
|
|
2103
|
+
parentVersion: current.version ?? 1,
|
|
2104
|
+
versionCreatedAt: now,
|
|
2105
|
+
versionReason: 'traces_added',
|
|
2106
|
+
traceCount: allTraceIds.length,
|
|
2107
|
+
traceSnapshot: allTraceIds,
|
|
2108
|
+
};
|
|
2109
|
+
await this.appendDataset(newVersion);
|
|
2110
|
+
const traceRecords = newTraceIds.map(traceId => ({ datasetId: options.datasetId, traceId, addedAt: now }));
|
|
2111
|
+
await this.appendDatasetTraces(traceRecords);
|
|
2112
|
+
return { action: 'add_traces', dataset: newVersion, tracesAdded: newTraceIds.length };
|
|
2113
|
+
}
|
|
2114
|
+
case 'remove_traces': {
|
|
2115
|
+
const all = await this.readDatasets();
|
|
2116
|
+
const current = this.latestVersion(all, options.datasetId);
|
|
2117
|
+
if (!current)
|
|
2118
|
+
throw new Error(`Dataset not found: ${options.datasetId}`);
|
|
2119
|
+
if (current.frozen === 1)
|
|
2120
|
+
throw new Error('Cannot modify a frozen dataset');
|
|
2121
|
+
const allTraces = await this.readDatasetTraces();
|
|
2122
|
+
const removeSet = new Set(options.traceIds);
|
|
2123
|
+
const before = allTraces.filter(t => t.datasetId === options.datasetId).length;
|
|
2124
|
+
const remaining = allTraces.filter(t => !(t.datasetId === options.datasetId && removeSet.has(t.traceId)));
|
|
2125
|
+
const after = remaining.filter(t => t.datasetId === options.datasetId).length;
|
|
2126
|
+
const removed = before - after;
|
|
2127
|
+
const remainingTraceIds = remaining
|
|
2128
|
+
.filter(t => t.datasetId === options.datasetId)
|
|
2129
|
+
.map(t => t.traceId);
|
|
2130
|
+
const newVersion = {
|
|
2131
|
+
...current,
|
|
2132
|
+
version: (current.version ?? 1) + 1,
|
|
2133
|
+
parentVersion: current.version ?? 1,
|
|
2134
|
+
versionCreatedAt: now,
|
|
2135
|
+
versionReason: 'traces_removed',
|
|
2136
|
+
traceCount: after,
|
|
2137
|
+
traceSnapshot: remainingTraceIds,
|
|
2138
|
+
};
|
|
2139
|
+
await this.appendDataset(newVersion);
|
|
2140
|
+
await this.rewriteDatasetTraces(remaining);
|
|
2141
|
+
return { action: 'remove_traces', dataset: newVersion, tracesRemoved: removed };
|
|
2142
|
+
}
|
|
2143
|
+
case 'get_version': {
|
|
2144
|
+
const all = await this.readDatasets();
|
|
2145
|
+
const version = all.find(d => d.id === options.datasetId && d.version === options.version);
|
|
2146
|
+
if (!version)
|
|
2147
|
+
throw new Error(`Version ${options.version} not found for dataset ${options.datasetId}`);
|
|
2148
|
+
// Use traceSnapshot if available (populated since L8), otherwise fall back to current traces
|
|
2149
|
+
if (version.traceSnapshot) {
|
|
2150
|
+
const snapshotSet = new Set(version.traceSnapshot);
|
|
2151
|
+
const allTraces = await this.readDatasetTraces();
|
|
2152
|
+
const traces = allTraces.filter(t => t.datasetId === options.datasetId && snapshotSet.has(t.traceId));
|
|
2153
|
+
return { action: 'get_version', dataset: version, traces };
|
|
2154
|
+
}
|
|
2155
|
+
const allTraces = await this.readDatasetTraces();
|
|
2156
|
+
const traces = allTraces.filter(t => t.datasetId === options.datasetId);
|
|
2157
|
+
return { action: 'get_version', dataset: version, traces };
|
|
2158
|
+
}
|
|
2159
|
+
case 'list_versions': {
|
|
2160
|
+
const all = await this.readDatasets();
|
|
2161
|
+
let versions = all
|
|
2162
|
+
.filter(d => d.id === options.datasetId)
|
|
2163
|
+
.sort((a, b) => (b.version ?? 0) - (a.version ?? 0));
|
|
2164
|
+
// Apply cursor (version number, descending)
|
|
2165
|
+
if (options.cursor) {
|
|
2166
|
+
const decoded = decodeCursor(options.cursor);
|
|
2167
|
+
const cursorVersion = decoded?.version;
|
|
2168
|
+
if (cursorVersion != null) {
|
|
2169
|
+
versions = versions.filter(d => (d.version ?? 0) < cursorVersion);
|
|
1188
2170
|
}
|
|
1189
2171
|
}
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
2172
|
+
const limit = options.limit ?? DEFAULT_QUERY_RESULT_LIMIT;
|
|
2173
|
+
const hasMore = versions.length > limit;
|
|
2174
|
+
const page = versions.slice(0, limit);
|
|
2175
|
+
const last = page[page.length - 1];
|
|
2176
|
+
return {
|
|
2177
|
+
action: 'list_versions',
|
|
2178
|
+
versions: page,
|
|
2179
|
+
count: page.length,
|
|
2180
|
+
hasMore,
|
|
2181
|
+
...(hasMore && last?.version != null && { nextCursor: encodeCursor({ version: last.version }) }),
|
|
2182
|
+
};
|
|
2183
|
+
}
|
|
2184
|
+
case 'promote': {
|
|
2185
|
+
const id = randomUUID();
|
|
2186
|
+
const traceIds = [...new Set(options.traceIds)];
|
|
2187
|
+
const record = {
|
|
2188
|
+
id,
|
|
2189
|
+
name: options.name,
|
|
2190
|
+
description: options.description ?? null,
|
|
2191
|
+
createdAt: now,
|
|
2192
|
+
frozen: options.frozen ?? 1,
|
|
2193
|
+
traceCount: traceIds.length,
|
|
2194
|
+
evaluationNames: options.evaluationNames ?? [],
|
|
2195
|
+
dateRangeStart: null,
|
|
2196
|
+
dateRangeEnd: null,
|
|
2197
|
+
selectionCriteria: null,
|
|
2198
|
+
version: 1,
|
|
2199
|
+
parentVersion: null,
|
|
2200
|
+
versionCreatedAt: now,
|
|
2201
|
+
versionReason: 'created',
|
|
2202
|
+
traceSnapshot: traceIds,
|
|
2203
|
+
};
|
|
2204
|
+
await this.appendDataset(record);
|
|
2205
|
+
const traceRecords = traceIds.map(traceId => ({ datasetId: id, traceId, addedAt: now }));
|
|
2206
|
+
await this.appendDatasetTraces(traceRecords);
|
|
2207
|
+
return { action: 'promote', dataset: record };
|
|
2208
|
+
}
|
|
2209
|
+
case 'list_runs': {
|
|
2210
|
+
const allRuns = await this.readDatasetRuns();
|
|
2211
|
+
let runs = allRuns
|
|
2212
|
+
.filter(r => r.datasetId === options.datasetId)
|
|
2213
|
+
.sort((a, b) => b.runAt.localeCompare(a.runAt));
|
|
2214
|
+
if (options.cursor) {
|
|
2215
|
+
const decoded = decodeCursor(options.cursor);
|
|
2216
|
+
if (decoded?.createdAt != null) {
|
|
2217
|
+
const cursorAt = decoded.createdAt;
|
|
2218
|
+
runs = runs.filter(r => r.runAt < cursorAt);
|
|
1217
2219
|
}
|
|
1218
2220
|
}
|
|
2221
|
+
const limit = options.limit ?? DEFAULT_QUERY_RESULT_LIMIT;
|
|
2222
|
+
const hasMore = runs.length > limit;
|
|
2223
|
+
const page = runs.slice(0, limit);
|
|
2224
|
+
const last = page[page.length - 1];
|
|
2225
|
+
return {
|
|
2226
|
+
action: 'list_runs',
|
|
2227
|
+
runs: page,
|
|
2228
|
+
count: page.length,
|
|
2229
|
+
hasMore,
|
|
2230
|
+
...(hasMore && last && { nextCursor: encodeCursor({ createdAt: last.runAt }) }),
|
|
2231
|
+
};
|
|
1219
2232
|
}
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
this.circuitBreaker.recordSuccess();
|
|
1223
|
-
logTiming();
|
|
1224
|
-
return paginated;
|
|
1225
|
-
}
|
|
1226
|
-
catch (error) {
|
|
1227
|
-
this.circuitBreaker.recordFailure();
|
|
1228
|
-
logTiming();
|
|
1229
|
-
throw error;
|
|
2233
|
+
default:
|
|
2234
|
+
throw new Error(`Unknown dataset action: ${options.action}`);
|
|
1230
2235
|
}
|
|
1231
2236
|
}
|
|
1232
2237
|
async healthCheck() {
|
|
@@ -1299,7 +2304,7 @@ export class MultiDirectoryBackend {
|
|
|
1299
2304
|
return this.directories;
|
|
1300
2305
|
}
|
|
1301
2306
|
async queryTraces(options) {
|
|
1302
|
-
const limit = options.limit ||
|
|
2307
|
+
const limit = options.limit || DEFAULT_QUERY_RESULT_LIMIT;
|
|
1303
2308
|
// Query all backends in parallel
|
|
1304
2309
|
const allBackendResults = await Promise.all(this.backends.map(b => b.queryTraces({ ...options, limit })));
|
|
1305
2310
|
// Merge results using bounded insertion for efficient top-K selection
|
|
@@ -1312,7 +2317,7 @@ export class MultiDirectoryBackend {
|
|
|
1312
2317
|
return topResults;
|
|
1313
2318
|
}
|
|
1314
2319
|
async queryLogs(options) {
|
|
1315
|
-
const limit = options.limit ||
|
|
2320
|
+
const limit = options.limit || DEFAULT_QUERY_RESULT_LIMIT;
|
|
1316
2321
|
// Query all backends in parallel
|
|
1317
2322
|
const allBackendResults = await Promise.all(this.backends.map(b => b.queryLogs({ ...options, limit })));
|
|
1318
2323
|
// Merge results using bounded insertion for efficient top-K selection
|
|
@@ -1325,7 +2330,7 @@ export class MultiDirectoryBackend {
|
|
|
1325
2330
|
return topResults;
|
|
1326
2331
|
}
|
|
1327
2332
|
async queryMetrics(options) {
|
|
1328
|
-
const limit = options.limit ||
|
|
2333
|
+
const limit = options.limit || DEFAULT_QUERY_RESULT_LIMIT;
|
|
1329
2334
|
// Query all backends in parallel
|
|
1330
2335
|
const allBackendResults = await Promise.all(this.backends.map(b => b.queryMetrics({ ...options, limit })));
|
|
1331
2336
|
// Flatten and limit results
|
|
@@ -1333,7 +2338,7 @@ export class MultiDirectoryBackend {
|
|
|
1333
2338
|
return allResults.slice(0, limit);
|
|
1334
2339
|
}
|
|
1335
2340
|
async queryLLMEvents(options) {
|
|
1336
|
-
const limit = options.limit ||
|
|
2341
|
+
const limit = options.limit || DEFAULT_QUERY_RESULT_LIMIT;
|
|
1337
2342
|
// Query all backends in parallel
|
|
1338
2343
|
const allBackendResults = await Promise.all(this.backends.map(b => b.queryLLMEvents({ ...options, limit })));
|
|
1339
2344
|
// Merge results using bounded insertion for efficient top-K selection
|
|
@@ -1346,7 +2351,7 @@ export class MultiDirectoryBackend {
|
|
|
1346
2351
|
return topResults;
|
|
1347
2352
|
}
|
|
1348
2353
|
async queryEvaluations(options) {
|
|
1349
|
-
const limit = options.limit ||
|
|
2354
|
+
const limit = options.limit || DEFAULT_QUERY_RESULT_LIMIT;
|
|
1350
2355
|
// Query all backends in parallel
|
|
1351
2356
|
const allBackendResults = await Promise.all(this.backends.map(b => b.queryEvaluations({ ...options, limit })));
|
|
1352
2357
|
// Merge results using bounded insertion for efficient top-K selection
|
|
@@ -1410,6 +2415,105 @@ export class MultiDirectoryBackend {
|
|
|
1410
2415
|
}
|
|
1411
2416
|
return result;
|
|
1412
2417
|
}
|
|
2418
|
+
/**
|
|
2419
|
+
* Get aggregated parse statistics from all backends
|
|
2420
|
+
*/
|
|
2421
|
+
getParseStats() {
|
|
2422
|
+
// Aggregate stats from all backends
|
|
2423
|
+
let totalFiles = 0;
|
|
2424
|
+
let totalLines = 0;
|
|
2425
|
+
let totalParsed = 0;
|
|
2426
|
+
let totalSkipped = 0;
|
|
2427
|
+
const allWorstFiles = [];
|
|
2428
|
+
for (const backend of this.backends) {
|
|
2429
|
+
const stats = backend.getParseStats();
|
|
2430
|
+
totalFiles += stats.totalFiles;
|
|
2431
|
+
totalLines += stats.totalLines;
|
|
2432
|
+
totalParsed += stats.totalParsed;
|
|
2433
|
+
totalSkipped += stats.totalSkipped;
|
|
2434
|
+
// Collect worst files with skip rates
|
|
2435
|
+
for (const file of stats.worstFiles) {
|
|
2436
|
+
const totalProcessed = file.parsedLines + file.skippedLines;
|
|
2437
|
+
if (totalProcessed > 0) {
|
|
2438
|
+
allWorstFiles.push({
|
|
2439
|
+
...file,
|
|
2440
|
+
skipRate: file.skippedLines / totalProcessed,
|
|
2441
|
+
});
|
|
2442
|
+
}
|
|
2443
|
+
}
|
|
2444
|
+
}
|
|
2445
|
+
// Calculate overall success rate
|
|
2446
|
+
const totalProcessed = totalParsed + totalSkipped;
|
|
2447
|
+
const parseSuccessRate = totalProcessed > 0 ? totalParsed / totalProcessed : 1;
|
|
2448
|
+
// Get top worst files across all backends
|
|
2449
|
+
const worstFiles = allWorstFiles
|
|
2450
|
+
.sort((a, b) => b.skipRate - a.skipRate)
|
|
2451
|
+
.slice(0, WORST_FILES_LIMIT)
|
|
2452
|
+
.map(({ skipRate: _, ...f }) => f);
|
|
2453
|
+
return {
|
|
2454
|
+
totalFiles,
|
|
2455
|
+
totalLines,
|
|
2456
|
+
totalParsed,
|
|
2457
|
+
totalSkipped,
|
|
2458
|
+
parseSuccessRate,
|
|
2459
|
+
worstFiles,
|
|
2460
|
+
};
|
|
2461
|
+
}
|
|
2462
|
+
/**
|
|
2463
|
+
* Get aggregated query latency statistics from all backends
|
|
2464
|
+
*/
|
|
2465
|
+
getQueryStats() {
|
|
2466
|
+
// Aggregate histogram data from all backends
|
|
2467
|
+
// For percentiles, we use the first backend's data since aggregating
|
|
2468
|
+
// percentiles across backends requires full data merge
|
|
2469
|
+
// Count and sum can be aggregated directly
|
|
2470
|
+
const types = ['traces', 'logs', 'metrics', 'llmEvents', 'evaluations'];
|
|
2471
|
+
const result = {};
|
|
2472
|
+
for (const type of types) {
|
|
2473
|
+
let totalCount = 0;
|
|
2474
|
+
let weightedP50 = 0;
|
|
2475
|
+
let weightedP95 = 0;
|
|
2476
|
+
let weightedP99 = 0;
|
|
2477
|
+
let minValue = Infinity;
|
|
2478
|
+
let maxValue = -Infinity;
|
|
2479
|
+
let totalSum = 0;
|
|
2480
|
+
for (const backend of this.backends) {
|
|
2481
|
+
const stats = backend.getQueryStats()[type];
|
|
2482
|
+
if (stats.count > 0) {
|
|
2483
|
+
totalCount += stats.count;
|
|
2484
|
+
totalSum += stats.mean * stats.count;
|
|
2485
|
+
weightedP50 += stats.p50 * stats.count;
|
|
2486
|
+
weightedP95 += stats.p95 * stats.count;
|
|
2487
|
+
weightedP99 += stats.p99 * stats.count;
|
|
2488
|
+
minValue = Math.min(minValue, stats.min);
|
|
2489
|
+
maxValue = Math.max(maxValue, stats.max);
|
|
2490
|
+
}
|
|
2491
|
+
}
|
|
2492
|
+
if (totalCount > 0) {
|
|
2493
|
+
result[type] = {
|
|
2494
|
+
min: minValue,
|
|
2495
|
+
max: maxValue,
|
|
2496
|
+
mean: totalSum / totalCount,
|
|
2497
|
+
p50: weightedP50 / totalCount,
|
|
2498
|
+
p95: weightedP95 / totalCount,
|
|
2499
|
+
p99: weightedP99 / totalCount,
|
|
2500
|
+
count: totalCount,
|
|
2501
|
+
};
|
|
2502
|
+
}
|
|
2503
|
+
else {
|
|
2504
|
+
result[type] = {
|
|
2505
|
+
min: 0,
|
|
2506
|
+
max: 0,
|
|
2507
|
+
mean: 0,
|
|
2508
|
+
p50: 0,
|
|
2509
|
+
p95: 0,
|
|
2510
|
+
p99: 0,
|
|
2511
|
+
count: 0,
|
|
2512
|
+
};
|
|
2513
|
+
}
|
|
2514
|
+
}
|
|
2515
|
+
return result;
|
|
2516
|
+
}
|
|
1413
2517
|
/**
|
|
1414
2518
|
* Export traces in OTLP JSON format
|
|
1415
2519
|
*/
|
|
@@ -1431,5 +2535,110 @@ export class MultiDirectoryBackend {
|
|
|
1431
2535
|
const metrics = await this.queryMetrics(options);
|
|
1432
2536
|
return convertToOTLPMetrics(metrics);
|
|
1433
2537
|
}
|
|
2538
|
+
async manageDatasets(options) {
|
|
2539
|
+
if (this.backends.length === 0)
|
|
2540
|
+
throw new Error('No telemetry directories available');
|
|
2541
|
+
const READ_ACTIONS = new Set(['list', 'get', 'get_version', 'list_versions', 'list_runs']);
|
|
2542
|
+
// Write actions delegate to first backend
|
|
2543
|
+
if (!READ_ACTIONS.has(options.action)) {
|
|
2544
|
+
return this.backends[0].manageDatasets(options);
|
|
2545
|
+
}
|
|
2546
|
+
// Single backend — no merge needed
|
|
2547
|
+
if (this.backends.length === 1) {
|
|
2548
|
+
return this.backends[0].manageDatasets(options);
|
|
2549
|
+
}
|
|
2550
|
+
// Fan out read actions across all backends and merge
|
|
2551
|
+
switch (options.action) {
|
|
2552
|
+
case 'list': {
|
|
2553
|
+
const results = await Promise.all(this.backends.map(b => b.manageDatasets(options).catch(() => null)));
|
|
2554
|
+
const byId = new Map();
|
|
2555
|
+
for (const r of results) {
|
|
2556
|
+
if (!r || r.action !== 'list')
|
|
2557
|
+
continue;
|
|
2558
|
+
for (const d of r.datasets) {
|
|
2559
|
+
const existing = byId.get(d.id);
|
|
2560
|
+
if (!existing || (d.version ?? 0) > (existing.version ?? 0)) {
|
|
2561
|
+
byId.set(d.id, d);
|
|
2562
|
+
}
|
|
2563
|
+
}
|
|
2564
|
+
}
|
|
2565
|
+
const datasets = [...byId.values()];
|
|
2566
|
+
const limit = options.limit ?? DEFAULT_QUERY_RESULT_LIMIT;
|
|
2567
|
+
return {
|
|
2568
|
+
action: 'list',
|
|
2569
|
+
datasets: datasets.slice(0, limit),
|
|
2570
|
+
count: datasets.length,
|
|
2571
|
+
hasMore: datasets.length > limit,
|
|
2572
|
+
};
|
|
2573
|
+
}
|
|
2574
|
+
case 'get': {
|
|
2575
|
+
let best = null;
|
|
2576
|
+
for (const b of this.backends) {
|
|
2577
|
+
try {
|
|
2578
|
+
const r = await b.manageDatasets(options);
|
|
2579
|
+
if (r.action === 'get') {
|
|
2580
|
+
if (!best || best.action !== 'get' ||
|
|
2581
|
+
(r.dataset.version ?? 0) > (best.dataset.version ?? 0)) {
|
|
2582
|
+
best = r;
|
|
2583
|
+
}
|
|
2584
|
+
}
|
|
2585
|
+
}
|
|
2586
|
+
catch { /* not found in this backend */ }
|
|
2587
|
+
}
|
|
2588
|
+
if (!best)
|
|
2589
|
+
throw new Error(`Dataset not found: ${options.datasetId}`);
|
|
2590
|
+
return best;
|
|
2591
|
+
}
|
|
2592
|
+
case 'get_version': {
|
|
2593
|
+
for (const b of this.backends) {
|
|
2594
|
+
try {
|
|
2595
|
+
return await b.manageDatasets(options);
|
|
2596
|
+
}
|
|
2597
|
+
catch { /* not in this backend */ }
|
|
2598
|
+
}
|
|
2599
|
+
throw new Error(`Version ${options.version} not found for dataset ${options.datasetId}`);
|
|
2600
|
+
}
|
|
2601
|
+
case 'list_versions': {
|
|
2602
|
+
const results = await Promise.all(this.backends.map(b => b.manageDatasets(options).catch(() => null)));
|
|
2603
|
+
const byVersion = new Map();
|
|
2604
|
+
for (const r of results) {
|
|
2605
|
+
if (!r || r.action !== 'list_versions')
|
|
2606
|
+
continue;
|
|
2607
|
+
for (const v of r.versions) {
|
|
2608
|
+
if (!byVersion.has(v.version ?? 0)) {
|
|
2609
|
+
byVersion.set(v.version ?? 0, v);
|
|
2610
|
+
}
|
|
2611
|
+
}
|
|
2612
|
+
}
|
|
2613
|
+
const versions = [...byVersion.values()].sort((a, b) => (b.version ?? 0) - (a.version ?? 0));
|
|
2614
|
+
const limit = options.limit ?? DEFAULT_QUERY_RESULT_LIMIT;
|
|
2615
|
+
return {
|
|
2616
|
+
action: 'list_versions',
|
|
2617
|
+
versions: versions.slice(0, limit),
|
|
2618
|
+
count: versions.length,
|
|
2619
|
+
hasMore: versions.length > limit,
|
|
2620
|
+
};
|
|
2621
|
+
}
|
|
2622
|
+
case 'list_runs': {
|
|
2623
|
+
const results = await Promise.all(this.backends.map(b => b.manageDatasets(options).catch(() => null)));
|
|
2624
|
+
const allRuns = [];
|
|
2625
|
+
for (const r of results) {
|
|
2626
|
+
if (!r || r.action !== 'list_runs')
|
|
2627
|
+
continue;
|
|
2628
|
+
allRuns.push(...r.runs);
|
|
2629
|
+
}
|
|
2630
|
+
allRuns.sort((a, b) => b.runAt.localeCompare(a.runAt));
|
|
2631
|
+
const limit = options.limit ?? DEFAULT_QUERY_RESULT_LIMIT;
|
|
2632
|
+
return {
|
|
2633
|
+
action: 'list_runs',
|
|
2634
|
+
runs: allRuns.slice(0, limit),
|
|
2635
|
+
count: allRuns.length,
|
|
2636
|
+
hasMore: allRuns.length > limit,
|
|
2637
|
+
};
|
|
2638
|
+
}
|
|
2639
|
+
default:
|
|
2640
|
+
return this.backends[0].manageDatasets(options);
|
|
2641
|
+
}
|
|
2642
|
+
}
|
|
1434
2643
|
}
|
|
1435
2644
|
//# sourceMappingURL=local-jsonl.js.map
|