Repository navigation
agentpile 0.2.0
Chat rows for training and evaluation, OTLP output that follows the OpenInference and OTel GenAI conventions, a labels table that saves to Parquet, and recipes for downstream tools. The labels table's value column changes type, which can silently empty an existing filter (see Changed).
Added
ap.to_chat(runs, path=None, *, perspective=None, arguments="json", report=None)turns trajectories into OpenAI-style chat rows,{"messages": [...], "tools": [...]}, returned as a list or written as JSON Lines topath; thechat-jsonlwriter does the same for one run. The rows have the message shape that OpenAI fine-tuning files, Hugging Face chat templates and message-list auditors use, and writing them needs only the standard library. Messages followrun.events(), each tool result follows the assistant turn that issued its call, and tool definitions in the OpenAI, MCP or Anthropic shape become OpenAI function tools. Withperspective="X", agent X is the assistant and every other agent's turns become user text that starts with the agent's id; tau2-bench's simulated user is user text either way. Where the chat shape needs a value the source lacks, the row holds{},{"_raw": ...}or"". The optionalreportcounts each such change and gives the position and address of every event behind each message, so a row can be traced back to the record.- Seven recipes in
examples/integrations/hand agentpile output to downstream tools and check the result: tool-call scoring with Ragas and DeepEval, fine-tuning input with Hugging Facedatasetsand TRL, rule checks with Invariant Guardrails, table queries with DuckDB and Polars, and viewing in Harbor, Inspect AI and OTLP trace viewers (Phoenix, Jaeger, MLflow). Each pins its own environment and runs offline on the bundled fixtures. A CI workflow runs them weekly, and the README's Works With table shows each check's status and the date it last passed. The recipes are examples, not part of the package.
Changed
- The labels table of
ap.table(runs, "labels")andap.to_pandas(runs, "labels")stores eachvalueas text, orNonefor a null value. A newvalue_typecolumn recordsbool,int,float,str,jsonorNone. Keepvalueas it is whenvalue_typeisstrorNone, and usejson.loads(value)otherwise; into_pandasthese two columns keepNonerather than NaN, so the same rule works on a DataFrame. A value JSON cannot hold (a date, a tuple) comes back in its JSON form, andrun.labelskeeps the original typed values. Datasets label with booleans, numbers and strings alike, so the old column mixed types, and saving the labels table to Parquet (or any other Arrow conversion) failed for 7 of the 13 dataset fixtures and for all 13 together. A filter written for the old types needs the new form:df[df.value == True]now matches nothing, anddf[(df.value_type == "bool") & (df.value == "true")]replaces it.df.value.astype(float)still works on numeric labels. - OTLP export follows two pinned conventions: the attribute names of OpenInference 0.1.41, and the OTel GenAI attribute names and value schemas at revision
9ffc7e00c639be7712e544e3e54331e885dccf84. The record attributes (agentpile.*) are unchanged, and the reader still rebuilds the exact record, from new exports and from those 0.1.x wrote. What a trace viewer shows changes:- Tool spans that agentpile writes sit under the span of the step that made the call instead of directly under the trajectory span. Every span agentpile adds lies inside its parent and takes time, except under a source span that itself takes none; a source span without an end time counts as one, and a missing start time no longer lands after the recorded end. In 0.1.x, 187 of the 535 spans agentpile added across the fixtures lasted zero time: every tool span, and steps such as a trajectory's last one when it called no tool. A step whose source timestamp falls outside the source span or call it is drawn under moves inside it, marked
agentpile.time_synthetic; the record keeps the timestamp. - On a tool span,
input.valueandoutput.valuegive the call's arguments and result as the source recorded them.gen_ai.tool.call.argumentsandgen_ai.tool.call.resultnow appear only for a JSON object, as the GenAI schemas require; on the dataset fixtures this leaves out 6 of 94 argument values, the ones that did not parse, and 72 of 78 results, mostly plain text. Code that read either attribute for text should readinput.valueoroutput.value. A newtool.parametersholds the tool's parameter schema from an OpenAI, MCP or Anthropic definition, left out where two tools of the call's name could have different ones. - Model spans show in
input.valuewhat came in since the previous model call: system and user messages, the text of a step that made no model call (a scripted greeting), and tool results with their call ids. The value is markedagentpile.input_reconstructed, since ATIF does not record the prompt a model was given;llm.input_messagesandgen_ai.input.messagesstay absent. - The resource's
service.namefalls back to the run's dataset when the agent's name is missing orunknown, and toagentpilewithout a dataset.openinference.project.nameis the dataset, so Phoenix files each dataset under its own project. The dataset goes by the nameap.loadtakes, such astau_bench; a record from a dataset agentpile does not register keeps the source's own id, such as a hub repository. - Attributes the record does not hold stay absent, including ones the conventions require, such as
gen_ai.provider.name. Spans adopted from a source trace keep their attributes, parents and times.
- Tool spans that agentpile writes sit under the span of the step that made the call instead of directly under the trajectory span. Every span agentpile adds lies inside its parent and takes time, except under a source span that itself takes none; a source span without an end time counts as one, and a missing start time no longer lands after the recorded end. In 0.1.x, 187 of the 535 spans agentpile added across the fixtures lasted zero time: every tool span, and steps such as a trajectory's last one when it called no tool. A step whose source timestamp falls outside the source span or call it is drawn under moves inside it, marked
Fixed
- A model span in an OTLP export names a model when its step records none: the model the record's roster gives the step's agent, else the trajectory's model. tau-bench records its model only once per run, so its model spans named none before. A step the harness scripted, such as tau2-bench's fixed greeting, still names none, and the record itself is unchanged. The span is then named
chat <model>.