ek.agents.bridge
One-way adapters: turn whatever your agent already emits into an ek Episode.
The dependency direction is the ek -> ocracy rule restated, and it is a hard rule:
ek -> inspect_ai / deepeval / ragas (via the ek[agents] extra), never the reverse.
Note what that buys: ek core depends only on the shape of what those tools emit, so the
adapters here duck-type and import nothing. You need the extra to run Inspect or DeepEval;
you do not need it to score what they produced – exactly as ek evaluates any
OcrResult-shaped object without importing an OCR engine.
The workhorse is trajectory_from_messages(): provider-shaped chat transcripts (OpenAI- and
Anthropic-style tool_calls / tool_use blocks) are the universal wire format for a
tool-using agent, and parsing them needs no SDK at all.
Example
>>> messages = [
... {"role": "user", "content": "weather in Paris?"},
... {"role": "assistant", "tool_calls": [
... {"function": {"name": "get_weather", "arguments": '{"city": "Paris"}'}}]},
... {"role": "tool", "content": "18C"},
... {"role": "assistant", "content": "It is 18C in Paris."},
... ]
>>> traj = trajectory_from_messages(messages)
>>> traj.tools
('get_weather',)
>>> traj.steps[0].args, traj.steps[0].observation
({'city': 'Paris'}, '18C')
- ek.agents.bridge.as_agent(fn: Callable) Callable[source]
Wrap a plain
input -> answerfunction into the harness’sTaskSpec -> Episodeshape.Progressive disclosure: the trivial agent should not have to learn the Episode type.
Example
>>> from ek.agents.base import TaskSpec >>> agent = as_agent(str.upper) >>> agent(TaskSpec("t1", input="hi")).output 'HI'
- ek.agents.bridge.cost_from_usage(usage: Any, *, latency_s: float | None = None) Cost[source]
Build a
Costfrom a providerusageobject or dict.Understands the OpenAI (
prompt_tokens/completion_tokens) and Anthropic (input_tokens/output_tokens/cache_read_input_tokens) spellings.Example
>>> c = cost_from_usage({"prompt_tokens": 100, "completion_tokens": 20}) >>> c.input_tokens, c.output_tokens (100, 20)
- ek.agents.bridge.episode_from_messages(messages: Sequence[Mapping], *, task_id: str = '', usage: Any = None, output: Any = None, final_state: Any = None, latency_s: float | None = None) Episode[source]
Build a full
Episodefrom a transcript (+ optional usage/state).outputdefaults to the last assistant text – the agent’s final answer.
- ek.agents.bridge.from_deepeval_test_case(case: Any, *, task_id: str = '') Episode[source]
Adapt a DeepEval
LLMTestCase-shaped object into anEpisode(duck-typed).Reads
.actual_outputand.tools_called. Note DeepEval phones home by default (Confident-AI telemetry) – disable it before use if that matters to you;eknever enables it.
- ek.agents.bridge.from_inspect_sample(sample: Any, *, task_id: str = '') Episode[source]
Adapt an Inspect AI
EvalSample-shaped object into anEpisode(duck-typed).Reads
.messages,.outputand.id– noinspect_aiimport required, so scoring an Inspect log never drags the harness intoek’s dependency closure.
- ek.agents.bridge.trajectory_from_messages(messages: Sequence[Mapping]) Trajectory[source]
Parse a chat transcript into a
Trajectory.Each assistant tool call becomes a
Step, and the next tool/result message becomes that step’sobservation. A tool message carrying an error marks the step as errored – error recovery across later steps is itself an evaluable signal.