cade-cli 0.20.0__tar.gz → 0.20.2__tar.gz
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.
- {cade_cli-0.20.0 → cade_cli-0.20.2}/PKG-INFO +2 -1
- {cade_cli-0.20.0 → cade_cli-0.20.2}/pyproject.toml +2 -1
- {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/host_plugin/assets/.claude-plugin/plugin.json +1 -1
- {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/host_plugin/assets/.cursor-plugin/plugin.json +1 -1
- {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/providers/http_client.py +18 -8
- {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/providers/openai.py +12 -6
- {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/telemetry/otel.py +188 -25
- {cade_cli-0.20.0 → cade_cli-0.20.2}/.gitignore +0 -0
- {cade_cli-0.20.0 → cade_cli-0.20.2}/LICENSE +0 -0
- {cade_cli-0.20.0 → cade_cli-0.20.2}/README.md +0 -0
- {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cade_mcp_local/__init__.py +0 -0
- {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cade_mcp_local/__main__.py +0 -0
- {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cade_mcp_local/_metadata.py +0 -0
- {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cade_mcp_local/config.py +0 -0
- {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cade_mcp_local/errors.py +0 -0
- {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cade_mcp_local/grants.py +0 -0
- {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cade_mcp_local/resources.py +0 -0
- {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cade_mcp_local/sandbox.py +0 -0
- {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cade_mcp_local/server.py +0 -0
- {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cade_mcp_local/tools/__init__.py +0 -0
- {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cade_mcp_local/tools/_read_cache.py +0 -0
- {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cade_mcp_local/tools/access.py +0 -0
- {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cade_mcp_local/tools/agents.py +0 -0
- {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cade_mcp_local/tools/filesystem.py +0 -0
- {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cade_mcp_local/tools/git.py +0 -0
- {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cade_mcp_local/tools/notifications.py +0 -0
- {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cade_mcp_local/tools/questions.py +0 -0
- {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cade_mcp_local/tools/results.py +0 -0
- {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cade_mcp_local/tools/schemas.py +0 -0
- {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cade_mcp_local/tools/search.py +0 -0
- {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cade_mcp_local/tools/shell.py +0 -0
- {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cade_mcp_local/tools/tasks.py +0 -0
- {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cade_mcp_local/tools/web_fetch.py +0 -0
- {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cade_mcp_local/tools/web_search.py +0 -0
- {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cade_mcp_local/utils.py +0 -0
- {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cade_mcp_local/web/__init__.py +0 -0
- {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cade_mcp_local/web/arcade_client.py +0 -0
- {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cade_mcp_local/web/cache.py +0 -0
- {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cade_mcp_local/web/extract.py +0 -0
- {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cade_mcp_local/web/markdown.py +0 -0
- {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cade_mcp_local/web/preapproved.py +0 -0
- {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cade_mcp_local/web/prompts.py +0 -0
- {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cade_mcp_local/web/scrape.py +0 -0
- {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cade_mcp_local/web/search/__init__.py +0 -0
- {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cade_mcp_local/web/search/arcade.py +0 -0
- {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cade_mcp_local/web/search/base.py +0 -0
- {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cade_mcp_local/web/search/http.py +0 -0
- {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/__init__.py +0 -0
- {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/ai/__init__.py +0 -0
- {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/ai/prompts.py +0 -0
- {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/cli/__init__.py +0 -0
- {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/cli/app.py +0 -0
- {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/cli/auth.py +0 -0
- {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/cli/commands/__init__.py +0 -0
- {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/cli/commands/account.py +0 -0
- {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/cli/commands/agent_group.py +0 -0
- {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/cli/commands/agents.py +0 -0
- {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/cli/commands/auth.py +0 -0
- {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/cli/commands/chat.py +0 -0
- {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/cli/commands/cron.py +0 -0
- {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/cli/commands/daemon.py +0 -0
- {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/cli/commands/grant.py +0 -0
- {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/cli/commands/hooks.py +0 -0
- {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/cli/commands/mcp.py +0 -0
- {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/cli/commands/mem.py +0 -0
- {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/cli/commands/tasks.py +0 -0
- {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/cli/commands/thread.py +0 -0
- {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/cli/commands/tools.py +0 -0
- {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/cli/mcp_hosts.py +0 -0
- {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/cli/onboarding.py +0 -0
- {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/cli/ui.py +0 -0
- {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/core/__init__.py +0 -0
- {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/core/agent.py +0 -0
- {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/core/binary.py +0 -0
- {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/core/comms.py +0 -0
- {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/core/config.py +0 -0
- {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/core/constants.py +0 -0
- {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/core/errors.py +0 -0
- {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/core/frozen.py +0 -0
- {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/core/git.py +0 -0
- {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/core/inbox.py +0 -0
- {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/core/logging.py +0 -0
- {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/core/memory.py +0 -0
- {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/core/model_specs.py +0 -0
- {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/core/names.py +0 -0
- {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/core/paths.py +0 -0
- {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/core/runtime.py +0 -0
- {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/core/secrets.py +0 -0
- {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/core/spawn.py +0 -0
- {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/core/text.py +0 -0
- {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/core/types.py +0 -0
- {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/daemon/__init__.py +0 -0
- {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/daemon/adapters/__init__.py +0 -0
- {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/daemon/adapters/base.py +0 -0
- {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/daemon/adapters/factory.py +0 -0
- {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/daemon/adapters/imessage.py +0 -0
- {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/daemon/adapters/registry.py +0 -0
- {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/daemon/adapters/stub.py +0 -0
- {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/daemon/adapters/telegram.py +0 -0
- {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/daemon/allowlist.py +0 -0
- {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/daemon/attach.py +0 -0
- {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/daemon/chunker.py +0 -0
- {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/daemon/client.py +0 -0
- {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/daemon/commands.py +0 -0
- {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/daemon/config.py +0 -0
- {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/daemon/control.py +0 -0
- {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/daemon/cron.py +0 -0
- {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/daemon/elicitation.py +0 -0
- {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/daemon/ensure.py +0 -0
- {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/daemon/format.py +0 -0
- {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/daemon/gateway/__init__.py +0 -0
- {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/daemon/gateway/app.py +0 -0
- {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/daemon/gateway/auth.py +0 -0
- {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/daemon/gateway/constants.py +0 -0
- {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/daemon/gateway/elicitation.py +0 -0
- {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/daemon/gateway/exposure.py +0 -0
- {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/daemon/gateway/host_agent.py +0 -0
- {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/daemon/gateway/limits.py +0 -0
- {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/daemon/gateway/middleware.py +0 -0
- {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/daemon/gateway/resources.py +0 -0
- {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/daemon/gateway/subscriptions.py +0 -0
- {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/daemon/gateway/turn_bridge.py +0 -0
- {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/daemon/grants.py +0 -0
- {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/daemon/inbox.py +0 -0
- {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/daemon/inbox_cron.py +0 -0
- {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/daemon/install.py +0 -0
- {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/daemon/lock.py +0 -0
- {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/daemon/mesh.py +0 -0
- {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/daemon/message_pump.py +0 -0
- {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/daemon/observability.py +0 -0
- {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/daemon/owner.py +0 -0
- {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/daemon/progress_sink.py +0 -0
- {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/daemon/registry.py +0 -0
- {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/daemon/router.py +0 -0
- {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/daemon/runner.py +0 -0
- {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/daemon/runner_main.py +0 -0
- {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/daemon/secrets.py +0 -0
- {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/daemon/secrets_broker.py +0 -0
- {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/daemon/server.py +0 -0
- {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/daemon/services.py +0 -0
- {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/daemon/sinks.py +0 -0
- {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/daemon/supervisor.py +0 -0
- {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/daemon/tokens.py +0 -0
- {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/daemon/traffic.py +0 -0
- {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/daemon/turns.py +0 -0
- {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/daemon/wake.py +0 -0
- {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/execution/__init__.py +0 -0
- {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/execution/context_window.py +0 -0
- {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/execution/mcp_schema_index.py +0 -0
- {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/execution/orchestrator.py +0 -0
- {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/execution/parallel.py +0 -0
- {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/execution/tool_result_store.py +0 -0
- {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/execution/tool_schema_cache.py +0 -0
- {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/hooks/__init__.py +0 -0
- {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/hooks/config.py +0 -0
- {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/hooks/engine.py +0 -0
- {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/hooks/executors/__init__.py +0 -0
- {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/hooks/executors/callback.py +0 -0
- {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/hooks/executors/command.py +0 -0
- {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/hooks/executors/http.py +0 -0
- {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/hooks/executors/prompt.py +0 -0
- {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/hooks/executors/ssrf_guard.py +0 -0
- {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/hooks/matchers.py +0 -0
- {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/hooks/registry.py +0 -0
- {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/hooks/types.py +0 -0
- {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/host_plugin/__init__.py +0 -0
- {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/host_plugin/assets/.claude-plugin/marketplace.json +0 -0
- {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/host_plugin/assets/.cursor-plugin/marketplace.json +0 -0
- {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/host_plugin/assets/agents/operator.md +0 -0
- {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/host_plugin/assets/commands/ask.md +0 -0
- {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/host_plugin/assets/commands/memory.md +0 -0
- {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/host_plugin/assets/commands/tasks.md +0 -0
- {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/host_plugin/assets/mcp.json +0 -0
- {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/host_plugin/assets/rules/gateway.mdc +0 -0
- {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/host_plugin/assets/skills/ask/SKILL.md +0 -0
- {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/host_plugin/assets/skills/memory/SKILL.md +0 -0
- {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/host_plugin/assets/skills/tasks/SKILL.md +0 -0
- {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/host_plugin/doctor.py +0 -0
- {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/host_plugin/install.py +0 -0
- {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/host_plugin/paths.py +0 -0
- {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/host_plugin/state.py +0 -0
- {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/providers/__init__.py +0 -0
- {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/providers/anthropic.py +0 -0
- {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/providers/base.py +0 -0
- {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/providers/ollama.py +0 -0
- {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/storage/__init__.py +0 -0
- {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/storage/threads.py +0 -0
- {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/tasks/__init__.py +0 -0
- {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/tasks/scheduler.py +0 -0
- {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/tasks/store.py +0 -0
- {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/tasks/types.py +0 -0
- {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/telemetry/__init__.py +0 -0
- {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/templates/login_failed.html +0 -0
- {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/templates/login_success.html +0 -0
- {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/templates/serve/launchd.plist.tmpl +0 -0
- {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/templates/serve/serve.toml.example +0 -0
- {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/templates/serve/systemd.service.tmpl +0 -0
- {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/templates/styles.css +0 -0
- {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/tools/__init__.py +0 -0
- {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/tools/manager/__init__.py +0 -0
- {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/tools/manager/_request_ctx.py +0 -0
- {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/tools/manager/base.py +0 -0
- {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/tools/manager/composite.py +0 -0
- {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/tools/manager/config.py +0 -0
- {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/tools/manager/mcp.py +0 -0
- {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/tools/search/__init__.py +0 -0
- {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/tools/search/discovered.py +0 -0
- {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/tools/search/scoring.py +0 -0
- {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/tools/search/service.py +0 -0
- {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/ui/__init__.py +0 -0
- {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/ui/completion.py +0 -0
- {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/ui/display.py +0 -0
- {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/ui/elicitation.py +0 -0
- {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/ui/input.py +0 -0
- {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/ui/session.py +0 -0
- {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/ui/slash.py +0 -0
- {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/voice/__init__.py +0 -0
- {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/voice/audio.py +0 -0
- {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/voice/cleanup.py +0 -0
- {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/voice/session.py +0 -0
- {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/voice/stt.py +0 -0
- {cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/voice/tts.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: cade-cli
|
|
3
|
-
Version: 0.20.
|
|
3
|
+
Version: 0.20.2
|
|
4
4
|
Summary: Cade - The CLI Agent from Arcade.dev (install: curl -fsSL https://arcadeagent.dev/install | sh)
|
|
5
5
|
Project-URL: Homepage, https://arcade.dev
|
|
6
6
|
Project-URL: Documentation, https://arcadeagent.dev/docs
|
|
@@ -78,6 +78,7 @@ Requires-Dist: mkdocs-material<10.0.0,>=9.5.0; extra == 'docs'
|
|
|
78
78
|
Requires-Dist: mkdocs<2.0.0,>=1.6.0; extra == 'docs'
|
|
79
79
|
Provides-Extra: otel
|
|
80
80
|
Requires-Dist: opentelemetry-api<2.0.0,>=1.27.0; extra == 'otel'
|
|
81
|
+
Requires-Dist: opentelemetry-exporter-otlp-proto-grpc<2.0.0,>=1.27.0; extra == 'otel'
|
|
81
82
|
Requires-Dist: opentelemetry-exporter-otlp-proto-http<2.0.0,>=1.27.0; extra == 'otel'
|
|
82
83
|
Requires-Dist: opentelemetry-sdk<2.0.0,>=1.27.0; extra == 'otel'
|
|
83
84
|
Provides-Extra: training
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "cade-cli"
|
|
3
|
-
version = "0.20.
|
|
3
|
+
version = "0.20.2"
|
|
4
4
|
description = "Cade - The CLI Agent from Arcade.dev (install: curl -fsSL https://arcadeagent.dev/install | sh)"
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
requires-python = ">=3.11"
|
|
@@ -89,6 +89,7 @@ otel = [
|
|
|
89
89
|
"opentelemetry-api>=1.27.0,<2.0.0",
|
|
90
90
|
"opentelemetry-sdk>=1.27.0,<2.0.0",
|
|
91
91
|
"opentelemetry-exporter-otlp-proto-http>=1.27.0,<2.0.0",
|
|
92
|
+
"opentelemetry-exporter-otlp-proto-grpc>=1.27.0,<2.0.0",
|
|
92
93
|
]
|
|
93
94
|
training = [
|
|
94
95
|
"torch>=2.1.0",
|
{cade_cli-0.20.0 → cade_cli-0.20.2}/src/cadecoder/host_plugin/assets/.cursor-plugin/plugin.json
RENAMED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "cade",
|
|
3
3
|
"displayName": "Cade",
|
|
4
4
|
"description": "Your persistent on-device agent: notes memory, durable tasks, and connected tools via Ask.",
|
|
5
|
-
"version": "0.20.
|
|
5
|
+
"version": "0.20.2",
|
|
6
6
|
"author": {
|
|
7
7
|
"name": "Arcade.dev",
|
|
8
8
|
"email": "support@arcade.dev"
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
"""Shared httpx client helpers for LLM providers.
|
|
2
2
|
|
|
3
3
|
Frozen/PyInstaller builds have hit ``zlib.error: Error -3 while decompressing
|
|
4
|
-
data: incorrect header check`` on Anthropic SSE streams when
|
|
5
|
-
|
|
6
|
-
|
|
4
|
+
data: incorrect header check`` on Anthropic/OpenAI-compatible SSE streams when
|
|
5
|
+
httpx negotiates compression and the body is not actually compressed — common
|
|
6
|
+
with local vLLM/Ollama and some proxies. Default ``Accept-Encoding: identity``
|
|
7
|
+
so the client never attempts to decompress streams. Override with
|
|
8
|
+
``CADE_HTTP_ACCEPT_ENCODING`` (e.g. ``gzip, deflate``) if you want compression.
|
|
7
9
|
|
|
8
10
|
Also: shell profiles often export ``SSL_CERT_FILE`` / ``REQUESTS_CA_BUNDLE`` to a
|
|
9
11
|
path that exists on one machine and not another. httpx then raises
|
|
@@ -20,9 +22,10 @@ from pathlib import Path
|
|
|
20
22
|
|
|
21
23
|
import httpx
|
|
22
24
|
|
|
23
|
-
#
|
|
24
|
-
# binaries and
|
|
25
|
-
|
|
25
|
+
# Never advertise ``br`` / ``zstd`` / ``gzip`` by default — those decoders are
|
|
26
|
+
# fragile in frozen binaries and with OpenAI-compatible SSE servers that omit
|
|
27
|
+
# or mis-set Content-Encoding ("incorrect header check").
|
|
28
|
+
_DEFAULT_ACCEPT_ENCODING = "identity"
|
|
26
29
|
|
|
27
30
|
_SSL_ENV_KEYS = ("SSL_CERT_FILE", "REQUESTS_CA_BUNDLE", "CURL_CA_BUNDLE")
|
|
28
31
|
|
|
@@ -30,6 +33,12 @@ _SSL_ENV_KEYS = ("SSL_CERT_FILE", "REQUESTS_CA_BUNDLE", "CURL_CA_BUNDLE")
|
|
|
30
33
|
CLEARED_SSL_ENV_KEYS: list[str] = []
|
|
31
34
|
|
|
32
35
|
|
|
36
|
+
def accept_encoding_header() -> str:
|
|
37
|
+
"""Return the Accept-Encoding value for provider HTTP clients."""
|
|
38
|
+
raw = os.environ.get("CADE_HTTP_ACCEPT_ENCODING", "").strip()
|
|
39
|
+
return raw or _DEFAULT_ACCEPT_ENCODING
|
|
40
|
+
|
|
41
|
+
|
|
33
42
|
def scrub_broken_ssl_env() -> list[str]:
|
|
34
43
|
"""Unset SSL CA env vars that point at missing files.
|
|
35
44
|
|
|
@@ -94,7 +103,7 @@ def make_async_http_client(*, timeout: float = 600.0) -> httpx.AsyncClient:
|
|
|
94
103
|
"""Return an AsyncClient safe for Anthropic/OpenAI streaming in frozen apps."""
|
|
95
104
|
prepare_ssl_env()
|
|
96
105
|
return httpx.AsyncClient(
|
|
97
|
-
headers={"Accept-Encoding":
|
|
106
|
+
headers={"Accept-Encoding": accept_encoding_header()},
|
|
98
107
|
timeout=httpx.Timeout(timeout, connect=10.0),
|
|
99
108
|
follow_redirects=True,
|
|
100
109
|
verify=ssl_verify(),
|
|
@@ -105,7 +114,7 @@ def make_sync_http_client(*, timeout: float = 60.0) -> httpx.Client:
|
|
|
105
114
|
"""Return a sync Client with the same encoding constraints."""
|
|
106
115
|
prepare_ssl_env()
|
|
107
116
|
return httpx.Client(
|
|
108
|
-
headers={"Accept-Encoding":
|
|
117
|
+
headers={"Accept-Encoding": accept_encoding_header()},
|
|
109
118
|
timeout=httpx.Timeout(timeout, connect=10.0),
|
|
110
119
|
follow_redirects=True,
|
|
111
120
|
verify=ssl_verify(),
|
|
@@ -126,6 +135,7 @@ def is_decompress_error(exc: BaseException) -> bool:
|
|
|
126
135
|
|
|
127
136
|
__all__ = [
|
|
128
137
|
"CLEARED_SSL_ENV_KEYS",
|
|
138
|
+
"accept_encoding_header",
|
|
129
139
|
"is_decompress_error",
|
|
130
140
|
"make_async_http_client",
|
|
131
141
|
"make_sync_http_client",
|
|
@@ -220,12 +220,18 @@ class OpenAIProvider(Provider):
|
|
|
220
220
|
# Ask OpenAI (and compatible servers that honor it) for a final usage
|
|
221
221
|
# chunk so OTEL spans can record prompt/completion tokens. Some local
|
|
222
222
|
# OpenAI-compatible servers reject stream_options — fall back cleanly.
|
|
223
|
-
want_usage =
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
223
|
+
want_usage = False
|
|
224
|
+
if os.environ.get("CADE_OTEL", "").strip().lower() in ("1", "true", "yes", "on"):
|
|
225
|
+
# Official OpenAI honors stream_options; many local OpenAI-compatible
|
|
226
|
+
# servers (vLLM, etc.) mishandle it. Opt in for custom base_url with
|
|
227
|
+
# CADE_OTEL_STREAM_USAGE=1.
|
|
228
|
+
force_usage = os.environ.get("CADE_OTEL_STREAM_USAGE", "").strip().lower() in (
|
|
229
|
+
"1",
|
|
230
|
+
"true",
|
|
231
|
+
"yes",
|
|
232
|
+
"on",
|
|
233
|
+
)
|
|
234
|
+
want_usage = force_usage or not self.base_url
|
|
229
235
|
if want_usage:
|
|
230
236
|
openai_request["stream_options"] = {"include_usage": True}
|
|
231
237
|
|
|
@@ -2,21 +2,31 @@
|
|
|
2
2
|
|
|
3
3
|
Soft-depends on ``opentelemetry-*``: when the extra is missing or tracing is
|
|
4
4
|
disabled, every public helper is a no-op so callers never need a try/except.
|
|
5
|
+
|
|
6
|
+
Export defaults to OTLP HTTP at Phoenix (``http://127.0.0.1:6006/v1/traces``).
|
|
7
|
+
When the optional gRPC exporter is installed and Phoenix's gRPC port is
|
|
8
|
+
reachable, prefer gRPC (``127.0.0.1:4317``) — Phoenix's HTTP OTLP path is
|
|
9
|
+
known to intermittently reset connections under load, which used to dump
|
|
10
|
+
full SDK tracebacks into the chat TTY.
|
|
5
11
|
"""
|
|
6
12
|
|
|
7
13
|
from __future__ import annotations
|
|
8
14
|
|
|
9
15
|
import json
|
|
16
|
+
import logging
|
|
10
17
|
import os
|
|
11
18
|
import time
|
|
12
|
-
from collections.abc import Iterator
|
|
19
|
+
from collections.abc import Iterator, Sequence
|
|
13
20
|
from contextlib import contextmanager
|
|
14
21
|
from typing import Any
|
|
22
|
+
from urllib.parse import urlparse
|
|
15
23
|
|
|
16
24
|
from cadecoder.core.logging import log
|
|
17
25
|
|
|
18
26
|
_TRACER_NAME = "cade"
|
|
19
|
-
|
|
27
|
+
_DEFAULT_HTTP_ENDPOINT = "http://127.0.0.1:6006/v1/traces"
|
|
28
|
+
_DEFAULT_GRPC_ENDPOINT = "127.0.0.1:4317"
|
|
29
|
+
_MAX_MESSAGE_PART_ATTRS = 40
|
|
20
30
|
_initialized = False
|
|
21
31
|
_enabled = False
|
|
22
32
|
|
|
@@ -30,6 +40,143 @@ def is_enabled() -> bool:
|
|
|
30
40
|
return _enabled
|
|
31
41
|
|
|
32
42
|
|
|
43
|
+
def _silence_otel_loggers() -> None:
|
|
44
|
+
"""Keep OTEL SDK export failures out of the chat TTY.
|
|
45
|
+
|
|
46
|
+
Cade configures file-only logging and clears root handlers. Without a
|
|
47
|
+
handler, Python's ``lastResort`` prints WARNING+ (including the OTEL
|
|
48
|
+
exporter's ``logging.exception`` tracebacks) straight to stderr — which
|
|
49
|
+
lands in the middle of ``cade chat``.
|
|
50
|
+
"""
|
|
51
|
+
for name in (
|
|
52
|
+
"opentelemetry",
|
|
53
|
+
"opentelemetry.sdk",
|
|
54
|
+
"opentelemetry.sdk.trace",
|
|
55
|
+
"opentelemetry.sdk.trace.export",
|
|
56
|
+
"opentelemetry.exporter",
|
|
57
|
+
"opentelemetry.exporter.otlp",
|
|
58
|
+
"opentelemetry.exporter.otlp.proto.http",
|
|
59
|
+
"opentelemetry.exporter.otlp.proto.http.trace_exporter",
|
|
60
|
+
"opentelemetry.exporter.otlp.proto.grpc",
|
|
61
|
+
"opentelemetry.exporter.otlp.proto.grpc.trace_exporter",
|
|
62
|
+
):
|
|
63
|
+
logger = logging.getLogger(name)
|
|
64
|
+
logger.handlers.clear()
|
|
65
|
+
logger.addHandler(logging.NullHandler())
|
|
66
|
+
logger.propagate = False
|
|
67
|
+
logger.setLevel(logging.CRITICAL)
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
class _QuietSpanExporter:
|
|
71
|
+
"""Wrap an OTEL span exporter so failures stay out of the chat UI."""
|
|
72
|
+
|
|
73
|
+
def __init__(self, inner: Any, *, label: str) -> None:
|
|
74
|
+
self._inner = inner
|
|
75
|
+
self._label = label
|
|
76
|
+
self._failures = 0
|
|
77
|
+
|
|
78
|
+
def export(self, spans: Sequence[Any]) -> Any:
|
|
79
|
+
from opentelemetry.sdk.trace.export import SpanExportResult
|
|
80
|
+
|
|
81
|
+
try:
|
|
82
|
+
result = self._inner.export(spans)
|
|
83
|
+
except Exception as exc: # noqa: BLE001
|
|
84
|
+
self._note_failure(exc)
|
|
85
|
+
return SpanExportResult.FAILURE
|
|
86
|
+
if result == SpanExportResult.FAILURE:
|
|
87
|
+
self._note_failure(RuntimeError(f"{self._label} returned FAILURE"))
|
|
88
|
+
return result
|
|
89
|
+
|
|
90
|
+
def shutdown(self) -> None:
|
|
91
|
+
shutdown = getattr(self._inner, "shutdown", None)
|
|
92
|
+
if callable(shutdown):
|
|
93
|
+
try:
|
|
94
|
+
shutdown()
|
|
95
|
+
except Exception as exc: # noqa: BLE001
|
|
96
|
+
log.debug(f"otel exporter shutdown failed: {exc}")
|
|
97
|
+
|
|
98
|
+
def force_flush(self, timeout_millis: int = 30000) -> bool:
|
|
99
|
+
flush = getattr(self._inner, "force_flush", None)
|
|
100
|
+
if not callable(flush):
|
|
101
|
+
return True
|
|
102
|
+
try:
|
|
103
|
+
return bool(flush(timeout_millis))
|
|
104
|
+
except Exception as exc: # noqa: BLE001
|
|
105
|
+
self._note_failure(exc)
|
|
106
|
+
return False
|
|
107
|
+
|
|
108
|
+
def _note_failure(self, exc: BaseException) -> None:
|
|
109
|
+
self._failures += 1
|
|
110
|
+
# Rate-limit: first 3, then every 25th — enough signal without spam.
|
|
111
|
+
if self._failures <= 3 or self._failures % 25 == 0:
|
|
112
|
+
log.warning(
|
|
113
|
+
f"otel export via {self._label} failed ({self._failures}x): {exc} "
|
|
114
|
+
f"— is Phoenix up? (`make otel-ui`, endpoint "
|
|
115
|
+
f"{os.environ.get('OTEL_EXPORTER_OTLP_TRACES_ENDPOINT') or _DEFAULT_HTTP_ENDPOINT})"
|
|
116
|
+
)
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
def _resolve_http_endpoint() -> str:
|
|
120
|
+
endpoint = (
|
|
121
|
+
os.environ.get("OTEL_EXPORTER_OTLP_TRACES_ENDPOINT")
|
|
122
|
+
or os.environ.get("OTEL_EXPORTER_OTLP_ENDPOINT")
|
|
123
|
+
or _DEFAULT_HTTP_ENDPOINT
|
|
124
|
+
).rstrip("/")
|
|
125
|
+
if not endpoint.endswith("/v1/traces"):
|
|
126
|
+
endpoint = f"{endpoint}/v1/traces"
|
|
127
|
+
return endpoint
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
def _resolve_grpc_endpoint(http_endpoint: str) -> str:
|
|
131
|
+
explicit = (
|
|
132
|
+
os.environ.get("OTEL_EXPORTER_OTLP_TRACES_GRPC_ENDPOINT")
|
|
133
|
+
or os.environ.get("CADE_OTEL_GRPC_ENDPOINT")
|
|
134
|
+
or ""
|
|
135
|
+
).strip()
|
|
136
|
+
if explicit:
|
|
137
|
+
return explicit.removeprefix("http://").removeprefix("https://")
|
|
138
|
+
# When pointing at local Phoenix HTTP, use its companion gRPC port.
|
|
139
|
+
parsed = urlparse(http_endpoint)
|
|
140
|
+
host = parsed.hostname or "127.0.0.1"
|
|
141
|
+
if host in ("127.0.0.1", "localhost", "::1") and (parsed.port in (6006, None)):
|
|
142
|
+
return f"{host}:4317"
|
|
143
|
+
return _DEFAULT_GRPC_ENDPOINT
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
def _build_exporter() -> tuple[Any, str]:
|
|
147
|
+
"""Return ``(exporter, label)`` preferring gRPC for local Phoenix."""
|
|
148
|
+
from opentelemetry.exporter.otlp.proto.http import Compression
|
|
149
|
+
from opentelemetry.exporter.otlp.proto.http.trace_exporter import OTLPSpanExporter
|
|
150
|
+
|
|
151
|
+
http_endpoint = _resolve_http_endpoint()
|
|
152
|
+
prefer_grpc = not _truthy("CADE_OTEL_HTTP_ONLY")
|
|
153
|
+
|
|
154
|
+
if prefer_grpc:
|
|
155
|
+
try:
|
|
156
|
+
from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import (
|
|
157
|
+
OTLPSpanExporter as GrpcOTLPSpanExporter,
|
|
158
|
+
)
|
|
159
|
+
|
|
160
|
+
grpc_endpoint = _resolve_grpc_endpoint(http_endpoint)
|
|
161
|
+
exporter = GrpcOTLPSpanExporter(endpoint=grpc_endpoint, insecure=True)
|
|
162
|
+
return exporter, f"otlp/grpc:{grpc_endpoint}"
|
|
163
|
+
except ImportError:
|
|
164
|
+
pass
|
|
165
|
+
except Exception as exc: # noqa: BLE001
|
|
166
|
+
log.debug(f"otel gRPC exporter unavailable, falling back to HTTP: {exc}")
|
|
167
|
+
|
|
168
|
+
try:
|
|
169
|
+
exporter = OTLPSpanExporter(
|
|
170
|
+
endpoint=http_endpoint,
|
|
171
|
+
compression=Compression.Gzip,
|
|
172
|
+
timeout=5,
|
|
173
|
+
)
|
|
174
|
+
except TypeError:
|
|
175
|
+
# Older exporter builds may not accept compression=.
|
|
176
|
+
exporter = OTLPSpanExporter(endpoint=http_endpoint, timeout=5)
|
|
177
|
+
return exporter, f"otlp/http:{http_endpoint}"
|
|
178
|
+
|
|
179
|
+
|
|
33
180
|
def init_telemetry(*, force: bool = False) -> bool:
|
|
34
181
|
"""Initialize the tracer provider when ``CADE_OTEL=1``.
|
|
35
182
|
|
|
@@ -49,7 +196,6 @@ def init_telemetry(*, force: bool = False) -> bool:
|
|
|
49
196
|
|
|
50
197
|
try:
|
|
51
198
|
from opentelemetry import trace
|
|
52
|
-
from opentelemetry.exporter.otlp.proto.http.trace_exporter import OTLPSpanExporter
|
|
53
199
|
from opentelemetry.sdk.resources import Resource
|
|
54
200
|
from opentelemetry.sdk.trace import TracerProvider
|
|
55
201
|
from opentelemetry.sdk.trace.export import BatchSpanProcessor, SimpleSpanProcessor
|
|
@@ -61,15 +207,17 @@ def init_telemetry(*, force: bool = False) -> bool:
|
|
|
61
207
|
_enabled = False
|
|
62
208
|
return False
|
|
63
209
|
|
|
210
|
+
_silence_otel_loggers()
|
|
211
|
+
|
|
64
212
|
service_name = os.environ.get("OTEL_SERVICE_NAME", "cade").strip() or "cade"
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
endpoint = f"{endpoint}/v1/traces"
|
|
213
|
+
try:
|
|
214
|
+
raw_exporter, label = _build_exporter()
|
|
215
|
+
except Exception as exc: # noqa: BLE001
|
|
216
|
+
log.warning(f"CADE_OTEL=1 but exporter setup failed: {exc}")
|
|
217
|
+
_enabled = False
|
|
218
|
+
return False
|
|
72
219
|
|
|
220
|
+
exporter = _QuietSpanExporter(raw_exporter, label=label)
|
|
73
221
|
resource = Resource.create(
|
|
74
222
|
{
|
|
75
223
|
"service.name": service_name,
|
|
@@ -78,19 +226,26 @@ def init_telemetry(*, force: bool = False) -> bool:
|
|
|
78
226
|
}
|
|
79
227
|
)
|
|
80
228
|
provider = TracerProvider(resource=resource)
|
|
81
|
-
exporter = OTLPSpanExporter(endpoint=endpoint)
|
|
82
|
-
# Simple processor in tests / when CADE_OTEL_SYNC=1 so spans flush immediately.
|
|
83
229
|
if _truthy("CADE_OTEL_SYNC"):
|
|
84
230
|
provider.add_span_processor(SimpleSpanProcessor(exporter))
|
|
85
231
|
else:
|
|
86
|
-
|
|
232
|
+
# Small batches + short delay: Phoenix's HTTP OTLP path is flaky with
|
|
233
|
+
# large concurrent exports (connection resets mid-batch).
|
|
234
|
+
provider.add_span_processor(
|
|
235
|
+
BatchSpanProcessor(
|
|
236
|
+
exporter,
|
|
237
|
+
max_queue_size=2048,
|
|
238
|
+
schedule_delay_millis=500,
|
|
239
|
+
max_export_batch_size=16,
|
|
240
|
+
export_timeout_millis=5000,
|
|
241
|
+
)
|
|
242
|
+
)
|
|
87
243
|
trace.set_tracer_provider(provider)
|
|
88
244
|
_enabled = True
|
|
89
|
-
# Flush on process exit so short-lived CLI turns still export.
|
|
90
245
|
import atexit
|
|
91
246
|
|
|
92
247
|
atexit.register(shutdown_telemetry)
|
|
93
|
-
log.info(f"OpenTelemetry tracing enabled → {
|
|
248
|
+
log.info(f"OpenTelemetry tracing enabled → {label} (service={service_name})")
|
|
94
249
|
return True
|
|
95
250
|
|
|
96
251
|
|
|
@@ -220,8 +375,8 @@ def trace_tool_call(
|
|
|
220
375
|
"openinference.span.kind": "TOOL",
|
|
221
376
|
"tool.name": name,
|
|
222
377
|
"tool.call_id": tool_call_id or None,
|
|
223
|
-
"tool.parameters": _truncate(arguments) if arguments is not None else None,
|
|
224
|
-
"input.value": _truncate(arguments) if arguments is not None else None,
|
|
378
|
+
"tool.parameters": _truncate(arguments, 512) if arguments is not None else None,
|
|
379
|
+
"input.value": _truncate(arguments, 512) if arguments is not None else None,
|
|
225
380
|
},
|
|
226
381
|
) as span:
|
|
227
382
|
started = time.perf_counter()
|
|
@@ -268,12 +423,15 @@ def record_message_parts(
|
|
|
268
423
|
"""Record per-message role + estimated tokens for the prompt sent to the LLM.
|
|
269
424
|
|
|
270
425
|
Uses a cheap length heuristic so every thread message is visible in the
|
|
271
|
-
trace even when the provider does not report official usage yet.
|
|
426
|
+
trace even when the provider does not report official usage yet. Caps
|
|
427
|
+
per-message attributes so long threads (email triage, etc.) do not blow
|
|
428
|
+
up OTLP payloads.
|
|
272
429
|
"""
|
|
273
430
|
if span is None or not messages:
|
|
274
431
|
return
|
|
275
432
|
by_role: dict[str, int] = {}
|
|
276
433
|
parts: list[dict[str, Any]] = []
|
|
434
|
+
attributed = 0
|
|
277
435
|
for index, message in enumerate(messages):
|
|
278
436
|
role = str(message.get("role") or "unknown")
|
|
279
437
|
content = message.get("content")
|
|
@@ -293,15 +451,18 @@ def record_message_parts(
|
|
|
293
451
|
"index": index,
|
|
294
452
|
"role": role,
|
|
295
453
|
"estimated_tokens": estimated,
|
|
296
|
-
"content": _truncate(text,
|
|
454
|
+
"content": _truncate(text, 160),
|
|
297
455
|
}
|
|
298
456
|
)
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
457
|
+
if attributed < _MAX_MESSAGE_PART_ATTRS:
|
|
458
|
+
span.set_attribute(f"cade.message.{index}.role", role)
|
|
459
|
+
span.set_attribute(f"cade.message.{index}.estimated_tokens", estimated)
|
|
460
|
+
attributed += 1
|
|
461
|
+
span.set_attribute("cade.messages.count", len(messages))
|
|
302
462
|
for role, tokens in by_role.items():
|
|
303
463
|
span.set_attribute(f"cade.tokens.by_role.{role}", tokens)
|
|
304
|
-
|
|
464
|
+
# Bounded JSON summary — enough for Phoenix, small enough for OTLP HTTP.
|
|
465
|
+
span.set_attribute("llm.input_messages", _truncate(parts[-_MAX_MESSAGE_PART_ATTRS:], 4096))
|
|
305
466
|
|
|
306
467
|
|
|
307
468
|
def _record_exception(span: Any, exc: BaseException) -> None:
|
|
@@ -316,7 +477,7 @@ def _record_exception(span: Any, exc: BaseException) -> None:
|
|
|
316
477
|
pass
|
|
317
478
|
|
|
318
479
|
|
|
319
|
-
def set_span_output(span: Any, value: Any, *, limit: int =
|
|
480
|
+
def set_span_output(span: Any, value: Any, *, limit: int = 1024) -> None:
|
|
320
481
|
"""Record a truncated output payload on the span."""
|
|
321
482
|
if span is None or value is None:
|
|
322
483
|
return
|
|
@@ -337,7 +498,9 @@ def set_tool_result(
|
|
|
337
498
|
span.set_attribute("cade.tool.status", status)
|
|
338
499
|
if content is not None:
|
|
339
500
|
span.set_attribute("cade.tool.result_bytes", len(content.encode("utf-8", errors="replace")))
|
|
340
|
-
|
|
501
|
+
# Keep tool payloads small — Gmail/list results are huge and Phoenix's
|
|
502
|
+
# HTTP OTLP path resets connections on oversized batches.
|
|
503
|
+
set_span_output(span, content, limit=512)
|
|
341
504
|
if error:
|
|
342
505
|
span.set_attribute("cade.tool.error", _truncate(error, 512))
|
|
343
506
|
try:
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|