Skip to main content

Rubra

Agentic evaluation framework. Every aspect, nothing missed.

PyPI License Python CI

Rubra is a trace-first agent evaluation framework. Decorate your agent — Rubra automatically captures every tool call, LLM call, token, and cost. Then evaluate with 36 metrics, including 11 tool-orchestration metrics not commonly found elsewhere.

import rubra

@rubra.agent(task="Answer questions using web search")
def my_agent(question: str) -> str:
    context = search_web(question)
    return call_llm(context, question)

my_agent("What is the capital of France?")

report = rubra.evaluate(rubra.get_last_trace())
print(f"Rubra Score: {report.rubra_score:.3f}")   # 0.923
print(f"Passed:      {report.passed}/{report.total_metrics}")

How Rubra compares

Rubra is early (v0.1.x) and hasn't been battle-tested at the scale TruLens or RAGAS have — the table below reflects what each project's public docs and source describe as of this writing, not independent benchmarking. Treat it as a starting point for your own evaluation, not a verdict.

Feature Rubra TruLens RAGAS DeepEval
1-line agent instrumentation
Tool orchestration metrics (11 unique) Partial
OpenAI + Anthropic auto-trace Manual Manual Manual
Reference-free goal evaluation Partial Partial
LangGraph + LangChain integration Partial
Safety metrics (injection, PII, scope)
OpenTelemetry export
Self-hosted REST API + Dashboard
Pytest plugin
Zero config (SQLite default) Partial

Where Rubra is most confidently different is the 11 tool-orchestration metrics — precision/recall/F1 on tool selection, call-order scoring, redundant-call detection — which the others don't expose as first-class metrics today. Most of the rest of the table is closer to "different design choices" than "better or worse": TruLens and DeepEval in particular have mature ecosystems and production track records Rubra doesn't have yet.


Installation

pip install rubra                    # core (4 deps, no LLM required)
pip install "rubra[judge]"           # + LLM-judge metrics via litellm
pip install "rubra[openai]"          # + OpenAI SDK interceptor
pip install "rubra[anthropic]"       # + Anthropic Claude interceptor
pip install "rubra[langgraph]"       # + LangGraph node tracing
pip install "rubra[langchain]"       # + LangChain callback handler
pip install "rubra[otel]"            # + OpenTelemetry export
pip install "rubra[all]"             # everything

Quickstart

1. Basic agent (any framework)

import rubra

@rubra.tool
def search_web(query: str) -> str:
    return my_search_api(query)

@rubra.agent(
    task="Answer capital city questions",
    expected_tool_calls=["search_web"],   # optional: enables F1 metrics
)
def capital_agent(question: str) -> str:
    context = search_web(question)
    return my_llm(context, question)

capital_agent("What is the capital of Japan?")

trace = rubra.get_last_trace()
report = rubra.evaluate(trace, metrics="all")
print(report.summary())

2. With OpenAI — zero-change LLM tracing

import openai
import rubra

client = rubra.patch(openai.OpenAI())   # one line — that's it

@rubra.agent(task="Capital cities")
def agent(q: str) -> str:
    response = client.chat.completions.create(   # automatically traced
        model="gpt-4o-mini",
        messages=[{"role": "user", "content": q}],
    )
    return response.choices[0].message.content

3. With Anthropic Claude

import anthropic
import rubra

client = rubra.patch_anthropic(anthropic.Anthropic())

@rubra.agent(task="Summarise documents")
def agent(text: str) -> str:
    response = client.messages.create(
        model="claude-3-5-sonnet-20241022",
        max_tokens=1024,
        messages=[{"role": "user", "content": text}],
    )
    return response.content[0].text

4. In pytest — evaluate your agent in CI

# test_agent.py  (no conftest.py needed — plugin registers automatically)

def test_capital_agent_quality(rubra_trace):
    result = capital_agent("What is the capital of Japan?")
    assert result == "Tokyo"

    report = rubra_trace.evaluate(metrics="execution")
    assert report.get("task_completion_rate").passed
    assert report.rubra_score >= 0.70

# One-liner shorthand:
def test_passes_score_threshold(rubra_trace):
    capital_agent("What is the capital of Germany?")
    rubra_trace.assert_score(min_rubra_score=0.70, min_pass_rate=0.80)

5. LangGraph

from langgraph.graph import StateGraph
from rubra.integrations.langgraph import patch
import rubra

graph = StateGraph(MyState)
graph.add_node("search", search_node)
graph.add_node("answer", answer_node)
app = patch(graph).compile()   # wraps every node as a tool span

@rubra.agent(task="Multi-hop question answering")
def run(question: str) -> str:
    return app.invoke({"question": question})["answer"]

6. LangChain

from rubra.integrations.langchain import RubraCallbackHandler
import rubra

handler = RubraCallbackHandler()

@rubra.agent(task="Chain execution")
def run(question: str) -> str:
    return my_chain.invoke({"question": question}, config={"callbacks": [handler]})

Available Metrics

Execution (13) — deterministic, no LLM needed

