mirror of
https://github.com/fabro-sh/fabro.git
synced 2026-10-16 04:33:16 +00:00
Merge branch 'main' into bot/internal-deps
This commit is contained in:
commit
e78b9a0bf6
4 changed files with 236 additions and 51 deletions
90
Cargo.lock
generated
90
Cargo.lock
generated
|
|
@ -1991,7 +1991,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "fabro-api"
|
||||
version = "0.386.0-nightly.0"
|
||||
version = "0.387.0-nightly.0"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"fabro-automation",
|
||||
|
|
@ -2017,7 +2017,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "fabro-auth"
|
||||
version = "0.386.0-nightly.0"
|
||||
version = "0.387.0-nightly.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-trait",
|
||||
|
|
@ -2042,7 +2042,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "fabro-automation"
|
||||
version = "0.386.0-nightly.0"
|
||||
version = "0.387.0-nightly.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"chrono",
|
||||
|
|
@ -2063,11 +2063,11 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "fabro-build-support"
|
||||
version = "0.386.0-nightly.0"
|
||||
version = "0.387.0-nightly.0"
|
||||
|
||||
[[package]]
|
||||
name = "fabro-checkpoint"
|
||||
version = "0.386.0-nightly.0"
|
||||
version = "0.387.0-nightly.0"
|
||||
dependencies = [
|
||||
"fabro-config",
|
||||
"fabro-types",
|
||||
|
|
@ -2075,7 +2075,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "fabro-cli"
|
||||
version = "0.386.0-nightly.0"
|
||||
version = "0.387.0-nightly.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"assert_cmd",
|
||||
|
|
@ -2171,7 +2171,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "fabro-client"
|
||||
version = "0.386.0-nightly.0"
|
||||
version = "0.387.0-nightly.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bytes",
|
||||
|
|
@ -2200,7 +2200,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "fabro-config"
|
||||
version = "0.386.0-nightly.0"
|
||||
version = "0.387.0-nightly.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"chrono",
|
||||
|
|
@ -2229,7 +2229,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "fabro-db"
|
||||
version = "0.386.0-nightly.0"
|
||||
version = "0.387.0-nightly.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"chrono",
|
||||
|
|
@ -2242,7 +2242,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "fabro-dev"
|
||||
version = "0.386.0-nightly.0"
|
||||
version = "0.387.0-nightly.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"assert_cmd",
|
||||
|
|
@ -2261,7 +2261,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "fabro-dot"
|
||||
version = "0.386.0-nightly.0"
|
||||
version = "0.387.0-nightly.0"
|
||||
dependencies = [
|
||||
"fabro-template",
|
||||
"fabro-types",
|
||||
|
|
@ -2273,7 +2273,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "fabro-dump"
|
||||
version = "0.386.0-nightly.0"
|
||||
version = "0.387.0-nightly.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bytes",
|
||||
|
|
@ -2287,7 +2287,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "fabro-environment"
|
||||
version = "0.386.0-nightly.0"
|
||||
version = "0.387.0-nightly.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"chrono",
|
||||
|
|
@ -2309,7 +2309,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "fabro-github"
|
||||
version = "0.386.0-nightly.0"
|
||||
version = "0.387.0-nightly.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-trait",
|
||||
|
|
@ -2334,7 +2334,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "fabro-graphviz"
|
||||
version = "0.386.0-nightly.0"
|
||||
version = "0.387.0-nightly.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"fabro-dot",
|
||||
|
|
@ -2344,7 +2344,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "fabro-http"
|
||||
version = "0.386.0-nightly.0"
|
||||
version = "0.387.0-nightly.0"
|
||||
dependencies = [
|
||||
"fabro-static",
|
||||
"http 1.4.0",
|
||||
|
|
@ -2354,7 +2354,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "fabro-install"
|
||||
version = "0.386.0-nightly.0"
|
||||
version = "0.387.0-nightly.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"fabro-config",
|
||||
|
|
@ -2371,7 +2371,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "fabro-interview"
|
||||
version = "0.386.0-nightly.0"
|
||||
version = "0.387.0-nightly.0"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"fabro-types",
|
||||
|
|
@ -2383,7 +2383,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "fabro-llm"
|
||||
version = "0.386.0-nightly.0"
|
||||
version = "0.387.0-nightly.0"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"bytes",
|
||||
|
|
@ -2408,7 +2408,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "fabro-macros"
|
||||
version = "0.386.0-nightly.0"
|
||||
version = "0.387.0-nightly.0"
|
||||
dependencies = [
|
||||
"clap",
|
||||
"fabro-options-metadata",
|
||||
|
|
@ -2419,7 +2419,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "fabro-manifest"
|
||||
version = "0.386.0-nightly.0"
|
||||
version = "0.387.0-nightly.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-trait",
|
||||
|
|
@ -2448,7 +2448,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "fabro-mcp-server"
|
||||
version = "0.386.0-nightly.0"
|
||||
version = "0.387.0-nightly.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"fabro-client",
|
||||
|
|
@ -2468,7 +2468,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "fabro-mcp-store"
|
||||
version = "0.386.0-nightly.0"
|
||||
version = "0.387.0-nightly.0"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"fabro-db",
|
||||
|
|
@ -2486,7 +2486,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "fabro-oauth"
|
||||
version = "0.386.0-nightly.0"
|
||||
version = "0.387.0-nightly.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"axum",
|
||||
|
|
@ -2508,7 +2508,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "fabro-options-metadata"
|
||||
version = "0.386.0-nightly.0"
|
||||
version = "0.387.0-nightly.0"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"serde_json",
|
||||
|
|
@ -2516,7 +2516,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "fabro-petri"
|
||||
version = "0.386.0-nightly.0"
|
||||
version = "0.387.0-nightly.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-trait",
|
||||
|
|
@ -2571,7 +2571,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "fabro-proc"
|
||||
version = "0.386.0-nightly.0"
|
||||
version = "0.387.0-nightly.0"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"tempfile",
|
||||
|
|
@ -2583,7 +2583,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "fabro-redact"
|
||||
version = "0.386.0-nightly.0"
|
||||
version = "0.387.0-nightly.0"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"pebble-coding-agent",
|
||||
|
|
@ -2600,7 +2600,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "fabro-server"
|
||||
version = "0.386.0-nightly.0"
|
||||
version = "0.387.0-nightly.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-trait",
|
||||
|
|
@ -2695,7 +2695,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "fabro-slack"
|
||||
version = "0.386.0-nightly.0"
|
||||
version = "0.387.0-nightly.0"
|
||||
dependencies = [
|
||||
"fabro-http",
|
||||
"fabro-interview",
|
||||
|
|
@ -2716,18 +2716,18 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "fabro-spa"
|
||||
version = "0.386.0-nightly.0"
|
||||
version = "0.387.0-nightly.0"
|
||||
dependencies = [
|
||||
"rust-embed",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fabro-static"
|
||||
version = "0.386.0-nightly.0"
|
||||
version = "0.387.0-nightly.0"
|
||||
|
||||
[[package]]
|
||||
name = "fabro-store"
|
||||
version = "0.386.0-nightly.0"
|
||||
version = "0.387.0-nightly.0"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"chrono",
|
||||
|
|
@ -2755,7 +2755,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "fabro-telemetry"
|
||||
version = "0.386.0-nightly.0"
|
||||
version = "0.387.0-nightly.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"base64",
|
||||
|
|
@ -2781,7 +2781,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "fabro-template"
|
||||
version = "0.386.0-nightly.0"
|
||||
version = "0.387.0-nightly.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"fabro-types",
|
||||
|
|
@ -2795,7 +2795,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "fabro-test"
|
||||
version = "0.386.0-nightly.0"
|
||||
version = "0.387.0-nightly.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"assert_cmd",
|
||||
|
|
@ -2820,7 +2820,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "fabro-tool"
|
||||
version = "0.386.0-nightly.0"
|
||||
version = "0.387.0-nightly.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-trait",
|
||||
|
|
@ -2844,7 +2844,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "fabro-tracker"
|
||||
version = "0.386.0-nightly.0"
|
||||
version = "0.387.0-nightly.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-trait",
|
||||
|
|
@ -2858,7 +2858,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "fabro-types"
|
||||
version = "0.386.0-nightly.0"
|
||||
version = "0.387.0-nightly.0"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"clap",
|
||||
|
|
@ -2885,7 +2885,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "fabro-util"
|
||||
version = "0.386.0-nightly.0"
|
||||
version = "0.387.0-nightly.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"console 0.15.11",
|
||||
|
|
@ -2908,7 +2908,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "fabro-variable"
|
||||
version = "0.386.0-nightly.0"
|
||||
version = "0.387.0-nightly.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"chrono",
|
||||
|
|
@ -2925,7 +2925,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "fabro-vault"
|
||||
version = "0.386.0-nightly.0"
|
||||
version = "0.387.0-nightly.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"chrono",
|
||||
|
|
@ -2944,7 +2944,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "fabro-workflow"
|
||||
version = "0.386.0-nightly.0"
|
||||
version = "0.387.0-nightly.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-trait",
|
||||
|
|
@ -2989,7 +2989,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "fabro-workflow-version"
|
||||
version = "0.386.0-nightly.0"
|
||||
version = "0.387.0-nightly.0"
|
||||
dependencies = [
|
||||
"fabro-config",
|
||||
"fabro-dot",
|
||||
|
|
@ -7925,7 +7925,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "twin-github"
|
||||
version = "0.386.0-nightly.0"
|
||||
version = "0.387.0-nightly.0"
|
||||
dependencies = [
|
||||
"axum",
|
||||
"base64",
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ resolver = "2"
|
|||
|
||||
[workspace.package]
|
||||
edition = "2021"
|
||||
version = "0.386.0-nightly.0"
|
||||
version = "0.387.0-nightly.0"
|
||||
license = "MIT"
|
||||
|
||||
[workspace.dependencies]
|
||||
|
|
|
|||
|
|
@ -31,20 +31,24 @@ Skills can be invoked in two ways.
|
|||
|
||||
### Slash syntax (user input)
|
||||
|
||||
Users can reference a skill with a `/` prefix in their input:
|
||||
On the native agent backend, start authored input with `/skill-name` to invoke a skill:
|
||||
|
||||
```
|
||||
/commit fix the off-by-one error in pagination
|
||||
```
|
||||
|
||||
Fabro detects the `/commit` token, finds the matching skill, and expands the input by replacing the skill reference with the full template. The remaining text (`fix the off-by-one error in pagination`) is substituted into the `{{user_input}}` placeholder.
|
||||
Fabro detects the leading `/commit` command, finds the matching skill, and expands the authored input using its template. The remaining text (`fix the off-by-one error in pagination`) is substituted into the `{{user_input}}` placeholder. A template without that placeholder replaces the authored input.
|
||||
|
||||
Rules for slash syntax:
|
||||
- The `/` must be preceded by whitespace or be at the start of the input
|
||||
- The command must be at the start of authored input, after any leading whitespace
|
||||
- The skill name must be followed by whitespace or end of input
|
||||
- Only one skill reference per input is allowed
|
||||
- Later slash tokens are ordinary arguments or text: `/commit mention /review` invokes only `commit`, while `Please run /commit` invokes no skill
|
||||
- File paths like `/usr/bin/bash` are not matched (the character after `/` must be a lowercase letter, and the name cannot contain `/`)
|
||||
|
||||
For native workflow agent stages, only the authored node prompt is expanded. Workflow preambles, prior-stage context, `for_each` item data, and output contracts remain outside expansion and are preserved even when the template has no `{{user_input}}` placeholder. For example, prior-stage text mentioning `/tmp` or `/commit` stays literal.
|
||||
|
||||
User-delivered follow-ups also support a leading command. Generated output-repair turns bypass expansion. ACP agents receive the assembled workflow input and own any command interpretation; one-shot prompt nodes do not expand skills.
|
||||
|
||||
### use_skill tool (agent-initiated)
|
||||
|
||||
When skills are available, Fabro adds a `use_skill` tool to the agent's toolset and lists the available skills in the system prompt:
|
||||
|
|
@ -69,5 +73,5 @@ Returns the skill's template text.
|
|||
|
||||
1. **Discovery** -- On session startup, Fabro scans the skill directories and parses all valid `SKILL.md` files
|
||||
2. **Registration** -- If any skills are found, the `use_skill` tool is registered and the skill list is added to the system prompt
|
||||
3. **Expansion** -- When user input contains a `/skill-name` reference, Fabro expands it before sending to the LLM
|
||||
3. **Expansion** -- On the native agent backend, a leading `/skill-name` command in authored input is expanded before sending to the LLM. Leading whitespace is allowed; later slash tokens stay literal. Workflow context and output requirements remain outside expansion, and generated output-repair turns bypass it
|
||||
4. **Tool use** -- The agent can also call `use_skill` at any time to load a skill's template mid-conversation
|
||||
|
|
|
|||
181
lib/components/fabro-petri/tests/skills.rs
Normal file
181
lib/components/fabro-petri/tests/skills.rs
Normal file
|
|
@ -0,0 +1,181 @@
|
|||
//! Skills discovered through Fabro's runtime expand only authored native input.
|
||||
|
||||
mod support;
|
||||
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
use fabro_llm::test_support::{self, ScriptedAdapter};
|
||||
use fabro_petri::check::Launch;
|
||||
use fabro_petri::engine::{self, RunStatus};
|
||||
use fabro_petri::runtime::RuntimeSpec;
|
||||
use lithos_llm::Client;
|
||||
use lithos_llm::catalog::ProviderId;
|
||||
use lithos_llm::middleware::{Call, Observer, ObserverMiddleware};
|
||||
use lithos_llm::types::{ContentPart, Request};
|
||||
use petri_store::MemoryRunStore;
|
||||
use support::{Silent, no_questions, run_request};
|
||||
use tokio::fs;
|
||||
|
||||
const MODEL: &str = "gpt-5.4";
|
||||
const TEMPLATE: &str = "COMMIT TEMPLATE SENTINEL";
|
||||
const PRIOR: &str = "smoke-tested via a /tmp build; reviewer mentioned /commit";
|
||||
|
||||
struct Requests(Arc<Mutex<Vec<Request>>>);
|
||||
|
||||
impl Observer for Requests {
|
||||
fn on_start(&self, call: &Call) {
|
||||
self.0
|
||||
.lock()
|
||||
.expect("request capture lock")
|
||||
.push(call.request().clone());
|
||||
}
|
||||
}
|
||||
|
||||
async fn run_native(workflow: &str, responses: &[&str]) -> Vec<Request> {
|
||||
let root = tempfile::tempdir().expect("temporary runtime home");
|
||||
let home = root.path().join("fabro-home");
|
||||
let skill = home.join("skills/commit");
|
||||
fs::create_dir_all(&skill).await.expect("skill directory");
|
||||
// No placeholder: expansion must replace only the authored input.
|
||||
fs::write(
|
||||
skill.join("SKILL.md"),
|
||||
format!("---\nname: commit\ndescription: Test commit instructions\n---\n{TEMPLATE}\n"),
|
||||
)
|
||||
.await
|
||||
.expect("skill template");
|
||||
let requests = Arc::new(Mutex::new(Vec::new()));
|
||||
let client = Client::builder()
|
||||
.catalog(test_support::test_catalog())
|
||||
.adapter(
|
||||
ProviderId::new("openai"),
|
||||
ScriptedAdapter::new(
|
||||
responses
|
||||
.iter()
|
||||
.map(|text| test_support::text_response("openai", MODEL, text))
|
||||
.collect(),
|
||||
),
|
||||
)
|
||||
.middleware(ObserverMiddleware::new(Requests(Arc::clone(&requests))))
|
||||
.build()
|
||||
.expect("scripted model client")
|
||||
.client;
|
||||
let runtime = RuntimeSpec {
|
||||
model_client: Some(client),
|
||||
fabro_home: Some(home),
|
||||
..RuntimeSpec::default()
|
||||
};
|
||||
let graphs = support::admit(
|
||||
&[
|
||||
("workflow.fabro", workflow),
|
||||
("workflow.toml", support::SETTINGS),
|
||||
],
|
||||
Launch {
|
||||
default_model: Some(format!("openai/{MODEL}")),
|
||||
..Launch::default()
|
||||
},
|
||||
&runtime,
|
||||
);
|
||||
let outcome = engine::run(run_request(
|
||||
"skills",
|
||||
&root.path().join("run"),
|
||||
graphs,
|
||||
Arc::new(MemoryRunStore::new()),
|
||||
runtime,
|
||||
no_questions(Arc::new(Silent)),
|
||||
))
|
||||
.await
|
||||
.expect("workflow run settles");
|
||||
assert_eq!(outcome.status, RunStatus::Success, "{outcome:?}");
|
||||
let requests = requests.lock().expect("request capture lock").clone();
|
||||
for request in &requests {
|
||||
for message in request.messages() {
|
||||
assert!(
|
||||
message
|
||||
.content()
|
||||
.iter()
|
||||
.all(|part| !matches!(part, ContentPart::Text { text } if text.is_empty())),
|
||||
"absent segments must not become invalid empty text blocks: {message:?}"
|
||||
);
|
||||
}
|
||||
}
|
||||
requests
|
||||
}
|
||||
|
||||
fn user_text(request: &Request) -> String {
|
||||
request
|
||||
.messages()
|
||||
.last()
|
||||
.expect("user input message")
|
||||
.content()
|
||||
.iter()
|
||||
.filter_map(|part| match part {
|
||||
ContentPart::Text { text } => Some(text.as_str()),
|
||||
_ => None,
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
.join("")
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn authored_skill_commands_preserve_workflow_context_and_output_requirements() {
|
||||
for (authored, expands) in [
|
||||
(" /commit authored arguments /tmp /commit", true),
|
||||
("Address the feedback about /commit and /tmp", false),
|
||||
] {
|
||||
let workflow = format!(
|
||||
r#"digraph Skills {{
|
||||
graph [backend="api", goal="Preserve workflow context", default_max_retries=0]
|
||||
start [shape=Mdiamond]
|
||||
prior [prompt="Report the previous result"]
|
||||
task [prompt="{authored}", fidelity="summary:high", output_schema="routing", output_retries=0]
|
||||
exit [shape=Msquare]
|
||||
start -> prior -> task -> exit
|
||||
}}"#
|
||||
);
|
||||
let requests = run_native(&workflow, &[PRIOR, r#"{"outcome":"succeeded"}"#]).await;
|
||||
assert_eq!(requests.len(), 2);
|
||||
let sent = user_text(&requests[1]);
|
||||
assert!(sent.contains("Preserve workflow context"), "{sent}");
|
||||
assert!(sent.contains(PRIOR), "{sent}");
|
||||
assert!(sent.contains("Fabro final-output contract"), "{sent}");
|
||||
assert_eq!(sent.contains(TEMPLATE), expands, "{sent}");
|
||||
if expands {
|
||||
assert!(!sent.contains(authored), "{sent}");
|
||||
assert!(sent.find(PRIOR) < sent.find(TEMPLATE), "{sent}");
|
||||
assert!(
|
||||
sent.find(TEMPLATE) < sent.find("Fabro final-output contract"),
|
||||
"{sent}"
|
||||
);
|
||||
} else {
|
||||
assert!(sent.contains(authored), "{sent}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn absent_native_prompt_segments_do_not_emit_empty_model_text_blocks() {
|
||||
for (fidelity, authored) in [
|
||||
("full", "Say done"),
|
||||
("compact", "Say done"),
|
||||
("compact", ""),
|
||||
] {
|
||||
let workflow = format!(
|
||||
r#"digraph Segments {{
|
||||
graph [backend="api", goal="Keep the nonempty preamble", default_max_retries=0]
|
||||
start [shape=Mdiamond]
|
||||
task [prompt="{authored}", fidelity="{fidelity}"]
|
||||
exit [shape=Msquare]
|
||||
start -> task -> exit
|
||||
}}"#
|
||||
);
|
||||
let requests = run_native(&workflow, &["done"]).await;
|
||||
assert_eq!(requests.len(), 1);
|
||||
let sent = user_text(&requests[0]);
|
||||
if fidelity == "full" {
|
||||
assert_eq!(sent, authored);
|
||||
} else {
|
||||
assert!(sent.contains("Keep the nonempty preamble"), "{sent}");
|
||||
assert!(sent.contains(authored), "{sent}");
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue