@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.
Files changed (533) hide show
  1. package/out/agentpack/types.js +2 -5
  2. package/out/analytics/dashboard/artifactManager.js +2 -439
  3. package/out/analytics/dashboard/fleetDisplayUtils.js +2 -162
  4. package/out/analytics/dashboard/fleetOrchestrator.js +3 -142
  5. package/out/analytics/dashboard/fleetRegistry.js +2 -79
  6. package/out/analytics/dashboard/fleetRowDefaults.js +2 -87
  7. package/out/analytics/dashboard/fleetScriptAdapter.js +2 -146
  8. package/out/analytics/dashboard/fleetTypes.js +2 -29
  9. package/out/analytics/dashboard/index.js +2 -17
  10. package/out/analytics/dashboard/indexingService.js +2 -422
  11. package/out/analytics/dashboard/localFleetAdapter.js +2 -222
  12. package/out/analytics/dashboard/performanceMonitor.js +5 -76
  13. package/out/analytics/dashboard/processManager.js +2 -209
  14. package/out/analytics/dashboard/provider.js +38 -3817
  15. package/out/analytics/dashboard/queryRunner.js +62 -750
  16. package/out/analytics/dashboard/scheduledFleetStorage.js +2 -241
  17. package/out/analytics/dashboard/sessionJsonlFleet.js +3 -204
  18. package/out/analytics/dashboard/teamViewDiscovery.js +2 -106
  19. package/out/analytics/dashboard/webview/panel.js +34 -2318
  20. package/out/analytics/dashboard/webview/runtime-bridge.js +2 -82
  21. package/out/analytics/lifecycle.js +2 -43
  22. package/out/analytics/service.js +8 -1277
  23. package/out/auth/authBaseUrl.js +2 -59
  24. package/out/auth/bootstrapApply.js +2 -71
  25. package/out/auth/brokerClient.js +2 -160
  26. package/out/auth/callbackUri.js +2 -59
  27. package/out/auth/githubStoreGate.js +2 -70
  28. package/out/auth/loopbackAuthServer.js +4 -159
  29. package/out/auth/onboardingPrompt.js +2 -112
  30. package/out/auth/pkce.js +2 -9
  31. package/out/auth/provider.js +2 -226
  32. package/out/auth/sessionStore.js +2 -156
  33. package/out/auth/signInFlow.js +2 -183
  34. package/out/cli/index.js +619 -551
  35. package/out/cli/run-custom-engine-runner.js +3 -318
  36. package/out/cli/vscode-shim.js +2 -86
  37. package/out/engines/factory.js +2 -45
  38. package/out/engines/types.js +2 -17
  39. package/out/examples/run-headless-repl.js +6 -286
  40. package/out/examples/run-headless.js +2 -187
  41. package/out/extension.js +32 -4473
  42. package/out/gateway/gatewayRouteIndex.js +6 -371
  43. package/out/gateway/gatewaySessionStore.js +3 -243
  44. package/out/gateway/index.js +2 -31
  45. package/out/gateway/resetMappings.js +2 -52
  46. package/out/gateway/service.js +5 -1249
  47. package/out/mcp/publish.js +2 -143
  48. package/out/remote/handleRemoteChat.js +2 -169
  49. package/out/remote/remotePackStatusBar.js +3 -103
  50. package/out/remote/remoteTrust.js +6 -277
  51. package/out/remote/serveClient.js +4 -277
  52. package/out/remoteSkills.js +3 -191
  53. package/out/runtime/index.js +2 -32
  54. package/out/runtime/vscode/index.js +2 -20
  55. package/out/runtime/vscode/vscode-config-provider.js +2 -77
  56. package/out/runtime/vscode/vscode-edit-tracker.js +2 -122
  57. package/out/runtime/vscode/vscode-logger.js +2 -30
  58. package/out/runtime/vscode/vscode-permission-handler.js +2 -72
  59. package/out/scripts/agentide-litellm.sh +0 -1
  60. package/out/scripts/agentide_litellm_hooks_v1.py +170 -418
  61. package/out/scripts/analytics/agent-fleet-widget.js +18 -257
  62. package/out/scripts/analytics/agentide-litellm-otel.config.yaml +4 -4
  63. package/out/scripts/analytics/duckdbLocator.js +2 -28
  64. package/out/scripts/analytics/generate-default-charts.py +43 -323
  65. package/out/scripts/analytics/hotspot-analysis-widget.js +4 -430
  66. package/out/scripts/analytics/index-traces.py +78 -1036
  67. package/out/scripts/analytics/session-comparison-widget.js +26 -472
  68. package/out/scripts/analytics/session-timeline-widget.js +25 -846
  69. package/out/scripts/analytics/setup-analytics.ps1 +8 -1
  70. package/out/scripts/analytics/setup-analytics.sh +13 -2
  71. package/out/scripts/analytics/token-cost-flamegraph-widget.js +4 -461
  72. package/out/scripts/analytics/token-cost-trend-widget.js +4 -534
  73. package/out/scripts/analytics/workflows-collector.cjs +4 -314
  74. package/out/scripts/analytics/workflows-daemon.mjs +9 -185
  75. package/out/scripts/analytics/workflows-widget.cjs +15 -159
  76. package/out/scripts/analytics/wow-model-widget.js +10 -120
  77. package/out/scripts/vendor-tools.mjs +2 -233
  78. package/out/skills/publish.js +3 -178
  79. package/out/store/github/setup.js +7 -212
  80. package/out/store/oramaIndex.js +2 -884
  81. package/out/sync.js +26 -1829
  82. package/out/terminalLogger.js +5 -358
  83. package/out/utils/pathSafety.js +2 -60
  84. package/out/utils/pythonRunner.js +4 -258
  85. package/out/utils/toolPath.js +2 -83
  86. package/out/workspaceFolders.js +2 -59
  87. package/package.json +1 -1
  88. package/out/agentpack/github.js +0 -547
  89. package/out/agentpack/index.js +0 -67
  90. package/out/agentpack/install.js +0 -297
  91. package/out/agentpack/manager.js +0 -441
  92. package/out/agentpack/manifest.js +0 -135
  93. package/out/agentpack/paths.js +0 -498
  94. package/out/agentpack/publish.js +0 -248
  95. package/out/agentpack/stores.js +0 -554
  96. package/out/analytics/dashboard/fleetStatusUtils.js +0 -30
  97. package/out/analytics/dashboard/fleetTitleUtils.js +0 -68
  98. package/out/analytics/dashboard/refreshPolicy.js +0 -16
  99. package/out/analytics/dashboard/scheduledFleetFingerprint.js +0 -22
  100. package/out/analytics/dashboard/scheduledFleetUtils.js +0 -22
  101. package/out/analytics/dashboard/sessionEventFleetStats.js +0 -118
  102. package/out/analytics/dashboard/viewsConfigMerge.js +0 -221
  103. package/out/analytics/dashboard/webview/codicon.ttf +0 -0
  104. package/out/analytics/identity.js +0 -0
  105. package/out/analytics/sessionEvents.js +0 -323
  106. package/out/auth/callbackUriParse.js +0 -59
  107. package/out/auth/types.js +0 -12
  108. package/out/auth-broker/config.js +0 -112
  109. package/out/auth-broker/jwt.js +0 -105
  110. package/out/auth-broker/pkce.js +0 -59
  111. package/out/auth-broker/server.js +0 -677
  112. package/out/auth-broker/store.js +0 -275
  113. package/out/authority/warrant.js +0 -206
  114. package/out/base-prompts/mcp-servers/.mcp.json +0 -13
  115. package/out/base-prompts/mcp-servers/langfuse-analytics/README.md +0 -142
  116. package/out/base-prompts/mcp-servers/langfuse-analytics/mcp.json +0 -13
  117. package/out/base-prompts/mcp-servers/langfuse-analytics/package-lock.json +0 -1192
  118. package/out/base-prompts/mcp-servers/langfuse-analytics/package.json +0 -23
  119. package/out/base-prompts/mcp-servers/langfuse-analytics/scripts/run-with-env.sh +0 -12
  120. package/out/base-prompts/mcp-servers/langfuse-analytics/scripts/sdk-test.js +0 -102
  121. package/out/base-prompts/mcp-servers/langfuse-analytics/scripts/simple-test.js +0 -137
  122. package/out/base-prompts/mcp-servers/langfuse-analytics/scripts/test-client.js +0 -118
  123. package/out/base-prompts/mcp-servers/langfuse-analytics/scripts/test-server.js +0 -209
  124. package/out/base-prompts/mcp-servers/langfuse-analytics/src/client.ts +0 -431
  125. package/out/base-prompts/mcp-servers/langfuse-analytics/src/index.ts +0 -270
  126. package/out/base-prompts/mcp-servers/langfuse-analytics/tsconfig.json +0 -18
  127. package/out/base-prompts/rules/AGENTS.md +0 -204
  128. package/out/base-prompts/skills/agent-browser/SKILL.md +0 -102
  129. package/out/base-prompts/skills/agent-builder/SKILL.md +0 -73
  130. package/out/base-prompts/skills/agent-builder/references/agent-pack.md +0 -63
  131. package/out/base-prompts/skills/agent-builder/references/crewai.md +0 -66
  132. package/out/base-prompts/skills/agent-builder/references/langchain.md +0 -60
  133. package/out/base-prompts/skills/agent-builder/references/raw-sdk.md +0 -73
  134. package/out/base-prompts/skills/agent-engine-creator/SKILL.md +0 -586
  135. package/out/base-prompts/skills/agent-engine-installer/SKILL.md +0 -106
  136. package/out/base-prompts/skills/agent-engine-installer/scripts/install-engine.mjs +0 -259
  137. package/out/base-prompts/skills/agent-engine-installer/scripts/install-engine.ps1 +0 -189
  138. package/out/base-prompts/skills/agenthippo-cli/SKILL.md +0 -69
  139. package/out/base-prompts/skills/agenthippo-cli/scripts/install.sh +0 -63
  140. package/out/base-prompts/skills/agentpack-creator/SKILL.md +0 -375
  141. package/out/base-prompts/skills/agentpack-creator/references/agent-templates.md +0 -261
  142. package/out/base-prompts/skills/agentpack-creator/references/agents-md-guide.md +0 -195
  143. package/out/base-prompts/skills/agentpack-creator/scripts/init_pack.py +0 -391
  144. package/out/base-prompts/skills/agentpack-creator/scripts/init_pack_test.py +0 -294
  145. package/out/base-prompts/skills/agentpack-testbench/SKILL.md +0 -135
  146. package/out/base-prompts/skills/algorithmic-art/LICENSE.txt +0 -202
  147. package/out/base-prompts/skills/algorithmic-art/SKILL.md +0 -405
  148. package/out/base-prompts/skills/algorithmic-art/templates/generator_template.js +0 -223
  149. package/out/base-prompts/skills/algorithmic-art/templates/viewer.html +0 -599
  150. package/out/base-prompts/skills/debug-agent/SKILL.md +0 -83
  151. package/out/base-prompts/skills/desktop-automation/SKILL.md +0 -166
  152. package/out/base-prompts/skills/desktop-automation/references/agenthippo-ui-recipes.md +0 -441
  153. package/out/base-prompts/skills/desktop-automation/scripts/windows-desktop-uia.psm1 +0 -280
  154. package/out/base-prompts/skills/docx/LICENSE.txt +0 -30
  155. package/out/base-prompts/skills/docx/SKILL.md +0 -197
  156. package/out/base-prompts/skills/docx/docx-js.md +0 -350
  157. package/out/base-prompts/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/dml-chart.xsd +0 -1499
  158. package/out/base-prompts/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/dml-chartDrawing.xsd +0 -146
  159. package/out/base-prompts/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/dml-diagram.xsd +0 -1085
  160. package/out/base-prompts/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/dml-lockedCanvas.xsd +0 -11
  161. package/out/base-prompts/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/dml-main.xsd +0 -3081
  162. package/out/base-prompts/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/dml-picture.xsd +0 -23
  163. package/out/base-prompts/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/dml-spreadsheetDrawing.xsd +0 -185
  164. package/out/base-prompts/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/dml-wordprocessingDrawing.xsd +0 -287
  165. package/out/base-prompts/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/pml.xsd +0 -1676
  166. package/out/base-prompts/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-additionalCharacteristics.xsd +0 -28
  167. package/out/base-prompts/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-bibliography.xsd +0 -144
  168. package/out/base-prompts/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-commonSimpleTypes.xsd +0 -174
  169. package/out/base-prompts/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-customXmlDataProperties.xsd +0 -25
  170. package/out/base-prompts/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-customXmlSchemaProperties.xsd +0 -18
  171. package/out/base-prompts/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesCustom.xsd +0 -59
  172. package/out/base-prompts/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesExtended.xsd +0 -56
  173. package/out/base-prompts/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesVariantTypes.xsd +0 -195
  174. package/out/base-prompts/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-math.xsd +0 -582
  175. package/out/base-prompts/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-relationshipReference.xsd +0 -25
  176. package/out/base-prompts/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/sml.xsd +0 -4439
  177. package/out/base-prompts/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/vml-main.xsd +0 -570
  178. package/out/base-prompts/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/vml-officeDrawing.xsd +0 -509
  179. package/out/base-prompts/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/vml-presentationDrawing.xsd +0 -12
  180. package/out/base-prompts/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/vml-spreadsheetDrawing.xsd +0 -108
  181. package/out/base-prompts/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/vml-wordprocessingDrawing.xsd +0 -96
  182. package/out/base-prompts/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/wml.xsd +0 -3646
  183. package/out/base-prompts/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/xml.xsd +0 -116
  184. package/out/base-prompts/skills/docx/ooxml/schemas/ecma/fouth-edition/opc-contentTypes.xsd +0 -42
  185. package/out/base-prompts/skills/docx/ooxml/schemas/ecma/fouth-edition/opc-coreProperties.xsd +0 -50
  186. package/out/base-prompts/skills/docx/ooxml/schemas/ecma/fouth-edition/opc-digSig.xsd +0 -49
  187. package/out/base-prompts/skills/docx/ooxml/schemas/ecma/fouth-edition/opc-relationships.xsd +0 -33
  188. package/out/base-prompts/skills/docx/ooxml/schemas/mce/mc.xsd +0 -75
  189. package/out/base-prompts/skills/docx/ooxml/schemas/microsoft/wml-2010.xsd +0 -560
  190. package/out/base-prompts/skills/docx/ooxml/schemas/microsoft/wml-2012.xsd +0 -67
  191. package/out/base-prompts/skills/docx/ooxml/schemas/microsoft/wml-2018.xsd +0 -14
  192. package/out/base-prompts/skills/docx/ooxml/schemas/microsoft/wml-cex-2018.xsd +0 -20
  193. package/out/base-prompts/skills/docx/ooxml/schemas/microsoft/wml-cid-2016.xsd +0 -13
  194. package/out/base-prompts/skills/docx/ooxml/schemas/microsoft/wml-sdtdatahash-2020.xsd +0 -4
  195. package/out/base-prompts/skills/docx/ooxml/schemas/microsoft/wml-symex-2015.xsd +0 -8
  196. package/out/base-prompts/skills/docx/ooxml/scripts/pack.py +0 -159
  197. package/out/base-prompts/skills/docx/ooxml/scripts/unpack.py +0 -29
  198. package/out/base-prompts/skills/docx/ooxml/scripts/validate.py +0 -69
  199. package/out/base-prompts/skills/docx/ooxml/scripts/validation/__init__.py +0 -15
  200. package/out/base-prompts/skills/docx/ooxml/scripts/validation/base.py +0 -951
  201. package/out/base-prompts/skills/docx/ooxml/scripts/validation/docx.py +0 -274
  202. package/out/base-prompts/skills/docx/ooxml/scripts/validation/pptx.py +0 -315
  203. package/out/base-prompts/skills/docx/ooxml/scripts/validation/redlining.py +0 -279
  204. package/out/base-prompts/skills/docx/ooxml.md +0 -610
  205. package/out/base-prompts/skills/docx/scripts/__init__.py +0 -1
  206. package/out/base-prompts/skills/docx/scripts/document.py +0 -1276
  207. package/out/base-prompts/skills/docx/scripts/templates/comments.xml +0 -3
  208. package/out/base-prompts/skills/docx/scripts/templates/commentsExtended.xml +0 -3
  209. package/out/base-prompts/skills/docx/scripts/templates/commentsExtensible.xml +0 -3
  210. package/out/base-prompts/skills/docx/scripts/templates/commentsIds.xml +0 -3
  211. package/out/base-prompts/skills/docx/scripts/templates/people.xml +0 -3
  212. package/out/base-prompts/skills/docx/scripts/utilities.py +0 -374
  213. package/out/base-prompts/skills/gateway-outbound/SKILL.md +0 -136
  214. package/out/base-prompts/skills/mcp-builder/LICENSE.txt +0 -202
  215. package/out/base-prompts/skills/mcp-builder/SKILL.md +0 -328
  216. package/out/base-prompts/skills/mcp-builder/reference/evaluation.md +0 -602
  217. package/out/base-prompts/skills/mcp-builder/reference/mcp_best_practices.md +0 -249
  218. package/out/base-prompts/skills/mcp-builder/reference/node_mcp_server.md +0 -970
  219. package/out/base-prompts/skills/mcp-builder/reference/python_mcp_server.md +0 -719
  220. package/out/base-prompts/skills/mcp-builder/scripts/connections.py +0 -151
  221. package/out/base-prompts/skills/mcp-builder/scripts/evaluation.py +0 -373
  222. package/out/base-prompts/skills/mcp-builder/scripts/example_evaluation.xml +0 -22
  223. package/out/base-prompts/skills/mcp-builder/scripts/requirements.txt +0 -2
  224. package/out/base-prompts/skills/openclaw/SKILL.md +0 -406
  225. package/out/base-prompts/skills/openclaw/scripts/run-smoke.sh +0 -61
  226. package/out/base-prompts/skills/pdf/LICENSE.txt +0 -30
  227. package/out/base-prompts/skills/pdf/SKILL.md +0 -294
  228. package/out/base-prompts/skills/pdf/forms.md +0 -205
  229. package/out/base-prompts/skills/pdf/reference.md +0 -612
  230. package/out/base-prompts/skills/pdf/scripts/check_bounding_boxes.py +0 -70
  231. package/out/base-prompts/skills/pdf/scripts/check_bounding_boxes_test.py +0 -226
  232. package/out/base-prompts/skills/pdf/scripts/check_fillable_fields.py +0 -12
  233. package/out/base-prompts/skills/pdf/scripts/convert_pdf_to_images.py +0 -35
  234. package/out/base-prompts/skills/pdf/scripts/create_validation_image.py +0 -41
  235. package/out/base-prompts/skills/pdf/scripts/extract_form_field_info.py +0 -152
  236. package/out/base-prompts/skills/pdf/scripts/fill_fillable_fields.py +0 -114
  237. package/out/base-prompts/skills/pdf/scripts/fill_pdf_form_with_annotations.py +0 -108
  238. package/out/base-prompts/skills/pptx/LICENSE.txt +0 -30
  239. package/out/base-prompts/skills/pptx/SKILL.md +0 -484
  240. package/out/base-prompts/skills/pptx/html2pptx.md +0 -625
  241. package/out/base-prompts/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/dml-chart.xsd +0 -1499
  242. package/out/base-prompts/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/dml-chartDrawing.xsd +0 -146
  243. package/out/base-prompts/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/dml-diagram.xsd +0 -1085
  244. package/out/base-prompts/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/dml-lockedCanvas.xsd +0 -11
  245. package/out/base-prompts/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/dml-main.xsd +0 -3081
  246. package/out/base-prompts/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/dml-picture.xsd +0 -23
  247. package/out/base-prompts/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/dml-spreadsheetDrawing.xsd +0 -185
  248. package/out/base-prompts/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/dml-wordprocessingDrawing.xsd +0 -287
  249. package/out/base-prompts/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/pml.xsd +0 -1676
  250. package/out/base-prompts/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-additionalCharacteristics.xsd +0 -28
  251. package/out/base-prompts/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-bibliography.xsd +0 -144
  252. package/out/base-prompts/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-commonSimpleTypes.xsd +0 -174
  253. package/out/base-prompts/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-customXmlDataProperties.xsd +0 -25
  254. package/out/base-prompts/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-customXmlSchemaProperties.xsd +0 -18
  255. package/out/base-prompts/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesCustom.xsd +0 -59
  256. package/out/base-prompts/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesExtended.xsd +0 -56
  257. package/out/base-prompts/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesVariantTypes.xsd +0 -195
  258. package/out/base-prompts/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-math.xsd +0 -582
  259. package/out/base-prompts/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-relationshipReference.xsd +0 -25
  260. package/out/base-prompts/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/sml.xsd +0 -4439
  261. package/out/base-prompts/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/vml-main.xsd +0 -570
  262. package/out/base-prompts/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/vml-officeDrawing.xsd +0 -509
  263. package/out/base-prompts/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/vml-presentationDrawing.xsd +0 -12
  264. package/out/base-prompts/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/vml-spreadsheetDrawing.xsd +0 -108
  265. package/out/base-prompts/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/vml-wordprocessingDrawing.xsd +0 -96
  266. package/out/base-prompts/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/wml.xsd +0 -3646
  267. package/out/base-prompts/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/xml.xsd +0 -116
  268. package/out/base-prompts/skills/pptx/ooxml/schemas/ecma/fouth-edition/opc-contentTypes.xsd +0 -42
  269. package/out/base-prompts/skills/pptx/ooxml/schemas/ecma/fouth-edition/opc-coreProperties.xsd +0 -50
  270. package/out/base-prompts/skills/pptx/ooxml/schemas/ecma/fouth-edition/opc-digSig.xsd +0 -49
  271. package/out/base-prompts/skills/pptx/ooxml/schemas/ecma/fouth-edition/opc-relationships.xsd +0 -33
  272. package/out/base-prompts/skills/pptx/ooxml/schemas/mce/mc.xsd +0 -75
  273. package/out/base-prompts/skills/pptx/ooxml/schemas/microsoft/wml-2010.xsd +0 -560
  274. package/out/base-prompts/skills/pptx/ooxml/schemas/microsoft/wml-2012.xsd +0 -67
  275. package/out/base-prompts/skills/pptx/ooxml/schemas/microsoft/wml-2018.xsd +0 -14
  276. package/out/base-prompts/skills/pptx/ooxml/schemas/microsoft/wml-cex-2018.xsd +0 -20
  277. package/out/base-prompts/skills/pptx/ooxml/schemas/microsoft/wml-cid-2016.xsd +0 -13
  278. package/out/base-prompts/skills/pptx/ooxml/schemas/microsoft/wml-sdtdatahash-2020.xsd +0 -4
  279. package/out/base-prompts/skills/pptx/ooxml/schemas/microsoft/wml-symex-2015.xsd +0 -8
  280. package/out/base-prompts/skills/pptx/ooxml/scripts/pack.py +0 -159
  281. package/out/base-prompts/skills/pptx/ooxml/scripts/unpack.py +0 -29
  282. package/out/base-prompts/skills/pptx/ooxml/scripts/validate.py +0 -69
  283. package/out/base-prompts/skills/pptx/ooxml/scripts/validation/__init__.py +0 -15
  284. package/out/base-prompts/skills/pptx/ooxml/scripts/validation/base.py +0 -951
  285. package/out/base-prompts/skills/pptx/ooxml/scripts/validation/docx.py +0 -274
  286. package/out/base-prompts/skills/pptx/ooxml/scripts/validation/pptx.py +0 -315
  287. package/out/base-prompts/skills/pptx/ooxml/scripts/validation/redlining.py +0 -279
  288. package/out/base-prompts/skills/pptx/ooxml.md +0 -427
  289. package/out/base-prompts/skills/pptx/scripts/html2pptx.js +0 -984
  290. package/out/base-prompts/skills/pptx/scripts/inventory.py +0 -1020
  291. package/out/base-prompts/skills/pptx/scripts/rearrange.py +0 -231
  292. package/out/base-prompts/skills/pptx/scripts/replace.py +0 -385
  293. package/out/base-prompts/skills/pptx/scripts/thumbnail.py +0 -450
  294. package/out/base-prompts/skills/prompt-lab/SKILL.md +0 -149
  295. package/out/base-prompts/skills/prompt-lab/scripts/query-analytics.sh +0 -2551
  296. package/out/base-prompts/skills/skill-creator/LICENSE.txt +0 -202
  297. package/out/base-prompts/skills/skill-creator/SKILL.md +0 -371
  298. package/out/base-prompts/skills/skill-creator/references/output-patterns.md +0 -82
  299. package/out/base-prompts/skills/skill-creator/references/workflows.md +0 -28
  300. package/out/base-prompts/skills/skill-creator/scripts/init_skill.py +0 -303
  301. package/out/base-prompts/skills/skill-creator/scripts/package_skill.py +0 -110
  302. package/out/base-prompts/skills/skill-creator/scripts/quick_validate.py +0 -95
  303. package/out/base-prompts/skills/skill-installer/SKILL.md +0 -251
  304. package/out/base-prompts/skills/skill-installer/references/store-api.md +0 -219
  305. package/out/base-prompts/skills/spotlight-dashboard/SKILL.md +0 -83
  306. package/out/base-prompts/skills/spotlight-insights/SKILL.md +0 -101
  307. package/out/base-prompts/skills/spotlight-optimize/SKILL.md +0 -93
  308. package/out/base-prompts/skills/spotlight-optimize/scripts/query-analytics.sh +0 -2551
  309. package/out/base-prompts/skills/spotlight-query/SKILL.md +0 -314
  310. package/out/base-prompts/skills/spotlight-query/references/otel-schema.md +0 -163
  311. package/out/base-prompts/skills/spotlight-query/scripts/query-analytics.sh +0 -2551
  312. package/out/base-prompts/skills/spotlight-query/scripts/query-analytics.test.sh +0 -744
  313. package/out/base-prompts/skills/spotlight-query/scripts/test-fixtures/analytics/sessions/sess-test-1.jsonl +0 -14
  314. package/out/base-prompts/skills/spotlight-query/scripts/test-fixtures/analytics/traces/.duckdb-index.json +0 -7
  315. package/out/base-prompts/skills/spotlight-query/scripts/test-fixtures/analytics/traces/sample.jsonl +0 -5
  316. package/out/base-prompts/skills/spotlight-report/SKILL.md +0 -86
  317. package/out/base-prompts/skills/spotlight-visualize/SKILL.md +0 -92
  318. package/out/base-prompts/skills/spotlight-visualize/scripts/query-analytics.sh +0 -2551
  319. package/out/base-prompts/skills/xlsx/LICENSE.txt +0 -30
  320. package/out/base-prompts/skills/xlsx/SKILL.md +0 -289
  321. package/out/base-prompts/skills/xlsx/recalc.py +0 -178
  322. package/out/branding.js +0 -49
  323. package/out/cli/commands/anywhere.js +0 -66
  324. package/out/cli/commands/ask.js +0 -246
  325. package/out/cli/commands/auth-broker.js +0 -24
  326. package/out/cli/commands/auth.js +0 -219
  327. package/out/cli/commands/chat.js +0 -357
  328. package/out/cli/commands/conformance.js +0 -249
  329. package/out/cli/commands/data-plane.js +0 -67
  330. package/out/cli/commands/deploy.js +0 -263
  331. package/out/cli/commands/gateway.js +0 -218
  332. package/out/cli/commands/idp.js +0 -85
  333. package/out/cli/commands/pack.js +0 -399
  334. package/out/cli/commands/probe-custom-engine.js +0 -82
  335. package/out/cli/commands/sandbox.js +0 -332
  336. package/out/cli/commands/serve.js +0 -1758
  337. package/out/cli/commands/store.js +0 -927
  338. package/out/cli/credential-context.js +0 -91
  339. package/out/cli/data-plane/broker.js +0 -78
  340. package/out/cli/data-plane/connector-host.js +0 -121
  341. package/out/cli/data-plane/egress.js +0 -189
  342. package/out/cli/data-plane/gateway.js +0 -218
  343. package/out/cli/data-plane/index.js +0 -152
  344. package/out/cli/data-plane/registry.js +0 -89
  345. package/out/cli/data-plane/remote.js +0 -106
  346. package/out/cli/data-plane/sidecar.js +0 -161
  347. package/out/cli/deploy/blueprints/registry.js +0 -84
  348. package/out/cli/deploy/layers/identity.js +0 -172
  349. package/out/cli/deploy-init.js +0 -3023
  350. package/out/cli/deps.js +0 -342
  351. package/out/cli/idp-init.js +0 -338
  352. package/out/cli/pack-install-ref.js +0 -15
  353. package/out/cli/pack-loader.js +0 -418
  354. package/out/cli/pack-runtime.js +0 -61
  355. package/out/cli/runtime.js +0 -450
  356. package/out/cli/serve-hardening.js +0 -607
  357. package/out/cli/state-sync.js +0 -424
  358. package/out/cli/utils.js +0 -231
  359. package/out/engines/base.js +0 -188
  360. package/out/engines/claude.js +0 -1254
  361. package/out/engines/codex.js +0 -862
  362. package/out/engines/custom.js +0 -674
  363. package/out/engines/customEngineIpc.js +0 -95
  364. package/out/engines/customEngineRunnerEmbed.js +0 -107
  365. package/out/engines/customRegistry.js +0 -274
  366. package/out/engines/embeddedCustomEngineRunner.generated.js +0 -9
  367. package/out/engines/engineApiKey.js +0 -138
  368. package/out/engines/install.js +0 -372
  369. package/out/engines/persistentEngineWorker.js +0 -320
  370. package/out/engines/publish.js +0 -341
  371. package/out/evidence/sessionShip.js +0 -610
  372. package/out/gateway/bridge.js +0 -843
  373. package/out/gateway/cli-agent-id.js +0 -36
  374. package/out/gateway/client.js +0 -336
  375. package/out/gateway/client.test.js +0 -395
  376. package/out/gateway/gateway-scope-errors.js +0 -14
  377. package/out/gateway/gatewayBackup.js +0 -76
  378. package/out/gateway/installSource.js +0 -68
  379. package/out/gateway/lifecycle.js +0 -3190
  380. package/out/gateway/openclaw-channel-routing.js +0 -317
  381. package/out/gateway/outboundHandles.js +0 -373
  382. package/out/gateway/outboundPaths.js +0 -75
  383. package/out/gateway/outboundSend.js +0 -254
  384. package/out/gateway/protocol.js +0 -48
  385. package/out/gateway/secretsPreflight.js +0 -90
  386. package/out/gateway/sessionRouting.js +0 -77
  387. package/out/headless/index.js +0 -539
  388. package/out/headless/sync.js +0 -473
  389. package/out/live-e2e/mcp-demo/server.mjs +0 -32
  390. package/out/live-probe/connector.mjs +0 -8
  391. package/out/live-probe/env-report.mjs +0 -8
  392. package/out/mcp.js +0 -654
  393. package/out/pack/caps.js +0 -271
  394. package/out/pack/governor.js +0 -309
  395. package/out/pack/lease-schema.js +0 -270
  396. package/out/pack/lease.js +0 -640
  397. package/out/pack/reconcile.js +0 -217
  398. package/out/pack/signature.js +0 -839
  399. package/out/permissions.js +0 -108
  400. package/out/permissions.mapping.yaml +0 -68
  401. package/out/registry/index.js +0 -332
  402. package/out/runner/host.js +0 -131
  403. package/out/runner/protocol.js +0 -108
  404. package/out/runner/remoteEngine.js +0 -130
  405. package/out/runtime/interfaces/config-provider.js +0 -32
  406. package/out/runtime/interfaces/edit-tracker.js +0 -24
  407. package/out/runtime/interfaces/index.js +0 -29
  408. package/out/runtime/interfaces/logger.js +0 -40
  409. package/out/runtime/interfaces/permission-handler.js +0 -66
  410. package/out/runtime/interfaces/session-store.js +0 -342
  411. package/out/runtime/vscode/vscode-session-store.js +0 -22
  412. package/out/sandbox/containerDriver.js +0 -570
  413. package/out/sandbox/processDriver.js +0 -315
  414. package/out/sandbox/sandboxRunner.js +0 -191
  415. package/out/sandbox/support.js +0 -301
  416. package/out/sandbox/types.js +0 -55
  417. package/out/scripts/analytics/fleet-local-adapter.js +0 -246
  418. package/out/scripts/analytics/live-fleet-4-collector.cjs +0 -315
  419. package/out/scripts/analytics/live-fleet-4-daemon.mjs +0 -134
  420. package/out/scripts/analytics/live-fleet-4-widget.cjs +0 -433
  421. package/out/scripts/gateway/Dockerfile.serve +0 -45
  422. package/out/scripts/gateway/docker-serve.sh +0 -248
  423. package/out/scripts/gateway/setup-gateway-server.ps1 +0 -953
  424. package/out/scripts/gateway/setup-gateway-server.sh +0 -935
  425. package/out/session.js +0 -272
  426. package/out/shared/agentEngines.js +0 -170
  427. package/out/shared/index.js +0 -109
  428. package/out/shared/models.js +0 -136
  429. package/out/shared/providers.js +0 -205
  430. package/out/spotlight/install.js +0 -345
  431. package/out/spotlight/publish.js +0 -544
  432. package/out/spotlight/yaml.js +0 -31
  433. package/out/store/index.js +0 -786
  434. package/out/store/shared.js +0 -241
  435. package/out/test/agentEnvFile.test.js +0 -97
  436. package/out/test/agentYaml.test.js +0 -373
  437. package/out/test/agentpack.test.js +0 -639
  438. package/out/test/analytics-lifecycle.test.js +0 -174
  439. package/out/test/analytics.test.js +0 -756
  440. package/out/test/auth-baseurl.test.js +0 -132
  441. package/out/test/auth-bootstrapApply.test.js +0 -123
  442. package/out/test/auth-broker-hardening.test.js +0 -227
  443. package/out/test/auth-broker.test.js +0 -541
  444. package/out/test/auth-callback.test.js +0 -62
  445. package/out/test/blueprint-registry.test.js +0 -105
  446. package/out/test/branding.test.js +0 -242
  447. package/out/test/caps-governor.test.js +0 -455
  448. package/out/test/cli-agent-id.test.js +0 -55
  449. package/out/test/codex-new-file-survival.test.js +0 -119
  450. package/out/test/configuration-scopes.test.js +0 -131
  451. package/out/test/credential-context.test.js +0 -160
  452. package/out/test/custom-env-layering.test.js +0 -151
  453. package/out/test/customEngineInstall.test.js +0 -100
  454. package/out/test/customRegistry.test.js +0 -122
  455. package/out/test/data-plane-engine-wiring.test.js +0 -250
  456. package/out/test/data-plane.test.js +0 -311
  457. package/out/test/deploy-init.test.js +0 -854
  458. package/out/test/deps-auto-install.test.js +0 -81
  459. package/out/test/egress-broker.test.js +0 -172
  460. package/out/test/engine-integration.test.js +0 -297
  461. package/out/test/enginePaths.test.js +0 -195
  462. package/out/test/enginePublish.test.js +0 -82
  463. package/out/test/fleetDisplayUtils.test.js +0 -100
  464. package/out/test/gateway-client.test.js +0 -409
  465. package/out/test/gateway-install-source.test.js +0 -111
  466. package/out/test/gateway-lifecycle.test.js +0 -608
  467. package/out/test/gateway-outbound.test.js +0 -538
  468. package/out/test/gateway-reset-mappings.test.js +0 -221
  469. package/out/test/gateway-route-index.test.js +0 -235
  470. package/out/test/gateway-secrets-preflight.test.js +0 -127
  471. package/out/test/gateway-session-routing.test.js +0 -65
  472. package/out/test/gateway-session-store.test.js +0 -216
  473. package/out/test/github.test.js +0 -232
  474. package/out/test/governor-hardening.test.js +0 -217
  475. package/out/test/headless-sync.test.js +0 -301
  476. package/out/test/helpers/manifest-fixtures.js +0 -27
  477. package/out/test/idp-init.test.js +0 -151
  478. package/out/test/lease-signature.test.js +0 -357
  479. package/out/test/lease-trust-root.test.js +0 -273
  480. package/out/test/lease-validation.test.js +0 -169
  481. package/out/test/lease.test.js +0 -114
  482. package/out/test/mcp-obo.test.js +0 -107
  483. package/out/test/mcp.test.js +0 -389
  484. package/out/test/model-routing.test.js +0 -133
  485. package/out/test/openclaw-channel-routing.test.js +0 -191
  486. package/out/test/pack-admission.test.js +0 -216
  487. package/out/test/pack-digest-parity.test.js +0 -140
  488. package/out/test/pack-reconcile.test.js +0 -183
  489. package/out/test/pack-signature-artifact.test.js +0 -103
  490. package/out/test/pack-signature.test.js +0 -176
  491. package/out/test/pack-trust-root.test.js +0 -202
  492. package/out/test/pathSafety.test.js +0 -63
  493. package/out/test/permissions.test.js +0 -175
  494. package/out/test/persistent-engine-worker.test.js +0 -220
  495. package/out/test/refreshPolicy.test.js +0 -60
  496. package/out/test/registry.test.js +0 -351
  497. package/out/test/remote-trust.test.js +0 -242
  498. package/out/test/runner-protocol.test.js +0 -249
  499. package/out/test/runtime.test.js +0 -349
  500. package/out/test/sandbox-driver.test.js +0 -553
  501. package/out/test/sandbox-hardening.test.js +0 -395
  502. package/out/test/sandbox-runner.test.js +0 -343
  503. package/out/test/scheduledFleetStorage.test.js +0 -72
  504. package/out/test/serve-gateway-turn.test.js +0 -267
  505. package/out/test/serve-hardening.test.js +0 -418
  506. package/out/test/serveClient.test.js +0 -209
  507. package/out/test/sessionJsonlFleet.test.js +0 -78
  508. package/out/test/sessionShip.test.js +0 -560
  509. package/out/test/shared.test.js +0 -173
  510. package/out/test/skills-mcp.test.js +0 -299
  511. package/out/test/spotlightPublish.test.js +0 -327
  512. package/out/test/spotlightYaml.test.js +0 -58
  513. package/out/test/state-sync-peer.test.js +0 -156
  514. package/out/test/store-install-safe.test.js +0 -69
  515. package/out/test/store.test.js +0 -331
  516. package/out/test/stores.test.js +0 -504
  517. package/out/test/sync-switch.test.js +0 -251
  518. package/out/test/teamViewDiscovery.test.js +0 -90
  519. package/out/test/tls-bypass.test.js +0 -90
  520. package/out/test/toolPath.test.js +0 -120
  521. package/out/test/utils.test.js +0 -308
  522. package/out/test/viewsConfigMerge.test.js +0 -180
  523. package/out/test/warrant.test.js +0 -138
  524. package/out/test/windows-command-line.test.js +0 -75
  525. package/out/utils/agentEnvFile.js +0 -131
  526. package/out/utils/binary.js +0 -175
  527. package/out/utils/index.js +0 -37
  528. package/out/utils/network.js +0 -74
  529. package/out/utils/paths.js +0 -116
  530. package/out/utils/pid.js +0 -180
  531. package/out/utils/resolveNodeBinary.js +0 -168
  532. package/out/utils/windowsCommandLine.js +0 -29
  533. package/out/utils.js +0 -968
