@agenthippo-ai/agenthippo 1.110.11 → 1.110.20

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