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