@@ -1,27 +1,6 @@
1
- #!/usr/bin/env python3
2
- # Copyright (c) AgentHippo.ai. All rights reserved.
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"{os.getpid()}\n".encode('utf-8'))
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"Failed to load state file: {e}")
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"Failed to save state file: {e}")
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"Failed to get JSONL mtimes: {e}")
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
- """Create normalized LLM-agent analytics tables, marts, and indexes."""
177
- conn.execute("""
178
- CREATE TABLE IF NOT EXISTS model_calls (
179
- model_call_id VARCHAR PRIMARY KEY,
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("ALTER TABLE spans ADD COLUMN IF NOT EXISTS engine VARCHAR")
116
+ conn.execute('ALTER TABLE spans ADD COLUMN IF NOT EXISTS engine VARCHAR')
314
117
  except Exception as e:
315
- log.debug(f"Spans engine column (may already exist): {e}")
316
-
317
- conn.execute("""
318
- CREATE TABLE IF NOT EXISTS processed_files (
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"Index creation hint (may already exist): {e}")
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 [{"role": default_role, "content": str(parsed)}]
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({"role": default_role, "content": str(entry)})
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"{session_id or ''}|{trace_id}|tool|{idx}"
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
- if block_type == 'tool_call' and not block.get('function') and isinstance(block.get('name'), str):
726
- block['function'] = {
727
- 'name': block.get('name'),
728
- 'arguments': block.get('arguments') or block.get('input'),
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
- block.get('name')
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(f"""
843
- CREATE TEMP TABLE tmp_model_calls AS
844
- WITH raw_spans AS (
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
- # Rebuild session rollups for sessions touched in this file.
1191
- conn.execute("DROP TABLE IF EXISTS tmp_touched_sessions")
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"Traces directory not found: {traces_dir}")
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"No JSONL files found in {traces_dir}")
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("duckdb module not found. Install with: pip install duckdb")
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"Connecting to database: {db_path}")
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
- log.info("Index is up-to-date; no changed files")
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"Indexing file: {jsonl_file}")
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"Ingestion complete. Total model_calls: {total_records}")
1370
-
424
+ log.debug(f'Ingestion complete. Total model_calls: {total_records}')
1371
425
  conn.close()
1372
-
1373
- log.info(f"Indexing complete: {db_path}")
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"Indexing failed: {e}")
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())