Telemetry¶
Kedi provides a dependency-free telemetry seam in the runtime. It is a no-op
until the separate opentelemetry-instrumentation-kedi package installs an
OpenTelemetry backend. Application code keeps ownership of the OpenTelemetry
SDK, resource, sampler, exporter, and shutdown lifecycle.
Installation¶
Install the instrumentor beside Kedi:
Enable it once during application startup:
from opentelemetry.instrumentation.kedi import KediInstrumentor
instrumentor = KediInstrumentor()
instrumentor.instrument()
Configure the tracer and meter providers with service.name=kedi. The
instrumentor uses three instrumentation scope names:
| Scope | Responsibility |
|---|---|
kedi.runtime |
parsing, compilation, execution, procedures, and embedded Python |
kedi.agent |
agents, models, tools, MCP, approvals, subagents, and workflows |
kedi.artifacts |
artifact admission, storage, reads, release, expiry, and cleanup |
Importing Kedi or the instrumentor does not configure an exporter and does not start telemetry by itself.
Logfire¶
Logfire accepts the native OpenTelemetry data emitted by the instrumentor:
import logfire
from opentelemetry.instrumentation.kedi import KediInstrumentor
logfire.configure(service_name="kedi")
KediInstrumentor().instrument()
Pydantic AI instrumentation is enabled by default, so Pydantic AI model and tool spans appear at their native detail beneath the Kedi operation that owns them. Kedi reclassifies those native spans with Kedi agent semantics instead of emitting duplicate agent, chat, and tool spans.
HTTPX instrumentation is disabled by default. Install the extra and enable it when outbound request spans are useful:
Do not separately call logfire.instrument_pydantic_ai() when the Kedi
instrumentor owns that integration. HTTPX setup is process-wide and one-way;
Kedi does not uninstrument HTTPX during teardown because doing so could remove
instrumentation installed by another library.
Configuration¶
instrument() accepts strict, validated options:
KediInstrumentor().instrument(
runtime_detail="lifecycle", # "off", "lifecycle", or "detailed"
agent_enabled=True,
artifacts_enabled=True,
capture_content=False,
capture_binary_content=False,
capture_source_paths=False,
capture_source_snippets=False,
capture_model_request_parameters=False,
capture_tool_definitions=False,
capture_exception_messages=False,
capture_exception_stacktraces=False,
max_metric_instruments=128,
instrument_pydantic_ai=True,
instrument_httpx=False,
tracer_provider=tracer_provider,
meter_provider=meter_provider,
)
Unknown option names, non-boolean values for boolean options, and invalid
runtime_detail values fail immediately. The three scope switches behave as
follows:
runtime_detail="off"disables all runtime spans and metrics.runtime_detail="lifecycle"records bounded parse, compile, and run lifecycle operations while omitting detailed procedure and Python spans.runtime_detail="detailed"also records procedure calls and embedded Python execution.agent_enabled=Falsedisables the complete agent surface, including model and tool calls, MCP, approvals, subagents, and dynamic workflows.artifacts_enabled=Falsedisables artifact spans, events, and metrics.
max_metric_instruments bounds the number of metric instruments the backend
may create. This prevents unbounded instrument growth if an extension supplies
unexpected metric names.
Span Hierarchy¶
Span names are concise operation labels intended for trace UIs. Machine-facing
semantics remain in kedi.operation.name and standard gen_ai.* attributes.
A representative agent run is shaped like this:
kedi run program.kedi
`-- agent researcher
|-- chat openrouter:google/gemini-3-flash-preview
|-- call web_search
|-- await approval write_file
|-- planner run
`-- researcher workflow
The principal spans are:
| Span name | Operation | Notes |
|---|---|---|
kedi run <program> |
run_program |
complete program execution |
parse <program> |
parse_program |
source parsing and diagnostics |
compile <program> |
compile_program |
declarations, profiles, and runtime construction |
compile module <name> |
compile_module |
imported-module compilation |
call <procedure> |
call_procedure |
detailed procedure execution |
run python |
run_python |
detailed inline, fenced, prelude, or type Python execution |
agent <profile> |
run_agent |
one semantic Kedi agent call |
chat <model> |
chat |
adapter/model boundary when no native owner exists |
call <tool> |
call_tool |
approved tool execution and output admission |
initialize mcp <server> |
initialize_mcp |
MCP connection and tool discovery |
await approval <tool> |
await_approval |
blocking CLI, Python, or native approval decision |
<profile> run |
run_agent |
a subagent run; replaces the delegation tool wrapper |
<profile> workflow |
run_workflow |
a dynamic workflow run |
search tools |
search_tools |
bounded CodeMode catalog discovery |
get tool schema |
get_tool_schema |
exact CodeMode schema hydration |
execute code |
execute_code |
one bounded Monty notebook cell and its nested calls |
process history |
process_history |
deterministic Kedi history selection after the reduction threshold is reached |
compact history |
compact_history |
one actual native or Kedi-owned compaction attempt |
store artifact |
store_artifact |
material artifact storage |
cleanup artifacts |
cleanup_artifacts |
scheduled or explicit cleanup |
Lightweight artifact searches, reads, releases, expirations, and context preflight decisions are events on the active operation rather than extra nested spans. This keeps traces readable during artifact-heavy runs.
Attributes and Events¶
Kedi uses OpenTelemetry GenAI semantic attributes where they apply:
gen_ai.agent.name,gen_ai.request.model, andgen_ai.output.typedescribe agent and model calls;gen_ai.tool.nameidentifies the invoked tool;gen_ai.usage.input_tokensandgen_ai.usage.output_tokensreport observed usage;kedi.adapter.name,kedi.adapter.kind, andkedi.agent.call.kindidentify the Kedi boundary;kedi.tool.source,kedi.tool.risk, andkedi.tool.result.artifactizeddescribe tool execution;kedi.approval.mechanism,kedi.approval.risk, andkedi.approval.decisiondescribe approval without recording arguments;kedi.subagent.*andkedi.workflow.*carry run identity, depth, budget, concurrency, outcome, and child-call counts;kedi.codemode.*carries catalog, hydration, code-size, restart, nested-call, nested-result-byte, duration, and outcome observations without code or tool payloads;kedi.history.compaction.*carries mode, trigger, input/output message and token counts, reduction ratio, retained-prefix validation, checkpoint state, and whether the cache epoch changed;kedi.artifact.*carries sizes, offsets, lifecycle outcome, and context bytes avoided without exposing the payload.
Events mark meaningful transitions such as agent request prepared, tool
execution started, tool output admitted, approval requested, approval
pending, mcp connected, artifact released, and context preflight.
Compaction uses compaction triggered, checkpoint validated, checkpoint
committed, cache epoch advanced, and compaction rejected. A
compact history span means a backend was actually invoked or a validated Kedi
checkpoint commit was attempted; applying provider request settings alone does
not emit it. Below-threshold history checks do not create spans.
Metrics¶
Runtime metrics include:
kedi.run.count,kedi.run.duration, andkedi.run.active;kedi.parse.countandkedi.parse.duration;kedi.compile.countandkedi.compile.duration;kedi.procedure.callsandkedi.procedure.duration;kedi.python.execution.countandkedi.python.execution.duration.
Agent metrics include:
kedi.agent.invocationsandkedi.agent.duration;gen_ai.client.operation.durationandgen_ai.client.token.usage;kedi.tool.callsandkedi.tool.duration;kedi.approval.requestsandkedi.approval.wait.duration;kedi.mcp.initializationsandkedi.mcp.initialization.duration;kedi.codemode.operationsandkedi.codemode.duration;- adapter, subagent, and workflow duration/count observations carried by their respective operations.
History compaction metrics include:
kedi.history.compactionsandkedi.history.compaction.duration;kedi.history.compaction.input_tokensandkedi.history.compaction.output_tokens;kedi.history.compaction.reduction_ratio;kedi.history.compaction.failures.
Compaction metric dimensions are limited to mode, trigger, and result. Provider cache-read and cache-write token usage is already reported by agent/model telemetry and is deliberately not duplicated by compaction metrics.
Artifact metrics include:
kedi.artifact.created,kedi.artifact.released, andkedi.artifact.expired;kedi.artifact.activeandkedi.artifact.active_bytes;kedi.artifact.readsandkedi.artifact.read_bytes;kedi.artifact.context_bytes_avoided;kedi.artifact.quota_rejectionsandkedi.artifact.cleanup_duration;kedi.tool.output_admissionsandkedi.tool.output_size.
Outcome, adapter, call kind, tool source/risk, transport, and token type are bounded metric dimensions. Prompt text, paths, arbitrary tool names, and artifact IDs are not metric dimensions.
Privacy and Content Capture¶
The default configuration does not capture:
- prompt or model output content;
- binary content;
- source paths or source snippets;
- model request parameters;
- tool definitions;
- exception messages or stack traces.
Each category has an independent opt-in. Exception type remains observable without exposing the message. Captured exception messages and stack traces are bounded. Content options should be enabled only after reviewing provider data, tool arguments, source code, and credentials that may enter telemetry.
Lifecycle and Ownership¶
Instrumentation setup and teardown are serialized and idempotent.
instrumentor.uninstrument() restores the previous Kedi telemetry backend only
if the active backend is still the one installed by that instrumentor. It also
restores Pydantic AI instrumentation only while Kedi still owns the exact
setting. Newer application-owned configuration is never overwritten.
If one teardown step fails, Kedi attempts the remaining cleanup steps and then
raises KediInstrumentationCleanupError with every cause. If setup fails, the
setup exception remains primary and rollback failure is chained beneath it.
Kedi deliberately keeps tracing optional: without an installed backend, span, event, and metric calls remain no-ops and do not require OpenTelemetry at runtime.