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 -> answer function into the harness’s TaskSpec -> Episode shape.

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 Cost from a provider usage object 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 Episode from a transcript (+ optional usage/state).

output defaults 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 an Episode (duck-typed).

Reads .actual_output and .tools_called. Note DeepEval phones home by default (Confident-AI telemetry) – disable it before use if that matters to you; ek never enables it.

ek.agents.bridge.from_inspect_sample(sample: Any, *, task_id: str = '') Episode[source]

Adapt an Inspect AI EvalSample-shaped object into an Episode (duck-typed).

Reads .messages, .output and .id – no inspect_ai import required, so scoring an Inspect log never drags the harness into ek’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’s observation. A tool message carrying an error marks the step as errored – error recovery across later steps is itself an evaluable signal.