Radicas Docs
Python SDK

Feature tagging

Attribute agent cost to product features and users — the default feature, radicas.feature() scoping, and radicas.user() for per-user allocation.

Radicas allocates cost by feature — the product capability an agent invocation serves — via the radicas.feature span attribute, and by user via user.id. The SDK stamps both for you.

The default feature

Set once at init; stamped on every agent invocation (invoke_agent span):

radicas.init(service="pricing-agent", feature="flight-pricing")

or RADICAS_FEATURE=flight-pricing in the environment (legacy FEATURE_NAME also works).

Per-request scoping: radicas.feature()

When one process serves several features, override the default for a block:

with radicas.feature("re-pricing-batch"):
    agent.run(prompt)              # this invocation is allocated to re-pricing-batch
agent.run(prompt2)                 # back to the init() default

Inside the block, the override is stamped on the invoke_agent root and every child span (so the whole subtree is marked); outside it, only invocation roots carry the default.

Per-user allocation: radicas.user()

user.id feeds per-user cost allocation (who is driving the spend):

with radicas.user(request.user_id):
    agent.run(prompt)

Both context managers nest and compose:

with radicas.feature("chat-assist"), radicas.user("u-42"):
    agent.run(prompt)

Propagation and its limits

The overrides ride the OpenTelemetry context, so they follow:

  • normal synchronous call stacks,
  • asyncio tasks created inside the block,
  • threads and executors that propagate OTel context (OTel-instrumented executors, or manually attaching a captured context in the worker).

Limitation — raw threads: a plain threading.Thread(target=...) started inside the block does not inherit the OTel context, so spans created there fall back to the init default. Either attach a captured context in the worker:

from opentelemetry import context as otel_context

ctx = otel_context.get_current()          # capture inside the with-block

def worker():
    token = otel_context.attach(ctx)      # re-attach in the thread
    try:
        agent.run(prompt)
    finally:
        otel_context.detach(token)

or set the feature at init() when the whole process serves one feature.

Naming guidance

  • Name product features, not code paths: flight-pricing, refund-triage, support-chat — the names appear in cost dashboards read by non-engineers.
  • Use stable, lowercase, hyphenated slugs; renaming a feature splits its cost history.
  • Keep cardinality low (dozens, not thousands). For per-request identifiers use user.id or the conversation id — not the feature tag.
  • The fallback feature is literally default: if you see it in the dashboard, an agent is running without a configured feature.

On this page