@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.
- package/out/agentpack/types.js +2 -5
- package/out/analytics/dashboard/artifactManager.js +2 -439
- package/out/analytics/dashboard/fleetDisplayUtils.js +2 -162
- package/out/analytics/dashboard/fleetOrchestrator.js +3 -142
- package/out/analytics/dashboard/fleetRegistry.js +2 -79
- package/out/analytics/dashboard/fleetRowDefaults.js +2 -87
- package/out/analytics/dashboard/fleetScriptAdapter.js +2 -146
- package/out/analytics/dashboard/fleetTypes.js +2 -29
- package/out/analytics/dashboard/index.js +2 -17
- package/out/analytics/dashboard/indexingService.js +2 -422
- package/out/analytics/dashboard/localFleetAdapter.js +2 -302
- package/out/analytics/dashboard/performanceMonitor.js +5 -76
- package/out/analytics/dashboard/processManager.js +2 -209
- package/out/analytics/dashboard/provider.js +38 -3817
- package/out/analytics/dashboard/queryRunner.js +62 -750
- package/out/analytics/dashboard/scheduledFleetStorage.js +2 -241
- package/out/analytics/dashboard/sessionJsonlFleet.js +3 -204
- package/out/analytics/dashboard/teamViewDiscovery.js +2 -106
- package/out/analytics/dashboard/webview/panel.js +34 -2318
- package/out/analytics/dashboard/webview/runtime-bridge.js +2 -82
- package/out/analytics/lifecycle.js +2 -43
- package/out/analytics/service.js +8 -1283
- package/out/auth/authBaseUrl.js +2 -59
- package/out/auth/bootstrapApply.js +2 -71
- package/out/auth/brokerClient.js +2 -160
- package/out/auth/callbackUri.js +2 -59
- package/out/auth/githubStoreGate.js +2 -70
- package/out/auth/loopbackAuthServer.js +4 -159
- package/out/auth/onboardingPrompt.js +2 -112
- package/out/auth/pkce.js +2 -9
- package/out/auth/provider.js +2 -226
- package/out/auth/sessionStore.js +2 -156
- package/out/auth/signInFlow.js +2 -183
- package/out/cli/index.js +618 -564
- package/out/cli/run-custom-engine-runner.js +3 -318
- package/out/cli/vscode-shim.js +2 -86
- package/out/engines/factory.js +2 -45
- package/out/engines/types.js +2 -17
- package/out/examples/run-headless-repl.js +6 -286
- package/out/examples/run-headless.js +2 -187
- package/out/extension.js +32 -4473
- package/out/gateway/gatewayRouteIndex.js +6 -371
- package/out/gateway/gatewaySessionStore.js +3 -243
- package/out/gateway/index.js +2 -31
- package/out/gateway/resetMappings.js +2 -52
- package/out/gateway/service.js +5 -1249
- package/out/mcp/publish.js +2 -143
- package/out/remote/handleRemoteChat.js +2 -169
- package/out/remote/remotePackStatusBar.js +3 -103
- package/out/remote/remoteTrust.js +6 -277
- package/out/remote/serveClient.js +4 -277
- package/out/remoteSkills.js +3 -191
- package/out/runtime/index.js +2 -32
- package/out/runtime/vscode/index.js +2 -20
- package/out/runtime/vscode/vscode-config-provider.js +2 -77
- package/out/runtime/vscode/vscode-edit-tracker.js +2 -122
- package/out/runtime/vscode/vscode-logger.js +2 -30
- package/out/runtime/vscode/vscode-permission-handler.js +2 -72
- package/out/scripts/agentide-litellm.sh +0 -1
- package/out/scripts/agentide_litellm_hooks_v1.py +127 -459
- package/out/scripts/analytics/agent-fleet-widget.js +18 -257
- package/out/scripts/analytics/agentide-litellm-otel.config.yaml +0 -4
- package/out/scripts/analytics/duckdbLocator.js +2 -28
- package/out/scripts/analytics/generate-default-charts.py +43 -323
- package/out/scripts/analytics/hotspot-analysis-widget.js +4 -430
- package/out/scripts/analytics/index-traces.py +78 -1036
- package/out/scripts/analytics/session-comparison-widget.js +26 -472
- package/out/scripts/analytics/session-timeline-widget.js +25 -846
- package/out/scripts/analytics/token-cost-flamegraph-widget.js +4 -461
- package/out/scripts/analytics/token-cost-trend-widget.js +4 -534
- package/out/scripts/analytics/workflows-collector.cjs +4 -314
- package/out/scripts/analytics/workflows-daemon.mjs +9 -185
- package/out/scripts/analytics/workflows-widget.cjs +15 -159
- package/out/scripts/analytics/wow-model-widget.js +10 -120
- package/out/scripts/vendor-tools.mjs +2 -233
- package/out/skills/publish.js +3 -178
- package/out/store/github/setup.js +7 -212
- package/out/store/oramaIndex.js +2 -884
- package/out/sync.js +26 -1829
- package/out/terminalLogger.js +5 -358
- package/out/utils/pathSafety.js +2 -60
- package/out/utils/pythonRunner.js +4 -258
- package/out/utils/toolPath.js +2 -83
- package/out/workspaceFolders.js +2 -59
- package/package.json +10 -6
- package/out/agentpack/github.js +0 -547
- package/out/agentpack/index.js +0 -69
- package/out/agentpack/install.js +0 -297
- package/out/agentpack/manager.js +0 -441
- package/out/agentpack/manifest.js +0 -135
- package/out/agentpack/paths.js +0 -521
- package/out/agentpack/publish.js +0 -248
- package/out/agentpack/stores.js +0 -554
- package/out/analytics/dashboard/fleetStatusUtils.js +0 -30
- package/out/analytics/dashboard/fleetTitleUtils.js +0 -68
- package/out/analytics/dashboard/refreshPolicy.js +0 -16
- package/out/analytics/dashboard/scheduledFleetFingerprint.js +0 -22
- package/out/analytics/dashboard/scheduledFleetUtils.js +0 -22
- package/out/analytics/dashboard/sessionEventFleetStats.js +0 -118
- package/out/analytics/dashboard/viewsConfigMerge.js +0 -221
- package/out/analytics/dashboard/webview/codicon.ttf +0 -0
- package/out/analytics/identity.js +0 -0
- package/out/analytics/sessionEvents.js +0 -324
- package/out/auth/callbackUriParse.js +0 -59
- package/out/auth/types.js +0 -12
- package/out/auth-broker/config.js +0 -112
- package/out/auth-broker/jwt.js +0 -105
- package/out/auth-broker/pkce.js +0 -59
- package/out/auth-broker/server.js +0 -681
- package/out/auth-broker/store.js +0 -275
- package/out/authority/warrant.js +0 -206
- package/out/base-prompts/mcp-servers/.mcp.json +0 -13
- package/out/base-prompts/mcp-servers/langfuse-analytics/README.md +0 -142
- package/out/base-prompts/mcp-servers/langfuse-analytics/mcp.json +0 -13
- package/out/base-prompts/mcp-servers/langfuse-analytics/package-lock.json +0 -1192
- package/out/base-prompts/mcp-servers/langfuse-analytics/package.json +0 -23
- package/out/base-prompts/mcp-servers/langfuse-analytics/scripts/run-with-env.sh +0 -12
- package/out/base-prompts/mcp-servers/langfuse-analytics/scripts/sdk-test.js +0 -102
- package/out/base-prompts/mcp-servers/langfuse-analytics/scripts/simple-test.js +0 -137
- package/out/base-prompts/mcp-servers/langfuse-analytics/scripts/test-client.js +0 -118
- package/out/base-prompts/mcp-servers/langfuse-analytics/scripts/test-server.js +0 -209
- package/out/base-prompts/mcp-servers/langfuse-analytics/src/client.ts +0 -431
- package/out/base-prompts/mcp-servers/langfuse-analytics/src/index.ts +0 -270
- package/out/base-prompts/mcp-servers/langfuse-analytics/tsconfig.json +0 -18
- package/out/base-prompts/rules/AGENTS.md +0 -204
- package/out/base-prompts/skills/agent-browser/SKILL.md +0 -102
- package/out/base-prompts/skills/agent-builder/SKILL.md +0 -73
- package/out/base-prompts/skills/agent-builder/references/agent-pack.md +0 -63
- package/out/base-prompts/skills/agent-builder/references/crewai.md +0 -66
- package/out/base-prompts/skills/agent-builder/references/langchain.md +0 -60
- package/out/base-prompts/skills/agent-builder/references/raw-sdk.md +0 -73
- package/out/base-prompts/skills/agent-engine-creator/SKILL.md +0 -586
- package/out/base-prompts/skills/agent-engine-installer/SKILL.md +0 -106
- package/out/base-prompts/skills/agent-engine-installer/scripts/install-engine.mjs +0 -259
- package/out/base-prompts/skills/agent-engine-installer/scripts/install-engine.ps1 +0 -189
- package/out/base-prompts/skills/agenthippo-cli/SKILL.md +0 -69
- package/out/base-prompts/skills/agenthippo-cli/scripts/install.sh +0 -63
- package/out/base-prompts/skills/agentpack-creator/SKILL.md +0 -375
- package/out/base-prompts/skills/agentpack-creator/references/agent-templates.md +0 -261
- package/out/base-prompts/skills/agentpack-creator/references/agents-md-guide.md +0 -195
- package/out/base-prompts/skills/agentpack-creator/scripts/init_pack.py +0 -391
- package/out/base-prompts/skills/agentpack-creator/scripts/init_pack_test.py +0 -294
- package/out/base-prompts/skills/agentpack-testbench/SKILL.md +0 -135
- package/out/base-prompts/skills/algorithmic-art/LICENSE.txt +0 -202
- package/out/base-prompts/skills/algorithmic-art/SKILL.md +0 -405
- package/out/base-prompts/skills/algorithmic-art/templates/generator_template.js +0 -223
- package/out/base-prompts/skills/algorithmic-art/templates/viewer.html +0 -599
- package/out/base-prompts/skills/debug-agent/SKILL.md +0 -83
- package/out/base-prompts/skills/desktop-automation/SKILL.md +0 -166
- package/out/base-prompts/skills/desktop-automation/references/agenthippo-ui-recipes.md +0 -441
- package/out/base-prompts/skills/desktop-automation/scripts/windows-desktop-uia.psm1 +0 -280
- package/out/base-prompts/skills/docx/LICENSE.txt +0 -30
- package/out/base-prompts/skills/docx/SKILL.md +0 -197
- package/out/base-prompts/skills/docx/docx-js.md +0 -350
- package/out/base-prompts/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/dml-chart.xsd +0 -1499
- package/out/base-prompts/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/dml-chartDrawing.xsd +0 -146
- package/out/base-prompts/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/dml-diagram.xsd +0 -1085
- package/out/base-prompts/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/dml-lockedCanvas.xsd +0 -11
- package/out/base-prompts/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/dml-main.xsd +0 -3081
- package/out/base-prompts/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/dml-picture.xsd +0 -23
- package/out/base-prompts/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/dml-spreadsheetDrawing.xsd +0 -185
- package/out/base-prompts/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/dml-wordprocessingDrawing.xsd +0 -287
- package/out/base-prompts/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/pml.xsd +0 -1676
- package/out/base-prompts/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-additionalCharacteristics.xsd +0 -28
- package/out/base-prompts/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-bibliography.xsd +0 -144
- package/out/base-prompts/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-commonSimpleTypes.xsd +0 -174
- package/out/base-prompts/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-customXmlDataProperties.xsd +0 -25
- package/out/base-prompts/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-customXmlSchemaProperties.xsd +0 -18
- package/out/base-prompts/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesCustom.xsd +0 -59
- package/out/base-prompts/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesExtended.xsd +0 -56
- package/out/base-prompts/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesVariantTypes.xsd +0 -195
- package/out/base-prompts/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-math.xsd +0 -582
- package/out/base-prompts/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-relationshipReference.xsd +0 -25
- package/out/base-prompts/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/sml.xsd +0 -4439
- package/out/base-prompts/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/vml-main.xsd +0 -570
- package/out/base-prompts/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/vml-officeDrawing.xsd +0 -509
- package/out/base-prompts/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/vml-presentationDrawing.xsd +0 -12
- package/out/base-prompts/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/vml-spreadsheetDrawing.xsd +0 -108
- package/out/base-prompts/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/vml-wordprocessingDrawing.xsd +0 -96
- package/out/base-prompts/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/wml.xsd +0 -3646
- package/out/base-prompts/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/xml.xsd +0 -116
- package/out/base-prompts/skills/docx/ooxml/schemas/ecma/fouth-edition/opc-contentTypes.xsd +0 -42
- package/out/base-prompts/skills/docx/ooxml/schemas/ecma/fouth-edition/opc-coreProperties.xsd +0 -50
- package/out/base-prompts/skills/docx/ooxml/schemas/ecma/fouth-edition/opc-digSig.xsd +0 -49
- package/out/base-prompts/skills/docx/ooxml/schemas/ecma/fouth-edition/opc-relationships.xsd +0 -33
- package/out/base-prompts/skills/docx/ooxml/schemas/mce/mc.xsd +0 -75
- package/out/base-prompts/skills/docx/ooxml/schemas/microsoft/wml-2010.xsd +0 -560
- package/out/base-prompts/skills/docx/ooxml/schemas/microsoft/wml-2012.xsd +0 -67
- package/out/base-prompts/skills/docx/ooxml/schemas/microsoft/wml-2018.xsd +0 -14
- package/out/base-prompts/skills/docx/ooxml/schemas/microsoft/wml-cex-2018.xsd +0 -20
- package/out/base-prompts/skills/docx/ooxml/schemas/microsoft/wml-cid-2016.xsd +0 -13
- package/out/base-prompts/skills/docx/ooxml/schemas/microsoft/wml-sdtdatahash-2020.xsd +0 -4
- package/out/base-prompts/skills/docx/ooxml/schemas/microsoft/wml-symex-2015.xsd +0 -8
- package/out/base-prompts/skills/docx/ooxml/scripts/pack.py +0 -159
- package/out/base-prompts/skills/docx/ooxml/scripts/unpack.py +0 -29
- package/out/base-prompts/skills/docx/ooxml/scripts/validate.py +0 -69
- package/out/base-prompts/skills/docx/ooxml/scripts/validation/__init__.py +0 -15
- package/out/base-prompts/skills/docx/ooxml/scripts/validation/base.py +0 -951
- package/out/base-prompts/skills/docx/ooxml/scripts/validation/docx.py +0 -274
- package/out/base-prompts/skills/docx/ooxml/scripts/validation/pptx.py +0 -315
- package/out/base-prompts/skills/docx/ooxml/scripts/validation/redlining.py +0 -279
- package/out/base-prompts/skills/docx/ooxml.md +0 -610
- package/out/base-prompts/skills/docx/scripts/__init__.py +0 -1
- package/out/base-prompts/skills/docx/scripts/document.py +0 -1276
- package/out/base-prompts/skills/docx/scripts/templates/comments.xml +0 -3
- package/out/base-prompts/skills/docx/scripts/templates/commentsExtended.xml +0 -3
- package/out/base-prompts/skills/docx/scripts/templates/commentsExtensible.xml +0 -3
- package/out/base-prompts/skills/docx/scripts/templates/commentsIds.xml +0 -3
- package/out/base-prompts/skills/docx/scripts/templates/people.xml +0 -3
- package/out/base-prompts/skills/docx/scripts/utilities.py +0 -374
- package/out/base-prompts/skills/gateway-outbound/SKILL.md +0 -136
- package/out/base-prompts/skills/mcp-builder/LICENSE.txt +0 -202
- package/out/base-prompts/skills/mcp-builder/SKILL.md +0 -328
- package/out/base-prompts/skills/mcp-builder/reference/evaluation.md +0 -602
- package/out/base-prompts/skills/mcp-builder/reference/mcp_best_practices.md +0 -249
- package/out/base-prompts/skills/mcp-builder/reference/node_mcp_server.md +0 -970
- package/out/base-prompts/skills/mcp-builder/reference/python_mcp_server.md +0 -719
- package/out/base-prompts/skills/mcp-builder/scripts/connections.py +0 -151
- package/out/base-prompts/skills/mcp-builder/scripts/evaluation.py +0 -373
- package/out/base-prompts/skills/mcp-builder/scripts/example_evaluation.xml +0 -22
- package/out/base-prompts/skills/mcp-builder/scripts/requirements.txt +0 -2
- package/out/base-prompts/skills/openclaw/SKILL.md +0 -406
- package/out/base-prompts/skills/openclaw/scripts/run-smoke.sh +0 -61
- package/out/base-prompts/skills/pdf/LICENSE.txt +0 -30
- package/out/base-prompts/skills/pdf/SKILL.md +0 -294
- package/out/base-prompts/skills/pdf/forms.md +0 -205
- package/out/base-prompts/skills/pdf/reference.md +0 -612
- package/out/base-prompts/skills/pdf/scripts/check_bounding_boxes.py +0 -70
- package/out/base-prompts/skills/pdf/scripts/check_bounding_boxes_test.py +0 -226
- package/out/base-prompts/skills/pdf/scripts/check_fillable_fields.py +0 -12
- package/out/base-prompts/skills/pdf/scripts/convert_pdf_to_images.py +0 -35
- package/out/base-prompts/skills/pdf/scripts/create_validation_image.py +0 -41
- package/out/base-prompts/skills/pdf/scripts/extract_form_field_info.py +0 -152
- package/out/base-prompts/skills/pdf/scripts/fill_fillable_fields.py +0 -114
- package/out/base-prompts/skills/pdf/scripts/fill_pdf_form_with_annotations.py +0 -108
- package/out/base-prompts/skills/pptx/LICENSE.txt +0 -30
- package/out/base-prompts/skills/pptx/SKILL.md +0 -484
- package/out/base-prompts/skills/pptx/html2pptx.md +0 -625
- package/out/base-prompts/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/dml-chart.xsd +0 -1499
- package/out/base-prompts/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/dml-chartDrawing.xsd +0 -146
- package/out/base-prompts/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/dml-diagram.xsd +0 -1085
- package/out/base-prompts/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/dml-lockedCanvas.xsd +0 -11
- package/out/base-prompts/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/dml-main.xsd +0 -3081
- package/out/base-prompts/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/dml-picture.xsd +0 -23
- package/out/base-prompts/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/dml-spreadsheetDrawing.xsd +0 -185
- package/out/base-prompts/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/dml-wordprocessingDrawing.xsd +0 -287
- package/out/base-prompts/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/pml.xsd +0 -1676
- package/out/base-prompts/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-additionalCharacteristics.xsd +0 -28
- package/out/base-prompts/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-bibliography.xsd +0 -144
- package/out/base-prompts/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-commonSimpleTypes.xsd +0 -174
- package/out/base-prompts/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-customXmlDataProperties.xsd +0 -25
- package/out/base-prompts/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-customXmlSchemaProperties.xsd +0 -18
- package/out/base-prompts/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesCustom.xsd +0 -59
- package/out/base-prompts/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesExtended.xsd +0 -56
- package/out/base-prompts/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesVariantTypes.xsd +0 -195
- package/out/base-prompts/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-math.xsd +0 -582
- package/out/base-prompts/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-relationshipReference.xsd +0 -25
- package/out/base-prompts/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/sml.xsd +0 -4439
- package/out/base-prompts/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/vml-main.xsd +0 -570
- package/out/base-prompts/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/vml-officeDrawing.xsd +0 -509
- package/out/base-prompts/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/vml-presentationDrawing.xsd +0 -12
- package/out/base-prompts/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/vml-spreadsheetDrawing.xsd +0 -108
- package/out/base-prompts/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/vml-wordprocessingDrawing.xsd +0 -96
- package/out/base-prompts/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/wml.xsd +0 -3646
- package/out/base-prompts/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/xml.xsd +0 -116
- package/out/base-prompts/skills/pptx/ooxml/schemas/ecma/fouth-edition/opc-contentTypes.xsd +0 -42
- package/out/base-prompts/skills/pptx/ooxml/schemas/ecma/fouth-edition/opc-coreProperties.xsd +0 -50
- package/out/base-prompts/skills/pptx/ooxml/schemas/ecma/fouth-edition/opc-digSig.xsd +0 -49
- package/out/base-prompts/skills/pptx/ooxml/schemas/ecma/fouth-edition/opc-relationships.xsd +0 -33
- package/out/base-prompts/skills/pptx/ooxml/schemas/mce/mc.xsd +0 -75
- package/out/base-prompts/skills/pptx/ooxml/schemas/microsoft/wml-2010.xsd +0 -560
- package/out/base-prompts/skills/pptx/ooxml/schemas/microsoft/wml-2012.xsd +0 -67
- package/out/base-prompts/skills/pptx/ooxml/schemas/microsoft/wml-2018.xsd +0 -14
- package/out/base-prompts/skills/pptx/ooxml/schemas/microsoft/wml-cex-2018.xsd +0 -20
- package/out/base-prompts/skills/pptx/ooxml/schemas/microsoft/wml-cid-2016.xsd +0 -13
- package/out/base-prompts/skills/pptx/ooxml/schemas/microsoft/wml-sdtdatahash-2020.xsd +0 -4
- package/out/base-prompts/skills/pptx/ooxml/schemas/microsoft/wml-symex-2015.xsd +0 -8
- package/out/base-prompts/skills/pptx/ooxml/scripts/pack.py +0 -159
- package/out/base-prompts/skills/pptx/ooxml/scripts/unpack.py +0 -29
- package/out/base-prompts/skills/pptx/ooxml/scripts/validate.py +0 -69
- package/out/base-prompts/skills/pptx/ooxml/scripts/validation/__init__.py +0 -15
- package/out/base-prompts/skills/pptx/ooxml/scripts/validation/base.py +0 -951
- package/out/base-prompts/skills/pptx/ooxml/scripts/validation/docx.py +0 -274
- package/out/base-prompts/skills/pptx/ooxml/scripts/validation/pptx.py +0 -315
- package/out/base-prompts/skills/pptx/ooxml/scripts/validation/redlining.py +0 -279
- package/out/base-prompts/skills/pptx/ooxml.md +0 -427
- package/out/base-prompts/skills/pptx/scripts/html2pptx.js +0 -984
- package/out/base-prompts/skills/pptx/scripts/inventory.py +0 -1020
- package/out/base-prompts/skills/pptx/scripts/rearrange.py +0 -231
- package/out/base-prompts/skills/pptx/scripts/replace.py +0 -385
- package/out/base-prompts/skills/pptx/scripts/thumbnail.py +0 -450
- package/out/base-prompts/skills/prompt-lab/SKILL.md +0 -149
- package/out/base-prompts/skills/prompt-lab/scripts/query-analytics.sh +0 -2551
- package/out/base-prompts/skills/skill-creator/LICENSE.txt +0 -202
- package/out/base-prompts/skills/skill-creator/SKILL.md +0 -371
- package/out/base-prompts/skills/skill-creator/references/output-patterns.md +0 -82
- package/out/base-prompts/skills/skill-creator/references/workflows.md +0 -28
- package/out/base-prompts/skills/skill-creator/scripts/init_skill.py +0 -303
- package/out/base-prompts/skills/skill-creator/scripts/package_skill.py +0 -110
- package/out/base-prompts/skills/skill-creator/scripts/quick_validate.py +0 -95
- package/out/base-prompts/skills/skill-installer/SKILL.md +0 -251
- package/out/base-prompts/skills/skill-installer/references/store-api.md +0 -219
- package/out/base-prompts/skills/spotlight-dashboard/SKILL.md +0 -83
- package/out/base-prompts/skills/spotlight-insights/SKILL.md +0 -101
- package/out/base-prompts/skills/spotlight-optimize/SKILL.md +0 -93
- package/out/base-prompts/skills/spotlight-optimize/scripts/query-analytics.sh +0 -2551
- package/out/base-prompts/skills/spotlight-query/SKILL.md +0 -314
- package/out/base-prompts/skills/spotlight-query/references/otel-schema.md +0 -163
- package/out/base-prompts/skills/spotlight-query/scripts/query-analytics.sh +0 -2551
- package/out/base-prompts/skills/spotlight-query/scripts/query-analytics.test.sh +0 -744
- package/out/base-prompts/skills/spotlight-query/scripts/test-fixtures/analytics/sessions/sess-test-1.jsonl +0 -14
- package/out/base-prompts/skills/spotlight-query/scripts/test-fixtures/analytics/traces/.duckdb-index.json +0 -7
- package/out/base-prompts/skills/spotlight-query/scripts/test-fixtures/analytics/traces/sample.jsonl +0 -5
- package/out/base-prompts/skills/spotlight-report/SKILL.md +0 -86
- package/out/base-prompts/skills/spotlight-visualize/SKILL.md +0 -92
- package/out/base-prompts/skills/spotlight-visualize/scripts/query-analytics.sh +0 -2551
- package/out/base-prompts/skills/xlsx/LICENSE.txt +0 -30
- package/out/base-prompts/skills/xlsx/SKILL.md +0 -289
- package/out/base-prompts/skills/xlsx/recalc.py +0 -178
- package/out/branding.js +0 -49
- package/out/cli/commands/anywhere.js +0 -66
- package/out/cli/commands/ask.js +0 -246
- package/out/cli/commands/auth-broker.js +0 -24
- package/out/cli/commands/auth.js +0 -219
- package/out/cli/commands/chat.js +0 -357
- package/out/cli/commands/conformance.js +0 -249
- package/out/cli/commands/data-plane.js +0 -151
- package/out/cli/commands/deploy.js +0 -263
- package/out/cli/commands/gateway.js +0 -218
- package/out/cli/commands/idp.js +0 -85
- package/out/cli/commands/pack.js +0 -399
- package/out/cli/commands/probe-custom-engine.js +0 -82
- package/out/cli/commands/sandbox.js +0 -351
- package/out/cli/commands/serve.js +0 -1950
- package/out/cli/commands/store.js +0 -927
- package/out/cli/credential-context.js +0 -108
- package/out/cli/data-plane/broker.js +0 -78
- package/out/cli/data-plane/connector-host.js +0 -121
- package/out/cli/data-plane/egress.js +0 -189
- package/out/cli/data-plane/gateway.js +0 -218
- package/out/cli/data-plane/index.js +0 -153
- package/out/cli/data-plane/registry.js +0 -89
- package/out/cli/data-plane/remote.js +0 -106
- package/out/cli/data-plane/sidecar.js +0 -225
- package/out/cli/deploy/blueprints/registry.js +0 -84
- package/out/cli/deploy/layers/identity.js +0 -175
- package/out/cli/deploy-init.js +0 -3023
- package/out/cli/deps.js +0 -342
- package/out/cli/idp-init.js +0 -338
- package/out/cli/pack-install-ref.js +0 -15
- package/out/cli/pack-loader.js +0 -418
- package/out/cli/pack-runtime.js +0 -61
- package/out/cli/runtime.js +0 -450
- package/out/cli/serve-hardening.js +0 -776
- package/out/cli/state-sync.js +0 -424
- package/out/cli/utils.js +0 -231
- package/out/engines/base.js +0 -188
- package/out/engines/claude.js +0 -1254
- package/out/engines/codex.js +0 -862
- package/out/engines/custom.js +0 -680
- package/out/engines/customEngineIpc.js +0 -95
- package/out/engines/customEngineRunnerEmbed.js +0 -107
- package/out/engines/customRegistry.js +0 -274
- package/out/engines/embeddedCustomEngineRunner.generated.js +0 -9
- package/out/engines/engineApiKey.js +0 -138
- package/out/engines/install.js +0 -372
- package/out/engines/persistentEngineWorker.js +0 -320
- package/out/engines/publish.js +0 -341
- package/out/evidence/sessionShip.js +0 -643
- package/out/gateway/bridge.js +0 -843
- package/out/gateway/cli-agent-id.js +0 -36
- package/out/gateway/client.js +0 -336
- package/out/gateway/client.test.js +0 -395
- package/out/gateway/gateway-scope-errors.js +0 -14
- package/out/gateway/gatewayBackup.js +0 -76
- package/out/gateway/installSource.js +0 -68
- package/out/gateway/lifecycle.js +0 -3190
- package/out/gateway/openclaw-channel-routing.js +0 -317
- package/out/gateway/outboundHandles.js +0 -373
- package/out/gateway/outboundPaths.js +0 -75
- package/out/gateway/outboundSend.js +0 -254
- package/out/gateway/protocol.js +0 -48
- package/out/gateway/secretsPreflight.js +0 -90
- package/out/gateway/sessionRouting.js +0 -77
- package/out/headless/index.js +0 -546
- package/out/headless/sync.js +0 -473
- package/out/live-e2e/mcp-demo/server.mjs +0 -32
- package/out/live-probe/connector.mjs +0 -8
- package/out/live-probe/env-report.mjs +0 -8
- package/out/mcp.js +0 -654
- package/out/pack/caps.js +0 -271
- package/out/pack/governor.js +0 -326
- package/out/pack/lease-schema.js +0 -273
- package/out/pack/lease.js +0 -682
- package/out/pack/reconcile.js +0 -217
- package/out/pack/signature.js +0 -839
- package/out/permissions.js +0 -108
- package/out/permissions.mapping.yaml +0 -68
- package/out/registry/index.js +0 -332
- package/out/runner/host.js +0 -141
- package/out/runner/protocol.js +0 -128
- package/out/runner/remoteEngine.js +0 -166
- package/out/runtime/interfaces/config-provider.js +0 -32
- package/out/runtime/interfaces/edit-tracker.js +0 -24
- package/out/runtime/interfaces/index.js +0 -29
- package/out/runtime/interfaces/logger.js +0 -40
- package/out/runtime/interfaces/permission-handler.js +0 -66
- package/out/runtime/interfaces/session-store.js +0 -342
- package/out/runtime/vscode/vscode-session-store.js +0 -22
- package/out/sandbox/containerDriver.js +0 -572
- package/out/sandbox/processDriver.js +0 -318
- package/out/sandbox/sandboxRunner.js +0 -303
- package/out/sandbox/support.js +0 -301
- package/out/sandbox/types.js +0 -55
- package/out/scripts/analytics/fleet-local-adapter.js +0 -246
- package/out/scripts/analytics/live-fleet-4-collector.cjs +0 -315
- package/out/scripts/analytics/live-fleet-4-daemon.mjs +0 -134
- package/out/scripts/analytics/live-fleet-4-widget.cjs +0 -433
- package/out/scripts/gateway/Dockerfile.serve +0 -45
- package/out/scripts/gateway/docker-serve.sh +0 -248
- package/out/scripts/gateway/setup-gateway-server.ps1 +0 -953
- package/out/scripts/gateway/setup-gateway-server.sh +0 -935
- package/out/session.js +0 -272
- package/out/shared/agentEngines.js +0 -170
- package/out/shared/index.js +0 -109
- package/out/shared/models.js +0 -136
- package/out/shared/providers.js +0 -205
- package/out/spotlight/install.js +0 -345
- package/out/spotlight/publish.js +0 -544
- package/out/spotlight/yaml.js +0 -31
- package/out/store/index.js +0 -786
- package/out/store/shared.js +0 -241
- package/out/test/agentEnvFile.test.js +0 -97
- package/out/test/agentYaml.test.js +0 -373
- package/out/test/agentpack.test.js +0 -639
- package/out/test/analytics-lifecycle.test.js +0 -174
- package/out/test/analytics.test.js +0 -756
- package/out/test/auth-baseurl.test.js +0 -132
- package/out/test/auth-bootstrapApply.test.js +0 -123
- package/out/test/auth-broker-hardening.test.js +0 -227
- package/out/test/auth-broker.test.js +0 -541
- package/out/test/auth-callback.test.js +0 -62
- package/out/test/blueprint-registry.test.js +0 -105
- package/out/test/branding.test.js +0 -242
- package/out/test/caps-governor.test.js +0 -460
- package/out/test/cli-agent-id.test.js +0 -55
- package/out/test/codex-new-file-survival.test.js +0 -119
- package/out/test/configuration-scopes.test.js +0 -131
- package/out/test/credential-context.test.js +0 -160
- package/out/test/custom-env-layering.test.js +0 -151
- package/out/test/customEngineInstall.test.js +0 -100
- package/out/test/customRegistry.test.js +0 -122
- package/out/test/data-plane-engine-wiring.test.js +0 -282
- package/out/test/data-plane.test.js +0 -624
- package/out/test/deploy-init.test.js +0 -860
- package/out/test/deps-auto-install.test.js +0 -81
- package/out/test/egress-broker.test.js +0 -172
- package/out/test/engine-integration.test.js +0 -297
- package/out/test/enginePaths.test.js +0 -195
- package/out/test/enginePublish.test.js +0 -82
- package/out/test/fleetDisplayUtils.test.js +0 -100
- package/out/test/gateway-client.test.js +0 -409
- package/out/test/gateway-install-source.test.js +0 -111
- package/out/test/gateway-lifecycle.test.js +0 -608
- package/out/test/gateway-outbound.test.js +0 -538
- package/out/test/gateway-reset-mappings.test.js +0 -221
- package/out/test/gateway-route-index.test.js +0 -235
- package/out/test/gateway-secrets-preflight.test.js +0 -127
- package/out/test/gateway-session-routing.test.js +0 -65
- package/out/test/gateway-session-store.test.js +0 -216
- package/out/test/github.test.js +0 -232
- package/out/test/governor-hardening.test.js +0 -222
- package/out/test/headless-sync.test.js +0 -301
- package/out/test/helpers/manifest-fixtures.js +0 -27
- package/out/test/idp-init.test.js +0 -151
- package/out/test/lease-signature.test.js +0 -357
- package/out/test/lease-trust-root.test.js +0 -273
- package/out/test/lease-validation.test.js +0 -224
- package/out/test/lease.test.js +0 -114
- package/out/test/localFleetAdapter.test.js +0 -91
- package/out/test/mcp-obo.test.js +0 -107
- package/out/test/mcp.test.js +0 -389
- package/out/test/model-routing.test.js +0 -133
- package/out/test/openclaw-channel-routing.test.js +0 -191
- package/out/test/pack-admission.test.js +0 -216
- package/out/test/pack-digest-parity.test.js +0 -140
- package/out/test/pack-reconcile.test.js +0 -183
- package/out/test/pack-signature-artifact.test.js +0 -103
- package/out/test/pack-signature.test.js +0 -176
- package/out/test/pack-trust-root.test.js +0 -202
- package/out/test/pathSafety.test.js +0 -63
- package/out/test/permissions.test.js +0 -175
- package/out/test/persistent-engine-worker.test.js +0 -220
- package/out/test/refreshPolicy.test.js +0 -60
- package/out/test/registry.test.js +0 -351
- package/out/test/remote-trust.test.js +0 -242
- package/out/test/runner-protocol.test.js +0 -249
- package/out/test/runtime.test.js +0 -349
- package/out/test/sandbox-driver.test.js +0 -553
- package/out/test/sandbox-hardening.test.js +0 -406
- package/out/test/sandbox-runner.test.js +0 -343
- package/out/test/sandbox-scope.test.js +0 -449
- package/out/test/scheduledFleetStorage.test.js +0 -72
- package/out/test/serve-cron-acceptance.test.js +0 -370
- package/out/test/serve-gateway-turn.test.js +0 -389
- package/out/test/serve-hardening.test.js +0 -591
- package/out/test/serveClient.test.js +0 -209
- package/out/test/sessionJsonlFleet.test.js +0 -78
- package/out/test/sessionShip.test.js +0 -618
- package/out/test/shared.test.js +0 -173
- package/out/test/skills-mcp.test.js +0 -299
- package/out/test/spotlightPublish.test.js +0 -327
- package/out/test/spotlightYaml.test.js +0 -58
- package/out/test/state-sync-peer.test.js +0 -156
- package/out/test/store-install-safe.test.js +0 -69
- package/out/test/store.test.js +0 -331
- package/out/test/stores.test.js +0 -504
- package/out/test/sync-switch.test.js +0 -251
- package/out/test/teamViewDiscovery.test.js +0 -90
- package/out/test/tls-bypass.test.js +0 -90
- package/out/test/toolPath.test.js +0 -120
- package/out/test/utils.test.js +0 -308
- package/out/test/viewsConfigMerge.test.js +0 -180
- package/out/test/warrant.test.js +0 -138
- package/out/test/windows-command-line.test.js +0 -75
- package/out/utils/agentEnvFile.js +0 -131
- package/out/utils/binary.js +0 -175
- package/out/utils/index.js +0 -37
- package/out/utils/network.js +0 -74
- package/out/utils/paths.js +0 -116
- package/out/utils/pid.js +0 -180
- package/out/utils/resolveNodeBinary.js +0 -168
- package/out/utils/windowsCommandLine.js +0 -29
- package/out/utils.js +0 -968
|
@@ -1,27 +1,5 @@
|
|
|
1
|
-
|
|
2
|
-
#
|
|
3
|
-
# Licensed under the MIT License. See LICENSE.txt in the project root for license information.
|
|
4
|
-
#---------------------------------------------------------------------------------------------
|
|
5
|
-
"""
|
|
6
|
-
AgentIDE LiteLLM proxy hooks - Clean structured assistant output with separate input/output
|
|
7
|
-
Mimics langfuse_otel style: gen_ai.input.messages (history) + gen_ai.output.messages (new reply only)
|
|
8
|
-
|
|
9
|
-
Usage in litellm config.yaml:
|
|
10
|
-
callbacks: ["agentide_litellm_hooks_v1.clean_separated_otel_output"]
|
|
11
|
-
|
|
12
|
-
PRIVACY DEFAULT (PLAN/compliance-req.md §6 "data minimization"):
|
|
13
|
-
Prompt and completion CONTENT is NOT captured unless an operator opts in with
|
|
14
|
-
|
|
15
|
-
AGENTHIPPO_EVIDENCE_CONTENT_CAPTURE=1
|
|
16
|
-
|
|
17
|
-
Without the opt-in every span carries metadata only — model, tokens, latency, status — plus a
|
|
18
|
-
sha256 + byte length of the content so a later disclosure can still be *matched* against a
|
|
19
|
-
span without the span holding the text. This is enforced here at span-write time, in addition
|
|
20
|
-
to `turn_off_message_logging: true` in the config, so a config that re-enables message logging
|
|
21
|
-
without the env opt-in still produces redacted spans (defence in depth: the config is edited by
|
|
22
|
-
hand far more often than this file).
|
|
23
|
-
"""
|
|
24
|
-
|
|
1
|
+
# Copyright (c) 2026 AgentHippo.ai. All rights reserved.
|
|
2
|
+
# Proprietary and confidential. See LICENSE.
|
|
25
3
|
from __future__ import annotations
|
|
26
4
|
from typing import Any
|
|
27
5
|
import ast
|
|
@@ -30,88 +8,33 @@ import json
|
|
|
30
8
|
import os
|
|
31
9
|
import re
|
|
32
10
|
import sys
|
|
33
|
-
|
|
34
11
|
from litellm.integrations.custom_logger import CustomLogger
|
|
35
12
|
from litellm.integrations.opentelemetry import OpenTelemetry
|
|
36
13
|
from opentelemetry.trace import Status, StatusCode
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
# Content capture opt-in (fix: full capture used to be the default)
|
|
43
|
-
# -----------------------------------------------------------------------------
|
|
44
|
-
|
|
45
|
-
CONTENT_CAPTURE_ENV = "AGENTHIPPO_EVIDENCE_CONTENT_CAPTURE"
|
|
46
|
-
|
|
47
|
-
_TRUTHY = frozenset({"1", "true", "yes", "on"})
|
|
48
|
-
|
|
49
|
-
# Span attributes that can carry prompt/completion text. Mirrors CONTENT_ATTRS in
|
|
50
|
-
# @agenthippo-ai/evidence-ship so the two redaction points cannot drift silently.
|
|
51
|
-
_CONTENT_ATTR_RE = re.compile(
|
|
52
|
-
r"^(gen_ai\.(input|output)\.messages"
|
|
53
|
-
r"|gen_ai\.(prompt|completion)"
|
|
54
|
-
r"|gen_ai\.system_instructions"
|
|
55
|
-
r"|llm\.(prompts|responses)"
|
|
56
|
-
r"|llm\.request\.functions\."
|
|
57
|
-
r"|error\.(message|stack_trace)"
|
|
58
|
-
r"|hidden_params"
|
|
59
|
-
r"|metadata\.requester_custom_headers)"
|
|
60
|
-
)
|
|
61
|
-
|
|
14
|
+
_OTEL_TEXT_MAX_LEN = 4000
|
|
15
|
+
_OTEL_PREVIEW_LEN = 180
|
|
16
|
+
CONTENT_CAPTURE_ENV = 'AGENTHIPPO_EVIDENCE_CONTENT_CAPTURE'
|
|
17
|
+
_TRUTHY = frozenset({'1', 'true', 'yes', 'on'})
|
|
18
|
+
_CONTENT_ATTR_RE = re.compile('^(gen_ai\\.(input|output)\\.messages|gen_ai\\.(prompt|completion)|gen_ai\\.system_instructions|llm\\.(prompts|responses)|llm\\.request\\.functions\\.|error\\.(message|stack_trace)|hidden_params|metadata\\.requester_custom_headers)')
|
|
62
19
|
_capture_banner_printed = False
|
|
63
20
|
|
|
64
|
-
|
|
65
21
|
def _content_capture_enabled() -> bool:
|
|
66
|
-
|
|
67
|
-
return (os.environ.get(CONTENT_CAPTURE_ENV, "") or "").strip().lower() in _TRUTHY
|
|
68
|
-
|
|
22
|
+
return (os.environ.get(CONTENT_CAPTURE_ENV, '') or '').strip().lower() in _TRUTHY
|
|
69
23
|
|
|
70
24
|
def _announce_content_capture() -> None:
|
|
71
|
-
"""Log loudly, once, whichever mode is in force — silence is how this got shipped on."""
|
|
72
25
|
global _capture_banner_printed
|
|
73
26
|
if _capture_banner_printed:
|
|
74
27
|
return
|
|
75
28
|
_capture_banner_printed = True
|
|
76
29
|
if _content_capture_enabled():
|
|
77
|
-
print(
|
|
78
|
-
"\n"
|
|
79
|
-
"!! AGENTHIPPO EVIDENCE: FULL PROMPT/COMPLETION CAPTURE IS ON !!\n"
|
|
80
|
-
f"!! {CONTENT_CAPTURE_ENV} is set. Every span will carry prompt and completion text.\n"
|
|
81
|
-
"!! Unset it to return to metadata-only (sha256 + length) spans.\n",
|
|
82
|
-
file=sys.stderr,
|
|
83
|
-
flush=True,
|
|
84
|
-
)
|
|
30
|
+
print(f'\n!! AGENTHIPPO EVIDENCE: FULL PROMPT/COMPLETION CAPTURE IS ON !!\n!! {CONTENT_CAPTURE_ENV} is set. Every span will carry prompt and completion text.\n!! Unset it to return to metadata-only (sha256 + length) spans.\n', file=sys.stderr, flush=True)
|
|
85
31
|
else:
|
|
86
|
-
print(
|
|
87
|
-
f"[agentide-otel] content capture OFF (metadata + sha256 only). "
|
|
88
|
-
f"Set {CONTENT_CAPTURE_ENV}=1 to record prompts/completions.",
|
|
89
|
-
file=sys.stderr,
|
|
90
|
-
flush=True,
|
|
91
|
-
)
|
|
92
|
-
|
|
32
|
+
print(f'[agentide-otel] content capture OFF (metadata + sha256 only). Set {CONTENT_CAPTURE_ENV}=1 to record prompts/completions.', file=sys.stderr, flush=True)
|
|
93
33
|
|
|
94
34
|
def _sha256(text: str) -> str:
|
|
95
|
-
return hashlib.sha256(text.encode(
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
# Correlation IDs Spotlight reads from the requester header blob (see FLEET_DATA_CONTRACT.md).
|
|
99
|
-
# These are minted by AgentHippo clients and carry no user content, so with capture off the
|
|
100
|
-
# blob is reduced to exactly these keys rather than dropped — unknown x-* headers from clients
|
|
101
|
-
# we do not control (e.g. x-api-key) still never reach the span in plaintext.
|
|
102
|
-
_METADATA_HEADERS_KEY = "metadata.requester_custom_headers"
|
|
103
|
-
|
|
104
|
-
_METADATA_HEADER_ALLOWLIST = frozenset({
|
|
105
|
-
"x-litellm-metadata-session-id",
|
|
106
|
-
"x-litellm-metadata-request-id",
|
|
107
|
-
"x-litellm-metadata-user-id",
|
|
108
|
-
"x-litellm-metadata-project-id",
|
|
109
|
-
"x-litellm-metadata-agent-id",
|
|
110
|
-
"x-litellm-metadata-agent-version",
|
|
111
|
-
"x-litellm-metadata-source",
|
|
112
|
-
"x-litellm-metadata-engine",
|
|
113
|
-
})
|
|
114
|
-
|
|
35
|
+
return hashlib.sha256(text.encode('utf-8', 'replace')).hexdigest()
|
|
36
|
+
_METADATA_HEADERS_KEY = 'metadata.requester_custom_headers'
|
|
37
|
+
_METADATA_HEADER_ALLOWLIST = frozenset({'x-litellm-metadata-session-id', 'x-litellm-metadata-request-id', 'x-litellm-metadata-user-id', 'x-litellm-metadata-project-id', 'x-litellm-metadata-agent-id', 'x-litellm-metadata-agent-version', 'x-litellm-metadata-source', 'x-litellm-metadata-engine'})
|
|
115
38
|
|
|
116
39
|
def _parse_attr_dict(value: Any) -> Any:
|
|
117
40
|
if not isinstance(value, str):
|
|
@@ -124,58 +47,31 @@ def _parse_attr_dict(value: Any) -> Any:
|
|
|
124
47
|
except ValueError:
|
|
125
48
|
return None
|
|
126
49
|
|
|
127
|
-
|
|
128
50
|
def _allowlisted_headers(headers: dict) -> dict:
|
|
129
|
-
return {
|
|
130
|
-
k.lower(): v for k, v in headers.items()
|
|
131
|
-
if isinstance(k, str) and k.lower() in _METADATA_HEADER_ALLOWLIST
|
|
132
|
-
}
|
|
133
|
-
|
|
51
|
+
return {k.lower(): v for k, v in headers.items() if isinstance(k, str) and k.lower() in _METADATA_HEADER_ALLOWLIST}
|
|
134
52
|
|
|
135
53
|
def _filtered_metadata_headers(value: Any) -> str | None:
|
|
136
|
-
"""Reduce the header blob to allowlisted keys, keeping the str(dict) repr format that
|
|
137
|
-
the DuckDB extraction regexes parse. None when nothing allowlisted survives."""
|
|
138
54
|
headers = _parse_attr_dict(value)
|
|
139
55
|
if not isinstance(headers, dict):
|
|
140
56
|
return None
|
|
141
57
|
kept = _allowlisted_headers(headers)
|
|
142
58
|
return str(kept) if kept else None
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
# LiteLLM also embeds the raw request headers as metadata.requester_metadata['headers'] on some
|
|
146
|
-
# routes (observed on /v1/chat/completions; absent on /v1/messages). The attribute's scalar keys
|
|
147
|
-
# (session_id, request_id, …) are a fallback landing spot the extraction chain parses, so the
|
|
148
|
-
# attribute itself must survive — only its embedded header dict gets the allowlist treatment.
|
|
149
|
-
_REQUESTER_METADATA_KEY = "metadata.requester_metadata"
|
|
150
|
-
|
|
59
|
+
_REQUESTER_METADATA_KEY = 'metadata.requester_metadata'
|
|
151
60
|
|
|
152
61
|
def _scrub_requester_metadata(value: Any) -> Any:
|
|
153
|
-
"""Scrubbed value, or None (caller drops the attribute) when an embedded header dict
|
|
154
|
-
cannot be safely isolated — reprs with non-literal values defeat ast.literal_eval, and
|
|
155
|
-
passing those through unparsed would leak the very headers this exists to remove."""
|
|
156
62
|
md = _parse_attr_dict(value)
|
|
157
|
-
if isinstance(md, dict) and isinstance(md.get(
|
|
158
|
-
return str({**md,
|
|
63
|
+
if isinstance(md, dict) and isinstance(md.get('headers'), dict):
|
|
64
|
+
return str({**md, 'headers': _allowlisted_headers(md['headers'])})
|
|
159
65
|
if isinstance(md, dict):
|
|
160
66
|
return value
|
|
161
|
-
return None if
|
|
162
|
-
|
|
67
|
+
return None if 'headers' in str(value) else value
|
|
163
68
|
|
|
164
69
|
class _RedactingSpan:
|
|
165
|
-
"""
|
|
166
|
-
Span proxy that drops content-bearing attributes and replaces them with a sha256 + length.
|
|
167
|
-
|
|
168
|
-
LiteLLM's own `set_attributes()` writes `gen_ai.input.messages` (the full history) and
|
|
169
|
-
friends. We cannot remove an attribute after the SDK has recorded it, so the only place to
|
|
170
|
-
stop it is between LiteLLM and the SDK — here. Everything that is not content passes through
|
|
171
|
-
untouched, so the span keeps model, tokens, latency, status, and the requester metadata the
|
|
172
|
-
evidence index reads.
|
|
173
|
-
"""
|
|
174
70
|
|
|
175
71
|
def __init__(self, span: Any) -> None:
|
|
176
72
|
self._span = span
|
|
177
73
|
|
|
178
|
-
def __getattr__(self, name: str) -> Any:
|
|
74
|
+
def __getattr__(self, name: str) -> Any:
|
|
179
75
|
return getattr(self._span, name)
|
|
180
76
|
|
|
181
77
|
def set_attribute(self, key: Any, value: Any) -> Any:
|
|
@@ -186,9 +82,9 @@ class _RedactingSpan:
|
|
|
186
82
|
return self._span.set_attribute(key, scrubbed)
|
|
187
83
|
if isinstance(key, str) and _CONTENT_ATTR_RE.match(key):
|
|
188
84
|
text = value if isinstance(value, str) else json.dumps(value, ensure_ascii=False, default=str)
|
|
189
|
-
self._span.set_attribute(f
|
|
190
|
-
self._span.set_attribute(f
|
|
191
|
-
self._span.set_attribute(f
|
|
85
|
+
self._span.set_attribute(f'{key}.sha256', _sha256(text))
|
|
86
|
+
self._span.set_attribute(f'{key}.length', len(text))
|
|
87
|
+
self._span.set_attribute(f'{key}.redacted', True)
|
|
192
88
|
if key == _METADATA_HEADERS_KEY:
|
|
193
89
|
kept = _filtered_metadata_headers(value)
|
|
194
90
|
if kept is not None:
|
|
@@ -203,7 +99,7 @@ class _RedactingSpan:
|
|
|
203
99
|
return None
|
|
204
100
|
return self._span.set_attributes(attributes)
|
|
205
101
|
|
|
206
|
-
def add_event(self, name: Any, attributes: Any
|
|
102
|
+
def add_event(self, name: Any, attributes: Any=None, **kwargs: Any) -> Any:
|
|
207
103
|
if isinstance(attributes, dict):
|
|
208
104
|
filtered = {}
|
|
209
105
|
for key, value in attributes.items():
|
|
@@ -213,8 +109,8 @@ class _RedactingSpan:
|
|
|
213
109
|
filtered[key] = scrubbed
|
|
214
110
|
elif isinstance(key, str) and _CONTENT_ATTR_RE.match(key):
|
|
215
111
|
text = value if isinstance(value, str) else json.dumps(value, ensure_ascii=False, default=str)
|
|
216
|
-
filtered[f
|
|
217
|
-
filtered[f
|
|
112
|
+
filtered[f'{key}.sha256'] = _sha256(text)
|
|
113
|
+
filtered[f'{key}.length'] = len(text)
|
|
218
114
|
if key == _METADATA_HEADERS_KEY:
|
|
219
115
|
kept = _filtered_metadata_headers(value)
|
|
220
116
|
if kept is not None:
|
|
@@ -224,18 +120,16 @@ class _RedactingSpan:
|
|
|
224
120
|
attributes = filtered
|
|
225
121
|
return self._span.add_event(name, attributes, **kwargs)
|
|
226
122
|
|
|
227
|
-
|
|
228
|
-
def _read_field(obj: Any, key: str, default: Any = None) -> Any:
|
|
123
|
+
def _read_field(obj: Any, key: str, default: Any=None) -> Any:
|
|
229
124
|
if isinstance(obj, dict):
|
|
230
125
|
return obj.get(key, default)
|
|
231
126
|
return getattr(obj, key, default)
|
|
232
127
|
|
|
233
|
-
|
|
234
128
|
def _content_blocks_to_text(content: Any) -> str:
|
|
235
129
|
if isinstance(content, str):
|
|
236
130
|
return content
|
|
237
131
|
if not isinstance(content, (list, tuple)):
|
|
238
|
-
return
|
|
132
|
+
return ''
|
|
239
133
|
parts: list[str] = []
|
|
240
134
|
for block in content:
|
|
241
135
|
if isinstance(block, str):
|
|
@@ -244,90 +138,65 @@ def _content_blocks_to_text(content: Any) -> str:
|
|
|
244
138
|
continue
|
|
245
139
|
if not isinstance(block, dict):
|
|
246
140
|
continue
|
|
247
|
-
text = block.get(
|
|
141
|
+
text = block.get('text') or block.get('output_text') or block.get('content')
|
|
248
142
|
if isinstance(text, str) and text.strip():
|
|
249
143
|
parts.append(text)
|
|
250
|
-
return
|
|
251
|
-
|
|
144
|
+
return '\n'.join(parts)
|
|
252
145
|
|
|
253
146
|
def _extract_assistant_text(response_obj: Any) -> str:
|
|
254
147
|
if response_obj is None:
|
|
255
|
-
return
|
|
256
|
-
|
|
257
|
-
choices = _read_field(response_obj, "choices")
|
|
148
|
+
return ''
|
|
149
|
+
choices = _read_field(response_obj, 'choices')
|
|
258
150
|
if isinstance(choices, list) and choices:
|
|
259
|
-
msg = _read_field(choices[0],
|
|
260
|
-
content = _read_field(msg,
|
|
151
|
+
msg = _read_field(choices[0], 'message')
|
|
152
|
+
content = _read_field(msg, 'content')
|
|
261
153
|
text = _content_blocks_to_text(content)
|
|
262
154
|
if text.strip():
|
|
263
155
|
return text
|
|
264
|
-
|
|
265
|
-
for key in ("output_text", "content", "output", "text"):
|
|
156
|
+
for key in ('output_text', 'content', 'output', 'text'):
|
|
266
157
|
val = _read_field(response_obj, key)
|
|
267
158
|
if val:
|
|
268
159
|
text = _content_blocks_to_text(val)
|
|
269
160
|
if text.strip():
|
|
270
161
|
return text
|
|
271
|
-
return
|
|
272
|
-
|
|
162
|
+
return ''
|
|
273
163
|
|
|
274
164
|
def _extract_text_from_success_kwargs(kwargs: dict) -> str:
|
|
275
|
-
text = _extract_assistant_text(kwargs.get(
|
|
165
|
+
text = _extract_assistant_text(kwargs.get('response_obj'))
|
|
276
166
|
if text:
|
|
277
167
|
return text
|
|
278
|
-
for k in (
|
|
168
|
+
for k in ('complete_response', 'model_response', 'response', 'original_response'):
|
|
279
169
|
candidate = kwargs.get(k)
|
|
280
170
|
if candidate:
|
|
281
171
|
text = _extract_assistant_text(candidate)
|
|
282
172
|
if text:
|
|
283
173
|
return text
|
|
284
|
-
return
|
|
285
|
-
|
|
174
|
+
return ''
|
|
286
175
|
|
|
287
176
|
def _make_output_messages_structured(text: str) -> list:
|
|
288
177
|
if not text.strip():
|
|
289
178
|
return []
|
|
290
179
|
trimmed = text[:_OTEL_TEXT_MAX_LEN]
|
|
291
|
-
|
|
292
|
-
return [{"role": "assistant", "parts": [{"type": "text", "content": trimmed}]}]
|
|
293
|
-
|
|
180
|
+
return [{'role': 'assistant', 'parts': [{'type': 'text', 'content': trimmed}]}]
|
|
294
181
|
|
|
295
182
|
def _model_name(model: Any) -> str:
|
|
296
|
-
return model.lower() if isinstance(model, str) else
|
|
297
|
-
|
|
183
|
+
return model.lower() if isinstance(model, str) else ''
|
|
298
184
|
|
|
299
185
|
def _is_reasoning_unsupported_model(model: Any) -> bool:
|
|
300
|
-
|
|
301
|
-
return "gpt-4.1-nano" in _model_name(model)
|
|
302
|
-
|
|
186
|
+
return 'gpt-4.1-nano' in _model_name(model)
|
|
303
187
|
|
|
304
188
|
def _is_anthropic_route(model: Any) -> bool:
|
|
305
|
-
"""True when the request will be forwarded to Anthropic."""
|
|
306
189
|
name = _model_name(model)
|
|
307
|
-
return
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
# Every model known to reject the Codex built-in web-search tools. Additive by policy: a
|
|
311
|
-
# deployment that still routes to an older model keeps its sanitizer when a newer one is added,
|
|
312
|
-
# because a dropped entry fails as upstream 400s on live traffic, not as a test failure.
|
|
313
|
-
_WEB_SEARCH_UNSUPPORTED_MODELS = ("gpt-5.6-luna", "gpt-4.1-nano")
|
|
314
|
-
|
|
190
|
+
return 'claude' in name or 'anthropic' in name
|
|
191
|
+
_WEB_SEARCH_UNSUPPORTED_MODELS = ('gpt-5.6-luna', 'gpt-4.1-nano')
|
|
315
192
|
|
|
316
193
|
def _is_web_search_unsupported_model(model: Any) -> bool:
|
|
317
|
-
"""True when the upstream model rejects Codex web-search tools."""
|
|
318
194
|
name = _model_name(model)
|
|
319
|
-
return any(m in name for m in _WEB_SEARCH_UNSUPPORTED_MODELS)
|
|
320
|
-
|
|
195
|
+
return any((m in name for m in _WEB_SEARCH_UNSUPPORTED_MODELS))
|
|
196
|
+
_OPENAI_RESPONSES_ONLY_KEYS = ('client_metadata',)
|
|
197
|
+
_UNSUPPORTED_TOOL_IDENTIFIERS = frozenset({'web_search_preview', 'web_search'})
|
|
321
198
|
|
|
322
|
-
|
|
323
|
-
_OPENAI_RESPONSES_ONLY_KEYS = ("client_metadata",)
|
|
324
|
-
|
|
325
|
-
# Codex built-in tools not supported on all OpenAI models.
|
|
326
|
-
_UNSUPPORTED_TOOL_IDENTIFIERS = frozenset({"web_search_preview", "web_search"})
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
def _strip_key_recursive(data: Any, key: str, _visited: set[int] | None = None) -> None:
|
|
330
|
-
"""Remove a key in nested dict/list payloads (bounded; avoids cyclic graphs)."""
|
|
199
|
+
def _strip_key_recursive(data: Any, key: str, _visited: set[int] | None=None) -> None:
|
|
331
200
|
if _visited is None:
|
|
332
201
|
_visited = set()
|
|
333
202
|
if isinstance(data, dict):
|
|
@@ -347,58 +216,49 @@ def _strip_key_recursive(data: Any, key: str, _visited: set[int] | None = None)
|
|
|
347
216
|
for item in data:
|
|
348
217
|
_strip_key_recursive(item, key, _visited)
|
|
349
218
|
|
|
350
|
-
|
|
351
219
|
def _resolve_route_model(payload: dict, model: Any) -> str:
|
|
352
220
|
name = _model_name(model)
|
|
353
221
|
if name:
|
|
354
222
|
return name
|
|
355
|
-
for candidate in (
|
|
356
|
-
payload.get("model"),
|
|
357
|
-
payload.get("model_group"),
|
|
358
|
-
payload.get("model_name"),
|
|
359
|
-
payload.get("deployment"),
|
|
360
|
-
):
|
|
223
|
+
for candidate in (payload.get('model'), payload.get('model_group'), payload.get('model_name'), payload.get('deployment')):
|
|
361
224
|
name = _model_name(candidate)
|
|
362
225
|
if name:
|
|
363
226
|
return name
|
|
364
|
-
metadata = payload.get(
|
|
227
|
+
metadata = payload.get('metadata')
|
|
365
228
|
if isinstance(metadata, dict):
|
|
366
|
-
for candidate in (metadata.get(
|
|
229
|
+
for candidate in (metadata.get('model_group'), metadata.get('model')):
|
|
367
230
|
name = _model_name(candidate)
|
|
368
231
|
if name:
|
|
369
232
|
return name
|
|
370
|
-
return
|
|
371
|
-
|
|
233
|
+
return ''
|
|
372
234
|
|
|
373
235
|
def _tool_is_unsupported(tool: Any) -> bool:
|
|
374
236
|
if not isinstance(tool, dict):
|
|
375
237
|
return False
|
|
376
|
-
tool_type = tool.get(
|
|
238
|
+
tool_type = tool.get('type')
|
|
377
239
|
if isinstance(tool_type, str) and tool_type in _UNSUPPORTED_TOOL_IDENTIFIERS:
|
|
378
240
|
return True
|
|
379
|
-
name = tool.get(
|
|
241
|
+
name = tool.get('name')
|
|
380
242
|
if isinstance(name, str) and name in _UNSUPPORTED_TOOL_IDENTIFIERS:
|
|
381
243
|
return True
|
|
382
|
-
fn = tool.get(
|
|
244
|
+
fn = tool.get('function')
|
|
383
245
|
if isinstance(fn, dict):
|
|
384
|
-
fn_name = fn.get(
|
|
246
|
+
fn_name = fn.get('name')
|
|
385
247
|
if isinstance(fn_name, str) and fn_name in _UNSUPPORTED_TOOL_IDENTIFIERS:
|
|
386
248
|
return True
|
|
387
249
|
return False
|
|
388
250
|
|
|
389
|
-
|
|
390
251
|
def _strip_unsupported_tools(data: dict) -> None:
|
|
391
|
-
tools = data.get(
|
|
252
|
+
tools = data.get('tools')
|
|
392
253
|
if not isinstance(tools, list):
|
|
393
254
|
return
|
|
394
255
|
filtered = [tool for tool in tools if not _tool_is_unsupported(tool)]
|
|
395
256
|
if len(filtered) == len(tools):
|
|
396
257
|
return
|
|
397
258
|
if filtered:
|
|
398
|
-
data[
|
|
259
|
+
data['tools'] = filtered
|
|
399
260
|
else:
|
|
400
|
-
data.pop(
|
|
401
|
-
|
|
261
|
+
data.pop('tools', None)
|
|
402
262
|
|
|
403
263
|
def _apply_codex_proxy_sanitizers(data: dict, model: Any) -> None:
|
|
404
264
|
route_model = _resolve_route_model(data, model)
|
|
@@ -408,14 +268,7 @@ def _apply_codex_proxy_sanitizers(data: dict, model: Any) -> None:
|
|
|
408
268
|
if _is_web_search_unsupported_model(route_model):
|
|
409
269
|
_strip_unsupported_tools(data)
|
|
410
270
|
|
|
411
|
-
|
|
412
|
-
def _strip_unsupported_reasoning_params(data: Any, _visited: set[int] | None = None) -> None:
|
|
413
|
-
"""
|
|
414
|
-
Mutates `data` in-place by removing unsupported reasoning controls.
|
|
415
|
-
|
|
416
|
-
OpenAI's API error reports `reasoning.effort`, but the payload is sent as:
|
|
417
|
-
{ "reasoning": { "effort": ... } }
|
|
418
|
-
"""
|
|
271
|
+
def _strip_unsupported_reasoning_params(data: Any, _visited: set[int] | None=None) -> None:
|
|
419
272
|
if _visited is None:
|
|
420
273
|
_visited = set()
|
|
421
274
|
if isinstance(data, dict):
|
|
@@ -423,8 +276,8 @@ def _strip_unsupported_reasoning_params(data: Any, _visited: set[int] | None = N
|
|
|
423
276
|
if obj_id in _visited:
|
|
424
277
|
return
|
|
425
278
|
_visited.add(obj_id)
|
|
426
|
-
data.pop(
|
|
427
|
-
data.pop(
|
|
279
|
+
data.pop('reasoning', None)
|
|
280
|
+
data.pop('reasoning_effort', None)
|
|
428
281
|
for v in list(data.values()):
|
|
429
282
|
_strip_unsupported_reasoning_params(v, _visited)
|
|
430
283
|
return
|
|
@@ -436,412 +289,227 @@ def _strip_unsupported_reasoning_params(data: Any, _visited: set[int] | None = N
|
|
|
436
289
|
for item in data:
|
|
437
290
|
_strip_unsupported_reasoning_params(item, _visited)
|
|
438
291
|
|
|
439
|
-
|
|
440
292
|
def _extract_text_content(message: dict[str, Any]) -> str:
|
|
441
|
-
|
|
442
|
-
content = message.get("content")
|
|
293
|
+
content = message.get('content')
|
|
443
294
|
if isinstance(content, str):
|
|
444
295
|
return content
|
|
445
296
|
if not isinstance(content, list):
|
|
446
|
-
return
|
|
297
|
+
return ''
|
|
447
298
|
parts: list[str] = []
|
|
448
299
|
for block in content:
|
|
449
|
-
if isinstance(block, dict) and block.get(
|
|
450
|
-
text = block.get(
|
|
300
|
+
if isinstance(block, dict) and block.get('type') == 'text':
|
|
301
|
+
text = block.get('text')
|
|
451
302
|
if isinstance(text, str) and text.strip():
|
|
452
303
|
parts.append(text)
|
|
453
|
-
return
|
|
454
|
-
|
|
304
|
+
return '\n'.join(parts)
|
|
455
305
|
|
|
456
306
|
def _set_text_content(message: dict[str, Any], text: str) -> None:
|
|
457
|
-
|
|
458
|
-
content = message.get("content")
|
|
307
|
+
content = message.get('content')
|
|
459
308
|
if isinstance(content, list):
|
|
460
|
-
|
|
461
|
-
preserved = [b for b in content if not (isinstance(b, dict) and b.get("type") == "text")]
|
|
309
|
+
preserved = [b for b in content if not (isinstance(b, dict) and b.get('type') == 'text')]
|
|
462
310
|
if text.strip():
|
|
463
|
-
preserved.append({
|
|
464
|
-
message[
|
|
311
|
+
preserved.append({'type': 'text', 'text': text})
|
|
312
|
+
message['content'] = preserved
|
|
465
313
|
return
|
|
466
|
-
message[
|
|
467
|
-
|
|
314
|
+
message['content'] = text
|
|
468
315
|
|
|
469
316
|
def _is_text_only_assistant(message: Any) -> bool:
|
|
470
|
-
|
|
471
|
-
if not isinstance(message, dict) or message.get("role") != "assistant":
|
|
317
|
+
if not isinstance(message, dict) or message.get('role') != 'assistant':
|
|
472
318
|
return False
|
|
473
|
-
tool_calls = message.get(
|
|
319
|
+
tool_calls = message.get('tool_calls')
|
|
474
320
|
if isinstance(tool_calls, list) and len(tool_calls) > 0:
|
|
475
321
|
return False
|
|
476
322
|
return bool(_extract_text_content(message).strip())
|
|
477
323
|
|
|
478
|
-
|
|
479
324
|
def _tool_result_ids_from_message(message: Any) -> set[str]:
|
|
480
|
-
"""Collect tool result ids from OpenAI or Anthropic-style tool result messages."""
|
|
481
325
|
if not isinstance(message, dict):
|
|
482
326
|
return set()
|
|
483
|
-
|
|
484
|
-
role
|
|
485
|
-
|
|
486
|
-
tool_call_id = message.get("tool_call_id")
|
|
327
|
+
role = message.get('role')
|
|
328
|
+
if role == 'tool':
|
|
329
|
+
tool_call_id = message.get('tool_call_id')
|
|
487
330
|
return {tool_call_id} if isinstance(tool_call_id, str) and tool_call_id else set()
|
|
488
|
-
|
|
489
|
-
content = message.get("content")
|
|
331
|
+
content = message.get('content')
|
|
490
332
|
if not isinstance(content, list):
|
|
491
333
|
return set()
|
|
492
334
|
ids: set[str] = set()
|
|
493
335
|
for block in content:
|
|
494
|
-
if isinstance(block, dict) and block.get(
|
|
495
|
-
tool_use_id = block.get(
|
|
336
|
+
if isinstance(block, dict) and block.get('type') == 'tool_result':
|
|
337
|
+
tool_use_id = block.get('tool_use_id')
|
|
496
338
|
if isinstance(tool_use_id, str) and tool_use_id:
|
|
497
339
|
ids.add(tool_use_id)
|
|
498
340
|
return ids
|
|
499
341
|
|
|
500
|
-
|
|
501
342
|
def sanitize_messages(messages: Any) -> Any:
|
|
502
|
-
"""
|
|
503
|
-
Lightweight tool-order sanitizer for Codex/OpenAI-style tool calls.
|
|
504
|
-
|
|
505
|
-
Ensures assistant tool calls are followed immediately by tool results.
|
|
506
|
-
If text-only assistant messages appear between tool call and tool result,
|
|
507
|
-
merge text into the tool-call assistant message.
|
|
508
|
-
If no immediate tool result exists, inject placeholder tool results.
|
|
509
|
-
"""
|
|
510
343
|
if not isinstance(messages, list) or not messages:
|
|
511
344
|
return messages
|
|
512
|
-
|
|
513
345
|
result: list[Any] = []
|
|
514
346
|
i = 0
|
|
515
347
|
changed = False
|
|
516
|
-
|
|
517
348
|
while i < len(messages):
|
|
518
349
|
msg = messages[i]
|
|
519
350
|
if not isinstance(msg, dict):
|
|
520
351
|
result.append(msg)
|
|
521
352
|
i += 1
|
|
522
353
|
continue
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
tool_calls
|
|
526
|
-
if role != "assistant" or not isinstance(tool_calls, list) or len(tool_calls) == 0:
|
|
354
|
+
role = msg.get('role')
|
|
355
|
+
tool_calls = msg.get('tool_calls')
|
|
356
|
+
if role != 'assistant' or not isinstance(tool_calls, list) or len(tool_calls) == 0:
|
|
527
357
|
result.append(msg)
|
|
528
358
|
i += 1
|
|
529
359
|
continue
|
|
530
|
-
|
|
531
|
-
# Assistant tool-call message.
|
|
532
360
|
tool_ids: list[str] = []
|
|
533
361
|
for tc in tool_calls:
|
|
534
362
|
if isinstance(tc, dict):
|
|
535
|
-
tid = tc.get(
|
|
363
|
+
tid = tc.get('id')
|
|
536
364
|
if isinstance(tid, str) and tid:
|
|
537
365
|
tool_ids.append(tid)
|
|
538
|
-
|
|
539
366
|
merged = dict(msg)
|
|
540
367
|
merged_text = _extract_text_content(merged)
|
|
541
368
|
j = i + 1
|
|
542
|
-
|
|
543
|
-
# Merge consecutive text-only assistant messages.
|
|
544
369
|
while j < len(messages) and _is_text_only_assistant(messages[j]):
|
|
545
370
|
extra = _extract_text_content(messages[j])
|
|
546
371
|
if extra.strip():
|
|
547
|
-
merged_text = f
|
|
372
|
+
merged_text = f'{merged_text}\n{extra}' if merged_text.strip() else extra
|
|
548
373
|
j += 1
|
|
549
374
|
changed = True
|
|
550
|
-
|
|
551
375
|
if merged_text != _extract_text_content(merged):
|
|
552
376
|
_set_text_content(merged, merged_text)
|
|
553
|
-
|
|
554
377
|
result.append(merged)
|
|
555
|
-
|
|
556
|
-
# Immediate next message must carry tool results for all tool ids.
|
|
557
378
|
next_ids = _tool_result_ids_from_message(messages[j]) if j < len(messages) else set()
|
|
558
379
|
missing_ids = [tid for tid in tool_ids if tid not in next_ids]
|
|
559
380
|
if missing_ids:
|
|
560
381
|
changed = True
|
|
561
382
|
for tid in missing_ids:
|
|
562
|
-
result.append({
|
|
563
|
-
"role": "tool",
|
|
564
|
-
"tool_call_id": tid,
|
|
565
|
-
"content": "[Placeholder: tool result was not provided]",
|
|
566
|
-
})
|
|
567
|
-
|
|
383
|
+
result.append({'role': 'tool', 'tool_call_id': tid, 'content': '[Placeholder: tool result was not provided]'})
|
|
568
384
|
i = j
|
|
569
|
-
|
|
570
385
|
return result if changed else messages
|
|
571
386
|
|
|
572
|
-
|
|
573
387
|
class ReasoningParamsSanitizer(CustomLogger):
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
async def async_pre_call_hook(
|
|
577
|
-
self,
|
|
578
|
-
user_api_key_dict: Any,
|
|
579
|
-
cache: Any,
|
|
580
|
-
data: dict,
|
|
581
|
-
call_type: Any,
|
|
582
|
-
) -> dict:
|
|
388
|
+
|
|
389
|
+
async def async_pre_call_hook(self, user_api_key_dict: Any, cache: Any, data: dict, call_type: Any) -> dict:
|
|
583
390
|
if not isinstance(data, dict):
|
|
584
391
|
return data
|
|
585
|
-
model = data.get(
|
|
392
|
+
model = data.get('model')
|
|
586
393
|
if not _is_reasoning_unsupported_model(model):
|
|
587
394
|
return data
|
|
588
395
|
_strip_unsupported_reasoning_params(data)
|
|
589
396
|
return data
|
|
590
397
|
|
|
591
|
-
async def async_pre_call_deployment_hook(
|
|
592
|
-
self,
|
|
593
|
-
request_kwargs: dict,
|
|
594
|
-
call_type: Any,
|
|
595
|
-
) -> dict:
|
|
596
|
-
"""Extra safety: strip during deployment-layer payload building."""
|
|
398
|
+
async def async_pre_call_deployment_hook(self, request_kwargs: dict, call_type: Any) -> dict:
|
|
597
399
|
if not isinstance(request_kwargs, dict):
|
|
598
400
|
return request_kwargs
|
|
599
|
-
model = request_kwargs.get(
|
|
401
|
+
model = request_kwargs.get('model')
|
|
600
402
|
if not _is_reasoning_unsupported_model(model):
|
|
601
403
|
return request_kwargs
|
|
602
404
|
_strip_unsupported_reasoning_params(request_kwargs)
|
|
603
405
|
return request_kwargs
|
|
604
406
|
|
|
605
|
-
async def async_pre_request_hook(
|
|
606
|
-
self,
|
|
607
|
-
model: str,
|
|
608
|
-
messages: list[Any],
|
|
609
|
-
request_kwargs: dict,
|
|
610
|
-
) -> dict:
|
|
611
|
-
"""Extra safety: strip right before provider call."""
|
|
407
|
+
async def async_pre_request_hook(self, model: str, messages: list[Any], request_kwargs: dict) -> dict:
|
|
612
408
|
if not _is_reasoning_unsupported_model(model):
|
|
613
409
|
return request_kwargs
|
|
614
410
|
if isinstance(request_kwargs, dict):
|
|
615
411
|
_strip_unsupported_reasoning_params(request_kwargs)
|
|
616
412
|
return request_kwargs
|
|
617
413
|
|
|
618
|
-
|
|
619
414
|
class CodexProxySanitizer(CustomLogger):
|
|
620
|
-
"""
|
|
621
|
-
LiteLLM proxy hook: strip Codex/OpenAI Responses fields/tools that break proxy routes.
|
|
622
|
-
|
|
623
|
-
- Anthropic: drop client_metadata (OpenAI-only Responses telemetry).
|
|
624
|
-
- gpt-5.6-luna: drop web_search_preview and related built-in search tools.
|
|
625
|
-
"""
|
|
626
415
|
|
|
627
416
|
def _sanitize(self, payload: dict, model: Any) -> dict:
|
|
628
417
|
if isinstance(payload, dict):
|
|
629
418
|
_apply_codex_proxy_sanitizers(payload, model)
|
|
630
419
|
return payload
|
|
631
420
|
|
|
632
|
-
async def async_pre_call_hook(
|
|
633
|
-
self,
|
|
634
|
-
user_api_key_dict: Any,
|
|
635
|
-
cache: Any,
|
|
636
|
-
data: dict,
|
|
637
|
-
call_type: Any,
|
|
638
|
-
) -> dict:
|
|
421
|
+
async def async_pre_call_hook(self, user_api_key_dict: Any, cache: Any, data: dict, call_type: Any) -> dict:
|
|
639
422
|
if not isinstance(data, dict):
|
|
640
423
|
return data
|
|
641
|
-
return self._sanitize(data, data.get(
|
|
424
|
+
return self._sanitize(data, data.get('model'))
|
|
642
425
|
|
|
643
|
-
async def async_pre_call_deployment_hook(
|
|
644
|
-
self,
|
|
645
|
-
request_kwargs: dict,
|
|
646
|
-
call_type: Any,
|
|
647
|
-
) -> dict:
|
|
426
|
+
async def async_pre_call_deployment_hook(self, request_kwargs: dict, call_type: Any) -> dict:
|
|
648
427
|
if not isinstance(request_kwargs, dict):
|
|
649
428
|
return request_kwargs
|
|
650
|
-
return self._sanitize(request_kwargs, request_kwargs.get(
|
|
651
|
-
|
|
652
|
-
async def async_pre_request_hook(
|
|
653
|
-
self,
|
|
654
|
-
model: str,
|
|
655
|
-
messages: list[Any],
|
|
656
|
-
request_kwargs: dict,
|
|
657
|
-
) -> dict:
|
|
429
|
+
return self._sanitize(request_kwargs, request_kwargs.get('model'))
|
|
430
|
+
|
|
431
|
+
async def async_pre_request_hook(self, model: str, messages: list[Any], request_kwargs: dict) -> dict:
|
|
658
432
|
if isinstance(request_kwargs, dict):
|
|
659
433
|
self._sanitize(request_kwargs, model)
|
|
660
434
|
return request_kwargs
|
|
661
435
|
|
|
662
|
-
|
|
663
436
|
class ToolOrderSanitizer(CustomLogger):
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
async def async_pre_call_hook(
|
|
667
|
-
self,
|
|
668
|
-
user_api_key_dict: Any,
|
|
669
|
-
cache: Any,
|
|
670
|
-
data: dict,
|
|
671
|
-
call_type: Any,
|
|
672
|
-
) -> dict:
|
|
437
|
+
|
|
438
|
+
async def async_pre_call_hook(self, user_api_key_dict: Any, cache: Any, data: dict, call_type: Any) -> dict:
|
|
673
439
|
if not isinstance(data, dict):
|
|
674
440
|
return data
|
|
675
|
-
messages = data.get(
|
|
441
|
+
messages = data.get('messages')
|
|
676
442
|
if isinstance(messages, list) and messages:
|
|
677
|
-
data[
|
|
443
|
+
data['messages'] = sanitize_messages(messages)
|
|
678
444
|
return data
|
|
679
445
|
|
|
680
|
-
|
|
681
446
|
def strip_empty_thinking_blocks(messages: list[Any]) -> list[Any]:
|
|
682
|
-
"""
|
|
683
|
-
Strip empty thinking/reasoning blocks from OpenAI-format messages.
|
|
684
|
-
|
|
685
|
-
Anthropic's API rejects empty `thinking` content blocks and empty
|
|
686
|
-
`reasoning_content` strings. LiteLLM proxies these fields unchanged,
|
|
687
|
-
so we sanitize outbound messages here at the proxy layer.
|
|
688
|
-
|
|
689
|
-
Handles three shapes:
|
|
690
|
-
1. Top-level `reasoning_content: ""` on assistant messages
|
|
691
|
-
2. `{ type: "thinking", thinking: "" }` inside content arrays
|
|
692
|
-
3. Removes assistant messages that become entirely empty after stripping
|
|
693
|
-
"""
|
|
694
447
|
result: list[Any] = []
|
|
695
|
-
|
|
696
448
|
for msg in messages:
|
|
697
449
|
if not isinstance(msg, dict):
|
|
698
450
|
result.append(msg)
|
|
699
451
|
continue
|
|
700
|
-
|
|
701
452
|
cleaned = msg
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
cleaned = {k: v for k, v in cleaned.items() if k != "reasoning_content"}
|
|
708
|
-
|
|
709
|
-
# 2. Filter empty thinking blocks from content arrays
|
|
710
|
-
content = cleaned.get("content")
|
|
453
|
+
if cleaned.get('role') == 'assistant' and 'reasoning_content' in cleaned:
|
|
454
|
+
rc = cleaned['reasoning_content']
|
|
455
|
+
if not rc or (isinstance(rc, str) and (not rc.strip())):
|
|
456
|
+
cleaned = {k: v for k, v in cleaned.items() if k != 'reasoning_content'}
|
|
457
|
+
content = cleaned.get('content')
|
|
711
458
|
if isinstance(content, list):
|
|
712
459
|
original_len = len(content)
|
|
713
|
-
filtered = [
|
|
714
|
-
block for block in content
|
|
715
|
-
if not (
|
|
716
|
-
isinstance(block, dict)
|
|
717
|
-
and block.get("type") == "thinking"
|
|
718
|
-
and (not block.get("thinking") or (isinstance(block.get("thinking"), str) and not block["thinking"].strip()))
|
|
719
|
-
)
|
|
720
|
-
]
|
|
460
|
+
filtered = [block for block in content if not (isinstance(block, dict) and block.get('type') == 'thinking' and (not block.get('thinking') or (isinstance(block.get('thinking'), str) and (not block['thinking'].strip()))))]
|
|
721
461
|
if len(filtered) != original_len:
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
if cleaned.get("role") == "assistant" and not cleaned.get("tool_calls"):
|
|
729
|
-
c = cleaned.get("content")
|
|
730
|
-
if (isinstance(c, str) and not c.strip()) or (isinstance(c, list) and len(c) == 0):
|
|
462
|
+
if not filtered and cleaned.get('role') == 'assistant':
|
|
463
|
+
filtered = [{'type': 'text', 'text': ''}]
|
|
464
|
+
cleaned = {**cleaned, 'content': filtered}
|
|
465
|
+
if cleaned.get('role') == 'assistant' and (not cleaned.get('tool_calls')):
|
|
466
|
+
c = cleaned.get('content')
|
|
467
|
+
if isinstance(c, str) and (not c.strip()) or (isinstance(c, list) and len(c) == 0):
|
|
731
468
|
continue
|
|
732
|
-
|
|
733
469
|
result.append(cleaned)
|
|
734
|
-
|
|
735
470
|
return result
|
|
736
471
|
|
|
737
|
-
|
|
738
472
|
class EmptyThinkingBlockSanitizer(CustomLogger):
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
async def async_pre_call_hook(
|
|
742
|
-
self,
|
|
743
|
-
user_api_key_dict: Any,
|
|
744
|
-
cache: Any,
|
|
745
|
-
data: dict,
|
|
746
|
-
call_type: Any,
|
|
747
|
-
) -> dict:
|
|
473
|
+
|
|
474
|
+
async def async_pre_call_hook(self, user_api_key_dict: Any, cache: Any, data: dict, call_type: Any) -> dict:
|
|
748
475
|
if not isinstance(data, dict):
|
|
749
476
|
return data
|
|
750
|
-
messages = data.get(
|
|
477
|
+
messages = data.get('messages')
|
|
751
478
|
if isinstance(messages, list) and messages:
|
|
752
|
-
data[
|
|
479
|
+
data['messages'] = strip_empty_thinking_blocks(messages)
|
|
753
480
|
return data
|
|
754
481
|
|
|
755
|
-
|
|
756
482
|
class CleanSeparatedOtelOutput(OpenTelemetry):
|
|
757
|
-
"""
|
|
758
|
-
LiteLLM OTEL with clean separation:
|
|
759
|
-
- gen_ai.input.messages → full history (as set by LiteLLM)
|
|
760
|
-
- gen_ai.output.messages → only the new assistant reply (stringified structured array)
|
|
761
|
-
- Short preview event for quick visibility
|
|
762
|
-
|
|
763
|
-
Content (both directions) is recorded ONLY when AGENTHIPPO_EVIDENCE_CONTENT_CAPTURE is set.
|
|
764
|
-
Otherwise every content attribute becomes `<key>.sha256` + `<key>.length` — enough to prove
|
|
765
|
-
which bytes a turn saw, without shipping the bytes.
|
|
766
|
-
"""
|
|
767
483
|
|
|
768
484
|
def set_attributes(self, span: Any, *args: Any, **kwargs: Any) -> Any:
|
|
769
|
-
"""
|
|
770
|
-
Redaction chokepoint. Overriding here rather than at the call site covers every path in
|
|
771
|
-
the base class that sets attributes, including ones this subclass does not override.
|
|
772
|
-
"""
|
|
773
485
|
_announce_content_capture()
|
|
774
486
|
if not _content_capture_enabled():
|
|
775
487
|
span = _RedactingSpan(span)
|
|
776
488
|
return super().set_attributes(span, *args, **kwargs)
|
|
777
489
|
|
|
778
490
|
def _get_span_name(self, kwargs: dict) -> str:
|
|
779
|
-
# Evidence pipeline indexes spans named litellm_request; main-stable semconv uses "chat …".
|
|
780
|
-
#
|
|
781
|
-
# The constant is a PRIVATE litellm symbol and this runs on the span-creation path, so an
|
|
782
|
-
# upstream rename would raise for every span — total evidence loss at layer 1, not a
|
|
783
|
-
# degraded span name. The literal is the same value; falling back to it keeps the pipeline
|
|
784
|
-
# indexing while the import is fixed.
|
|
785
491
|
try:
|
|
786
492
|
from litellm.integrations.opentelemetry import LITELLM_REQUEST_SPAN_NAME
|
|
787
|
-
|
|
788
493
|
return LITELLM_REQUEST_SPAN_NAME
|
|
789
|
-
except Exception:
|
|
790
|
-
return
|
|
791
|
-
|
|
792
|
-
def _start_primary_span(
|
|
793
|
-
self,
|
|
794
|
-
kwargs: dict,
|
|
795
|
-
response_obj: Any,
|
|
796
|
-
start_time: Any,
|
|
797
|
-
end_time: Any,
|
|
798
|
-
context: Any,
|
|
799
|
-
):
|
|
800
|
-
otel_tracer = self.get_tracer_to_use_for_request(kwargs)
|
|
494
|
+
except Exception:
|
|
495
|
+
return 'litellm_request'
|
|
801
496
|
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
context=context,
|
|
806
|
-
)
|
|
497
|
+
def _start_primary_span(self, kwargs: dict, response_obj: Any, start_time: Any, end_time: Any, context: Any):
|
|
498
|
+
otel_tracer = self.get_tracer_to_use_for_request(kwargs)
|
|
499
|
+
span = otel_tracer.start_span(name=self._get_span_name(kwargs), start_time=self._to_ns(start_time), context=context)
|
|
807
500
|
span.set_status(Status(StatusCode.OK))
|
|
808
501
|
self.set_attributes(span, kwargs, response_obj)
|
|
809
|
-
|
|
810
|
-
# -- Extract only the new assistant output -------------------------------
|
|
811
|
-
text = _extract_text_from_success_kwargs(kwargs) or \
|
|
812
|
-
_extract_assistant_text(response_obj)
|
|
813
|
-
|
|
502
|
+
text = _extract_text_from_success_kwargs(kwargs) or _extract_assistant_text(response_obj)
|
|
814
503
|
if text.strip():
|
|
815
504
|
capture = _content_capture_enabled()
|
|
816
|
-
event_attrs: dict[str, Any] = {
|
|
817
|
-
# Always present: proves a reply of exactly these bytes was produced, and lets a
|
|
818
|
-
# disclosed transcript be matched back to the span without storing the text.
|
|
819
|
-
"gen_ai.output.sha256": _sha256(text),
|
|
820
|
-
"gen_ai.output.length": len(text),
|
|
821
|
-
"gen_ai.output.truncated": len(text) > _OTEL_TEXT_MAX_LEN,
|
|
822
|
-
"gen_ai.output.captured": capture,
|
|
823
|
-
"gen_ai.response.model": _read_field(kwargs, "model", "unknown"),
|
|
824
|
-
}
|
|
825
|
-
|
|
505
|
+
event_attrs: dict[str, Any] = {'gen_ai.output.sha256': _sha256(text), 'gen_ai.output.length': len(text), 'gen_ai.output.truncated': len(text) > _OTEL_TEXT_MAX_LEN, 'gen_ai.output.captured': capture, 'gen_ai.response.model': _read_field(kwargs, 'model', 'unknown')}
|
|
826
506
|
if capture:
|
|
827
|
-
# Structured output (separate from input history) — opt-in only.
|
|
828
507
|
structured_msgs = _make_output_messages_structured(text)
|
|
829
|
-
span.set_attribute(
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
)
|
|
833
|
-
# Short preview event - easy to spot, low noise
|
|
834
|
-
event_attrs["gen_ai.output.preview"] = (
|
|
835
|
-
text[:_OTEL_PREVIEW_LEN] + ("..." if len(text) > _OTEL_PREVIEW_LEN else "")
|
|
836
|
-
)
|
|
837
|
-
|
|
838
|
-
span.add_event(name="gen_ai.output.generated", attributes=event_attrs)
|
|
839
|
-
|
|
508
|
+
span.set_attribute('gen_ai.output.messages', json.dumps(structured_msgs, ensure_ascii=False))
|
|
509
|
+
event_attrs['gen_ai.output.preview'] = text[:_OTEL_PREVIEW_LEN] + ('...' if len(text) > _OTEL_PREVIEW_LEN else '')
|
|
510
|
+
span.add_event(name='gen_ai.output.generated', attributes=event_attrs)
|
|
840
511
|
span.end(end_time=self._to_ns(end_time))
|
|
841
512
|
return span
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
# Register
|
|
845
513
|
structured_genai_otel = CleanSeparatedOtelOutput()
|
|
846
514
|
reasoning_params_sanitizer = ReasoningParamsSanitizer()
|
|
847
515
|
codex_proxy_sanitizer = CodexProxySanitizer()
|