Skip to content

agentpile 0.2.0

Choose a tag to compare

@yzhao062 yzhao062 released this 23 Oct 19:27
· 7 commits to main since this release

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 to path; the chat-jsonl writer 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 follow run.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. With perspective="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 optional report counts 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 Face datasets and 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") and ap.to_pandas(runs, "labels") stores each value as text, or None for a null value. A new value_type column records bool, int, float, str, json or None. Keep value as it is when value_type is str or None, and use json.loads(value) otherwise; in to_pandas these two columns keep None rather 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, and run.labels keeps 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, and df[(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.value and output.value give the call's arguments and result as the source recorded them. gen_ai.tool.call.arguments and gen_ai.tool.call.result now 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 read input.value or output.value. A new tool.parameters holds 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.value what 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 marked agentpile.input_reconstructed, since ATIF does not record the prompt a model was given; llm.input_messages and gen_ai.input.messages stay absent.
    • The resource's service.name falls back to the run's dataset when the agent's name is missing or unknown, and to agentpile without a dataset. openinference.project.name is the dataset, so Phoenix files each dataset under its own project. The dataset goes by the name ap.load takes, such as tau_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.

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>.