Portia Labs Eval framework for evaluating agentic workflows.
Project description
🧵 SteelThread: Agent Evaluation Framework
SteelThread is a flexible evaluation framework built around Portia, designed to support robust evals and stream based testing of agentic workflows. It enables configurable datasets, custom metric definitions, LLM-based judging, and stubbed tool behaviors for reproducible and interpretable scoring.
🚀 Getting Started
1. Install using your framework of choice
pip
pip install steel-thread
poetry
poetry add steel-thread
uv
uv add steel-thread
2. Create your datasets
SteelThread is designed around deep integration with Portia. It uses data from Portia Cloud to generate test cases and evals.
When running monitoring through SteelThread we offer two distinct types:
- Evals are static datasets designed to be run multiple times to allow you to analyze how changes to your agents affect performance.
- Streams are dynamic streams that automatically include your latest plans and plan runs, allowing you to measure performance in production.
Both types of monitoring can be configured via the cloud dashboard.. Once you've created a dataset record the name of it.
3. Basic Usage
Run a full suite of evals and streams using the name of the dataset from step 2. This will use the built in set of evaluators to give you data out of the box.
from portia import Config, LogLevel, Portia
from steelthread.steelthread import SteelThread, StreamConfig, EvalConfig
# Setup
config = Config.from_default(default_log_level=LogLevel.CRITICAL)
st = SteelThread()
# Process stream
st.process_stream(
StreamConfig(stream_name="stream_v1", config=config, additional_tags={"feeling": "neutral"})
)
# Run evals
portia = Portia(config)
st.run_evals(
portia,
EvalConfig(
eval_dataset_name="evals_v1",
config=config,
iterations=4,
),
)
🛠️ Features
🧪 Custom Metrics
Define your own evaluators by subclassing Evaluator:
from steelthread.evals.evaluator import Evaluator
from steelthread.metrics.metric import Metric
class EmojiEvaluator(Evaluator):
def eval_test_case(self,
test_case: EvalTestCase,
final_plan: Plan,
final_plan_run: PlanRun,
additional_data: PlanRunMetadata,
):
output = final_plan_run.outputs.final_output.get_value() or ""
count = output.count("😊")
score = min(count / 2, 1.0)
return Metric(score=score, name="emoji_score", description="Checks for emoji use")
🧩 Tool Stubbing
Stub tool responses deterministically for fast and reproducible testing:
from steelthread.portia.tools import ToolStubRegistry, ToolStubContext
def weather_stub_response(
ctx: ToolStubContext,
) -> str:
"""Stub for weather tool to return deterministic weather."""
city = ctx.kwargs.get("city", "").lower()
if city == "sydney":
return "33.28"
if city == "london":
return "2.00"
return f"Unknown city: {city}"
# Run evals with stubs + custom evaluators.
portia = Portia(
config,
tools=ToolStubRegistry(
DefaultToolRegistry(config),
stubs={
"weather_tool": weather_stub_response,
},
),
)
📊 Metric Reporting
SteelThread is designed around plugable metrics backends. By default metrics are logged and sent to Portia Cloud for visualization but you can add additional backends via the config options.
🧪 Example: End-to-End Test Script
See how everything fits together:
from steelthread.steelthread import SteelThread, EvalConfig
from steelthread.portia.tools import ToolStubRegistry
from steelthread.metrics.metric import Metric
from steelthread.evals.evaluator import Evaluator
from portia import Config, Portia, DefaultToolRegistry, ToolRunContext
# Custom tool stub
def weather_stub_response(
ctx: ToolStubContext,
) -> str:
"""Stub for weather tool to return deterministic weather."""
city = ctx.kwargs.get("city", "").lower()
if city == "sydney":
return "33.28"
if city == "london":
return "2.00"
return f"Unknown city: {city}"
# Custom evaluator
class EmojiEvaluator(Evaluator):
def eval_test_case(self, test_case,plan, plan_run, metadata):
out = plan_run.outputs.final_output.get_value() or ""
count = out.count("🌞")
return Metric(score=min(count / 2, 1.0), name="emoji_score", description="Emoji usage")
# Setup
config = Config.from_default()
st = SteelThread()
portia = Portia(
config,
tools=ToolStubRegistry(DefaultToolRegistry(config), {"weather_tool": weather_stub_response})
)
st.run_evals(
portia,
EvalConfig(
eval_dataset_name="evals_v1",
config=config,
iterations=4,
),
)
🧪 Testing
Write tests for your metrics, plans, or evaluator logic using pytest:
uv run pytest tests/
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file steel_thread-0.1.10.tar.gz.
File metadata
- Download URL: steel_thread-0.1.10.tar.gz
- Upload date:
- Size: 21.3 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.8.6
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
05a3262c0b2a5f9880f1fe83435e7c546bf9b503fd66db8b78ef7035aad954e6
|
|
| MD5 |
e35bc3f2049f66cb4a6f8a551939b3a3
|
|
| BLAKE2b-256 |
6faca74ab6540754a018a8706b1a430baa679b051fd634e0b92bdc70f458b86b
|
File details
Details for the file steel_thread-0.1.10-py3-none-any.whl.
File metadata
- Download URL: steel_thread-0.1.10-py3-none-any.whl
- Upload date:
- Size: 30.1 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.8.6
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
e8be27c0fb5728a8275d6954aaa655158d10f5d57707510cc3657c166376091f
|
|
| MD5 |
8b59940ae891c64f98afe49e8ef7173b
|
|
| BLAKE2b-256 |
3313eac6297ccaffeb19b5de0e7c2c8e1f159bc451001ee4fa4f46583b8d2fbb
|