Skip to content

load prompt from file in spec - #60

Merged
thorrester merged 4 commits into
mainfrom
structured-output
Mar 28, 2026
Merged

load prompt from file in spec#60
thorrester merged 4 commits into
mainfrom
structured-output

Conversation

@thorrester

@thorrester thorrester commented Mar 28, 2026

Copy link
Copy Markdown
Member

Pull Request

Short Summary

Adds PromptRef to SpecLoader, letting DAG task specs reference a prompt YAML file instead of an inline string. The prompt field in a TaskSpec now accepts either "some text" (existing behavior, unchanged) or { path: "./path/to/prompt.yaml" }. File loading is async-safe via spawn_blocking, with basic path traversal rejection.

Context

Previously, TaskSpec.prompt was a plain String — always treated as an inline message. There was no way to point a workflow task at an existing prompt file, even though Prompt::from_path already supported loading .yaml/.json prompt definitions with full provider config, settings, and variable parameters.

PromptRef deserialization — a custom serde Visitor dispatches on value type: a YAML scalar becomes Inline(String), a mapping with a path key becomes File(String). This is entirely backwards-compatible; no existing YAML spec needs to change.

Before:

tasks:
  - id: t1
    agent: worker
    prompt: "Summarize the following text."

After (both forms valid):

tasks:
  - id: t1
    agent: worker
    prompt: "Summarize the following text."   # unchanged

  - id: t2
    agent: worker
    prompt:
      path: "./prompts/summarize.yaml"         # new

Async safetyPrompt::from_path calls std::fs::read_to_string synchronously. Since build_workflow is async, wrapping in tokio::task::spawn_blocking keeps the blocking syscall off the runtime worker threads.

Path safety — paths containing .. components are rejected at the loader level before any I/O. Full canonicalization/sandboxing is intentionally deferred; SpecLoader is developer-facing and not exposed to untrusted user input.

New error variantSpecError::PromptLoad { path, reason } distinguishes prompt file load failures from general workflow build errors.

File Change
crates/potato_spec/src/spec.rs Adds PromptRef enum with custom Deserialize; changes TaskSpec.prompt: StringTaskSpec.prompt: PromptRef; adds 3 unit tests for deser branches
crates/potato_spec/src/loader.rs build_workflow branches on PromptRef; File arm uses spawn_blocking + .. rejection
crates/potato_spec/src/error.rs Adds PromptLoad { path, reason } variant
crates/potato_spec/src/lib.rs Re-exports PromptRef
crates/baked_potato/tests/agent/spec_test.rs Adds 2 integration tests: happy path (file prompt runs end-to-end) and error path (nonexistent file → SpecError::PromptLoad)
crates/baked_potato/tests/agent/fixtures/prompt.yaml New OpenAI prompt fixture used by the integration test

Is this a Breaking Change?

No. The prompt field previously held a String; all existing YAML specs with prompt: "text" deserialize to PromptRef::Inline("text") and follow the same code path as before. The only public API addition is PromptRef itself and SpecError::PromptLoad.


Open with Devin

devin-ai-integration[bot]

This comment was marked as resolved.

@thorrester
thorrester merged commit e6a5e29 into main Mar 28, 2026
11 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant