@agenthippo-ai/agenthippo 1.110.9 → 1.110.19
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/out/agentpack/types.js +2 -5
- package/out/analytics/dashboard/artifactManager.js +2 -439
- package/out/analytics/dashboard/fleetDisplayUtils.js +2 -162
- package/out/analytics/dashboard/fleetOrchestrator.js +3 -142
- package/out/analytics/dashboard/fleetRegistry.js +2 -79
- package/out/analytics/dashboard/fleetRowDefaults.js +2 -87
- package/out/analytics/dashboard/fleetScriptAdapter.js +2 -146
- package/out/analytics/dashboard/fleetTypes.js +2 -29
- package/out/analytics/dashboard/index.js +2 -17
- package/out/analytics/dashboard/indexingService.js +2 -422
- package/out/analytics/dashboard/localFleetAdapter.js +2 -222
- package/out/analytics/dashboard/performanceMonitor.js +5 -76
- package/out/analytics/dashboard/processManager.js +2 -209
- package/out/analytics/dashboard/provider.js +38 -3817
- package/out/analytics/dashboard/queryRunner.js +62 -750
- package/out/analytics/dashboard/scheduledFleetStorage.js +2 -241
- package/out/analytics/dashboard/sessionJsonlFleet.js +3 -204
- package/out/analytics/dashboard/teamViewDiscovery.js +2 -106
- package/out/analytics/dashboard/webview/panel.js +34 -2318
- package/out/analytics/dashboard/webview/runtime-bridge.js +2 -82
- package/out/analytics/lifecycle.js +2 -43
- package/out/analytics/service.js +8 -1277
- package/out/auth/authBaseUrl.js +2 -59
- package/out/auth/bootstrapApply.js +2 -71
- package/out/auth/brokerClient.js +2 -160
- package/out/auth/callbackUri.js +2 -59
- package/out/auth/githubStoreGate.js +2 -70
- package/out/auth/loopbackAuthServer.js +4 -159
- package/out/auth/onboardingPrompt.js +2 -112
- package/out/auth/pkce.js +2 -9
- package/out/auth/provider.js +2 -226
- package/out/auth/sessionStore.js +2 -156
- package/out/auth/signInFlow.js +2 -183
- package/out/cli/index.js +619 -551
- package/out/cli/run-custom-engine-runner.js +3 -318
- package/out/cli/vscode-shim.js +2 -86
- package/out/engines/factory.js +2 -45
- package/out/engines/types.js +2 -17
- package/out/examples/run-headless-repl.js +6 -286
- package/out/examples/run-headless.js +2 -187
- package/out/extension.js +32 -4473
- package/out/gateway/gatewayRouteIndex.js +6 -371
- package/out/gateway/gatewaySessionStore.js +3 -243
- package/out/gateway/index.js +2 -31
- package/out/gateway/resetMappings.js +2 -52
- package/out/gateway/service.js +5 -1249
- package/out/mcp/publish.js +2 -143
- package/out/remote/handleRemoteChat.js +2 -169
- package/out/remote/remotePackStatusBar.js +3 -103
- package/out/remote/remoteTrust.js +6 -277
- package/out/remote/serveClient.js +4 -277
- package/out/remoteSkills.js +3 -191
- package/out/runtime/index.js +2 -32
- package/out/runtime/vscode/index.js +2 -20
- package/out/runtime/vscode/vscode-config-provider.js +2 -77
- package/out/runtime/vscode/vscode-edit-tracker.js +2 -122
- package/out/runtime/vscode/vscode-logger.js +2 -30
- package/out/runtime/vscode/vscode-permission-handler.js +2 -72
- package/out/scripts/agentide-litellm.sh +0 -1
- package/out/scripts/agentide_litellm_hooks_v1.py +170 -418
- package/out/scripts/analytics/agent-fleet-widget.js +18 -257
- package/out/scripts/analytics/agentide-litellm-otel.config.yaml +4 -4
- package/out/scripts/analytics/duckdbLocator.js +2 -28
- package/out/scripts/analytics/generate-default-charts.py +43 -323
- package/out/scripts/analytics/hotspot-analysis-widget.js +4 -430
- package/out/scripts/analytics/index-traces.py +78 -1036
- package/out/scripts/analytics/session-comparison-widget.js +26 -472
- package/out/scripts/analytics/session-timeline-widget.js +25 -846
- package/out/scripts/analytics/setup-analytics.ps1 +8 -1
- package/out/scripts/analytics/setup-analytics.sh +13 -2
- package/out/scripts/analytics/token-cost-flamegraph-widget.js +4 -461
- package/out/scripts/analytics/token-cost-trend-widget.js +4 -534
- package/out/scripts/analytics/workflows-collector.cjs +4 -314
- package/out/scripts/analytics/workflows-daemon.mjs +9 -185
- package/out/scripts/analytics/workflows-widget.cjs +15 -159
- package/out/scripts/analytics/wow-model-widget.js +10 -120
- package/out/scripts/vendor-tools.mjs +2 -233
- package/out/skills/publish.js +3 -178
- package/out/store/github/setup.js +7 -212
- package/out/store/oramaIndex.js +2 -884
- package/out/sync.js +26 -1829
- package/out/terminalLogger.js +5 -358
- package/out/utils/pathSafety.js +2 -60
- package/out/utils/pythonRunner.js +4 -258
- package/out/utils/toolPath.js +2 -83
- package/out/workspaceFolders.js +2 -59
- package/package.json +1 -1
- package/out/agentpack/github.js +0 -547
- package/out/agentpack/index.js +0 -67
- package/out/agentpack/install.js +0 -297
- package/out/agentpack/manager.js +0 -441
- package/out/agentpack/manifest.js +0 -135
- package/out/agentpack/paths.js +0 -498
- package/out/agentpack/publish.js +0 -248
- package/out/agentpack/stores.js +0 -554
- package/out/analytics/dashboard/fleetStatusUtils.js +0 -30
- package/out/analytics/dashboard/fleetTitleUtils.js +0 -68
- package/out/analytics/dashboard/refreshPolicy.js +0 -16
- package/out/analytics/dashboard/scheduledFleetFingerprint.js +0 -22
- package/out/analytics/dashboard/scheduledFleetUtils.js +0 -22
- package/out/analytics/dashboard/sessionEventFleetStats.js +0 -118
- package/out/analytics/dashboard/viewsConfigMerge.js +0 -221
- package/out/analytics/dashboard/webview/codicon.ttf +0 -0
- package/out/analytics/identity.js +0 -0
- package/out/analytics/sessionEvents.js +0 -323
- package/out/auth/callbackUriParse.js +0 -59
- package/out/auth/types.js +0 -12
- package/out/auth-broker/config.js +0 -112
- package/out/auth-broker/jwt.js +0 -105
- package/out/auth-broker/pkce.js +0 -59
- package/out/auth-broker/server.js +0 -677
- package/out/auth-broker/store.js +0 -275
- package/out/authority/warrant.js +0 -206
- package/out/base-prompts/mcp-servers/.mcp.json +0 -13
- package/out/base-prompts/mcp-servers/langfuse-analytics/README.md +0 -142
- package/out/base-prompts/mcp-servers/langfuse-analytics/mcp.json +0 -13
- package/out/base-prompts/mcp-servers/langfuse-analytics/package-lock.json +0 -1192
- package/out/base-prompts/mcp-servers/langfuse-analytics/package.json +0 -23
- package/out/base-prompts/mcp-servers/langfuse-analytics/scripts/run-with-env.sh +0 -12
- package/out/base-prompts/mcp-servers/langfuse-analytics/scripts/sdk-test.js +0 -102
- package/out/base-prompts/mcp-servers/langfuse-analytics/scripts/simple-test.js +0 -137
- package/out/base-prompts/mcp-servers/langfuse-analytics/scripts/test-client.js +0 -118
- package/out/base-prompts/mcp-servers/langfuse-analytics/scripts/test-server.js +0 -209
- package/out/base-prompts/mcp-servers/langfuse-analytics/src/client.ts +0 -431
- package/out/base-prompts/mcp-servers/langfuse-analytics/src/index.ts +0 -270
- package/out/base-prompts/mcp-servers/langfuse-analytics/tsconfig.json +0 -18
- package/out/base-prompts/rules/AGENTS.md +0 -204
- package/out/base-prompts/skills/agent-browser/SKILL.md +0 -102
- package/out/base-prompts/skills/agent-builder/SKILL.md +0 -73
- package/out/base-prompts/skills/agent-builder/references/agent-pack.md +0 -63
- package/out/base-prompts/skills/agent-builder/references/crewai.md +0 -66
- package/out/base-prompts/skills/agent-builder/references/langchain.md +0 -60
- package/out/base-prompts/skills/agent-builder/references/raw-sdk.md +0 -73
- package/out/base-prompts/skills/agent-engine-creator/SKILL.md +0 -586
- package/out/base-prompts/skills/agent-engine-installer/SKILL.md +0 -106
- package/out/base-prompts/skills/agent-engine-installer/scripts/install-engine.mjs +0 -259
- package/out/base-prompts/skills/agent-engine-installer/scripts/install-engine.ps1 +0 -189
- package/out/base-prompts/skills/agenthippo-cli/SKILL.md +0 -69
- package/out/base-prompts/skills/agenthippo-cli/scripts/install.sh +0 -63
- package/out/base-prompts/skills/agentpack-creator/SKILL.md +0 -375
- package/out/base-prompts/skills/agentpack-creator/references/agent-templates.md +0 -261
- package/out/base-prompts/skills/agentpack-creator/references/agents-md-guide.md +0 -195
- package/out/base-prompts/skills/agentpack-creator/scripts/init_pack.py +0 -391
- package/out/base-prompts/skills/agentpack-creator/scripts/init_pack_test.py +0 -294
- package/out/base-prompts/skills/agentpack-testbench/SKILL.md +0 -135
- package/out/base-prompts/skills/algorithmic-art/LICENSE.txt +0 -202
- package/out/base-prompts/skills/algorithmic-art/SKILL.md +0 -405
- package/out/base-prompts/skills/algorithmic-art/templates/generator_template.js +0 -223
- package/out/base-prompts/skills/algorithmic-art/templates/viewer.html +0 -599
- package/out/base-prompts/skills/debug-agent/SKILL.md +0 -83
- package/out/base-prompts/skills/desktop-automation/SKILL.md +0 -166
- package/out/base-prompts/skills/desktop-automation/references/agenthippo-ui-recipes.md +0 -441
- package/out/base-prompts/skills/desktop-automation/scripts/windows-desktop-uia.psm1 +0 -280
- package/out/base-prompts/skills/docx/LICENSE.txt +0 -30
- package/out/base-prompts/skills/docx/SKILL.md +0 -197
- package/out/base-prompts/skills/docx/docx-js.md +0 -350
- package/out/base-prompts/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/dml-chart.xsd +0 -1499
- package/out/base-prompts/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/dml-chartDrawing.xsd +0 -146
- package/out/base-prompts/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/dml-diagram.xsd +0 -1085
- package/out/base-prompts/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/dml-lockedCanvas.xsd +0 -11
- package/out/base-prompts/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/dml-main.xsd +0 -3081
- package/out/base-prompts/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/dml-picture.xsd +0 -23
- package/out/base-prompts/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/dml-spreadsheetDrawing.xsd +0 -185
- package/out/base-prompts/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/dml-wordprocessingDrawing.xsd +0 -287
- package/out/base-prompts/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/pml.xsd +0 -1676
- package/out/base-prompts/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-additionalCharacteristics.xsd +0 -28
- package/out/base-prompts/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-bibliography.xsd +0 -144
- package/out/base-prompts/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-commonSimpleTypes.xsd +0 -174
- package/out/base-prompts/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-customXmlDataProperties.xsd +0 -25
- package/out/base-prompts/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-customXmlSchemaProperties.xsd +0 -18
- package/out/base-prompts/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesCustom.xsd +0 -59
- package/out/base-prompts/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesExtended.xsd +0 -56
- package/out/base-prompts/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesVariantTypes.xsd +0 -195
- package/out/base-prompts/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-math.xsd +0 -582
- package/out/base-prompts/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-relationshipReference.xsd +0 -25
- package/out/base-prompts/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/sml.xsd +0 -4439
- package/out/base-prompts/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/vml-main.xsd +0 -570
- package/out/base-prompts/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/vml-officeDrawing.xsd +0 -509
- package/out/base-prompts/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/vml-presentationDrawing.xsd +0 -12
- package/out/base-prompts/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/vml-spreadsheetDrawing.xsd +0 -108
- package/out/base-prompts/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/vml-wordprocessingDrawing.xsd +0 -96
- package/out/base-prompts/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/wml.xsd +0 -3646
- package/out/base-prompts/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/xml.xsd +0 -116
- package/out/base-prompts/skills/docx/ooxml/schemas/ecma/fouth-edition/opc-contentTypes.xsd +0 -42
- package/out/base-prompts/skills/docx/ooxml/schemas/ecma/fouth-edition/opc-coreProperties.xsd +0 -50
- package/out/base-prompts/skills/docx/ooxml/schemas/ecma/fouth-edition/opc-digSig.xsd +0 -49
- package/out/base-prompts/skills/docx/ooxml/schemas/ecma/fouth-edition/opc-relationships.xsd +0 -33
- package/out/base-prompts/skills/docx/ooxml/schemas/mce/mc.xsd +0 -75
- package/out/base-prompts/skills/docx/ooxml/schemas/microsoft/wml-2010.xsd +0 -560
- package/out/base-prompts/skills/docx/ooxml/schemas/microsoft/wml-2012.xsd +0 -67
- package/out/base-prompts/skills/docx/ooxml/schemas/microsoft/wml-2018.xsd +0 -14
- package/out/base-prompts/skills/docx/ooxml/schemas/microsoft/wml-cex-2018.xsd +0 -20
- package/out/base-prompts/skills/docx/ooxml/schemas/microsoft/wml-cid-2016.xsd +0 -13
- package/out/base-prompts/skills/docx/ooxml/schemas/microsoft/wml-sdtdatahash-2020.xsd +0 -4
- package/out/base-prompts/skills/docx/ooxml/schemas/microsoft/wml-symex-2015.xsd +0 -8
- package/out/base-prompts/skills/docx/ooxml/scripts/pack.py +0 -159
- package/out/base-prompts/skills/docx/ooxml/scripts/unpack.py +0 -29
- package/out/base-prompts/skills/docx/ooxml/scripts/validate.py +0 -69
- package/out/base-prompts/skills/docx/ooxml/scripts/validation/__init__.py +0 -15
- package/out/base-prompts/skills/docx/ooxml/scripts/validation/base.py +0 -951
- package/out/base-prompts/skills/docx/ooxml/scripts/validation/docx.py +0 -274
- package/out/base-prompts/skills/docx/ooxml/scripts/validation/pptx.py +0 -315
- package/out/base-prompts/skills/docx/ooxml/scripts/validation/redlining.py +0 -279
- package/out/base-prompts/skills/docx/ooxml.md +0 -610
- package/out/base-prompts/skills/docx/scripts/__init__.py +0 -1
- package/out/base-prompts/skills/docx/scripts/document.py +0 -1276
- package/out/base-prompts/skills/docx/scripts/templates/comments.xml +0 -3
- package/out/base-prompts/skills/docx/scripts/templates/commentsExtended.xml +0 -3
- package/out/base-prompts/skills/docx/scripts/templates/commentsExtensible.xml +0 -3
- package/out/base-prompts/skills/docx/scripts/templates/commentsIds.xml +0 -3
- package/out/base-prompts/skills/docx/scripts/templates/people.xml +0 -3
- package/out/base-prompts/skills/docx/scripts/utilities.py +0 -374
- package/out/base-prompts/skills/gateway-outbound/SKILL.md +0 -136
- package/out/base-prompts/skills/mcp-builder/LICENSE.txt +0 -202
- package/out/base-prompts/skills/mcp-builder/SKILL.md +0 -328
- package/out/base-prompts/skills/mcp-builder/reference/evaluation.md +0 -602
- package/out/base-prompts/skills/mcp-builder/reference/mcp_best_practices.md +0 -249
- package/out/base-prompts/skills/mcp-builder/reference/node_mcp_server.md +0 -970
- package/out/base-prompts/skills/mcp-builder/reference/python_mcp_server.md +0 -719
- package/out/base-prompts/skills/mcp-builder/scripts/connections.py +0 -151
- package/out/base-prompts/skills/mcp-builder/scripts/evaluation.py +0 -373
- package/out/base-prompts/skills/mcp-builder/scripts/example_evaluation.xml +0 -22
- package/out/base-prompts/skills/mcp-builder/scripts/requirements.txt +0 -2
- package/out/base-prompts/skills/openclaw/SKILL.md +0 -406
- package/out/base-prompts/skills/openclaw/scripts/run-smoke.sh +0 -61
- package/out/base-prompts/skills/pdf/LICENSE.txt +0 -30
- package/out/base-prompts/skills/pdf/SKILL.md +0 -294
- package/out/base-prompts/skills/pdf/forms.md +0 -205
- package/out/base-prompts/skills/pdf/reference.md +0 -612
- package/out/base-prompts/skills/pdf/scripts/check_bounding_boxes.py +0 -70
- package/out/base-prompts/skills/pdf/scripts/check_bounding_boxes_test.py +0 -226
- package/out/base-prompts/skills/pdf/scripts/check_fillable_fields.py +0 -12
- package/out/base-prompts/skills/pdf/scripts/convert_pdf_to_images.py +0 -35
- package/out/base-prompts/skills/pdf/scripts/create_validation_image.py +0 -41
- package/out/base-prompts/skills/pdf/scripts/extract_form_field_info.py +0 -152
- package/out/base-prompts/skills/pdf/scripts/fill_fillable_fields.py +0 -114
- package/out/base-prompts/skills/pdf/scripts/fill_pdf_form_with_annotations.py +0 -108
- package/out/base-prompts/skills/pptx/LICENSE.txt +0 -30
- package/out/base-prompts/skills/pptx/SKILL.md +0 -484
- package/out/base-prompts/skills/pptx/html2pptx.md +0 -625
- package/out/base-prompts/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/dml-chart.xsd +0 -1499
- package/out/base-prompts/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/dml-chartDrawing.xsd +0 -146
- package/out/base-prompts/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/dml-diagram.xsd +0 -1085
- package/out/base-prompts/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/dml-lockedCanvas.xsd +0 -11
- package/out/base-prompts/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/dml-main.xsd +0 -3081
- package/out/base-prompts/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/dml-picture.xsd +0 -23
- package/out/base-prompts/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/dml-spreadsheetDrawing.xsd +0 -185
- package/out/base-prompts/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/dml-wordprocessingDrawing.xsd +0 -287
- package/out/base-prompts/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/pml.xsd +0 -1676
- package/out/base-prompts/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-additionalCharacteristics.xsd +0 -28
- package/out/base-prompts/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-bibliography.xsd +0 -144
- package/out/base-prompts/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-commonSimpleTypes.xsd +0 -174
- package/out/base-prompts/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-customXmlDataProperties.xsd +0 -25
- package/out/base-prompts/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-customXmlSchemaProperties.xsd +0 -18
- package/out/base-prompts/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesCustom.xsd +0 -59
- package/out/base-prompts/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesExtended.xsd +0 -56
- package/out/base-prompts/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesVariantTypes.xsd +0 -195
- package/out/base-prompts/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-math.xsd +0 -582
- package/out/base-prompts/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-relationshipReference.xsd +0 -25
- package/out/base-prompts/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/sml.xsd +0 -4439
- package/out/base-prompts/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/vml-main.xsd +0 -570
- package/out/base-prompts/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/vml-officeDrawing.xsd +0 -509
- package/out/base-prompts/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/vml-presentationDrawing.xsd +0 -12
- package/out/base-prompts/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/vml-spreadsheetDrawing.xsd +0 -108
- package/out/base-prompts/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/vml-wordprocessingDrawing.xsd +0 -96
- package/out/base-prompts/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/wml.xsd +0 -3646
- package/out/base-prompts/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/xml.xsd +0 -116
- package/out/base-prompts/skills/pptx/ooxml/schemas/ecma/fouth-edition/opc-contentTypes.xsd +0 -42
- package/out/base-prompts/skills/pptx/ooxml/schemas/ecma/fouth-edition/opc-coreProperties.xsd +0 -50
- package/out/base-prompts/skills/pptx/ooxml/schemas/ecma/fouth-edition/opc-digSig.xsd +0 -49
- package/out/base-prompts/skills/pptx/ooxml/schemas/ecma/fouth-edition/opc-relationships.xsd +0 -33
- package/out/base-prompts/skills/pptx/ooxml/schemas/mce/mc.xsd +0 -75
- package/out/base-prompts/skills/pptx/ooxml/schemas/microsoft/wml-2010.xsd +0 -560
- package/out/base-prompts/skills/pptx/ooxml/schemas/microsoft/wml-2012.xsd +0 -67
- package/out/base-prompts/skills/pptx/ooxml/schemas/microsoft/wml-2018.xsd +0 -14
- package/out/base-prompts/skills/pptx/ooxml/schemas/microsoft/wml-cex-2018.xsd +0 -20
- package/out/base-prompts/skills/pptx/ooxml/schemas/microsoft/wml-cid-2016.xsd +0 -13
- package/out/base-prompts/skills/pptx/ooxml/schemas/microsoft/wml-sdtdatahash-2020.xsd +0 -4
- package/out/base-prompts/skills/pptx/ooxml/schemas/microsoft/wml-symex-2015.xsd +0 -8
- package/out/base-prompts/skills/pptx/ooxml/scripts/pack.py +0 -159
- package/out/base-prompts/skills/pptx/ooxml/scripts/unpack.py +0 -29
- package/out/base-prompts/skills/pptx/ooxml/scripts/validate.py +0 -69
- package/out/base-prompts/skills/pptx/ooxml/scripts/validation/__init__.py +0 -15
- package/out/base-prompts/skills/pptx/ooxml/scripts/validation/base.py +0 -951
- package/out/base-prompts/skills/pptx/ooxml/scripts/validation/docx.py +0 -274
- package/out/base-prompts/skills/pptx/ooxml/scripts/validation/pptx.py +0 -315
- package/out/base-prompts/skills/pptx/ooxml/scripts/validation/redlining.py +0 -279
- package/out/base-prompts/skills/pptx/ooxml.md +0 -427
- package/out/base-prompts/skills/pptx/scripts/html2pptx.js +0 -984
- package/out/base-prompts/skills/pptx/scripts/inventory.py +0 -1020
- package/out/base-prompts/skills/pptx/scripts/rearrange.py +0 -231
- package/out/base-prompts/skills/pptx/scripts/replace.py +0 -385
- package/out/base-prompts/skills/pptx/scripts/thumbnail.py +0 -450
- package/out/base-prompts/skills/prompt-lab/SKILL.md +0 -149
- package/out/base-prompts/skills/prompt-lab/scripts/query-analytics.sh +0 -2551
- package/out/base-prompts/skills/skill-creator/LICENSE.txt +0 -202
- package/out/base-prompts/skills/skill-creator/SKILL.md +0 -371
- package/out/base-prompts/skills/skill-creator/references/output-patterns.md +0 -82
- package/out/base-prompts/skills/skill-creator/references/workflows.md +0 -28
- package/out/base-prompts/skills/skill-creator/scripts/init_skill.py +0 -303
- package/out/base-prompts/skills/skill-creator/scripts/package_skill.py +0 -110
- package/out/base-prompts/skills/skill-creator/scripts/quick_validate.py +0 -95
- package/out/base-prompts/skills/skill-installer/SKILL.md +0 -251
- package/out/base-prompts/skills/skill-installer/references/store-api.md +0 -219
- package/out/base-prompts/skills/spotlight-dashboard/SKILL.md +0 -83
- package/out/base-prompts/skills/spotlight-insights/SKILL.md +0 -101
- package/out/base-prompts/skills/spotlight-optimize/SKILL.md +0 -93
- package/out/base-prompts/skills/spotlight-optimize/scripts/query-analytics.sh +0 -2551
- package/out/base-prompts/skills/spotlight-query/SKILL.md +0 -314
- package/out/base-prompts/skills/spotlight-query/references/otel-schema.md +0 -163
- package/out/base-prompts/skills/spotlight-query/scripts/query-analytics.sh +0 -2551
- package/out/base-prompts/skills/spotlight-query/scripts/query-analytics.test.sh +0 -744
- package/out/base-prompts/skills/spotlight-query/scripts/test-fixtures/analytics/sessions/sess-test-1.jsonl +0 -14
- package/out/base-prompts/skills/spotlight-query/scripts/test-fixtures/analytics/traces/.duckdb-index.json +0 -7
- package/out/base-prompts/skills/spotlight-query/scripts/test-fixtures/analytics/traces/sample.jsonl +0 -5
- package/out/base-prompts/skills/spotlight-report/SKILL.md +0 -86
- package/out/base-prompts/skills/spotlight-visualize/SKILL.md +0 -92
- package/out/base-prompts/skills/spotlight-visualize/scripts/query-analytics.sh +0 -2551
- package/out/base-prompts/skills/xlsx/LICENSE.txt +0 -30
- package/out/base-prompts/skills/xlsx/SKILL.md +0 -289
- package/out/base-prompts/skills/xlsx/recalc.py +0 -178
- package/out/branding.js +0 -49
- package/out/cli/commands/anywhere.js +0 -66
- package/out/cli/commands/ask.js +0 -246
- package/out/cli/commands/auth-broker.js +0 -24
- package/out/cli/commands/auth.js +0 -219
- package/out/cli/commands/chat.js +0 -357
- package/out/cli/commands/conformance.js +0 -249
- package/out/cli/commands/data-plane.js +0 -67
- package/out/cli/commands/deploy.js +0 -263
- package/out/cli/commands/gateway.js +0 -218
- package/out/cli/commands/idp.js +0 -85
- package/out/cli/commands/pack.js +0 -399
- package/out/cli/commands/probe-custom-engine.js +0 -82
- package/out/cli/commands/sandbox.js +0 -332
- package/out/cli/commands/serve.js +0 -1758
- package/out/cli/commands/store.js +0 -927
- package/out/cli/credential-context.js +0 -91
- package/out/cli/data-plane/broker.js +0 -78
- package/out/cli/data-plane/connector-host.js +0 -121
- package/out/cli/data-plane/egress.js +0 -189
- package/out/cli/data-plane/gateway.js +0 -218
- package/out/cli/data-plane/index.js +0 -152
- package/out/cli/data-plane/registry.js +0 -89
- package/out/cli/data-plane/remote.js +0 -106
- package/out/cli/data-plane/sidecar.js +0 -161
- package/out/cli/deploy/blueprints/registry.js +0 -84
- package/out/cli/deploy/layers/identity.js +0 -172
- package/out/cli/deploy-init.js +0 -3023
- package/out/cli/deps.js +0 -342
- package/out/cli/idp-init.js +0 -338
- package/out/cli/pack-install-ref.js +0 -15
- package/out/cli/pack-loader.js +0 -418
- package/out/cli/pack-runtime.js +0 -61
- package/out/cli/runtime.js +0 -450
- package/out/cli/serve-hardening.js +0 -607
- package/out/cli/state-sync.js +0 -424
- package/out/cli/utils.js +0 -231
- package/out/engines/base.js +0 -188
- package/out/engines/claude.js +0 -1254
- package/out/engines/codex.js +0 -862
- package/out/engines/custom.js +0 -674
- package/out/engines/customEngineIpc.js +0 -95
- package/out/engines/customEngineRunnerEmbed.js +0 -107
- package/out/engines/customRegistry.js +0 -274
- package/out/engines/embeddedCustomEngineRunner.generated.js +0 -9
- package/out/engines/engineApiKey.js +0 -138
- package/out/engines/install.js +0 -372
- package/out/engines/persistentEngineWorker.js +0 -320
- package/out/engines/publish.js +0 -341
- package/out/evidence/sessionShip.js +0 -610
- package/out/gateway/bridge.js +0 -843
- package/out/gateway/cli-agent-id.js +0 -36
- package/out/gateway/client.js +0 -336
- package/out/gateway/client.test.js +0 -395
- package/out/gateway/gateway-scope-errors.js +0 -14
- package/out/gateway/gatewayBackup.js +0 -76
- package/out/gateway/installSource.js +0 -68
- package/out/gateway/lifecycle.js +0 -3190
- package/out/gateway/openclaw-channel-routing.js +0 -317
- package/out/gateway/outboundHandles.js +0 -373
- package/out/gateway/outboundPaths.js +0 -75
- package/out/gateway/outboundSend.js +0 -254
- package/out/gateway/protocol.js +0 -48
- package/out/gateway/secretsPreflight.js +0 -90
- package/out/gateway/sessionRouting.js +0 -77
- package/out/headless/index.js +0 -539
- package/out/headless/sync.js +0 -473
- package/out/live-e2e/mcp-demo/server.mjs +0 -32
- package/out/live-probe/connector.mjs +0 -8
- package/out/live-probe/env-report.mjs +0 -8
- package/out/mcp.js +0 -654
- package/out/pack/caps.js +0 -271
- package/out/pack/governor.js +0 -309
- package/out/pack/lease-schema.js +0 -270
- package/out/pack/lease.js +0 -640
- package/out/pack/reconcile.js +0 -217
- package/out/pack/signature.js +0 -839
- package/out/permissions.js +0 -108
- package/out/permissions.mapping.yaml +0 -68
- package/out/registry/index.js +0 -332
- package/out/runner/host.js +0 -131
- package/out/runner/protocol.js +0 -108
- package/out/runner/remoteEngine.js +0 -130
- package/out/runtime/interfaces/config-provider.js +0 -32
- package/out/runtime/interfaces/edit-tracker.js +0 -24
- package/out/runtime/interfaces/index.js +0 -29
- package/out/runtime/interfaces/logger.js +0 -40
- package/out/runtime/interfaces/permission-handler.js +0 -66
- package/out/runtime/interfaces/session-store.js +0 -342
- package/out/runtime/vscode/vscode-session-store.js +0 -22
- package/out/sandbox/containerDriver.js +0 -570
- package/out/sandbox/processDriver.js +0 -315
- package/out/sandbox/sandboxRunner.js +0 -191
- package/out/sandbox/support.js +0 -301
- package/out/sandbox/types.js +0 -55
- package/out/scripts/analytics/fleet-local-adapter.js +0 -246
- package/out/scripts/analytics/live-fleet-4-collector.cjs +0 -315
- package/out/scripts/analytics/live-fleet-4-daemon.mjs +0 -134
- package/out/scripts/analytics/live-fleet-4-widget.cjs +0 -433
- package/out/scripts/gateway/Dockerfile.serve +0 -45
- package/out/scripts/gateway/docker-serve.sh +0 -248
- package/out/scripts/gateway/setup-gateway-server.ps1 +0 -953
- package/out/scripts/gateway/setup-gateway-server.sh +0 -935
- package/out/session.js +0 -272
- package/out/shared/agentEngines.js +0 -170
- package/out/shared/index.js +0 -109
- package/out/shared/models.js +0 -136
- package/out/shared/providers.js +0 -205
- package/out/spotlight/install.js +0 -345
- package/out/spotlight/publish.js +0 -544
- package/out/spotlight/yaml.js +0 -31
- package/out/store/index.js +0 -786
- package/out/store/shared.js +0 -241
- package/out/test/agentEnvFile.test.js +0 -97
- package/out/test/agentYaml.test.js +0 -373
- package/out/test/agentpack.test.js +0 -639
- package/out/test/analytics-lifecycle.test.js +0 -174
- package/out/test/analytics.test.js +0 -756
- package/out/test/auth-baseurl.test.js +0 -132
- package/out/test/auth-bootstrapApply.test.js +0 -123
- package/out/test/auth-broker-hardening.test.js +0 -227
- package/out/test/auth-broker.test.js +0 -541
- package/out/test/auth-callback.test.js +0 -62
- package/out/test/blueprint-registry.test.js +0 -105
- package/out/test/branding.test.js +0 -242
- package/out/test/caps-governor.test.js +0 -455
- package/out/test/cli-agent-id.test.js +0 -55
- package/out/test/codex-new-file-survival.test.js +0 -119
- package/out/test/configuration-scopes.test.js +0 -131
- package/out/test/credential-context.test.js +0 -160
- package/out/test/custom-env-layering.test.js +0 -151
- package/out/test/customEngineInstall.test.js +0 -100
- package/out/test/customRegistry.test.js +0 -122
- package/out/test/data-plane-engine-wiring.test.js +0 -250
- package/out/test/data-plane.test.js +0 -311
- package/out/test/deploy-init.test.js +0 -854
- package/out/test/deps-auto-install.test.js +0 -81
- package/out/test/egress-broker.test.js +0 -172
- package/out/test/engine-integration.test.js +0 -297
- package/out/test/enginePaths.test.js +0 -195
- package/out/test/enginePublish.test.js +0 -82
- package/out/test/fleetDisplayUtils.test.js +0 -100
- package/out/test/gateway-client.test.js +0 -409
- package/out/test/gateway-install-source.test.js +0 -111
- package/out/test/gateway-lifecycle.test.js +0 -608
- package/out/test/gateway-outbound.test.js +0 -538
- package/out/test/gateway-reset-mappings.test.js +0 -221
- package/out/test/gateway-route-index.test.js +0 -235
- package/out/test/gateway-secrets-preflight.test.js +0 -127
- package/out/test/gateway-session-routing.test.js +0 -65
- package/out/test/gateway-session-store.test.js +0 -216
- package/out/test/github.test.js +0 -232
- package/out/test/governor-hardening.test.js +0 -217
- package/out/test/headless-sync.test.js +0 -301
- package/out/test/helpers/manifest-fixtures.js +0 -27
- package/out/test/idp-init.test.js +0 -151
- package/out/test/lease-signature.test.js +0 -357
- package/out/test/lease-trust-root.test.js +0 -273
- package/out/test/lease-validation.test.js +0 -169
- package/out/test/lease.test.js +0 -114
- package/out/test/mcp-obo.test.js +0 -107
- package/out/test/mcp.test.js +0 -389
- package/out/test/model-routing.test.js +0 -133
- package/out/test/openclaw-channel-routing.test.js +0 -191
- package/out/test/pack-admission.test.js +0 -216
- package/out/test/pack-digest-parity.test.js +0 -140
- package/out/test/pack-reconcile.test.js +0 -183
- package/out/test/pack-signature-artifact.test.js +0 -103
- package/out/test/pack-signature.test.js +0 -176
- package/out/test/pack-trust-root.test.js +0 -202
- package/out/test/pathSafety.test.js +0 -63
- package/out/test/permissions.test.js +0 -175
- package/out/test/persistent-engine-worker.test.js +0 -220
- package/out/test/refreshPolicy.test.js +0 -60
- package/out/test/registry.test.js +0 -351
- package/out/test/remote-trust.test.js +0 -242
- package/out/test/runner-protocol.test.js +0 -249
- package/out/test/runtime.test.js +0 -349
- package/out/test/sandbox-driver.test.js +0 -553
- package/out/test/sandbox-hardening.test.js +0 -395
- package/out/test/sandbox-runner.test.js +0 -343
- package/out/test/scheduledFleetStorage.test.js +0 -72
- package/out/test/serve-gateway-turn.test.js +0 -267
- package/out/test/serve-hardening.test.js +0 -418
- package/out/test/serveClient.test.js +0 -209
- package/out/test/sessionJsonlFleet.test.js +0 -78
- package/out/test/sessionShip.test.js +0 -560
- package/out/test/shared.test.js +0 -173
- package/out/test/skills-mcp.test.js +0 -299
- package/out/test/spotlightPublish.test.js +0 -327
- package/out/test/spotlightYaml.test.js +0 -58
- package/out/test/state-sync-peer.test.js +0 -156
- package/out/test/store-install-safe.test.js +0 -69
- package/out/test/store.test.js +0 -331
- package/out/test/stores.test.js +0 -504
- package/out/test/sync-switch.test.js +0 -251
- package/out/test/teamViewDiscovery.test.js +0 -90
- package/out/test/tls-bypass.test.js +0 -90
- package/out/test/toolPath.test.js +0 -120
- package/out/test/utils.test.js +0 -308
- package/out/test/viewsConfigMerge.test.js +0 -180
- package/out/test/warrant.test.js +0 -138
- package/out/test/windows-command-line.test.js +0 -75
- package/out/utils/agentEnvFile.js +0 -131
- package/out/utils/binary.js +0 -175
- package/out/utils/index.js +0 -37
- package/out/utils/network.js +0 -74
- package/out/utils/paths.js +0 -116
- package/out/utils/pid.js +0 -180
- package/out/utils/resolveNodeBinary.js +0 -168
- package/out/utils/windowsCommandLine.js +0 -29
- package/out/utils.js +0 -968
|
@@ -1,27 +1,6 @@
|
|
|
1
|
-
|
|
2
|
-
#
|
|
3
|
-
"""
|
|
4
|
-
Incremental JSONL → DuckDB indexing worker.
|
|
5
|
-
|
|
6
|
-
Converts OpenTelemetry JSONL trace files to indexed DuckDB database for fast analytics queries.
|
|
7
|
-
|
|
8
|
-
Usage:
|
|
9
|
-
python3 index-traces.py <traces_dir>
|
|
10
|
-
python3 index-traces.py <traces_dir> <output_db_path>
|
|
11
|
-
|
|
12
|
-
Environment variables:
|
|
13
|
-
AGENTIDE_TRACES_DIR - Path to traces directory (can also be passed as arg)
|
|
14
|
-
AGENTIDE_INDEX_DB_PATH - Path to output .duckdb file
|
|
15
|
-
|
|
16
|
-
Exit codes:
|
|
17
|
-
0 = success (indexing completed)
|
|
18
|
-
1 = error (indexing failed)
|
|
19
|
-
2 = already indexed (no changes needed)
|
|
20
|
-
3 = indexing already in progress
|
|
21
|
-
"""
|
|
22
|
-
|
|
1
|
+
# Copyright (c) 2026 AgentHippo.ai. All rights reserved.
|
|
2
|
+
# Proprietary and confidential. See LICENSE.
|
|
23
3
|
from __future__ import annotations
|
|
24
|
-
|
|
25
4
|
import ast
|
|
26
5
|
import hashlib
|
|
27
6
|
import json
|
|
@@ -31,66 +10,39 @@ import sys
|
|
|
31
10
|
from datetime import datetime
|
|
32
11
|
from pathlib import Path
|
|
33
12
|
from typing import Any
|
|
34
|
-
|
|
35
|
-
# Setup logging
|
|
36
|
-
logging.basicConfig(
|
|
37
|
-
level=logging.DEBUG,
|
|
38
|
-
format='%(levelname)s: %(message)s',
|
|
39
|
-
stream=sys.stderr,
|
|
40
|
-
)
|
|
13
|
+
logging.basicConfig(level=logging.DEBUG, format='%(levelname)s: %(message)s', stream=sys.stderr)
|
|
41
14
|
log = logging.getLogger(__name__)
|
|
42
15
|
|
|
43
|
-
|
|
44
|
-
def get_traces_dir(arg_traces_dir: str | None = None) -> Path:
|
|
45
|
-
"""Get traces directory from arg, env var, or default."""
|
|
16
|
+
def get_traces_dir(arg_traces_dir: str | None=None) -> Path:
|
|
46
17
|
if arg_traces_dir:
|
|
47
18
|
return Path(arg_traces_dir)
|
|
48
|
-
|
|
49
19
|
env_traces_dir = os.environ.get('AGENTIDE_TRACES_DIR', '').strip()
|
|
50
20
|
if env_traces_dir:
|
|
51
21
|
return Path(env_traces_dir)
|
|
52
|
-
|
|
53
|
-
# Default: ~/.agent-hippo/analytics/traces
|
|
54
22
|
agent_home = Path.home() / '.agent-hippo'
|
|
55
23
|
return agent_home / 'analytics' / 'traces'
|
|
56
24
|
|
|
57
|
-
|
|
58
|
-
def get_db_path(traces_dir: Path, arg_db_path: str | None = None) -> Path:
|
|
59
|
-
"""Get database path from arg, env var, or default (traces_dir/analytics.duckdb)."""
|
|
25
|
+
def get_db_path(traces_dir: Path, arg_db_path: str | None=None) -> Path:
|
|
60
26
|
if arg_db_path:
|
|
61
27
|
return Path(arg_db_path)
|
|
62
|
-
|
|
63
28
|
env_db_path = os.environ.get('AGENTIDE_INDEX_DB_PATH', '').strip()
|
|
64
29
|
if env_db_path:
|
|
65
30
|
return Path(env_db_path)
|
|
66
|
-
|
|
67
31
|
return traces_dir / 'analytics.duckdb'
|
|
68
32
|
|
|
69
|
-
|
|
70
33
|
def get_state_file(traces_dir: Path) -> Path:
|
|
71
|
-
"""Get indexing state file path."""
|
|
72
34
|
return traces_dir / '.duckdb-index.json'
|
|
73
35
|
|
|
74
|
-
|
|
75
36
|
def get_lock_file(traces_dir: Path) -> Path:
|
|
76
|
-
"""Get process lock file path."""
|
|
77
37
|
return traces_dir / '.indexing.lock'
|
|
78
38
|
|
|
79
|
-
|
|
80
39
|
def try_acquire_index_lock(lock_file: Path):
|
|
81
|
-
"""
|
|
82
|
-
Try to acquire a cross-process exclusive lock.
|
|
83
|
-
Returns an open file handle when acquired, otherwise None.
|
|
84
|
-
"""
|
|
85
40
|
lock_file.parent.mkdir(parents=True, exist_ok=True)
|
|
86
41
|
handle = lock_file.open('a+b')
|
|
87
|
-
|
|
88
|
-
# Ensure the file has at least one byte for Windows byte-range locking.
|
|
89
42
|
handle.seek(0, os.SEEK_END)
|
|
90
43
|
if handle.tell() == 0:
|
|
91
44
|
handle.write(b'0')
|
|
92
45
|
handle.flush()
|
|
93
|
-
|
|
94
46
|
try:
|
|
95
47
|
if os.name == 'nt':
|
|
96
48
|
import msvcrt
|
|
@@ -102,21 +54,15 @@ def try_acquire_index_lock(lock_file: Path):
|
|
|
102
54
|
except OSError:
|
|
103
55
|
handle.close()
|
|
104
56
|
return None
|
|
105
|
-
|
|
106
|
-
# Record owner PID for diagnostics.
|
|
107
57
|
handle.seek(0)
|
|
108
58
|
handle.truncate(0)
|
|
109
|
-
handle.write(f
|
|
59
|
+
handle.write(f'{os.getpid()}\n'.encode('utf-8'))
|
|
110
60
|
handle.flush()
|
|
111
|
-
|
|
112
61
|
return handle
|
|
113
62
|
|
|
114
|
-
|
|
115
63
|
def release_index_lock(handle) -> None:
|
|
116
|
-
"""Release the process lock (best effort)."""
|
|
117
64
|
if handle is None:
|
|
118
65
|
return
|
|
119
|
-
|
|
120
66
|
try:
|
|
121
67
|
if os.name == 'nt':
|
|
122
68
|
import msvcrt
|
|
@@ -130,34 +76,22 @@ def release_index_lock(handle) -> None:
|
|
|
130
76
|
finally:
|
|
131
77
|
handle.close()
|
|
132
78
|
|
|
133
|
-
|
|
134
79
|
def load_index_state(state_file: Path) -> dict[str, Any]:
|
|
135
|
-
"""Load indexing state from disk."""
|
|
136
80
|
try:
|
|
137
81
|
if state_file.exists():
|
|
138
82
|
content = state_file.read_text('utf-8')
|
|
139
83
|
return json.loads(content)
|
|
140
84
|
except Exception as e:
|
|
141
|
-
log.debug(f
|
|
142
|
-
|
|
143
|
-
return {
|
|
144
|
-
'version': 2,
|
|
145
|
-
'indexed': False,
|
|
146
|
-
'lastMtime': 0,
|
|
147
|
-
'duckdbPath': '',
|
|
148
|
-
}
|
|
149
|
-
|
|
85
|
+
log.debug(f'Failed to load state file: {e}')
|
|
86
|
+
return {'version': 2, 'indexed': False, 'lastMtime': 0, 'duckdbPath': ''}
|
|
150
87
|
|
|
151
88
|
def save_index_state(state_file: Path, state: dict[str, Any]) -> None:
|
|
152
|
-
"""Save indexing state to disk."""
|
|
153
89
|
try:
|
|
154
90
|
state_file.write_text(json.dumps(state, indent=2), 'utf-8')
|
|
155
91
|
except Exception as e:
|
|
156
|
-
log.warning(f
|
|
157
|
-
|
|
92
|
+
log.warning(f'Failed to save state file: {e}')
|
|
158
93
|
|
|
159
94
|
def get_latest_jsonl_mtime(traces_dir: Path) -> int:
|
|
160
|
-
"""Get maximum mtime of all JSONL files in traces_dir."""
|
|
161
95
|
try:
|
|
162
96
|
max_mtime = 0
|
|
163
97
|
for jsonl_file in traces_dir.glob('*.jsonl'):
|
|
@@ -168,259 +102,71 @@ def get_latest_jsonl_mtime(traces_dir: Path) -> int:
|
|
|
168
102
|
pass
|
|
169
103
|
return max_mtime
|
|
170
104
|
except Exception as e:
|
|
171
|
-
log.error(f
|
|
105
|
+
log.error(f'Failed to get JSONL mtimes: {e}')
|
|
172
106
|
return 0
|
|
173
107
|
|
|
174
|
-
|
|
175
108
|
def ensure_schema(conn) -> None:
|
|
176
|
-
|
|
177
|
-
conn.execute(
|
|
178
|
-
CREATE TABLE IF NOT EXISTS
|
|
179
|
-
|
|
180
|
-
trace_id VARCHAR
|
|
181
|
-
span_id VARCHAR,
|
|
182
|
-
span_name VARCHAR,
|
|
183
|
-
session_id VARCHAR,
|
|
184
|
-
turn_id VARCHAR,
|
|
185
|
-
request_id VARCHAR,
|
|
186
|
-
user_id VARCHAR,
|
|
187
|
-
project_id VARCHAR,
|
|
188
|
-
agent_id VARCHAR,
|
|
189
|
-
agent_version VARCHAR,
|
|
190
|
-
source VARCHAR,
|
|
191
|
-
engine VARCHAR,
|
|
192
|
-
model VARCHAR,
|
|
193
|
-
start_ms BIGINT,
|
|
194
|
-
end_ms BIGINT,
|
|
195
|
-
duration_ms DOUBLE,
|
|
196
|
-
prompt_tokens BIGINT,
|
|
197
|
-
completion_tokens BIGINT,
|
|
198
|
-
total_tokens BIGINT,
|
|
199
|
-
total_cost_usd DOUBLE,
|
|
200
|
-
status VARCHAR,
|
|
201
|
-
error_type VARCHAR,
|
|
202
|
-
error_message VARCHAR,
|
|
203
|
-
input_messages_raw VARCHAR,
|
|
204
|
-
output_messages_raw VARCHAR,
|
|
205
|
-
created_at TIMESTAMP,
|
|
206
|
-
updated_at TIMESTAMP
|
|
207
|
-
)
|
|
208
|
-
""")
|
|
209
|
-
|
|
210
|
-
conn.execute("""
|
|
211
|
-
CREATE TABLE IF NOT EXISTS sessions (
|
|
212
|
-
session_id VARCHAR PRIMARY KEY,
|
|
213
|
-
user_id VARCHAR,
|
|
214
|
-
project_id VARCHAR,
|
|
215
|
-
agent_id VARCHAR,
|
|
216
|
-
agent_version VARCHAR,
|
|
217
|
-
source VARCHAR,
|
|
218
|
-
engine VARCHAR,
|
|
219
|
-
started_at TIMESTAMP,
|
|
220
|
-
ended_at TIMESTAMP,
|
|
221
|
-
status VARCHAR,
|
|
222
|
-
total_turns BIGINT,
|
|
223
|
-
total_requests BIGINT,
|
|
224
|
-
total_cost_usd DOUBLE,
|
|
225
|
-
total_tokens BIGINT,
|
|
226
|
-
created_at TIMESTAMP,
|
|
227
|
-
updated_at TIMESTAMP
|
|
228
|
-
)
|
|
229
|
-
""")
|
|
230
|
-
|
|
231
|
-
conn.execute("""
|
|
232
|
-
CREATE TABLE IF NOT EXISTS turns (
|
|
233
|
-
turn_id VARCHAR PRIMARY KEY,
|
|
234
|
-
session_id VARCHAR,
|
|
235
|
-
request_id VARCHAR,
|
|
236
|
-
turn_index BIGINT,
|
|
237
|
-
started_at TIMESTAMP,
|
|
238
|
-
ended_at TIMESTAMP,
|
|
239
|
-
status VARCHAR,
|
|
240
|
-
model VARCHAR,
|
|
241
|
-
provider VARCHAR,
|
|
242
|
-
prompt_tokens BIGINT,
|
|
243
|
-
completion_tokens BIGINT,
|
|
244
|
-
total_tokens BIGINT,
|
|
245
|
-
cost_usd DOUBLE,
|
|
246
|
-
trace_id VARCHAR,
|
|
247
|
-
created_at TIMESTAMP,
|
|
248
|
-
updated_at TIMESTAMP
|
|
249
|
-
)
|
|
250
|
-
""")
|
|
251
|
-
|
|
252
|
-
conn.execute("""
|
|
253
|
-
CREATE TABLE IF NOT EXISTS tool_calls (
|
|
254
|
-
tool_call_id VARCHAR PRIMARY KEY,
|
|
255
|
-
session_id VARCHAR,
|
|
256
|
-
turn_id VARCHAR,
|
|
257
|
-
request_id VARCHAR,
|
|
258
|
-
model_call_id VARCHAR,
|
|
259
|
-
trace_id VARCHAR,
|
|
260
|
-
span_id VARCHAR,
|
|
261
|
-
tool_name VARCHAR,
|
|
262
|
-
arguments_json JSON,
|
|
263
|
-
arguments_text VARCHAR,
|
|
264
|
-
call_ts TIMESTAMP,
|
|
265
|
-
result_json JSON,
|
|
266
|
-
result_text VARCHAR,
|
|
267
|
-
status VARCHAR,
|
|
268
|
-
duration_ms DOUBLE,
|
|
269
|
-
error_type VARCHAR,
|
|
270
|
-
error_message VARCHAR,
|
|
271
|
-
created_at TIMESTAMP,
|
|
272
|
-
updated_at TIMESTAMP
|
|
273
|
-
)
|
|
274
|
-
""")
|
|
275
|
-
|
|
276
|
-
conn.execute("""
|
|
277
|
-
CREATE TABLE IF NOT EXISTS artifacts (
|
|
278
|
-
artifact_id VARCHAR PRIMARY KEY,
|
|
279
|
-
session_id VARCHAR,
|
|
280
|
-
turn_id VARCHAR,
|
|
281
|
-
trace_id VARCHAR,
|
|
282
|
-
kind VARCHAR,
|
|
283
|
-
mime_type VARCHAR,
|
|
284
|
-
storage_uri VARCHAR,
|
|
285
|
-
sha256 VARCHAR,
|
|
286
|
-
byte_size BIGINT,
|
|
287
|
-
created_at TIMESTAMP
|
|
288
|
-
)
|
|
289
|
-
""")
|
|
290
|
-
|
|
291
|
-
# Gold compatibility/fast dashboard table
|
|
292
|
-
conn.execute("""
|
|
293
|
-
CREATE TABLE IF NOT EXISTS spans (
|
|
294
|
-
trace_id VARCHAR,
|
|
295
|
-
span_id VARCHAR,
|
|
296
|
-
span_name VARCHAR,
|
|
297
|
-
model VARCHAR,
|
|
298
|
-
cost DOUBLE,
|
|
299
|
-
input_tokens BIGINT,
|
|
300
|
-
output_tokens BIGINT,
|
|
301
|
-
session_id VARCHAR,
|
|
302
|
-
user_id VARCHAR,
|
|
303
|
-
project_id VARCHAR,
|
|
304
|
-
agent_id VARCHAR,
|
|
305
|
-
engine VARCHAR,
|
|
306
|
-
duration_ms DOUBLE,
|
|
307
|
-
start_ms BIGINT,
|
|
308
|
-
created_at TIMESTAMP,
|
|
309
|
-
PRIMARY KEY(trace_id, span_id)
|
|
310
|
-
)
|
|
311
|
-
""")
|
|
109
|
+
conn.execute('\n CREATE TABLE IF NOT EXISTS model_calls (\n model_call_id VARCHAR PRIMARY KEY,\n trace_id VARCHAR,\n span_id VARCHAR,\n span_name VARCHAR,\n session_id VARCHAR,\n turn_id VARCHAR,\n request_id VARCHAR,\n user_id VARCHAR,\n project_id VARCHAR,\n agent_id VARCHAR,\n agent_version VARCHAR,\n source VARCHAR,\n engine VARCHAR,\n model VARCHAR,\n start_ms BIGINT,\n end_ms BIGINT,\n duration_ms DOUBLE,\n prompt_tokens BIGINT,\n completion_tokens BIGINT,\n total_tokens BIGINT,\n total_cost_usd DOUBLE,\n status VARCHAR,\n error_type VARCHAR,\n error_message VARCHAR,\n input_messages_raw VARCHAR,\n output_messages_raw VARCHAR,\n created_at TIMESTAMP,\n updated_at TIMESTAMP\n )\n ')
|
|
110
|
+
conn.execute('\n CREATE TABLE IF NOT EXISTS sessions (\n session_id VARCHAR PRIMARY KEY,\n user_id VARCHAR,\n project_id VARCHAR,\n agent_id VARCHAR,\n agent_version VARCHAR,\n source VARCHAR,\n engine VARCHAR,\n started_at TIMESTAMP,\n ended_at TIMESTAMP,\n status VARCHAR,\n total_turns BIGINT,\n total_requests BIGINT,\n total_cost_usd DOUBLE,\n total_tokens BIGINT,\n created_at TIMESTAMP,\n updated_at TIMESTAMP\n )\n ')
|
|
111
|
+
conn.execute('\n CREATE TABLE IF NOT EXISTS turns (\n turn_id VARCHAR PRIMARY KEY,\n session_id VARCHAR,\n request_id VARCHAR,\n turn_index BIGINT,\n started_at TIMESTAMP,\n ended_at TIMESTAMP,\n status VARCHAR,\n model VARCHAR,\n provider VARCHAR,\n prompt_tokens BIGINT,\n completion_tokens BIGINT,\n total_tokens BIGINT,\n cost_usd DOUBLE,\n trace_id VARCHAR,\n created_at TIMESTAMP,\n updated_at TIMESTAMP\n )\n ')
|
|
112
|
+
conn.execute('\n CREATE TABLE IF NOT EXISTS tool_calls (\n tool_call_id VARCHAR PRIMARY KEY,\n session_id VARCHAR,\n turn_id VARCHAR,\n request_id VARCHAR,\n model_call_id VARCHAR,\n trace_id VARCHAR,\n span_id VARCHAR,\n tool_name VARCHAR,\n arguments_json JSON,\n arguments_text VARCHAR,\n call_ts TIMESTAMP,\n result_json JSON,\n result_text VARCHAR,\n status VARCHAR,\n duration_ms DOUBLE,\n error_type VARCHAR,\n error_message VARCHAR,\n created_at TIMESTAMP,\n updated_at TIMESTAMP\n )\n ')
|
|
113
|
+
conn.execute('\n CREATE TABLE IF NOT EXISTS artifacts (\n artifact_id VARCHAR PRIMARY KEY,\n session_id VARCHAR,\n turn_id VARCHAR,\n trace_id VARCHAR,\n kind VARCHAR,\n mime_type VARCHAR,\n storage_uri VARCHAR,\n sha256 VARCHAR,\n byte_size BIGINT,\n created_at TIMESTAMP\n )\n ')
|
|
114
|
+
conn.execute('\n CREATE TABLE IF NOT EXISTS spans (\n trace_id VARCHAR,\n span_id VARCHAR,\n span_name VARCHAR,\n model VARCHAR,\n cost DOUBLE,\n input_tokens BIGINT,\n output_tokens BIGINT,\n session_id VARCHAR,\n user_id VARCHAR,\n project_id VARCHAR,\n agent_id VARCHAR,\n engine VARCHAR,\n duration_ms DOUBLE,\n start_ms BIGINT,\n created_at TIMESTAMP,\n PRIMARY KEY(trace_id, span_id)\n )\n ')
|
|
312
115
|
try:
|
|
313
|
-
conn.execute(
|
|
116
|
+
conn.execute('ALTER TABLE spans ADD COLUMN IF NOT EXISTS engine VARCHAR')
|
|
314
117
|
except Exception as e:
|
|
315
|
-
log.debug(f
|
|
316
|
-
|
|
317
|
-
conn.execute(""
|
|
318
|
-
CREATE
|
|
319
|
-
file_path VARCHAR PRIMARY KEY,
|
|
320
|
-
mtime_ms BIGINT NOT NULL,
|
|
321
|
-
size_bytes BIGINT NOT NULL,
|
|
322
|
-
processed_at TIMESTAMP NOT NULL
|
|
323
|
-
)
|
|
324
|
-
""")
|
|
325
|
-
|
|
326
|
-
conn.execute("""
|
|
327
|
-
CREATE OR REPLACE VIEW agent_kpis_daily AS
|
|
328
|
-
SELECT
|
|
329
|
-
strftime(to_timestamp(start_ms / 1000.0), '%Y-%m-%d') AS day,
|
|
330
|
-
coalesce(agent_id, '(no agent)') AS agent_id,
|
|
331
|
-
coalesce(agent_version, '(unknown)') AS agent_version,
|
|
332
|
-
coalesce(model, '(unknown)') AS model,
|
|
333
|
-
count(*) AS requests,
|
|
334
|
-
count(distinct session_id) AS sessions,
|
|
335
|
-
count(distinct turn_id) AS turns,
|
|
336
|
-
round(sum(coalesce(total_cost_usd, 0.0)), 6) AS total_cost_usd,
|
|
337
|
-
sum(coalesce(total_tokens, 0)) AS total_tokens,
|
|
338
|
-
round(avg(duration_ms), 2) AS avg_latency_ms,
|
|
339
|
-
round(percentile_cont(0.50) within group (order by duration_ms), 2) AS p50_latency_ms,
|
|
340
|
-
round(percentile_cont(0.95) within group (order by duration_ms), 2) AS p95_latency_ms,
|
|
341
|
-
round(100.0 * sum(case when status = 'error' then 1 else 0 end) / nullif(count(*), 0), 2) AS error_rate_pct
|
|
342
|
-
FROM model_calls
|
|
343
|
-
GROUP BY 1, 2, 3, 4
|
|
344
|
-
""")
|
|
345
|
-
|
|
346
|
-
# Common indexes for dashboard and skill queries.
|
|
347
|
-
for stmt in [
|
|
348
|
-
"CREATE INDEX IF NOT EXISTS idx_model_calls_session_id ON model_calls(session_id)",
|
|
349
|
-
"CREATE INDEX IF NOT EXISTS idx_model_calls_start_ms ON model_calls(start_ms)",
|
|
350
|
-
"CREATE INDEX IF NOT EXISTS idx_model_calls_agent_id ON model_calls(agent_id)",
|
|
351
|
-
"CREATE INDEX IF NOT EXISTS idx_model_calls_project_id ON model_calls(project_id)",
|
|
352
|
-
"CREATE INDEX IF NOT EXISTS idx_tool_calls_session ON tool_calls(session_id)",
|
|
353
|
-
"CREATE INDEX IF NOT EXISTS idx_spans_session ON spans(session_id)",
|
|
354
|
-
"CREATE INDEX IF NOT EXISTS idx_spans_start_ms ON spans(start_ms)",
|
|
355
|
-
"CREATE INDEX IF NOT EXISTS idx_spans_model ON spans(model)",
|
|
356
|
-
"CREATE INDEX IF NOT EXISTS idx_spans_agent_id ON spans(agent_id)",
|
|
357
|
-
"CREATE INDEX IF NOT EXISTS idx_spans_project_id ON spans(project_id)",
|
|
358
|
-
]:
|
|
118
|
+
log.debug(f'Spans engine column (may already exist): {e}')
|
|
119
|
+
conn.execute('\n CREATE TABLE IF NOT EXISTS processed_files (\n file_path VARCHAR PRIMARY KEY,\n mtime_ms BIGINT NOT NULL,\n size_bytes BIGINT NOT NULL,\n processed_at TIMESTAMP NOT NULL\n )\n ')
|
|
120
|
+
conn.execute("\n CREATE OR REPLACE VIEW agent_kpis_daily AS\n SELECT\n strftime(to_timestamp(start_ms / 1000.0), '%Y-%m-%d') AS day,\n coalesce(agent_id, '(no agent)') AS agent_id,\n coalesce(agent_version, '(unknown)') AS agent_version,\n coalesce(model, '(unknown)') AS model,\n count(*) AS requests,\n count(distinct session_id) AS sessions,\n count(distinct turn_id) AS turns,\n round(sum(coalesce(total_cost_usd, 0.0)), 6) AS total_cost_usd,\n sum(coalesce(total_tokens, 0)) AS total_tokens,\n round(avg(duration_ms), 2) AS avg_latency_ms,\n round(percentile_cont(0.50) within group (order by duration_ms), 2) AS p50_latency_ms,\n round(percentile_cont(0.95) within group (order by duration_ms), 2) AS p95_latency_ms,\n round(100.0 * sum(case when status = 'error' then 1 else 0 end) / nullif(count(*), 0), 2) AS error_rate_pct\n FROM model_calls\n GROUP BY 1, 2, 3, 4\n ")
|
|
121
|
+
for stmt in ['CREATE INDEX IF NOT EXISTS idx_model_calls_session_id ON model_calls(session_id)', 'CREATE INDEX IF NOT EXISTS idx_model_calls_start_ms ON model_calls(start_ms)', 'CREATE INDEX IF NOT EXISTS idx_model_calls_agent_id ON model_calls(agent_id)', 'CREATE INDEX IF NOT EXISTS idx_model_calls_project_id ON model_calls(project_id)', 'CREATE INDEX IF NOT EXISTS idx_tool_calls_session ON tool_calls(session_id)', 'CREATE INDEX IF NOT EXISTS idx_spans_session ON spans(session_id)', 'CREATE INDEX IF NOT EXISTS idx_spans_start_ms ON spans(start_ms)', 'CREATE INDEX IF NOT EXISTS idx_spans_model ON spans(model)', 'CREATE INDEX IF NOT EXISTS idx_spans_agent_id ON spans(agent_id)', 'CREATE INDEX IF NOT EXISTS idx_spans_project_id ON spans(project_id)']:
|
|
359
122
|
try:
|
|
360
123
|
conn.execute(stmt)
|
|
361
124
|
except Exception as e:
|
|
362
|
-
log.debug(f
|
|
363
|
-
|
|
125
|
+
log.debug(f'Index creation hint (may already exist): {e}')
|
|
364
126
|
|
|
365
127
|
def get_file_stats(jsonl_file: Path) -> tuple[int, int]:
|
|
366
128
|
stats = jsonl_file.stat()
|
|
367
|
-
return int(stats.st_mtime * 1000), int(stats.st_size)
|
|
368
|
-
|
|
129
|
+
return (int(stats.st_mtime * 1000), int(stats.st_size))
|
|
369
130
|
|
|
370
131
|
def get_processed_file_map(conn) -> dict[str, tuple[int, int]]:
|
|
371
|
-
rows = conn.execute(
|
|
372
|
-
"SELECT file_path, mtime_ms, size_bytes FROM processed_files"
|
|
373
|
-
).fetchall()
|
|
132
|
+
rows = conn.execute('SELECT file_path, mtime_ms, size_bytes FROM processed_files').fetchall()
|
|
374
133
|
return {row[0]: (int(row[1]), int(row[2])) for row in rows}
|
|
375
134
|
|
|
376
|
-
|
|
377
135
|
def get_changed_files(conn, traces_dir: Path) -> list[Path]:
|
|
378
136
|
processed = get_processed_file_map(conn)
|
|
379
137
|
changed: list[Path] = []
|
|
380
|
-
|
|
381
138
|
for jsonl_file in sorted(traces_dir.glob('*.jsonl')):
|
|
382
139
|
try:
|
|
383
140
|
mtime_ms, size_bytes = get_file_stats(jsonl_file)
|
|
384
141
|
except OSError:
|
|
385
142
|
continue
|
|
386
|
-
|
|
387
143
|
key = str(jsonl_file)
|
|
388
144
|
previous = processed.get(key)
|
|
389
145
|
if previous is None or previous[0] != mtime_ms or previous[1] != size_bytes:
|
|
390
146
|
changed.append(jsonl_file)
|
|
391
|
-
|
|
392
147
|
return changed
|
|
393
148
|
|
|
394
|
-
|
|
395
149
|
def _safe_json_parse(raw: Any) -> Any:
|
|
396
150
|
if raw is None:
|
|
397
151
|
return None
|
|
398
152
|
if isinstance(raw, (dict, list)):
|
|
399
153
|
return raw
|
|
400
|
-
|
|
401
154
|
text = str(raw).strip()
|
|
402
155
|
if text == '':
|
|
403
156
|
return None
|
|
404
|
-
|
|
405
|
-
# try strict JSON first
|
|
406
157
|
try:
|
|
407
158
|
parsed = json.loads(text)
|
|
408
159
|
except Exception:
|
|
409
160
|
parsed = None
|
|
410
|
-
|
|
411
|
-
# fallback to python-literal-like payloads (single-quoted dict/list)
|
|
412
161
|
if parsed is None:
|
|
413
162
|
try:
|
|
414
163
|
parsed = ast.literal_eval(text)
|
|
415
164
|
except Exception:
|
|
416
165
|
parsed = None
|
|
417
|
-
|
|
418
166
|
if isinstance(parsed, str) and parsed != text:
|
|
419
167
|
return _safe_json_parse(parsed)
|
|
420
|
-
|
|
421
168
|
return parsed
|
|
422
169
|
|
|
423
|
-
|
|
424
170
|
def _to_json_text(value: Any) -> str | None:
|
|
425
171
|
if value is None:
|
|
426
172
|
return None
|
|
@@ -429,7 +175,6 @@ def _to_json_text(value: Any) -> str | None:
|
|
|
429
175
|
except Exception:
|
|
430
176
|
return None
|
|
431
177
|
|
|
432
|
-
|
|
433
178
|
def _extract_text_content(content: Any) -> str:
|
|
434
179
|
if content is None:
|
|
435
180
|
return ''
|
|
@@ -460,13 +205,10 @@ def _extract_text_content(content: Any) -> str:
|
|
|
460
205
|
return _extract_text_content(content.get('input'))
|
|
461
206
|
return str(content)
|
|
462
207
|
|
|
463
|
-
|
|
464
208
|
def _normalize_messages(payload: Any, default_role: str) -> list[dict[str, Any]]:
|
|
465
209
|
parsed = _safe_json_parse(payload)
|
|
466
|
-
|
|
467
210
|
if parsed is None:
|
|
468
211
|
return []
|
|
469
|
-
|
|
470
212
|
if isinstance(parsed, dict):
|
|
471
213
|
if isinstance(parsed.get('messages'), list):
|
|
472
214
|
parsed = parsed['messages']
|
|
@@ -480,81 +222,29 @@ def _normalize_messages(payload: Any, default_role: str) -> list[dict[str, Any]]
|
|
|
480
222
|
return out
|
|
481
223
|
else:
|
|
482
224
|
parsed = [parsed]
|
|
483
|
-
|
|
484
225
|
if not isinstance(parsed, list):
|
|
485
|
-
return [{
|
|
486
|
-
|
|
226
|
+
return [{'role': default_role, 'content': str(parsed)}]
|
|
487
227
|
out: list[dict[str, Any]] = []
|
|
488
228
|
for entry in parsed:
|
|
489
229
|
if isinstance(entry, dict):
|
|
490
230
|
out.append(entry)
|
|
491
231
|
else:
|
|
492
|
-
out.append({
|
|
232
|
+
out.append({'role': default_role, 'content': str(entry)})
|
|
493
233
|
return out
|
|
494
234
|
|
|
495
|
-
|
|
496
235
|
def _tool_call_id(session_id: str | None, trace_id: str, call_id: str | None, idx: int) -> str:
|
|
497
236
|
if call_id:
|
|
498
237
|
return call_id
|
|
499
|
-
seed = f
|
|
238
|
+
seed = f'{session_id or ''}|{trace_id}|tool|{idx}'
|
|
500
239
|
return hashlib.sha1(seed.encode('utf-8')).hexdigest()
|
|
501
240
|
|
|
502
|
-
|
|
503
241
|
def extract_messages_and_tools(conn) -> None:
|
|
504
|
-
|
|
505
|
-
Parse tool-call payloads from current temp batch (tmp_model_calls)
|
|
506
|
-
and upsert into tool_calls.
|
|
507
|
-
"""
|
|
508
|
-
rows = conn.execute(
|
|
509
|
-
"""
|
|
510
|
-
SELECT
|
|
511
|
-
model_call_id,
|
|
512
|
-
trace_id,
|
|
513
|
-
span_id,
|
|
514
|
-
session_id,
|
|
515
|
-
turn_id,
|
|
516
|
-
request_id,
|
|
517
|
-
start_ms,
|
|
518
|
-
end_ms,
|
|
519
|
-
status,
|
|
520
|
-
error_type,
|
|
521
|
-
error_message,
|
|
522
|
-
input_messages_raw,
|
|
523
|
-
output_messages_raw,
|
|
524
|
-
span_tool_name,
|
|
525
|
-
span_tool_call_id,
|
|
526
|
-
span_tool_arguments_raw,
|
|
527
|
-
span_tool_result_raw
|
|
528
|
-
FROM tmp_model_calls
|
|
529
|
-
"""
|
|
530
|
-
).fetchall()
|
|
531
|
-
|
|
242
|
+
rows = conn.execute('\n SELECT\n model_call_id,\n trace_id,\n span_id,\n session_id,\n turn_id,\n request_id,\n start_ms,\n end_ms,\n status,\n error_type,\n error_message,\n input_messages_raw,\n output_messages_raw,\n span_tool_name,\n span_tool_call_id,\n span_tool_arguments_raw,\n span_tool_result_raw\n FROM tmp_model_calls\n ').fetchall()
|
|
532
243
|
tool_results_by_id: dict[str, dict[str, Any]] = {}
|
|
533
|
-
|
|
534
244
|
for row in rows:
|
|
535
|
-
|
|
536
|
-
model_call_id,
|
|
537
|
-
trace_id,
|
|
538
|
-
span_id,
|
|
539
|
-
session_id,
|
|
540
|
-
turn_id,
|
|
541
|
-
request_id,
|
|
542
|
-
start_ms,
|
|
543
|
-
end_ms,
|
|
544
|
-
call_status,
|
|
545
|
-
call_error_type,
|
|
546
|
-
call_error_message,
|
|
547
|
-
input_raw,
|
|
548
|
-
output_raw,
|
|
549
|
-
span_tool_name,
|
|
550
|
-
span_tool_call_id,
|
|
551
|
-
span_tool_arguments_raw,
|
|
552
|
-
span_tool_result_raw,
|
|
553
|
-
) = row
|
|
554
|
-
|
|
245
|
+
model_call_id, trace_id, span_id, session_id, turn_id, request_id, start_ms, end_ms, call_status, call_error_type, call_error_message, input_raw, output_raw, span_tool_name, span_tool_call_id, span_tool_arguments_raw, span_tool_result_raw = row
|
|
555
246
|
tool_call_inserted = False
|
|
556
247
|
tool_base_ts = int(end_ms) if end_ms is not None else int(start_ms) if start_ms is not None else 0
|
|
557
|
-
|
|
558
248
|
if span_tool_name:
|
|
559
249
|
fallback_call_id = span_tool_call_id
|
|
560
250
|
if fallback_call_id is not None:
|
|
@@ -565,106 +255,17 @@ def extract_messages_and_tools(conn) -> None:
|
|
|
565
255
|
fallback_result_json = _to_json_text(_safe_json_parse(span_tool_result_raw) if span_tool_result_raw else None)
|
|
566
256
|
if fallback_result_json is None and span_tool_result_raw:
|
|
567
257
|
fallback_result_json = _to_json_text(span_tool_result_raw)
|
|
568
|
-
|
|
569
|
-
conn.execute(
|
|
570
|
-
"""
|
|
571
|
-
INSERT INTO tool_calls (
|
|
572
|
-
tool_call_id, session_id, turn_id, request_id, model_call_id,
|
|
573
|
-
trace_id, span_id, tool_name, arguments_json, arguments_text,
|
|
574
|
-
call_ts, result_json, result_text, status, duration_ms,
|
|
575
|
-
error_type, error_message, created_at, updated_at
|
|
576
|
-
)
|
|
577
|
-
VALUES (
|
|
578
|
-
?, ?, ?, ?, ?, ?, ?, ?, ?::JSON, ?,
|
|
579
|
-
to_timestamp(? / 1000.0), ?::JSON, ?, ?, NULL,
|
|
580
|
-
?, ?, now(), now()
|
|
581
|
-
)
|
|
582
|
-
ON CONFLICT(tool_call_id) DO UPDATE SET
|
|
583
|
-
result_json = COALESCE(excluded.result_json, tool_calls.result_json),
|
|
584
|
-
result_text = COALESCE(excluded.result_text, tool_calls.result_text),
|
|
585
|
-
status = CASE
|
|
586
|
-
WHEN excluded.status = 'error' THEN 'error'
|
|
587
|
-
WHEN excluded.status = 'ok' THEN 'ok'
|
|
588
|
-
ELSE tool_calls.status
|
|
589
|
-
END,
|
|
590
|
-
error_type = COALESCE(excluded.error_type, tool_calls.error_type),
|
|
591
|
-
error_message = COALESCE(excluded.error_message, tool_calls.error_message),
|
|
592
|
-
updated_at = now()
|
|
593
|
-
""",
|
|
594
|
-
[
|
|
595
|
-
fallback_tool_call_id,
|
|
596
|
-
session_id,
|
|
597
|
-
turn_id,
|
|
598
|
-
request_id,
|
|
599
|
-
model_call_id,
|
|
600
|
-
trace_id,
|
|
601
|
-
span_id,
|
|
602
|
-
str(span_tool_name),
|
|
603
|
-
_to_json_text(_safe_json_parse(span_tool_arguments_raw) if span_tool_arguments_raw else None)
|
|
604
|
-
or _to_json_text(span_tool_arguments_raw),
|
|
605
|
-
_extract_text_content(span_tool_arguments_raw),
|
|
606
|
-
tool_base_ts,
|
|
607
|
-
fallback_result_json,
|
|
608
|
-
fallback_result_text,
|
|
609
|
-
fallback_status,
|
|
610
|
-
call_error_type,
|
|
611
|
-
call_error_message,
|
|
612
|
-
],
|
|
613
|
-
)
|
|
258
|
+
conn.execute("\n INSERT INTO tool_calls (\n tool_call_id, session_id, turn_id, request_id, model_call_id,\n trace_id, span_id, tool_name, arguments_json, arguments_text,\n call_ts, result_json, result_text, status, duration_ms,\n error_type, error_message, created_at, updated_at\n )\n VALUES (\n ?, ?, ?, ?, ?, ?, ?, ?, ?::JSON, ?,\n to_timestamp(? / 1000.0), ?::JSON, ?, ?, NULL,\n ?, ?, now(), now()\n )\n ON CONFLICT(tool_call_id) DO UPDATE SET\n result_json = COALESCE(excluded.result_json, tool_calls.result_json),\n result_text = COALESCE(excluded.result_text, tool_calls.result_text),\n status = CASE\n WHEN excluded.status = 'error' THEN 'error'\n WHEN excluded.status = 'ok' THEN 'ok'\n ELSE tool_calls.status\n END,\n error_type = COALESCE(excluded.error_type, tool_calls.error_type),\n error_message = COALESCE(excluded.error_message, tool_calls.error_message),\n updated_at = now()\n ", [fallback_tool_call_id, session_id, turn_id, request_id, model_call_id, trace_id, span_id, str(span_tool_name), _to_json_text(_safe_json_parse(span_tool_arguments_raw) if span_tool_arguments_raw else None) or _to_json_text(span_tool_arguments_raw), _extract_text_content(span_tool_arguments_raw), tool_base_ts, fallback_result_json, fallback_result_text, fallback_status, call_error_type, call_error_message])
|
|
614
259
|
tool_call_inserted = True
|
|
615
|
-
|
|
616
|
-
if str(call_status or '').lower() == 'error' and not tool_call_inserted and call_error_message:
|
|
260
|
+
if str(call_status or '').lower() == 'error' and (not tool_call_inserted) and call_error_message:
|
|
617
261
|
synthetic_tool_call_id = _tool_call_id(session_id, str(trace_id), None, 0)
|
|
618
|
-
conn.execute(
|
|
619
|
-
"""
|
|
620
|
-
INSERT INTO tool_calls (
|
|
621
|
-
tool_call_id, session_id, turn_id, request_id, model_call_id,
|
|
622
|
-
trace_id, span_id, tool_name, arguments_json, arguments_text,
|
|
623
|
-
call_ts, result_json, result_text, status, duration_ms,
|
|
624
|
-
error_type, error_message, created_at, updated_at
|
|
625
|
-
)
|
|
626
|
-
VALUES (
|
|
627
|
-
?, ?, ?, ?, ?, ?, ?, ?, NULL, NULL,
|
|
628
|
-
to_timestamp(? / 1000.0), NULL, ?, 'error', NULL,
|
|
629
|
-
?, ?, now(), now()
|
|
630
|
-
)
|
|
631
|
-
ON CONFLICT(tool_call_id) DO UPDATE SET
|
|
632
|
-
status = 'error',
|
|
633
|
-
result_text = COALESCE(excluded.result_text, tool_calls.result_text),
|
|
634
|
-
error_type = COALESCE(excluded.error_type, tool_calls.error_type),
|
|
635
|
-
error_message = COALESCE(excluded.error_message, tool_calls.error_message),
|
|
636
|
-
updated_at = now()
|
|
637
|
-
""",
|
|
638
|
-
[
|
|
639
|
-
synthetic_tool_call_id,
|
|
640
|
-
session_id,
|
|
641
|
-
turn_id,
|
|
642
|
-
request_id,
|
|
643
|
-
model_call_id,
|
|
644
|
-
trace_id,
|
|
645
|
-
span_id,
|
|
646
|
-
'(tool-error)',
|
|
647
|
-
tool_base_ts,
|
|
648
|
-
call_error_message,
|
|
649
|
-
call_error_type,
|
|
650
|
-
call_error_message,
|
|
651
|
-
],
|
|
652
|
-
)
|
|
653
|
-
|
|
262
|
+
conn.execute("\n INSERT INTO tool_calls (\n tool_call_id, session_id, turn_id, request_id, model_call_id,\n trace_id, span_id, tool_name, arguments_json, arguments_text,\n call_ts, result_json, result_text, status, duration_ms,\n error_type, error_message, created_at, updated_at\n )\n VALUES (\n ?, ?, ?, ?, ?, ?, ?, ?, NULL, NULL,\n to_timestamp(? / 1000.0), NULL, ?, 'error', NULL,\n ?, ?, now(), now()\n )\n ON CONFLICT(tool_call_id) DO UPDATE SET\n status = 'error',\n result_text = COALESCE(excluded.result_text, tool_calls.result_text),\n error_type = COALESCE(excluded.error_type, tool_calls.error_type),\n error_message = COALESCE(excluded.error_message, tool_calls.error_message),\n updated_at = now()\n ", [synthetic_tool_call_id, session_id, turn_id, request_id, model_call_id, trace_id, span_id, '(tool-error)', tool_base_ts, call_error_message, call_error_type, call_error_message])
|
|
654
263
|
if span_tool_call_id:
|
|
655
|
-
tool_results_by_id[str(span_tool_call_id)] = {
|
|
656
|
-
'result_json': _to_json_text(_safe_json_parse(span_tool_result_raw) if span_tool_result_raw else None),
|
|
657
|
-
'result_text': _extract_text_content(span_tool_result_raw),
|
|
658
|
-
'status': 'error' if str(call_status or '').lower() == 'error' else 'ok',
|
|
659
|
-
'updated_at_ms': tool_base_ts,
|
|
660
|
-
}
|
|
264
|
+
tool_results_by_id[str(span_tool_call_id)] = {'result_json': _to_json_text(_safe_json_parse(span_tool_result_raw) if span_tool_result_raw else None), 'result_text': _extract_text_content(span_tool_result_raw), 'status': 'error' if str(call_status or '').lower() == 'error' else 'ok', 'updated_at_ms': tool_base_ts}
|
|
661
265
|
if tool_results_by_id[str(span_tool_call_id)]['result_json'] is None and span_tool_result_raw:
|
|
662
266
|
tool_results_by_id[str(span_tool_call_id)]['result_json'] = _to_json_text(span_tool_result_raw)
|
|
663
|
-
|
|
664
267
|
input_messages = _normalize_messages(input_raw, 'user')
|
|
665
268
|
output_messages = _normalize_messages(output_raw, 'assistant')
|
|
666
|
-
|
|
667
|
-
# tool_result blocks may appear in input messages and complete tool calls emitted in output
|
|
668
269
|
for i, msg in enumerate(input_messages):
|
|
669
270
|
content = msg.get('content', msg.get('parts', msg.get('text', '')))
|
|
670
271
|
if isinstance(content, list):
|
|
@@ -677,23 +278,13 @@ def extract_messages_and_tools(conn) -> None:
|
|
|
677
278
|
call_id = block.get('tool_use_id') or block.get('call_id') or block.get('id')
|
|
678
279
|
if not call_id:
|
|
679
280
|
continue
|
|
680
|
-
tool_results_by_id[str(call_id)] = {
|
|
681
|
-
'result_json': _to_json_text(block.get('content', block.get('result'))),
|
|
682
|
-
'result_text': _extract_text_content(block.get('content', block.get('result'))),
|
|
683
|
-
'status': 'ok',
|
|
684
|
-
'updated_at_ms': int(start_ms) + j if start_ms is not None else 0,
|
|
685
|
-
}
|
|
686
|
-
|
|
687
|
-
# Output-side tool calls
|
|
281
|
+
tool_results_by_id[str(call_id)] = {'result_json': _to_json_text(block.get('content', block.get('result'))), 'result_text': _extract_text_content(block.get('content', block.get('result'))), 'status': 'ok', 'updated_at_ms': int(start_ms) + j if start_ms is not None else 0}
|
|
688
282
|
for i, msg in enumerate(output_messages):
|
|
689
283
|
content = msg.get('content', msg.get('parts', msg.get('text', '')))
|
|
690
284
|
output_ts = int(end_ms) if end_ms is not None else int(start_ms) if start_ms is not None else 0
|
|
691
|
-
|
|
692
|
-
# Normalize tool blocks from multiple provider formats.
|
|
693
285
|
tool_blocks: list[dict[str, Any]] = []
|
|
694
286
|
if isinstance(content, list):
|
|
695
287
|
tool_blocks.extend([b for b in content if isinstance(b, dict)])
|
|
696
|
-
|
|
697
288
|
direct_tool_calls = msg.get('tool_calls')
|
|
698
289
|
if isinstance(direct_tool_calls, list):
|
|
699
290
|
for block in direct_tool_calls:
|
|
@@ -701,635 +292,108 @@ def extract_messages_and_tools(conn) -> None:
|
|
|
701
292
|
normalized = dict(block)
|
|
702
293
|
normalized.setdefault('type', 'tool_call')
|
|
703
294
|
tool_blocks.append(normalized)
|
|
704
|
-
|
|
705
295
|
function_call = msg.get('function_call')
|
|
706
296
|
if isinstance(function_call, dict):
|
|
707
297
|
tool_blocks.append({'type': 'function_call', 'function': function_call})
|
|
708
|
-
|
|
709
298
|
if not tool_blocks:
|
|
710
299
|
continue
|
|
711
|
-
|
|
712
300
|
for j, block in enumerate(tool_blocks):
|
|
713
301
|
if not isinstance(block, dict):
|
|
714
302
|
continue
|
|
715
|
-
|
|
716
303
|
block_type = str(block.get('type', '')).lower()
|
|
717
304
|
if block_type not in {'tool_use', 'tool_call', 'function_call', 'server_tool_use', 'mcp_tool_use'}:
|
|
718
305
|
continue
|
|
719
|
-
|
|
720
|
-
if block_type in {'server_tool_use', 'mcp_tool_use'} and not block.get('name'):
|
|
721
|
-
# Normalize Anthropic streaming variants that still represent tool invocation.
|
|
306
|
+
if block_type in {'server_tool_use', 'mcp_tool_use'} and (not block.get('name')):
|
|
722
307
|
block['name'] = block.get('tool_name') or block.get('toolName')
|
|
723
308
|
block.setdefault('input', block.get('arguments'))
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
}
|
|
730
|
-
|
|
731
|
-
if block_type == 'function_call' and not block.get('function') and (block.get('name') or block.get('arguments')):
|
|
732
|
-
block['function'] = {
|
|
733
|
-
'name': block.get('name'),
|
|
734
|
-
'arguments': block.get('arguments'),
|
|
735
|
-
}
|
|
736
|
-
|
|
737
|
-
if block_type == 'tool_use' and not block.get('name'):
|
|
309
|
+
if block_type == 'tool_call' and (not block.get('function')) and isinstance(block.get('name'), str):
|
|
310
|
+
block['function'] = {'name': block.get('name'), 'arguments': block.get('arguments') or block.get('input')}
|
|
311
|
+
if block_type == 'function_call' and (not block.get('function')) and (block.get('name') or block.get('arguments')):
|
|
312
|
+
block['function'] = {'name': block.get('name'), 'arguments': block.get('arguments')}
|
|
313
|
+
if block_type == 'tool_use' and (not block.get('name')):
|
|
738
314
|
block['name'] = block.get('tool_name') or block.get('toolName')
|
|
739
|
-
|
|
740
|
-
if block_type == 'tool_use' and block.get('input') is None and block.get('arguments') is not None:
|
|
315
|
+
if block_type == 'tool_use' and block.get('input') is None and (block.get('arguments') is not None):
|
|
741
316
|
block['input'] = block.get('arguments')
|
|
742
|
-
|
|
743
317
|
if block_type in {'tool_call', 'function_call'} and block.get('function') is None and block.get('name'):
|
|
744
|
-
block['function'] = {
|
|
745
|
-
'name': block.get('name'),
|
|
746
|
-
'arguments': block.get('arguments') or block.get('input'),
|
|
747
|
-
}
|
|
748
|
-
|
|
318
|
+
block['function'] = {'name': block.get('name'), 'arguments': block.get('arguments') or block.get('input')}
|
|
749
319
|
if block_type == 'function_call' and block.get('function') is None:
|
|
750
320
|
continue
|
|
751
|
-
|
|
752
|
-
if block_type == 'tool_call' and block.get('function') is None and block.get('name') is None:
|
|
321
|
+
if block_type == 'tool_call' and block.get('function') is None and (block.get('name') is None):
|
|
753
322
|
continue
|
|
754
|
-
|
|
755
|
-
if block_type in {'server_tool_use', 'mcp_tool_use'} and not block.get('name'):
|
|
323
|
+
if block_type in {'server_tool_use', 'mcp_tool_use'} and (not block.get('name')):
|
|
756
324
|
continue
|
|
757
|
-
|
|
758
|
-
if block_type == 'tool_use' and not block.get('name'):
|
|
325
|
+
if block_type == 'tool_use' and (not block.get('name')):
|
|
759
326
|
continue
|
|
760
|
-
|
|
761
|
-
if block_type == 'tool_call' and block.get('function') is None and not block.get('name'):
|
|
327
|
+
if block_type == 'tool_call' and block.get('function') is None and (not block.get('name')):
|
|
762
328
|
continue
|
|
763
|
-
|
|
764
329
|
if block_type == 'function_call' and block.get('function') is None:
|
|
765
330
|
continue
|
|
766
|
-
|
|
767
|
-
# Harmonize final type for downstream extraction.
|
|
768
331
|
if block_type in {'server_tool_use', 'mcp_tool_use'}:
|
|
769
332
|
block_type = 'tool_use'
|
|
770
|
-
|
|
771
333
|
block['type'] = block_type
|
|
772
|
-
|
|
773
|
-
|
|
774
334
|
call_id = block.get('id') or block.get('tool_call_id') or block.get('call_id')
|
|
775
|
-
tool_name = (
|
|
776
|
-
|
|
777
|
-
or (block.get('function', {}) or {}).get('name')
|
|
778
|
-
or 'unknown_tool'
|
|
779
|
-
)
|
|
780
|
-
|
|
781
|
-
args = (
|
|
782
|
-
block.get('input')
|
|
783
|
-
if block.get('input') is not None
|
|
784
|
-
else (block.get('function', {}) or {}).get('arguments')
|
|
785
|
-
)
|
|
335
|
+
tool_name = block.get('name') or (block.get('function', {}) or {}).get('name') or 'unknown_tool'
|
|
336
|
+
args = block.get('input') if block.get('input') is not None else (block.get('function', {}) or {}).get('arguments')
|
|
786
337
|
args_json = _to_json_text(args)
|
|
787
338
|
args_text = _extract_text_content(args)
|
|
788
|
-
|
|
789
339
|
tool_call_id = _tool_call_id(session_id, str(trace_id), str(call_id) if call_id else None, j)
|
|
790
340
|
tool_result = tool_results_by_id.get(str(call_id)) if call_id else None
|
|
791
|
-
|
|
792
341
|
result_json = tool_result['result_json'] if tool_result else None
|
|
793
342
|
result_text = tool_result['result_text'] if tool_result else None
|
|
794
343
|
status = tool_result['status'] if tool_result else 'called'
|
|
795
|
-
|
|
796
|
-
conn.execute(
|
|
797
|
-
"""
|
|
798
|
-
INSERT INTO tool_calls (
|
|
799
|
-
tool_call_id, session_id, turn_id, request_id, model_call_id,
|
|
800
|
-
trace_id, span_id, tool_name, arguments_json, arguments_text,
|
|
801
|
-
call_ts, result_json, result_text, status, duration_ms,
|
|
802
|
-
error_type, error_message, created_at, updated_at
|
|
803
|
-
)
|
|
804
|
-
VALUES (
|
|
805
|
-
?, ?, ?, ?, ?, ?, ?, ?, ?::JSON, ?,
|
|
806
|
-
to_timestamp(? / 1000.0), ?::JSON, ?, ?, NULL,
|
|
807
|
-
NULL, NULL, now(), now()
|
|
808
|
-
)
|
|
809
|
-
ON CONFLICT(tool_call_id) DO UPDATE SET
|
|
810
|
-
result_json = COALESCE(excluded.result_json, tool_calls.result_json),
|
|
811
|
-
result_text = COALESCE(excluded.result_text, tool_calls.result_text),
|
|
812
|
-
status = CASE
|
|
813
|
-
WHEN excluded.status = 'ok' THEN 'ok'
|
|
814
|
-
ELSE tool_calls.status
|
|
815
|
-
END,
|
|
816
|
-
updated_at = now()
|
|
817
|
-
""",
|
|
818
|
-
[
|
|
819
|
-
tool_call_id,
|
|
820
|
-
session_id,
|
|
821
|
-
turn_id,
|
|
822
|
-
request_id,
|
|
823
|
-
model_call_id,
|
|
824
|
-
trace_id,
|
|
825
|
-
span_id,
|
|
826
|
-
str(tool_name),
|
|
827
|
-
args_json,
|
|
828
|
-
args_text,
|
|
829
|
-
output_ts + j,
|
|
830
|
-
result_json,
|
|
831
|
-
result_text,
|
|
832
|
-
status,
|
|
833
|
-
],
|
|
834
|
-
)
|
|
835
|
-
|
|
344
|
+
conn.execute("\n INSERT INTO tool_calls (\n tool_call_id, session_id, turn_id, request_id, model_call_id,\n trace_id, span_id, tool_name, arguments_json, arguments_text,\n call_ts, result_json, result_text, status, duration_ms,\n error_type, error_message, created_at, updated_at\n )\n VALUES (\n ?, ?, ?, ?, ?, ?, ?, ?, ?::JSON, ?,\n to_timestamp(? / 1000.0), ?::JSON, ?, ?, NULL,\n NULL, NULL, now(), now()\n )\n ON CONFLICT(tool_call_id) DO UPDATE SET\n result_json = COALESCE(excluded.result_json, tool_calls.result_json),\n result_text = COALESCE(excluded.result_text, tool_calls.result_text),\n status = CASE\n WHEN excluded.status = 'ok' THEN 'ok'\n ELSE tool_calls.status\n END,\n updated_at = now()\n ", [tool_call_id, session_id, turn_id, request_id, model_call_id, trace_id, span_id, str(tool_name), args_json, args_text, output_ts + j, result_json, result_text, status])
|
|
836
345
|
|
|
837
346
|
def ingest_single_jsonl_file(conn, jsonl_file: Path) -> None:
|
|
838
|
-
"""Ingest one JSONL file into Silver and Gold analytics tables."""
|
|
839
347
|
escaped = str(jsonl_file).replace("'", "''")
|
|
840
|
-
|
|
841
|
-
conn.execute("DROP TABLE IF EXISTS tmp_model_calls")
|
|
842
|
-
conn.execute(
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
SELECT unnest(resourceSpans) as rs
|
|
846
|
-
FROM read_json('{escaped}', format='newline_delimited', ignore_errors=true)
|
|
847
|
-
),
|
|
848
|
-
spans_unnest AS (SELECT unnest(rs.scopeSpans) as ss FROM raw_spans),
|
|
849
|
-
flat_spans AS (SELECT unnest(ss.spans) as span FROM spans_unnest),
|
|
850
|
-
parsed AS (
|
|
851
|
-
SELECT
|
|
852
|
-
span.traceId as trace_id,
|
|
853
|
-
span.spanId as span_id,
|
|
854
|
-
span.name as span_name,
|
|
855
|
-
span.startTimeUnixNano::BIGINT / 1000000 as start_ms,
|
|
856
|
-
span.endTimeUnixNano::BIGINT / 1000000 as end_ms,
|
|
857
|
-
(span.endTimeUnixNano::BIGINT - span.startTimeUnixNano::BIGINT) / 1000000.0 as duration_ms,
|
|
858
|
-
json_extract(to_json(span), '$.attributes') as attrs_json
|
|
859
|
-
FROM flat_spans
|
|
860
|
-
),
|
|
861
|
-
attrs AS (
|
|
862
|
-
SELECT
|
|
863
|
-
trace_id,
|
|
864
|
-
span_id,
|
|
865
|
-
span_name,
|
|
866
|
-
duration_ms,
|
|
867
|
-
start_ms,
|
|
868
|
-
end_ms,
|
|
869
|
-
json_extract_string(value, '$.key') as attr_key,
|
|
870
|
-
json_extract_string(value, '$.value.stringValue') as attr_string,
|
|
871
|
-
json_extract_string(value, '$.value.intValue') as attr_int,
|
|
872
|
-
json_extract(value, '$.value.doubleValue') as attr_double
|
|
873
|
-
FROM parsed, json_each(coalesce(attrs_json, '[]'::JSON))
|
|
874
|
-
),
|
|
875
|
-
extracted AS (
|
|
876
|
-
SELECT
|
|
877
|
-
trace_id,
|
|
878
|
-
span_id,
|
|
879
|
-
span_name,
|
|
880
|
-
start_ms,
|
|
881
|
-
end_ms,
|
|
882
|
-
duration_ms,
|
|
883
|
-
max(CASE WHEN attr_key IN ('gen_ai.request.model','gen_ai.response.model','model') THEN attr_string END) as model,
|
|
884
|
-
max(CASE WHEN attr_key = 'gen_ai.cost.total_cost' THEN CAST(attr_double AS DOUBLE) END) as total_cost_usd,
|
|
885
|
-
max(CASE WHEN attr_key = 'gen_ai.usage.input_tokens' THEN CAST(attr_int AS BIGINT) END) as prompt_tokens,
|
|
886
|
-
max(CASE WHEN attr_key = 'gen_ai.usage.output_tokens' THEN CAST(attr_int AS BIGINT) END) as completion_tokens,
|
|
887
|
-
COALESCE(
|
|
888
|
-
NULLIF(regexp_extract(max(CASE WHEN attr_key = 'metadata.requester_custom_headers' THEN attr_string END), 'x-litellm-metadata-session-id'': ''([^'']+)''', 1), ''),
|
|
889
|
-
NULLIF(regexp_extract(max(CASE WHEN attr_key = 'metadata.requester_metadata' THEN attr_string END), 'session_id'': ''([^'']+)''', 1), '')
|
|
890
|
-
) as session_id,
|
|
891
|
-
COALESCE(
|
|
892
|
-
NULLIF(regexp_extract(max(CASE WHEN attr_key = 'metadata.requester_custom_headers' THEN attr_string END), 'x-litellm-metadata-user-id'': ''([^'']+)''', 1), ''),
|
|
893
|
-
NULLIF(regexp_extract(max(CASE WHEN attr_key = 'metadata.requester_metadata' THEN attr_string END), 'user_id'': ''([^'']+)''', 1), '')
|
|
894
|
-
) as user_id,
|
|
895
|
-
COALESCE(
|
|
896
|
-
NULLIF(regexp_extract(max(CASE WHEN attr_key = 'metadata.requester_custom_headers' THEN attr_string END), 'x-litellm-metadata-project-id'': ''([^'']+)''', 1), ''),
|
|
897
|
-
NULLIF(regexp_extract(max(CASE WHEN attr_key = 'metadata.requester_metadata' THEN attr_string END), 'project_id'': ''([^'']+)''', 1), '')
|
|
898
|
-
) as project_id,
|
|
899
|
-
COALESCE(
|
|
900
|
-
NULLIF(regexp_extract(max(CASE WHEN attr_key = 'metadata.requester_custom_headers' THEN attr_string END), 'x-litellm-metadata-agent-id'': ''([^'']+)''', 1), ''),
|
|
901
|
-
NULLIF(regexp_extract(max(CASE WHEN attr_key = 'metadata.requester_metadata' THEN attr_string END), 'agent_id'': ''([^'']+)''', 1), '')
|
|
902
|
-
) as agent_id,
|
|
903
|
-
COALESCE(
|
|
904
|
-
NULLIF(regexp_extract(max(CASE WHEN attr_key = 'metadata.requester_custom_headers' THEN attr_string END), 'x-litellm-metadata-agent-version'': ''([^'']+)''', 1), ''),
|
|
905
|
-
NULLIF(regexp_extract(max(CASE WHEN attr_key = 'metadata.requester_metadata' THEN attr_string END), 'agent_version'': ''([^'']+)''', 1), '')
|
|
906
|
-
) as agent_version,
|
|
907
|
-
COALESCE(
|
|
908
|
-
NULLIF(regexp_extract(max(CASE WHEN attr_key = 'metadata.requester_custom_headers' THEN attr_string END), 'x-litellm-metadata-source'': ''([^'']+)''', 1), ''),
|
|
909
|
-
NULLIF(regexp_extract(max(CASE WHEN attr_key = 'metadata.requester_metadata' THEN attr_string END), 'source'': ''([^'']+)''', 1), '')
|
|
910
|
-
) as source,
|
|
911
|
-
COALESCE(
|
|
912
|
-
NULLIF(regexp_extract(max(CASE WHEN attr_key = 'metadata.requester_custom_headers' THEN attr_string END), 'x-litellm-metadata-engine'': ''([^'']+)''', 1), ''),
|
|
913
|
-
NULLIF(regexp_extract(max(CASE WHEN attr_key = 'metadata.requester_metadata' THEN attr_string END), 'engine'': ''([^'']+)''', 1), '')
|
|
914
|
-
) as engine,
|
|
915
|
-
COALESCE(
|
|
916
|
-
NULLIF(regexp_extract(max(CASE WHEN attr_key = 'metadata.requester_custom_headers' THEN attr_string END), 'x-litellm-metadata-request-id'': ''([^'']+)''', 1), ''),
|
|
917
|
-
NULLIF(regexp_extract(max(CASE WHEN attr_key = 'metadata.requester_metadata' THEN attr_string END), 'request_id'': ''([^'']+)''', 1), '')
|
|
918
|
-
) as request_id,
|
|
919
|
-
max(CASE WHEN attr_key IN ('otel.status_code') THEN attr_string END) as otel_status,
|
|
920
|
-
max(CASE WHEN attr_key IN ('exception.type') THEN attr_string END) as error_type,
|
|
921
|
-
max(CASE WHEN attr_key IN ('exception.message') THEN attr_string END) as error_message,
|
|
922
|
-
max(CASE WHEN attr_key IN (
|
|
923
|
-
'gen_ai.tool.name',
|
|
924
|
-
'tool.name',
|
|
925
|
-
'toolName'
|
|
926
|
-
) THEN attr_string END) as span_tool_name,
|
|
927
|
-
max(CASE WHEN attr_key IN (
|
|
928
|
-
'gen_ai.tool.call.id',
|
|
929
|
-
'tool.call.id',
|
|
930
|
-
'tool_call_id',
|
|
931
|
-
'tool.use.id',
|
|
932
|
-
'tool_use_id'
|
|
933
|
-
) THEN attr_string END) as span_tool_call_id,
|
|
934
|
-
max(CASE WHEN attr_key IN (
|
|
935
|
-
'gen_ai.tool.arguments',
|
|
936
|
-
'tool.arguments',
|
|
937
|
-
'tool.input',
|
|
938
|
-
'tool.args'
|
|
939
|
-
) THEN attr_string END) as span_tool_arguments_raw,
|
|
940
|
-
max(CASE WHEN attr_key IN (
|
|
941
|
-
'gen_ai.tool.result',
|
|
942
|
-
'tool.result',
|
|
943
|
-
'tool.output'
|
|
944
|
-
) THEN attr_string END) as span_tool_result_raw,
|
|
945
|
-
max(CASE WHEN attr_key = 'gen_ai.input.messages' THEN attr_string END) as input_messages_raw,
|
|
946
|
-
max(CASE WHEN attr_key = 'gen_ai.output.messages' THEN attr_string END) as output_messages_raw
|
|
947
|
-
FROM attrs
|
|
948
|
-
GROUP BY trace_id, span_id, span_name, duration_ms, start_ms, end_ms
|
|
949
|
-
),
|
|
950
|
-
representative AS (
|
|
951
|
-
SELECT DISTINCT ON (trace_id)
|
|
952
|
-
trace_id,
|
|
953
|
-
span_id,
|
|
954
|
-
span_name,
|
|
955
|
-
session_id,
|
|
956
|
-
request_id,
|
|
957
|
-
user_id,
|
|
958
|
-
project_id,
|
|
959
|
-
agent_id,
|
|
960
|
-
agent_version,
|
|
961
|
-
source,
|
|
962
|
-
engine,
|
|
963
|
-
model,
|
|
964
|
-
start_ms,
|
|
965
|
-
end_ms,
|
|
966
|
-
duration_ms,
|
|
967
|
-
COALESCE(prompt_tokens, 0) as prompt_tokens,
|
|
968
|
-
COALESCE(completion_tokens, 0) as completion_tokens,
|
|
969
|
-
COALESCE(total_cost_usd, 0.0) as total_cost_usd,
|
|
970
|
-
error_type,
|
|
971
|
-
error_message,
|
|
972
|
-
span_tool_name,
|
|
973
|
-
span_tool_call_id,
|
|
974
|
-
span_tool_arguments_raw,
|
|
975
|
-
span_tool_result_raw,
|
|
976
|
-
input_messages_raw,
|
|
977
|
-
output_messages_raw
|
|
978
|
-
FROM extracted
|
|
979
|
-
ORDER BY
|
|
980
|
-
trace_id,
|
|
981
|
-
CASE WHEN output_messages_raw IS NOT NULL AND output_messages_raw != '' THEN 0 ELSE 1 END,
|
|
982
|
-
CASE WHEN input_messages_raw IS NOT NULL AND input_messages_raw != '' THEN 0 ELSE 1 END,
|
|
983
|
-
COALESCE(total_cost_usd, 0) DESC,
|
|
984
|
-
CASE
|
|
985
|
-
WHEN span_name = 'raw_gen_ai_request' THEN 0
|
|
986
|
-
WHEN span_name = 'litellm_request' THEN 1
|
|
987
|
-
ELSE 2
|
|
988
|
-
END,
|
|
989
|
-
start_ms DESC
|
|
990
|
-
),
|
|
991
|
-
merged AS (
|
|
992
|
-
SELECT
|
|
993
|
-
e.trace_id,
|
|
994
|
-
r.span_id,
|
|
995
|
-
r.span_name,
|
|
996
|
-
COALESCE(MAX(CASE WHEN e.session_id IS NOT NULL AND e.session_id != '' THEN e.session_id END), r.session_id) AS session_id,
|
|
997
|
-
COALESCE(MAX(CASE WHEN e.request_id IS NOT NULL AND e.request_id != '' THEN e.request_id END), r.request_id) AS request_id,
|
|
998
|
-
COALESCE(MAX(CASE WHEN e.user_id IS NOT NULL AND e.user_id != '' THEN e.user_id END), r.user_id) AS user_id,
|
|
999
|
-
COALESCE(MAX(CASE WHEN e.project_id IS NOT NULL AND e.project_id != '' THEN e.project_id END), r.project_id) AS project_id,
|
|
1000
|
-
COALESCE(MAX(CASE WHEN e.agent_id IS NOT NULL AND e.agent_id != '' THEN e.agent_id END), r.agent_id) AS agent_id,
|
|
1001
|
-
COALESCE(MAX(CASE WHEN e.agent_version IS NOT NULL AND e.agent_version != '' THEN e.agent_version END), r.agent_version) AS agent_version,
|
|
1002
|
-
COALESCE(MAX(CASE WHEN e.source IS NOT NULL AND e.source != '' THEN e.source END), r.source) AS source,
|
|
1003
|
-
COALESCE(MAX(CASE WHEN e.engine IS NOT NULL AND e.engine != '' THEN e.engine END), r.engine) AS engine,
|
|
1004
|
-
COALESCE(MAX(CASE WHEN e.model IS NOT NULL AND e.model != '' THEN e.model END), r.model) AS model,
|
|
1005
|
-
MIN(e.start_ms) AS start_ms,
|
|
1006
|
-
MAX(e.end_ms) AS end_ms,
|
|
1007
|
-
MAX(e.duration_ms) AS duration_ms,
|
|
1008
|
-
COALESCE(MAX(e.prompt_tokens), r.prompt_tokens, 0) AS prompt_tokens,
|
|
1009
|
-
COALESCE(MAX(e.completion_tokens), r.completion_tokens, 0) AS completion_tokens,
|
|
1010
|
-
COALESCE(MAX(e.total_cost_usd), r.total_cost_usd, 0.0) AS total_cost_usd,
|
|
1011
|
-
CASE
|
|
1012
|
-
WHEN SUM(CASE WHEN lower(coalesce(e.otel_status, '')) = 'error' OR e.error_type IS NOT NULL OR e.error_message IS NOT NULL THEN 1 ELSE 0 END) > 0 THEN 'error'
|
|
1013
|
-
ELSE 'ok'
|
|
1014
|
-
END AS status,
|
|
1015
|
-
COALESCE(MAX(CASE WHEN e.error_type IS NOT NULL AND e.error_type != '' THEN e.error_type END), r.error_type) AS error_type,
|
|
1016
|
-
COALESCE(MAX(CASE WHEN e.error_message IS NOT NULL AND e.error_message != '' THEN e.error_message END), r.error_message) AS error_message,
|
|
1017
|
-
COALESCE(MAX(CASE WHEN e.span_tool_name IS NOT NULL AND e.span_tool_name != '' THEN e.span_tool_name END), r.span_tool_name) AS span_tool_name,
|
|
1018
|
-
COALESCE(MAX(CASE WHEN e.span_tool_call_id IS NOT NULL AND e.span_tool_call_id != '' THEN e.span_tool_call_id END), r.span_tool_call_id) AS span_tool_call_id,
|
|
1019
|
-
COALESCE(MAX(CASE WHEN e.span_tool_arguments_raw IS NOT NULL AND e.span_tool_arguments_raw != '' THEN e.span_tool_arguments_raw END), r.span_tool_arguments_raw) AS span_tool_arguments_raw,
|
|
1020
|
-
COALESCE(MAX(CASE WHEN e.span_tool_result_raw IS NOT NULL AND e.span_tool_result_raw != '' THEN e.span_tool_result_raw END), r.span_tool_result_raw) AS span_tool_result_raw,
|
|
1021
|
-
COALESCE(MAX(CASE WHEN e.input_messages_raw IS NOT NULL AND e.input_messages_raw != '' THEN e.input_messages_raw END), r.input_messages_raw) AS input_messages_raw,
|
|
1022
|
-
COALESCE(MAX(CASE WHEN e.output_messages_raw IS NOT NULL AND e.output_messages_raw != '' THEN e.output_messages_raw END), r.output_messages_raw) AS output_messages_raw
|
|
1023
|
-
FROM extracted e
|
|
1024
|
-
JOIN representative r ON r.trace_id = e.trace_id
|
|
1025
|
-
GROUP BY e.trace_id, r.span_id, r.span_name, r.session_id, r.request_id, r.user_id, r.project_id, r.agent_id, r.agent_version, r.source, r.engine, r.model, r.prompt_tokens, r.completion_tokens, r.total_cost_usd, r.error_type, r.error_message, r.span_tool_name, r.span_tool_call_id, r.span_tool_arguments_raw, r.span_tool_result_raw, r.input_messages_raw, r.output_messages_raw
|
|
1026
|
-
)
|
|
1027
|
-
SELECT
|
|
1028
|
-
trace_id as model_call_id,
|
|
1029
|
-
trace_id,
|
|
1030
|
-
span_id,
|
|
1031
|
-
span_name,
|
|
1032
|
-
session_id,
|
|
1033
|
-
CASE
|
|
1034
|
-
WHEN session_id IS NOT NULL AND request_id IS NOT NULL THEN session_id || ':' || request_id
|
|
1035
|
-
ELSE trace_id
|
|
1036
|
-
END as turn_id,
|
|
1037
|
-
request_id,
|
|
1038
|
-
user_id,
|
|
1039
|
-
project_id,
|
|
1040
|
-
agent_id,
|
|
1041
|
-
agent_version,
|
|
1042
|
-
source,
|
|
1043
|
-
engine,
|
|
1044
|
-
model,
|
|
1045
|
-
start_ms,
|
|
1046
|
-
end_ms,
|
|
1047
|
-
duration_ms,
|
|
1048
|
-
prompt_tokens,
|
|
1049
|
-
completion_tokens,
|
|
1050
|
-
COALESCE(prompt_tokens, 0) + COALESCE(completion_tokens, 0) as total_tokens,
|
|
1051
|
-
total_cost_usd,
|
|
1052
|
-
status,
|
|
1053
|
-
error_type,
|
|
1054
|
-
error_message,
|
|
1055
|
-
span_tool_name,
|
|
1056
|
-
span_tool_call_id,
|
|
1057
|
-
span_tool_arguments_raw,
|
|
1058
|
-
span_tool_result_raw,
|
|
1059
|
-
input_messages_raw,
|
|
1060
|
-
output_messages_raw
|
|
1061
|
-
FROM merged
|
|
1062
|
-
""")
|
|
1063
|
-
|
|
1064
|
-
# Silver: model calls
|
|
1065
|
-
conn.execute("""
|
|
1066
|
-
INSERT INTO model_calls (
|
|
1067
|
-
model_call_id, trace_id, span_id, span_name, session_id, turn_id, request_id,
|
|
1068
|
-
user_id, project_id, agent_id, agent_version, source, engine, model,
|
|
1069
|
-
start_ms, end_ms, duration_ms, prompt_tokens, completion_tokens, total_tokens,
|
|
1070
|
-
total_cost_usd, status, error_type, error_message,
|
|
1071
|
-
input_messages_raw, output_messages_raw, created_at, updated_at
|
|
1072
|
-
)
|
|
1073
|
-
SELECT
|
|
1074
|
-
model_call_id, trace_id, span_id, span_name, session_id, turn_id, request_id,
|
|
1075
|
-
user_id, project_id, agent_id, agent_version, source, engine, model,
|
|
1076
|
-
start_ms, end_ms, duration_ms, prompt_tokens, completion_tokens, total_tokens,
|
|
1077
|
-
total_cost_usd, status, error_type, error_message,
|
|
1078
|
-
input_messages_raw, output_messages_raw, now(), now()
|
|
1079
|
-
FROM tmp_model_calls
|
|
1080
|
-
ON CONFLICT(model_call_id) DO UPDATE SET
|
|
1081
|
-
span_id = COALESCE(excluded.span_id, model_calls.span_id),
|
|
1082
|
-
span_name = COALESCE(excluded.span_name, model_calls.span_name),
|
|
1083
|
-
session_id = COALESCE(excluded.session_id, model_calls.session_id),
|
|
1084
|
-
turn_id = COALESCE(excluded.turn_id, model_calls.turn_id),
|
|
1085
|
-
request_id = COALESCE(excluded.request_id, model_calls.request_id),
|
|
1086
|
-
user_id = COALESCE(excluded.user_id, model_calls.user_id),
|
|
1087
|
-
project_id = COALESCE(excluded.project_id, model_calls.project_id),
|
|
1088
|
-
agent_id = COALESCE(excluded.agent_id, model_calls.agent_id),
|
|
1089
|
-
agent_version = COALESCE(excluded.agent_version, model_calls.agent_version),
|
|
1090
|
-
source = COALESCE(excluded.source, model_calls.source),
|
|
1091
|
-
engine = COALESCE(excluded.engine, model_calls.engine),
|
|
1092
|
-
model = COALESCE(excluded.model, model_calls.model),
|
|
1093
|
-
start_ms = COALESCE(excluded.start_ms, model_calls.start_ms),
|
|
1094
|
-
end_ms = COALESCE(excluded.end_ms, model_calls.end_ms),
|
|
1095
|
-
duration_ms = COALESCE(excluded.duration_ms, model_calls.duration_ms),
|
|
1096
|
-
prompt_tokens = COALESCE(excluded.prompt_tokens, model_calls.prompt_tokens),
|
|
1097
|
-
completion_tokens = COALESCE(excluded.completion_tokens, model_calls.completion_tokens),
|
|
1098
|
-
total_tokens = COALESCE(excluded.total_tokens, model_calls.total_tokens),
|
|
1099
|
-
total_cost_usd = COALESCE(excluded.total_cost_usd, model_calls.total_cost_usd),
|
|
1100
|
-
status = COALESCE(excluded.status, model_calls.status),
|
|
1101
|
-
error_type = COALESCE(excluded.error_type, model_calls.error_type),
|
|
1102
|
-
error_message = COALESCE(excluded.error_message, model_calls.error_message),
|
|
1103
|
-
input_messages_raw = COALESCE(excluded.input_messages_raw, model_calls.input_messages_raw),
|
|
1104
|
-
output_messages_raw = COALESCE(excluded.output_messages_raw, model_calls.output_messages_raw),
|
|
1105
|
-
updated_at = now()
|
|
1106
|
-
""")
|
|
1107
|
-
|
|
1108
|
-
# Gold summary table used by existing dashboards
|
|
1109
|
-
conn.execute("""
|
|
1110
|
-
INSERT INTO spans (
|
|
1111
|
-
trace_id, span_id, span_name, model, cost, input_tokens, output_tokens,
|
|
1112
|
-
session_id, user_id, project_id, agent_id, engine, duration_ms, start_ms, created_at
|
|
1113
|
-
)
|
|
1114
|
-
SELECT
|
|
1115
|
-
trace_id,
|
|
1116
|
-
span_id,
|
|
1117
|
-
span_name,
|
|
1118
|
-
model,
|
|
1119
|
-
total_cost_usd,
|
|
1120
|
-
prompt_tokens,
|
|
1121
|
-
completion_tokens,
|
|
1122
|
-
session_id,
|
|
1123
|
-
user_id,
|
|
1124
|
-
project_id,
|
|
1125
|
-
agent_id,
|
|
1126
|
-
engine,
|
|
1127
|
-
duration_ms,
|
|
1128
|
-
start_ms,
|
|
1129
|
-
now()
|
|
1130
|
-
FROM tmp_model_calls
|
|
1131
|
-
ON CONFLICT(trace_id, span_id) DO UPDATE SET
|
|
1132
|
-
span_name = COALESCE(excluded.span_name, spans.span_name),
|
|
1133
|
-
model = COALESCE(excluded.model, spans.model),
|
|
1134
|
-
cost = COALESCE(excluded.cost, spans.cost),
|
|
1135
|
-
input_tokens = COALESCE(excluded.input_tokens, spans.input_tokens),
|
|
1136
|
-
output_tokens = COALESCE(excluded.output_tokens, spans.output_tokens),
|
|
1137
|
-
session_id = COALESCE(excluded.session_id, spans.session_id),
|
|
1138
|
-
user_id = COALESCE(excluded.user_id, spans.user_id),
|
|
1139
|
-
project_id = COALESCE(excluded.project_id, spans.project_id),
|
|
1140
|
-
agent_id = COALESCE(excluded.agent_id, spans.agent_id),
|
|
1141
|
-
engine = COALESCE(excluded.engine, spans.engine),
|
|
1142
|
-
duration_ms = COALESCE(excluded.duration_ms, spans.duration_ms),
|
|
1143
|
-
start_ms = COALESCE(excluded.start_ms, spans.start_ms)
|
|
1144
|
-
""")
|
|
1145
|
-
|
|
1146
|
-
# Turns from model calls
|
|
1147
|
-
conn.execute("""
|
|
1148
|
-
INSERT INTO turns (
|
|
1149
|
-
turn_id, session_id, request_id, turn_index, started_at, ended_at, status,
|
|
1150
|
-
model, provider, prompt_tokens, completion_tokens, total_tokens, cost_usd,
|
|
1151
|
-
trace_id, created_at, updated_at
|
|
1152
|
-
)
|
|
1153
|
-
SELECT
|
|
1154
|
-
turn_id,
|
|
1155
|
-
session_id,
|
|
1156
|
-
request_id,
|
|
1157
|
-
NULL,
|
|
1158
|
-
to_timestamp(start_ms / 1000.0),
|
|
1159
|
-
to_timestamp(end_ms / 1000.0),
|
|
1160
|
-
status,
|
|
1161
|
-
model,
|
|
1162
|
-
engine,
|
|
1163
|
-
prompt_tokens,
|
|
1164
|
-
completion_tokens,
|
|
1165
|
-
total_tokens,
|
|
1166
|
-
total_cost_usd,
|
|
1167
|
-
trace_id,
|
|
1168
|
-
now(),
|
|
1169
|
-
now()
|
|
1170
|
-
FROM tmp_model_calls
|
|
1171
|
-
ON CONFLICT(turn_id) DO UPDATE SET
|
|
1172
|
-
session_id = excluded.session_id,
|
|
1173
|
-
request_id = excluded.request_id,
|
|
1174
|
-
started_at = LEAST(turns.started_at, excluded.started_at),
|
|
1175
|
-
ended_at = GREATEST(turns.ended_at, excluded.ended_at),
|
|
1176
|
-
status = CASE WHEN excluded.status = 'error' THEN 'error' ELSE turns.status END,
|
|
1177
|
-
model = excluded.model,
|
|
1178
|
-
provider = excluded.provider,
|
|
1179
|
-
prompt_tokens = COALESCE(excluded.prompt_tokens, turns.prompt_tokens),
|
|
1180
|
-
completion_tokens = COALESCE(excluded.completion_tokens, turns.completion_tokens),
|
|
1181
|
-
total_tokens = COALESCE(excluded.total_tokens, turns.total_tokens),
|
|
1182
|
-
cost_usd = COALESCE(excluded.cost_usd, turns.cost_usd),
|
|
1183
|
-
trace_id = excluded.trace_id,
|
|
1184
|
-
updated_at = now()
|
|
1185
|
-
""")
|
|
1186
|
-
|
|
1187
|
-
# Parse structured tool calls from raw message payloads.
|
|
348
|
+
conn.execute('DROP TABLE IF EXISTS tmp_model_calls')
|
|
349
|
+
conn.execute(f"\n CREATE TEMP TABLE tmp_model_calls AS\n WITH raw_spans AS (\n SELECT unnest(resourceSpans) as rs\n FROM read_json('{escaped}', format='newline_delimited', ignore_errors=true)\n ),\n spans_unnest AS (SELECT unnest(rs.scopeSpans) as ss FROM raw_spans),\n flat_spans AS (SELECT unnest(ss.spans) as span FROM spans_unnest),\n parsed AS (\n SELECT\n span.traceId as trace_id,\n span.spanId as span_id,\n span.name as span_name,\n span.startTimeUnixNano::BIGINT / 1000000 as start_ms,\n span.endTimeUnixNano::BIGINT / 1000000 as end_ms,\n (span.endTimeUnixNano::BIGINT - span.startTimeUnixNano::BIGINT) / 1000000.0 as duration_ms,\n json_extract(to_json(span), '$.attributes') as attrs_json\n FROM flat_spans\n ),\n attrs AS (\n SELECT\n trace_id,\n span_id,\n span_name,\n duration_ms,\n start_ms,\n end_ms,\n json_extract_string(value, '$.key') as attr_key,\n json_extract_string(value, '$.value.stringValue') as attr_string,\n json_extract_string(value, '$.value.intValue') as attr_int,\n json_extract(value, '$.value.doubleValue') as attr_double\n FROM parsed, json_each(coalesce(attrs_json, '[]'::JSON))\n ),\n extracted AS (\n SELECT\n trace_id,\n span_id,\n span_name,\n start_ms,\n end_ms,\n duration_ms,\n max(CASE WHEN attr_key IN ('gen_ai.request.model','gen_ai.response.model','model') THEN attr_string END) as model,\n max(CASE WHEN attr_key = 'gen_ai.cost.total_cost' THEN CAST(attr_double AS DOUBLE) END) as total_cost_usd,\n max(CASE WHEN attr_key = 'gen_ai.usage.input_tokens' THEN CAST(attr_int AS BIGINT) END) as prompt_tokens,\n max(CASE WHEN attr_key = 'gen_ai.usage.output_tokens' THEN CAST(attr_int AS BIGINT) END) as completion_tokens,\n COALESCE(\n NULLIF(regexp_extract(max(CASE WHEN attr_key = 'metadata.requester_custom_headers' THEN attr_string END), 'x-litellm-metadata-session-id'': ''([^'']+)''', 1), ''),\n NULLIF(regexp_extract(max(CASE WHEN attr_key = 'metadata.requester_metadata' THEN attr_string END), 'session_id'': ''([^'']+)''', 1), '')\n ) as session_id,\n COALESCE(\n NULLIF(regexp_extract(max(CASE WHEN attr_key = 'metadata.requester_custom_headers' THEN attr_string END), 'x-litellm-metadata-user-id'': ''([^'']+)''', 1), ''),\n NULLIF(regexp_extract(max(CASE WHEN attr_key = 'metadata.requester_metadata' THEN attr_string END), 'user_id'': ''([^'']+)''', 1), '')\n ) as user_id,\n COALESCE(\n NULLIF(regexp_extract(max(CASE WHEN attr_key = 'metadata.requester_custom_headers' THEN attr_string END), 'x-litellm-metadata-project-id'': ''([^'']+)''', 1), ''),\n NULLIF(regexp_extract(max(CASE WHEN attr_key = 'metadata.requester_metadata' THEN attr_string END), 'project_id'': ''([^'']+)''', 1), '')\n ) as project_id,\n COALESCE(\n NULLIF(regexp_extract(max(CASE WHEN attr_key = 'metadata.requester_custom_headers' THEN attr_string END), 'x-litellm-metadata-agent-id'': ''([^'']+)''', 1), ''),\n NULLIF(regexp_extract(max(CASE WHEN attr_key = 'metadata.requester_metadata' THEN attr_string END), 'agent_id'': ''([^'']+)''', 1), '')\n ) as agent_id,\n COALESCE(\n NULLIF(regexp_extract(max(CASE WHEN attr_key = 'metadata.requester_custom_headers' THEN attr_string END), 'x-litellm-metadata-agent-version'': ''([^'']+)''', 1), ''),\n NULLIF(regexp_extract(max(CASE WHEN attr_key = 'metadata.requester_metadata' THEN attr_string END), 'agent_version'': ''([^'']+)''', 1), '')\n ) as agent_version,\n COALESCE(\n NULLIF(regexp_extract(max(CASE WHEN attr_key = 'metadata.requester_custom_headers' THEN attr_string END), 'x-litellm-metadata-source'': ''([^'']+)''', 1), ''),\n NULLIF(regexp_extract(max(CASE WHEN attr_key = 'metadata.requester_metadata' THEN attr_string END), 'source'': ''([^'']+)''', 1), '')\n ) as source,\n COALESCE(\n NULLIF(regexp_extract(max(CASE WHEN attr_key = 'metadata.requester_custom_headers' THEN attr_string END), 'x-litellm-metadata-engine'': ''([^'']+)''', 1), ''),\n NULLIF(regexp_extract(max(CASE WHEN attr_key = 'metadata.requester_metadata' THEN attr_string END), 'engine'': ''([^'']+)''', 1), '')\n ) as engine,\n COALESCE(\n NULLIF(regexp_extract(max(CASE WHEN attr_key = 'metadata.requester_custom_headers' THEN attr_string END), 'x-litellm-metadata-request-id'': ''([^'']+)''', 1), ''),\n NULLIF(regexp_extract(max(CASE WHEN attr_key = 'metadata.requester_metadata' THEN attr_string END), 'request_id'': ''([^'']+)''', 1), '')\n ) as request_id,\n max(CASE WHEN attr_key IN ('otel.status_code') THEN attr_string END) as otel_status,\n max(CASE WHEN attr_key IN ('exception.type') THEN attr_string END) as error_type,\n max(CASE WHEN attr_key IN ('exception.message') THEN attr_string END) as error_message,\n max(CASE WHEN attr_key IN (\n 'gen_ai.tool.name',\n 'tool.name',\n 'toolName'\n ) THEN attr_string END) as span_tool_name,\n max(CASE WHEN attr_key IN (\n 'gen_ai.tool.call.id',\n 'tool.call.id',\n 'tool_call_id',\n 'tool.use.id',\n 'tool_use_id'\n ) THEN attr_string END) as span_tool_call_id,\n max(CASE WHEN attr_key IN (\n 'gen_ai.tool.arguments',\n 'tool.arguments',\n 'tool.input',\n 'tool.args'\n ) THEN attr_string END) as span_tool_arguments_raw,\n max(CASE WHEN attr_key IN (\n 'gen_ai.tool.result',\n 'tool.result',\n 'tool.output'\n ) THEN attr_string END) as span_tool_result_raw,\n max(CASE WHEN attr_key = 'gen_ai.input.messages' THEN attr_string END) as input_messages_raw,\n max(CASE WHEN attr_key = 'gen_ai.output.messages' THEN attr_string END) as output_messages_raw\n FROM attrs\n GROUP BY trace_id, span_id, span_name, duration_ms, start_ms, end_ms\n ),\n representative AS (\n SELECT DISTINCT ON (trace_id)\n trace_id,\n span_id,\n span_name,\n session_id,\n request_id,\n user_id,\n project_id,\n agent_id,\n agent_version,\n source,\n engine,\n model,\n start_ms,\n end_ms,\n duration_ms,\n COALESCE(prompt_tokens, 0) as prompt_tokens,\n COALESCE(completion_tokens, 0) as completion_tokens,\n COALESCE(total_cost_usd, 0.0) as total_cost_usd,\n error_type,\n error_message,\n span_tool_name,\n span_tool_call_id,\n span_tool_arguments_raw,\n span_tool_result_raw,\n input_messages_raw,\n output_messages_raw\n FROM extracted\n ORDER BY\n trace_id,\n CASE WHEN output_messages_raw IS NOT NULL AND output_messages_raw != '' THEN 0 ELSE 1 END,\n CASE WHEN input_messages_raw IS NOT NULL AND input_messages_raw != '' THEN 0 ELSE 1 END,\n COALESCE(total_cost_usd, 0) DESC,\n CASE\n WHEN span_name = 'raw_gen_ai_request' THEN 0\n WHEN span_name = 'litellm_request' THEN 1\n ELSE 2\n END,\n start_ms DESC\n ),\n merged AS (\n SELECT\n e.trace_id,\n r.span_id,\n r.span_name,\n COALESCE(MAX(CASE WHEN e.session_id IS NOT NULL AND e.session_id != '' THEN e.session_id END), r.session_id) AS session_id,\n COALESCE(MAX(CASE WHEN e.request_id IS NOT NULL AND e.request_id != '' THEN e.request_id END), r.request_id) AS request_id,\n COALESCE(MAX(CASE WHEN e.user_id IS NOT NULL AND e.user_id != '' THEN e.user_id END), r.user_id) AS user_id,\n COALESCE(MAX(CASE WHEN e.project_id IS NOT NULL AND e.project_id != '' THEN e.project_id END), r.project_id) AS project_id,\n COALESCE(MAX(CASE WHEN e.agent_id IS NOT NULL AND e.agent_id != '' THEN e.agent_id END), r.agent_id) AS agent_id,\n COALESCE(MAX(CASE WHEN e.agent_version IS NOT NULL AND e.agent_version != '' THEN e.agent_version END), r.agent_version) AS agent_version,\n COALESCE(MAX(CASE WHEN e.source IS NOT NULL AND e.source != '' THEN e.source END), r.source) AS source,\n COALESCE(MAX(CASE WHEN e.engine IS NOT NULL AND e.engine != '' THEN e.engine END), r.engine) AS engine,\n COALESCE(MAX(CASE WHEN e.model IS NOT NULL AND e.model != '' THEN e.model END), r.model) AS model,\n MIN(e.start_ms) AS start_ms,\n MAX(e.end_ms) AS end_ms,\n MAX(e.duration_ms) AS duration_ms,\n COALESCE(MAX(e.prompt_tokens), r.prompt_tokens, 0) AS prompt_tokens,\n COALESCE(MAX(e.completion_tokens), r.completion_tokens, 0) AS completion_tokens,\n COALESCE(MAX(e.total_cost_usd), r.total_cost_usd, 0.0) AS total_cost_usd,\n CASE\n WHEN SUM(CASE WHEN lower(coalesce(e.otel_status, '')) = 'error' OR e.error_type IS NOT NULL OR e.error_message IS NOT NULL THEN 1 ELSE 0 END) > 0 THEN 'error'\n ELSE 'ok'\n END AS status,\n COALESCE(MAX(CASE WHEN e.error_type IS NOT NULL AND e.error_type != '' THEN e.error_type END), r.error_type) AS error_type,\n COALESCE(MAX(CASE WHEN e.error_message IS NOT NULL AND e.error_message != '' THEN e.error_message END), r.error_message) AS error_message,\n COALESCE(MAX(CASE WHEN e.span_tool_name IS NOT NULL AND e.span_tool_name != '' THEN e.span_tool_name END), r.span_tool_name) AS span_tool_name,\n COALESCE(MAX(CASE WHEN e.span_tool_call_id IS NOT NULL AND e.span_tool_call_id != '' THEN e.span_tool_call_id END), r.span_tool_call_id) AS span_tool_call_id,\n COALESCE(MAX(CASE WHEN e.span_tool_arguments_raw IS NOT NULL AND e.span_tool_arguments_raw != '' THEN e.span_tool_arguments_raw END), r.span_tool_arguments_raw) AS span_tool_arguments_raw,\n COALESCE(MAX(CASE WHEN e.span_tool_result_raw IS NOT NULL AND e.span_tool_result_raw != '' THEN e.span_tool_result_raw END), r.span_tool_result_raw) AS span_tool_result_raw,\n COALESCE(MAX(CASE WHEN e.input_messages_raw IS NOT NULL AND e.input_messages_raw != '' THEN e.input_messages_raw END), r.input_messages_raw) AS input_messages_raw,\n COALESCE(MAX(CASE WHEN e.output_messages_raw IS NOT NULL AND e.output_messages_raw != '' THEN e.output_messages_raw END), r.output_messages_raw) AS output_messages_raw\n FROM extracted e\n JOIN representative r ON r.trace_id = e.trace_id\n GROUP BY e.trace_id, r.span_id, r.span_name, r.session_id, r.request_id, r.user_id, r.project_id, r.agent_id, r.agent_version, r.source, r.engine, r.model, r.prompt_tokens, r.completion_tokens, r.total_cost_usd, r.error_type, r.error_message, r.span_tool_name, r.span_tool_call_id, r.span_tool_arguments_raw, r.span_tool_result_raw, r.input_messages_raw, r.output_messages_raw\n )\n SELECT\n trace_id as model_call_id,\n trace_id,\n span_id,\n span_name,\n session_id,\n CASE\n WHEN session_id IS NOT NULL AND request_id IS NOT NULL THEN session_id || ':' || request_id\n ELSE trace_id\n END as turn_id,\n request_id,\n user_id,\n project_id,\n agent_id,\n agent_version,\n source,\n engine,\n model,\n start_ms,\n end_ms,\n duration_ms,\n prompt_tokens,\n completion_tokens,\n COALESCE(prompt_tokens, 0) + COALESCE(completion_tokens, 0) as total_tokens,\n total_cost_usd,\n status,\n error_type,\n error_message,\n span_tool_name,\n span_tool_call_id,\n span_tool_arguments_raw,\n span_tool_result_raw,\n input_messages_raw,\n output_messages_raw\n FROM merged\n ")
|
|
350
|
+
conn.execute('\n INSERT INTO model_calls (\n model_call_id, trace_id, span_id, span_name, session_id, turn_id, request_id,\n user_id, project_id, agent_id, agent_version, source, engine, model,\n start_ms, end_ms, duration_ms, prompt_tokens, completion_tokens, total_tokens,\n total_cost_usd, status, error_type, error_message,\n input_messages_raw, output_messages_raw, created_at, updated_at\n )\n SELECT\n model_call_id, trace_id, span_id, span_name, session_id, turn_id, request_id,\n user_id, project_id, agent_id, agent_version, source, engine, model,\n start_ms, end_ms, duration_ms, prompt_tokens, completion_tokens, total_tokens,\n total_cost_usd, status, error_type, error_message,\n input_messages_raw, output_messages_raw, now(), now()\n FROM tmp_model_calls\n ON CONFLICT(model_call_id) DO UPDATE SET\n span_id = COALESCE(excluded.span_id, model_calls.span_id),\n span_name = COALESCE(excluded.span_name, model_calls.span_name),\n session_id = COALESCE(excluded.session_id, model_calls.session_id),\n turn_id = COALESCE(excluded.turn_id, model_calls.turn_id),\n request_id = COALESCE(excluded.request_id, model_calls.request_id),\n user_id = COALESCE(excluded.user_id, model_calls.user_id),\n project_id = COALESCE(excluded.project_id, model_calls.project_id),\n agent_id = COALESCE(excluded.agent_id, model_calls.agent_id),\n agent_version = COALESCE(excluded.agent_version, model_calls.agent_version),\n source = COALESCE(excluded.source, model_calls.source),\n engine = COALESCE(excluded.engine, model_calls.engine),\n model = COALESCE(excluded.model, model_calls.model),\n start_ms = COALESCE(excluded.start_ms, model_calls.start_ms),\n end_ms = COALESCE(excluded.end_ms, model_calls.end_ms),\n duration_ms = COALESCE(excluded.duration_ms, model_calls.duration_ms),\n prompt_tokens = COALESCE(excluded.prompt_tokens, model_calls.prompt_tokens),\n completion_tokens = COALESCE(excluded.completion_tokens, model_calls.completion_tokens),\n total_tokens = COALESCE(excluded.total_tokens, model_calls.total_tokens),\n total_cost_usd = COALESCE(excluded.total_cost_usd, model_calls.total_cost_usd),\n status = COALESCE(excluded.status, model_calls.status),\n error_type = COALESCE(excluded.error_type, model_calls.error_type),\n error_message = COALESCE(excluded.error_message, model_calls.error_message),\n input_messages_raw = COALESCE(excluded.input_messages_raw, model_calls.input_messages_raw),\n output_messages_raw = COALESCE(excluded.output_messages_raw, model_calls.output_messages_raw),\n updated_at = now()\n ')
|
|
351
|
+
conn.execute('\n INSERT INTO spans (\n trace_id, span_id, span_name, model, cost, input_tokens, output_tokens,\n session_id, user_id, project_id, agent_id, engine, duration_ms, start_ms, created_at\n )\n SELECT\n trace_id,\n span_id,\n span_name,\n model,\n total_cost_usd,\n prompt_tokens,\n completion_tokens,\n session_id,\n user_id,\n project_id,\n agent_id,\n engine,\n duration_ms,\n start_ms,\n now()\n FROM tmp_model_calls\n ON CONFLICT(trace_id, span_id) DO UPDATE SET\n span_name = COALESCE(excluded.span_name, spans.span_name),\n model = COALESCE(excluded.model, spans.model),\n cost = COALESCE(excluded.cost, spans.cost),\n input_tokens = COALESCE(excluded.input_tokens, spans.input_tokens),\n output_tokens = COALESCE(excluded.output_tokens, spans.output_tokens),\n session_id = COALESCE(excluded.session_id, spans.session_id),\n user_id = COALESCE(excluded.user_id, spans.user_id),\n project_id = COALESCE(excluded.project_id, spans.project_id),\n agent_id = COALESCE(excluded.agent_id, spans.agent_id),\n engine = COALESCE(excluded.engine, spans.engine),\n duration_ms = COALESCE(excluded.duration_ms, spans.duration_ms),\n start_ms = COALESCE(excluded.start_ms, spans.start_ms)\n ')
|
|
352
|
+
conn.execute("\n INSERT INTO turns (\n turn_id, session_id, request_id, turn_index, started_at, ended_at, status,\n model, provider, prompt_tokens, completion_tokens, total_tokens, cost_usd,\n trace_id, created_at, updated_at\n )\n SELECT\n turn_id,\n session_id,\n request_id,\n NULL,\n to_timestamp(start_ms / 1000.0),\n to_timestamp(end_ms / 1000.0),\n status,\n model,\n engine,\n prompt_tokens,\n completion_tokens,\n total_tokens,\n total_cost_usd,\n trace_id,\n now(),\n now()\n FROM tmp_model_calls\n ON CONFLICT(turn_id) DO UPDATE SET\n session_id = excluded.session_id,\n request_id = excluded.request_id,\n started_at = LEAST(turns.started_at, excluded.started_at),\n ended_at = GREATEST(turns.ended_at, excluded.ended_at),\n status = CASE WHEN excluded.status = 'error' THEN 'error' ELSE turns.status END,\n model = excluded.model,\n provider = excluded.provider,\n prompt_tokens = COALESCE(excluded.prompt_tokens, turns.prompt_tokens),\n completion_tokens = COALESCE(excluded.completion_tokens, turns.completion_tokens),\n total_tokens = COALESCE(excluded.total_tokens, turns.total_tokens),\n cost_usd = COALESCE(excluded.cost_usd, turns.cost_usd),\n trace_id = excluded.trace_id,\n updated_at = now()\n ")
|
|
1188
353
|
extract_messages_and_tools(conn)
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
conn.execute("
|
|
1192
|
-
conn.execute(
|
|
1193
|
-
CREATE TEMP TABLE tmp_touched_sessions AS
|
|
1194
|
-
SELECT DISTINCT session_id
|
|
1195
|
-
FROM tmp_model_calls
|
|
1196
|
-
WHERE session_id IS NOT NULL AND session_id != ''
|
|
1197
|
-
""")
|
|
1198
|
-
|
|
1199
|
-
conn.execute("""
|
|
1200
|
-
INSERT INTO sessions (
|
|
1201
|
-
session_id, user_id, project_id, agent_id, agent_version, source, engine,
|
|
1202
|
-
started_at, ended_at, status, total_turns, total_requests,
|
|
1203
|
-
total_cost_usd, total_tokens, created_at, updated_at
|
|
1204
|
-
)
|
|
1205
|
-
SELECT
|
|
1206
|
-
mc.session_id,
|
|
1207
|
-
any_value(mc.user_id) as user_id,
|
|
1208
|
-
any_value(mc.project_id) as project_id,
|
|
1209
|
-
any_value(mc.agent_id) as agent_id,
|
|
1210
|
-
any_value(mc.agent_version) as agent_version,
|
|
1211
|
-
any_value(mc.source) as source,
|
|
1212
|
-
any_value(mc.engine) as engine,
|
|
1213
|
-
to_timestamp(min(mc.start_ms) / 1000.0) as started_at,
|
|
1214
|
-
to_timestamp(max(mc.end_ms) / 1000.0) as ended_at,
|
|
1215
|
-
CASE WHEN sum(CASE WHEN mc.status = 'error' THEN 1 ELSE 0 END) > 0 THEN 'error' ELSE 'ok' END as status,
|
|
1216
|
-
count(distinct mc.turn_id) as total_turns,
|
|
1217
|
-
count(*) as total_requests,
|
|
1218
|
-
sum(coalesce(mc.total_cost_usd, 0.0)) as total_cost_usd,
|
|
1219
|
-
sum(coalesce(mc.total_tokens, 0)) as total_tokens,
|
|
1220
|
-
now(),
|
|
1221
|
-
now()
|
|
1222
|
-
FROM model_calls mc
|
|
1223
|
-
JOIN tmp_touched_sessions t ON t.session_id = mc.session_id
|
|
1224
|
-
GROUP BY mc.session_id
|
|
1225
|
-
ON CONFLICT(session_id) DO UPDATE SET
|
|
1226
|
-
user_id = excluded.user_id,
|
|
1227
|
-
project_id = excluded.project_id,
|
|
1228
|
-
agent_id = excluded.agent_id,
|
|
1229
|
-
agent_version = excluded.agent_version,
|
|
1230
|
-
source = excluded.source,
|
|
1231
|
-
engine = excluded.engine,
|
|
1232
|
-
started_at = excluded.started_at,
|
|
1233
|
-
ended_at = excluded.ended_at,
|
|
1234
|
-
status = excluded.status,
|
|
1235
|
-
total_turns = excluded.total_turns,
|
|
1236
|
-
total_requests = excluded.total_requests,
|
|
1237
|
-
total_cost_usd = excluded.total_cost_usd,
|
|
1238
|
-
total_tokens = excluded.total_tokens,
|
|
1239
|
-
updated_at = now()
|
|
1240
|
-
""")
|
|
1241
|
-
|
|
1242
|
-
# Recompute per-session ordering for turns.
|
|
1243
|
-
conn.execute("""
|
|
1244
|
-
UPDATE turns AS t
|
|
1245
|
-
SET turn_index = ranked.turn_index
|
|
1246
|
-
FROM (
|
|
1247
|
-
SELECT
|
|
1248
|
-
turn_id,
|
|
1249
|
-
row_number() OVER (PARTITION BY session_id ORDER BY started_at, turn_id) - 1 AS turn_index
|
|
1250
|
-
FROM turns
|
|
1251
|
-
WHERE session_id IN (SELECT session_id FROM tmp_touched_sessions)
|
|
1252
|
-
) ranked
|
|
1253
|
-
WHERE t.turn_id = ranked.turn_id
|
|
1254
|
-
""")
|
|
1255
|
-
|
|
354
|
+
conn.execute('DROP TABLE IF EXISTS tmp_touched_sessions')
|
|
355
|
+
conn.execute("\n CREATE TEMP TABLE tmp_touched_sessions AS\n SELECT DISTINCT session_id\n FROM tmp_model_calls\n WHERE session_id IS NOT NULL AND session_id != ''\n ")
|
|
356
|
+
conn.execute("\n INSERT INTO sessions (\n session_id, user_id, project_id, agent_id, agent_version, source, engine,\n started_at, ended_at, status, total_turns, total_requests,\n total_cost_usd, total_tokens, created_at, updated_at\n )\n SELECT\n mc.session_id,\n any_value(mc.user_id) as user_id,\n any_value(mc.project_id) as project_id,\n any_value(mc.agent_id) as agent_id,\n any_value(mc.agent_version) as agent_version,\n any_value(mc.source) as source,\n any_value(mc.engine) as engine,\n to_timestamp(min(mc.start_ms) / 1000.0) as started_at,\n to_timestamp(max(mc.end_ms) / 1000.0) as ended_at,\n CASE WHEN sum(CASE WHEN mc.status = 'error' THEN 1 ELSE 0 END) > 0 THEN 'error' ELSE 'ok' END as status,\n count(distinct mc.turn_id) as total_turns,\n count(*) as total_requests,\n sum(coalesce(mc.total_cost_usd, 0.0)) as total_cost_usd,\n sum(coalesce(mc.total_tokens, 0)) as total_tokens,\n now(),\n now()\n FROM model_calls mc\n JOIN tmp_touched_sessions t ON t.session_id = mc.session_id\n GROUP BY mc.session_id\n ON CONFLICT(session_id) DO UPDATE SET\n user_id = excluded.user_id,\n project_id = excluded.project_id,\n agent_id = excluded.agent_id,\n agent_version = excluded.agent_version,\n source = excluded.source,\n engine = excluded.engine,\n started_at = excluded.started_at,\n ended_at = excluded.ended_at,\n status = excluded.status,\n total_turns = excluded.total_turns,\n total_requests = excluded.total_requests,\n total_cost_usd = excluded.total_cost_usd,\n total_tokens = excluded.total_tokens,\n updated_at = now()\n ")
|
|
357
|
+
conn.execute('\n UPDATE turns AS t\n SET turn_index = ranked.turn_index\n FROM (\n SELECT\n turn_id,\n row_number() OVER (PARTITION BY session_id ORDER BY started_at, turn_id) - 1 AS turn_index\n FROM turns\n WHERE session_id IN (SELECT session_id FROM tmp_touched_sessions)\n ) ranked\n WHERE t.turn_id = ranked.turn_id\n ')
|
|
1256
358
|
|
|
1257
359
|
def upsert_processed_file(conn, jsonl_file: Path) -> None:
|
|
1258
360
|
mtime_ms, size_bytes = get_file_stats(jsonl_file)
|
|
1259
|
-
conn.execute(
|
|
1260
|
-
"""
|
|
1261
|
-
INSERT INTO processed_files (file_path, mtime_ms, size_bytes, processed_at)
|
|
1262
|
-
VALUES (?, ?, ?, now())
|
|
1263
|
-
ON CONFLICT(file_path) DO UPDATE SET
|
|
1264
|
-
mtime_ms = excluded.mtime_ms,
|
|
1265
|
-
size_bytes = excluded.size_bytes,
|
|
1266
|
-
processed_at = excluded.processed_at
|
|
1267
|
-
""",
|
|
1268
|
-
[str(jsonl_file), mtime_ms, size_bytes],
|
|
1269
|
-
)
|
|
1270
|
-
|
|
361
|
+
conn.execute('\n INSERT INTO processed_files (file_path, mtime_ms, size_bytes, processed_at)\n VALUES (?, ?, ?, now())\n ON CONFLICT(file_path) DO UPDATE SET\n mtime_ms = excluded.mtime_ms,\n size_bytes = excluded.size_bytes,\n processed_at = excluded.processed_at\n ', [str(jsonl_file), mtime_ms, size_bytes])
|
|
1271
362
|
|
|
1272
363
|
def main() -> int:
|
|
1273
|
-
"""Main entry point."""
|
|
1274
364
|
traces_dir = Path(sys.argv[1]) if len(sys.argv) > 1 else None
|
|
1275
365
|
db_path = Path(sys.argv[2]) if len(sys.argv) > 2 else None
|
|
1276
|
-
|
|
1277
366
|
traces_dir = get_traces_dir(str(traces_dir) if traces_dir else None)
|
|
1278
367
|
db_path = get_db_path(traces_dir, str(db_path) if db_path else None)
|
|
1279
|
-
|
|
1280
368
|
if not traces_dir.exists():
|
|
1281
|
-
log.error(f
|
|
1282
|
-
print(json.dumps({
|
|
1283
|
-
'status': 'error',
|
|
1284
|
-
'indexed': False,
|
|
1285
|
-
'message': f'Traces directory not found: {traces_dir}',
|
|
1286
|
-
}))
|
|
369
|
+
log.error(f'Traces directory not found: {traces_dir}')
|
|
370
|
+
print(json.dumps({'status': 'error', 'indexed': False, 'message': f'Traces directory not found: {traces_dir}'}))
|
|
1287
371
|
return 1
|
|
1288
|
-
|
|
1289
372
|
lock_handle = try_acquire_index_lock(get_lock_file(traces_dir))
|
|
1290
373
|
if lock_handle is None:
|
|
1291
374
|
log.info('Indexing already in progress')
|
|
1292
|
-
print(json.dumps({
|
|
1293
|
-
'status': 'busy',
|
|
1294
|
-
'indexed': False,
|
|
1295
|
-
'message': 'Indexing already in progress',
|
|
1296
|
-
}))
|
|
375
|
+
print(json.dumps({'status': 'busy', 'indexed': False, 'message': 'Indexing already in progress'}))
|
|
1297
376
|
return 3
|
|
1298
|
-
|
|
1299
377
|
try:
|
|
1300
378
|
jsonl_files = list(traces_dir.glob('*.jsonl'))
|
|
1301
379
|
if not jsonl_files:
|
|
1302
|
-
log.info(f
|
|
1303
|
-
print(json.dumps({
|
|
1304
|
-
'status': 'ok',
|
|
1305
|
-
'indexed': False,
|
|
1306
|
-
'message': 'No trace files to index',
|
|
1307
|
-
}))
|
|
380
|
+
log.info(f'No JSONL files found in {traces_dir}')
|
|
381
|
+
print(json.dumps({'status': 'ok', 'indexed': False, 'message': 'No trace files to index'}))
|
|
1308
382
|
return 2
|
|
1309
|
-
|
|
1310
|
-
log.debug(f"Found {len(jsonl_files)} JSONL files")
|
|
1311
|
-
|
|
383
|
+
log.debug(f'Found {len(jsonl_files)} JSONL files')
|
|
1312
384
|
state_file = get_state_file(traces_dir)
|
|
1313
385
|
state = load_index_state(state_file)
|
|
1314
|
-
|
|
1315
386
|
db_path.parent.mkdir(parents=True, exist_ok=True)
|
|
1316
|
-
|
|
1317
387
|
try:
|
|
1318
388
|
import duckdb
|
|
1319
389
|
except ImportError:
|
|
1320
|
-
log.error(
|
|
1321
|
-
print(json.dumps({
|
|
1322
|
-
'status': 'error',
|
|
1323
|
-
'indexed': False,
|
|
1324
|
-
'message': 'duckdb module not found',
|
|
1325
|
-
}), file=sys.stderr)
|
|
390
|
+
log.error('duckdb module not found. Install with: pip install duckdb')
|
|
391
|
+
print(json.dumps({'status': 'error', 'indexed': False, 'message': 'duckdb module not found'}), file=sys.stderr)
|
|
1326
392
|
return 1
|
|
1327
|
-
|
|
1328
393
|
try:
|
|
1329
|
-
log.debug(f
|
|
394
|
+
log.debug(f'Connecting to database: {db_path}')
|
|
1330
395
|
conn = duckdb.connect(str(db_path))
|
|
1331
396
|
ensure_schema(conn)
|
|
1332
|
-
|
|
1333
397
|
changed_files = get_changed_files(conn, traces_dir)
|
|
1334
398
|
if not changed_files:
|
|
1335
399
|
current_mtime = get_latest_jsonl_mtime(traces_dir)
|
|
@@ -1339,23 +403,15 @@ def main() -> int:
|
|
|
1339
403
|
state['duckdbPath'] = str(db_path)
|
|
1340
404
|
state['indexedAt'] = datetime.utcnow().isoformat() + 'Z'
|
|
1341
405
|
save_index_state(state_file, state)
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
print(json.dumps({
|
|
1345
|
-
'status': 'ok',
|
|
1346
|
-
'indexed': True,
|
|
1347
|
-
'message': 'Already indexed',
|
|
1348
|
-
}))
|
|
406
|
+
log.info('Index is up-to-date; no changed files')
|
|
407
|
+
print(json.dumps({'status': 'ok', 'indexed': True, 'message': 'Already indexed'}))
|
|
1349
408
|
conn.close()
|
|
1350
409
|
return 2
|
|
1351
|
-
|
|
1352
|
-
log.info(f"Incremental indexing {len(changed_files)} changed file(s)")
|
|
1353
|
-
|
|
410
|
+
log.info(f'Incremental indexing {len(changed_files)} changed file(s)')
|
|
1354
411
|
for jsonl_file in changed_files:
|
|
1355
|
-
log.debug(f
|
|
412
|
+
log.debug(f'Indexing file: {jsonl_file}')
|
|
1356
413
|
ingest_single_jsonl_file(conn, jsonl_file)
|
|
1357
414
|
upsert_processed_file(conn, jsonl_file)
|
|
1358
|
-
|
|
1359
415
|
current_mtime = get_latest_jsonl_mtime(traces_dir)
|
|
1360
416
|
state['version'] = 2
|
|
1361
417
|
state['indexed'] = True
|
|
@@ -1363,32 +419,18 @@ def main() -> int:
|
|
|
1363
419
|
state['duckdbPath'] = str(db_path)
|
|
1364
420
|
state['indexedAt'] = datetime.utcnow().isoformat() + 'Z'
|
|
1365
421
|
save_index_state(state_file, state)
|
|
1366
|
-
|
|
1367
|
-
count_result = conn.execute("SELECT COUNT(*) FROM model_calls").fetchall()
|
|
422
|
+
count_result = conn.execute('SELECT COUNT(*) FROM model_calls').fetchall()
|
|
1368
423
|
total_records = count_result[0][0] if count_result else 0
|
|
1369
|
-
log.debug(f
|
|
1370
|
-
|
|
424
|
+
log.debug(f'Ingestion complete. Total model_calls: {total_records}')
|
|
1371
425
|
conn.close()
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
print(json.dumps({
|
|
1375
|
-
'status': 'ok',
|
|
1376
|
-
'indexed': True,
|
|
1377
|
-
'message': f'Indexed {len(changed_files)} changed file(s) at {state["indexedAt"]}',
|
|
1378
|
-
}))
|
|
426
|
+
log.info(f'Indexing complete: {db_path}')
|
|
427
|
+
print(json.dumps({'status': 'ok', 'indexed': True, 'message': f'Indexed {len(changed_files)} changed file(s) at {state['indexedAt']}'}))
|
|
1379
428
|
return 0
|
|
1380
|
-
|
|
1381
429
|
except Exception as e:
|
|
1382
|
-
log.error(f
|
|
1383
|
-
print(json.dumps({
|
|
1384
|
-
'status': 'error',
|
|
1385
|
-
'indexed': False,
|
|
1386
|
-
'message': str(e),
|
|
1387
|
-
}), file=sys.stderr)
|
|
430
|
+
log.error(f'Indexing failed: {e}')
|
|
431
|
+
print(json.dumps({'status': 'error', 'indexed': False, 'message': str(e)}), file=sys.stderr)
|
|
1388
432
|
return 1
|
|
1389
433
|
finally:
|
|
1390
434
|
release_index_lock(lock_handle)
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
435
|
if __name__ == '__main__':
|
|
1394
436
|
sys.exit(main())
|