Documentation
Get from pip install to your first trace in minutes with the FortifyRoot Ocelle SDK.
Quickstart
Create an API key in the FortifyRoot app, install the SDK, and initialize once.
pip install fortifyroot-ocelle
export FORTIFYROOT_API_KEY="fr_sk_..."import fortifyroot.ocelle as ocelle
# Reads FORTIFYROOT_API_KEY from the environment.
ocelle.init(
app_name="my-llm-app",
resource_attributes={"environment": "dev"},
)
# then call OpenAI / Anthropic / Bedrock / LangChain as usual, auto-instrumented- API keys are created in the FortifyRoot app.
- To enable runtime safety, set
FORTIFYROOT_CONFIG_PROFILE_IDto the ID from your app config profile. - Run your app, then verify traces, cost, and safety signals in FortifyRoot.
- The public import is
import fortifyroot.ocelle as ocelle. The shorterimport ocelleis a convenience alias.
Network requirements
api.fortifyroot.com. No inbound ports are required.- Ocelle exports telemetry over OTLP/HTTP to
/v1/traces,/v1/metrics, and/v1/logsonapi.fortifyroot.com. - SDK config polling uses
/v1/sdk/config/{config_profile_id}. - Hosted SaaS usage does not require exposing OTLP ports 4317 or 4318.
- Your workload still needs separate egress to whichever LLM providers it calls.
Core concepts
- Traces & spans: request- and operation-level observability for LLM applications.
- Logs & metrics: correlated signals for debugging and operational visibility.
- Config profiles: SDK-consumed runtime policy bundles for safety behavior.
- Safety rules: ALLOW/MASK behavior for prompt and completion content.
- Content-capture controls: prompt/response content capture can be disabled.
- Provider normalization: distinguishes model provider, routing provider, and billing provider.
- Retry-waste analytics: surfaces detected retry loops with total/failed attempts, wasted cost, and waste ratio. Strongest for provider-SDK retries (e.g.
max_retries=N) detected as one chain. - Cost analytics: cost, token, latency, and projection views from observed telemetry.
SDK configuration
Common environment variables:
FORTIFYROOT_API_KEYFORTIFYROOT_CONFIG_PROFILE_ID(optional; enables runtime safety)FORTIFYROOT_BASE_URLFORTIFYROOT_TRACE_CONTENT(setfalseto disable prompt/response capture)FORTIFYROOT_TRACING_ENABLEDFORTIFYROOT_METRICS_ENABLEDFORTIFYROOT_LOGGING_ENABLED
Decorators @ocelle.workflow(name="..."), @ocelle.task(name="..."), @ocelle.agent(name="..."), and @ocelle.tool(name="...") add structured spans, and set_association_properties(...) correlates calls by user/session/conversation.
Runtime safety
Safety runs SDK-side on prompt and completion text in your own runtime. FortifyRoot does not require an LLM gateway or proxy. Rules take one of two actions:
MASK: redacts matched text before it continues through the application flow.ALLOW: records the finding without modifying text.
New organizations get a default starter safety profile you can inspect and customize in the app. Free starts with 5 high-signal rules enabled:
Paid tiers include a broader 20-rule starter set, high-signal rules enabled, with broader/noisier examples included disabled for opt-in (SSN, IP address, DOB context, CVV/expiry context, medical record context, AWS/GitHub/bearer tokens, private-key headers, self-harm and violence starters, and more).
Supported categories:
Activate runtime safety by passing config_profile_id (or FORTIFYROOT_CONFIG_PROFILE_ID) at init. Out-of-the-box rules are starter-grade and customizable. They are not a replacement for a full DLP or compliance program.
Provider & framework examples
Initialize Ocelle once, then call providers, frameworks, agents, and tools as usual, instrumentation is automatic. Snippets use placeholders (fr_sk_…, your-key); swap in your own key from the app.
Basic provider instrumentation
Call any supported provider after init and verify the trace, tokens, and cost in FortifyRoot.
import fortifyroot.ocelle as ocelle
from openai import OpenAI
# Reads FORTIFYROOT_API_KEY from the environment.
ocelle.init(app_name="my-llm-app")
client = OpenAI() # OPENAI_API_KEY from your environment
client.chat.completions.create(
model="gpt-4o-mini",
messages=[{"role": "user", "content": "Summarize today's incidents."}],
)
# trace, tokens, and cost now appear in FortifyRootAgent & tool tracing
Wrap an agent function with @ocelle.agent(name="...") and helpers with @ocelle.tool(name="...") (or @ocelle.workflow(name="...") / @ocelle.task(name="...")) for structured spans.
import fortifyroot.ocelle as ocelle
# Reads FORTIFYROOT_API_KEY from the environment.
ocelle.init(app_name="my-llm-app")
@ocelle.tool(name="get_weather")
def get_weather(city: str) -> str:
return weather_api(city)
@ocelle.agent(name="trip_planner")
def plan_trip(prompt: str) -> str:
# LLM + tool calls inside show up as nested agent / tool spans
...Routed-provider visibility
Routed providers (OpenRouter, LiteLLM) are recognized through the OpenAI-compatible path and provider-role normalization, not a dedicated instrument. The Events view shows Provider / Routing / Billed Via where detected.
import fortifyroot.ocelle as ocelle
from openai import OpenAI
# Reads FORTIFYROOT_API_KEY from the environment.
ocelle.init(app_name="my-llm-app")
# OpenRouter / LiteLLM speak the OpenAI-compatible API. FortifyRoot maps the
# provider role automatically: model provider vs routing vs billing provider.
client = OpenAI(base_url="https://openrouter.ai/api/v1", api_key="your-key")
client.chat.completions.create(
model="anthropic/claude-3.5-sonnet",
messages=[{"role": "user", "content": "Hello"}],
)Retry-waste
Prefer provider-SDK retry config (e.g. max_retries) so FortifyRoot can group attempts into one retry chain and attribute wasted cost.
import fortifyroot.ocelle as ocelle
from openai import OpenAI
# Reads FORTIFYROOT_API_KEY from the environment.
ocelle.init(app_name="my-llm-app")
# Provider-SDK retries are detected as one retry chain, so FortifyRoot can
# attribute the wasted cost of the failed attempts.
client = OpenAI(max_retries=5)
client.chat.completions.create(model="gpt-4o-mini", messages=[...])More patterns, all on the same init:
- Anthropic, Google GenAI, AWS Bedrock: install the provider extras, then call each provider's own SDK as usual; calls are auto-instrumented.
- Frameworks: LangChain and LlamaIndex are instrumented directly; LangGraph runs via the LangChain instrumentation path.
- Runtime safety: pass
config_profile_id(orFORTIFYROOT_CONFIG_PROFILE_ID) to activate safety rules. - Content privacy: set
trace_content=False(orFORTIFYROOT_TRACE_CONTENT=false) to stop capturing prompt/response text. - Logs & metrics: metrics export by default; set
FORTIFYROOT_LOGGING_ENABLED=trueand use Pythonloggingfor correlated logs (stdout/stderr capture is post-MVP). - Correlation & sampling:
ocelle.set_association_properties({…})correlates by user/session, and asampler=(e.g.TraceIdRatioBased) trims high-volume services.
These are minimal public-safe excerpts. Keep full runnable samples in your own repo.
Supported providers & frameworks
Coverage is graded so you know exactly what's launch-certified vs. recognized vs. on the roadmap:
Live-tested
Verified with live provider traffic in the launch validation suite.Opt-in live-tested
Verified in opt-in live/dev coverage, outside the default launch gate.Mapper-supported
Provider role is recognized, but dedicated launch-certified instrumentation isn’t claimed.Planned
Roadmap only, not launch-supported.LangGraph runs via the LangChain instrumentation path. OpenRouter and LiteLLM are recognized as routed-provider roles (provider normalization). The model provider stays the underlying vendor. xAI is recognized through OpenAI-compatible/OpenRouter paths and provider-role mapping, not a dedicated vendored instrument.
Analytics surfaces
Once telemetry arrives, FortifyRoot surfaces it across these views (each appears when matching data exists in the selected window):
- Dashboard: total/trend cost, cost by model provider and billed-via, projected cost, retry waste, safety summary.
- LLM Analytics: Overview, Events (per-call Provider / Routing / Billed Via), Metrics, and Retries.
- Safety: violations, masked/allowed counts, breakdowns.
- Logs & Traces: correlated signals with cost/token aggregates.
Model values are observed from telemetry, not a static certified catalog. Not every provider or model emits every metric, and direct calls normally have no routing provider.
Metrics tab catalog
Usage-event-derived metrics are queried from your LLM usage events and shown as guided panels:
| Metric | Source name | Appears when |
|---|---|---|
| Token usage | gen_ai.client.token.usage | an LLM call reports token usage |
| Operation duration | gen_ai.client.operation.duration | an LLM call's duration is captured |
| Time to first token | gen_ai.server.time_to_first_token | a streaming path emits/derives TTFT |
| Streaming time to generate | llm.chat_completions.streaming_time_to_generate | a streaming path emits/derives it |
Discovery-gated SDK metrics appear only when FortifyRoot finds usable tenant samples:
| Metric | Source name | Appears when |
|---|---|---|
| Generation choices | gen_ai.client.generation.choices | a provider emits generation-choice counters |
| Prompt caching | gen_ai.prompt_caching | a provider emits prompt-cache metrics (hits can be probabilistic) |
| OpenAI chat errors | llm.openai.chat_completions.exceptions | OpenAI chat exception telemetry exists |
| Anthropic completion errors | llm.anthropic.completion.exceptions | Anthropic completion exception telemetry exists |
| Bedrock guardrail metrics | gen_ai.bedrock.guardrail.* | a Bedrock guardrail is configured and emits samples |
- Metrics can group by model, provider, routing provider, billing provider, service, and environment when those labels exist.
- Older telemetry can lack newer provider-role labels.
- Retry-waste analytics are strongest for provider-SDK retries detected as one chain; grouped analytics for application-layer retries are post-MVP.
OTLP & API surfaces
Ocelle exports over OpenTelemetry. There's no JSON REST to wire up by hand. For normal usage the SDK handles all of this automatically.
- OTLP ingestion (OpenTelemetry-compatible, API-key auth) on
https://api.fortifyroot.com:POST /v1/traces,/v1/metrics,/v1/logs. - SDK config polling: when
config_profile_idis set, Ocelle pollsGET /v1/sdk/config/{config_profile_id}. - Query APIs: the dashboard is backed by query APIs for traces, logs, metrics, analytics, LLM events, retry loops, and safety. Full public OpenAPI reference docs are planned post-MVP.
Troubleshooting
- No traces: confirm the API key, outbound HTTPS to
api.fortifyroot.com, provider extras installed, and tracing enabled. - Invalid API key: create or rotate the key in the app and check the environment variable name.
- Safety not applying: confirm
config_profile_idand that the SDK can reach the config endpoint. - Missing content: check
FORTIFYROOT_TRACE_CONTENT. - No logs: set
FORTIFYROOT_LOGGING_ENABLED=trueand use Pythonlogging. - Retry loops not appearing: for MVP, prefer provider-SDK retry config such as OpenAI/Anthropic
max_retries=N; confirm the time range includes retrying calls and that retries are for the same provider/model chain (not fallback routing to a different model). Application-layerfor/whileretries still show per-attempt cost/usage events, but grouped retry-loop analytics for those is post-MVP.
Security & data handling
- SDK-side detection and masking run in your own runtime.
- Prompt/response content capture can be disabled.
- FortifyRoot's MVP cloud deployment is operated in a US single-region environment.
- Data-access retention is tier-enforced; physical purge, backups, queues, logs, and operational stores follow platform-level retention schedules.
See our Privacy Policy and Terms of Service.