Skip to content

v0.21.0

Choose a tag to compare

@thorrester thorrester released this 10 Apr 15:13
· 22 commits to main since this release
d6c4b8f

v0.21.0

Released 2026-04-10

Two things in this release. PyO3 was bumped from 0.27 to 0.28, which required touching every #[pyclass] in the codebase to opt into the new FromPyObject model. And potato-spec now supports loading task prompts from files — the prompt field in a TaskSpec accepts either an inline string or a { path: "..." } map.


Breaking changes

PyO3 0.28: FromPyObject no longer auto-derived for #[pyclass] types

PyO3 0.28 removed the implicit FromPyObject impl for #[pyclass] structs and enums. Every type that needs to cross the Python→Rust boundary now requires an explicit from_py_object attribute. Types that are output-only (passed Rust→Python but never back) use skip_from_py_object.

This does not change the Python API. If you're consuming potato_head from Python, nothing breaks. If you have downstream Rust code that wraps or re-exports #[pyclass] types from this crate, you need PyO3 0.28 to compile.

TaskSpec.prompt is now PromptRef, not String

The prompt field on TaskSpec changed type from String to a PromptRef enum. Inline specs are backward-compatible at the YAML level — prompt: "some text" still deserializes correctly. If you're constructing TaskSpec values directly in Rust code, the field type changed and your code will not compile without updating.


What's new

File-based prompts in potato-spec

TaskSpec.prompt now accepts a { path: "..." } map in addition to an inline string. When a path is provided, the loader reads the YAML file at that location and constructs the Prompt from it — model, provider, and messages all come from the file rather than the task spec.

# Before: inline only
tasks:
  - id: t1
    agent: worker
    prompt: "Summarize the input."

# After: file reference also works
tasks:
  - id: t1
    agent: worker
    prompt:
      path: "./prompts/summarize.yaml"

The prompt file format:

model: gpt-4o
provider: OpenAI
messages:
  - "Summarize the input."

One concrete consequence: agents with no model_override in the spec can now run DAG tasks, as long as the prompt file specifies the model. Previously, a missing model would produce a SpecError::WorkflowBuild at load time. A file prompt that omits both model and provider will still fail — the error is SpecError::PromptLoad.

Paths are resolved relative to where they appear in the YAML at load time, not the working directory. Dotdot components (../) are allowed. Absolute paths work as-is.

PyO3 types now bidirectionally usable from Python

The from_py_object annotations added throughout potato-type, potato-agent, potato-provider, potato-util, and potato-workflow mean that any type annotated this way can now be passed back into a Rust function from Python. Before this release, many types were Rust→Python only. Output-only types (WorkflowResult, PyWorkflow, PyAgentResponse, AnthropicUsage) are marked skip_from_py_object and remain one-directional.


Upgrading from v0.20.0

  1. Update your PyO3 dependency to 0.28.* if you have downstream Rust crates that depend on potato-type or other potatohead crates directly.
  2. If you construct TaskSpec in Rust: change prompt: Stringprompt: PromptRef::Inline(...) or PromptRef::File(...).
  3. YAML specs using inline prompt: "..." strings require no changes.

Contributors

@Thorrester

Full changelog: v0.20.0...v0.21.0