Metric Description
task_completion_rate Did the agent reach COMPLETED status?
tool_call_success_rate Fraction of tool calls with no error
error_rate 1 − (error spans / total spans)
step_efficiency Penalty for exceeding max_steps
latency_score Penalty for slow traces
token_efficiency Penalty for excess token usage
cost_efficiency Linear decay past budget
tool_diversity Unique tools / total calls
retry_rate Same-tool-after-error retries
hallucination_free_calls Empty-argument proxy
response_completeness Final output length check
tool_output_utilization Tool output present in final response
execution_time_distribution Dominant span fraction check

Tool Orchestration (11) — USP, unique to Rubra

Metric Description
tool_selection_precision TP / (TP + FP) vs expected tool calls
tool_selection_recall TP / (TP + FN)
tool_selection_f1 Harmonic mean of precision + recall
tool_call_order_score LCS-based sequence alignment
tool_trajectory_equivalence Jaccard + order for non-deterministic paths
redundant_tool_call_rate Same tool + args called twice
tool_error_recovery_rate Does agent continue after tool failure?
intermediate_step_grounding Next-call args reference prior response
tool_argument_completeness All argument values non-empty
tool_response_latency_score Per-tool latency check
tool_chain_validity Every TOOL_CALL has a matching TOOL_RESPONSE

Safety (3)

prompt_injection_resistance · scope_creep_score · pii_propagation_count

Quality (4)

answer_relevance_proxy · output_coherence_score · format_compliance_score · response_groundedness

Goal / LLM-judge (5) — requires rubra[judge]

goal_completion · answer_correctness · reasoning_quality · task_understanding · hallucination_score

The judge model is configurable and works with any litellm-supported model — including free local models via Ollama, so you can exercise these metrics with zero API cost:

report = rubra.evaluate(trace, metrics="all", judge_model="ollama/llama3.2")

Composite scores (automatic)

  • rubra_score — weighted average across all scored metrics
  • tool_intelligence_score — average of tool-category metrics
  • agentic_efficiency_score — completion × average efficiency

REST API + Dashboard

See rubra-server for the self-hosted FastAPI backend and live dashboard.

git clone https://github.com/pm1715/rubra-server
cd rubra-server
docker compose up
# Dashboard → http://localhost:8000
# API docs  → http://localhost:8000/docs

CLI

rubra traces              # list recent traces
rubra eval                # evaluate latest trace
rubra eval <TRACE_ID>     # evaluate specific trace
rubra report -o out.html  # generate HTML report

Architecture

Rubra uses Python contextvars.ContextVar for async-safe, thread-safe trace propagation — no globals, no thread-locals, no locks. Each @rubra.agent call creates an isolated Trace with its own ContextVar token, making concurrent agents safe by design.

@rubra.agent ──► Trace (ContextVar)
    @rubra.tool ──► TOOL_CALL + TOOL_RESPONSE spans
    rubra.patch ──► LLM_CALL spans (auto)
evaluate(trace) ──► EvalReport (36 metrics + 3 composite scores)

Author

Rubra was designed and built by Prayansh Mishra (@pm1715 · LinkedIn).

License

Apache 2.0 — see LICENSE.

Contributing

See CONTRIBUTING.md.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

rubra-0.1.4.tar.gz (71.3 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

rubra-0.1.4-py3-none-any.whl (58.5 kB view details)

Uploaded Python 3

File details

Details for the file rubra-0.1.4.tar.gz.

File metadata

  • Download URL: rubra-0.1.4.tar.gz
  • Upload date:
  • Size: 71.3 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for rubra-0.1.4.tar.gz
Algorithm Hash digest
SHA256 c48a03b36c22cb4f63a35bb9d3bbf3171181c61eefe0e2c1239e7b8aca4f103f
MD5 ba1061ed92a7b1a13822a259abaa3957
BLAKE2b-256 f22698be6038398f59b08b9bb093b4c22f526d4a856beb91898493e76e3726a0

See more details on using hashes here.

Provenance

The following attestation bundles were made for rubra-0.1.4.tar.gz:

Publisher: publish.yml on pm1715/rubra-sdk

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file rubra-0.1.4-py3-none-any.whl.

File metadata

  • Download URL: rubra-0.1.4-py3-none-any.whl
  • Upload date:
  • Size: 58.5 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for rubra-0.1.4-py3-none-any.whl
Algorithm Hash digest
SHA256 463cd1d55fb88f54400aeee753c562315365acd94bd2fa7d4ba0c90e155bebfd
MD5 6ff51235d6e065c6d028354972e54570
BLAKE2b-256 b8f9a96f0e44657cfe1ef40e488eeb53df2b6bdb376e30f17b20e22f77101bc1

See more details on using hashes here.

Provenance

The following attestation bundles were made for rubra-0.1.4-py3-none-any.whl:

Publisher: publish.yml on pm1715/rubra-sdk

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Release history Release notifications | RSS feed

0.1.6

2 files

This release

0.1.4 This release

2 files

0.1.2

2 files

0.1.1

2 files

0.1.0

2 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page