Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
49d7e9d
Add GlobalName naming grammar to shared API
vinniefalco Sep 14, 2026
ea7ed45
Re-base ToolId on GlobalName and migrate built-in ids
vinniefalco Sep 14, 2026
8bc2313
Migrate the picker's ToolId onto the global name grammar
vinniefalco Sep 14, 2026
8236e17
Consolidate the run interface into Environment/RunContext
vinniefalco Sep 14, 2026
caad802
Add capabilities, tools, args, models frontmatter keys
vinniefalco Sep 14, 2026
f2d0a2b
Surface structured locations on parse and internal errors
vinniefalco Sep 14, 2026
1d0c2a5
Add capability trait and activation types
vinniefalco Sep 14, 2026
f859eca
Add explicit CapabilityRegistry with registration lint
vinniefalco Sep 14, 2026
81609fd
Add prepare pass, Requirements report, and per-run VFS
vinniefalco Sep 14, 2026
f62844d
Add ModelBindings with the trivial fill and refusal
vinniefalco Sep 14, 2026
e8bae1e
Assemble the tool catalog and check co-activation conflicts
vinniefalco Sep 14, 2026
ddaadcb
Fill tool slots and journal ToolBindings at prepare
vinniefalco Sep 14, 2026
0ac3e27
Bind from the frontmatter and run H1 as section 0
vinniefalco Sep 14, 2026
2109f20
Add the args/argv surface and its substitution namespace
vinniefalco Sep 14, 2026
5007809
Migrate prose bindings to frontmatter declarations
vinniefalco Sep 14, 2026
2cdd278
Add promptforge/web capability packing fetch and search
vinniefalco Sep 14, 2026
e7d8272
Wire Workshop chat onto the shared capability environment
vinniefalco Sep 14, 2026
3a3db8c
Document the frontmatter contract and facade Web
vinniefalco Sep 14, 2026
89b4c83
Close plan: capabilities and global naming
vinniefalco Sep 14, 2026
232204c
Surface tool dispatch failures to the Workshop SPA
vinniefalco Sep 14, 2026
090fc74
Skip non-inference entries in the model catalog fetch
vinniefalco Sep 14, 2026
eeca0a1
Declare the chat role's context minimum in chat.md
vinniefalco Sep 14, 2026
6f17034
Re-type ToolId::capability() to return CapabilityId
vinniefalco Sep 14, 2026
89e8002
Reject punctuation-twin capability ids at registration
vinniefalco Sep 14, 2026
42d830e
Report catalog failures instead of binding a fallback
vinniefalco Sep 14, 2026
08fcd07
Convert bound-tool dispatch failures into tool results
vinniefalco Sep 14, 2026
9a82d42
Document bound tool failures as untrusted tool results
vinniefalco Sep 14, 2026
72bec47
Close plan: Debt removal: capabilities follow-ups
vinniefalco Sep 14, 2026
1537ecf
Move vibe session logs into monthly subdirectories
vinniefalco Sep 14, 2026
9627cb9
[WIP] Plan: Provider Model Sheets
vinniefalco Sep 14, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ Multi-crate Rust workspace for the PromptForge pipeline runtime, inference gatew
2. Make the smallest improvement to an existing facility which enables the capability.
3. Add a new facility. New machinery must have a material benefit beyond tidiness.
- When improving an existing facility, prefer an improvement that serves a problem class beyond the current case over one that solves only the case at hand, when the general shape costs no more.
- Error and status messages are designed assuming model consumption: concise, factual, and self-contained, naming what is missing or unmet with required versus actual, because a message may arrive as tool output that a model reasons about.

## Roles

Expand Down
15 changes: 15 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ gateway-stt = { path = "crates/gateway-stt", version = "0.3.0" }
promptforge-store = { path = "crates/promptforge-store", version = "0.3.0" }
promptforge-vfs = { path = "crates/promptforge-vfs", version = "0.3.0" }
promptforge-webfetch = { path = "crates/promptforge-webfetch", version = "0.3.0" }
promptforge-web = { path = "crates/promptforge-web", version = "0.3.0" }
promptforge-tool-picker = { path = "crates/promptforge-tool-picker", version = "0.3.0" }
gateway-stt-engine = { path = "crates/gateway-stt-engine", version = "0.3.0" }
gateway-stt-backend-whisper = { path = "crates/gateway-stt-backend-whisper", version = "0.3.0" }
Expand Down
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,12 +37,14 @@ The prompt language is the programming surface. A prompt is a markdown document:
name: greet
description: Greet the named input using a Lua-computed value
promptforge: 0
models:
writer: {}
---

# Greet

```lua
models.default("writer", "A model suited for careful analysis, coding, and general assistance")
models.default("writer")
```

## Main
Expand Down
3 changes: 3 additions & 0 deletions crates/promptforge-api/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,14 @@ promptforge-parser.workspace = true
promptforge-store.workspace = true
promptforge-tool-picker.workspace = true
promptforge-vfs.workspace = true
promptforge-web.workspace = true
promptforge-web-search.workspace = true
rand.workspace = true
serde.workspace = true
serde_json.workspace = true
shared-vfs.workspace = true
thiserror.workspace = true
tracing.workspace = true
mlua.workspace = true
time.workspace = true
tokio = { workspace = true, features = ["macros", "rt", "sync", "time"] }
Expand All @@ -37,6 +39,7 @@ criterion.workspace = true
promptforge-parser = { workspace = true, features = ["test-support"] }
promptforge-tool-picker = { workspace = true, features = ["test-fixtures"] }
tokio = { workspace = true, features = ["test-util"] }
tracing-subscriber.workspace = true

[[bench]]
name = "models_loop"
Expand Down
25 changes: 9 additions & 16 deletions crates/promptforge-api/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,26 +15,19 @@ shared-promptforge-api = "0.1"
```

```rust
use promptforge_api::{Prompt, ResolutionContext, RunConfig, run};
use shared_promptforge_api::models::ModelCatalog;
use promptforge_api::{Environment, Prompt, RunContext, RunResult};
use shared_promptforge_api::observe::NullObserver;
use shared_promptforge_api::tools::ToolCatalog;

async fn execute(source: &str) -> Result<String, Box<dyn std::error::Error>> {
let prompt = Prompt::parse(source, "readme", &NullObserver::default())?;
// Capability-free agents pass no picker; the store handle defaults to a
// stock in-memory mount.
let models = ModelCatalog::empty();
let tools = ToolCatalog::new(&[])?;

let result = run(
&prompt,
"",
ResolutionContext::new(None, &models, &tools),
RunConfig::new("readme"),
)
.await?;
Ok(result)
// Capability-free agents use the default environment (no picker, empty
// catalogs); the store handle defaults to a stock in-memory mount.
let env = Environment::new();
match env.run(&prompt, "", RunContext::new("readme")).await {
RunResult::Ok(text) => Ok(text),
RunResult::Cancelled => Err("the run was cancelled".into()),
RunResult::Failure(error) => Err(error.into()),
}
}
```

Expand Down
56 changes: 28 additions & 28 deletions crates/promptforge-api/benches/models_loop.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,10 @@ use axum::response::IntoResponse;
use axum::routing::post;
use criterion::{Criterion, criterion_group, criterion_main};
use promptforge_api::client::{GatewayClient, GatewayEndpoint, SecretString};
use promptforge_api::{Prompt, ResolutionContext, RunConfig, run};
use promptforge_api::{Environment, Prompt, RunContext, RunResult};
use promptforge_tool_picker::{Catalog, Config, ToolPicker};
use shared_promptforge_api::models::{ModelCatalog, ModelDescriptor, ModelId, ThinkingMode};
use shared_promptforge_api::observe::NullObserver;
use shared_promptforge_api::tools::ToolCatalog;

const EXECUTION: &str = "bench";

Expand Down Expand Up @@ -122,10 +121,10 @@ fn bench_catalog(context: u32) -> ModelCatalog {
}

/// One section driving `models.loop` over a builder-made list.
const LOOP_PROMPT: &str = "---\nname: bench_loop\ndescription: d\npromptforge: 0\n---\n\n\
const LOOP_PROMPT: &str = "---\nname: bench_loop\ndescription: d\npromptforge: 0\nmodels:\n writer: {}\n---\n\n\
# Bench\n\n\
```lua\n\
models.default('writer', 'A general model for benches')\n\
models.default('writer')\n\
```\n\n\
## Only\n\n\
```lua\n\
Expand All @@ -141,14 +140,10 @@ fn parse_loop_prompt() -> Prompt {
.expect("the bench prompt parses")
}

/// The resolution context every bench run shares: an empty tool picker and
/// no tools, so the loop is one terminal turn.
fn resolution<'a>(
picker: &'a ToolPicker,
models: &'a ModelCatalog,
tools: &'a ToolCatalog,
) -> ResolutionContext<'a> {
ResolutionContext::new(Some(picker), models, tools)
/// The environment every bench run shares: an empty tool picker and no
/// tools, so the loop is one terminal turn.
fn bench_env(picker: ToolPicker) -> Environment {
Environment::new().picker(picker)
}

/// One `models.loop` turn end to end: parse is excluded, so the measurement
Expand All @@ -164,20 +159,23 @@ fn models_loop(c: &mut Criterion) {
let prompt = parse_loop_prompt();
let picker = ToolPicker::build(Catalog::new(Vec::new()), Config::default())
.expect("the empty bench picker builds");
let models = bench_catalog(131_072);
let tools = ToolCatalog::new(&[]).expect("the empty bench tool catalog builds");
let env = bench_env(picker);
c.bench_function("models_loop", |b| {
b.iter(|| {
runtime
.block_on(run(
let result = runtime.block_on(
env.run(
&prompt,
"",
resolution(&picker, &models, &tools),
RunConfig::new(EXECUTION)
RunContext::new(EXECUTION)
.observer(Arc::new(NullObserver::default()))
.model(bench_catalog(131_072).models()[0].clone())
.client(gateway.client()),
))
.expect("the loop bench run succeeds");
),
);
assert!(
matches!(result, RunResult::Ok(_)),
"the loop bench run succeeds: {result:?}"
);
});
});
assert!(
Expand All @@ -199,20 +197,22 @@ fn compactors_fail(c: &mut Criterion) {
let prompt = parse_loop_prompt();
let picker = ToolPicker::build(Catalog::new(Vec::new()), Config::default())
.expect("the empty bench picker builds");
let models = bench_catalog(1);
let tools = ToolCatalog::new(&[]).expect("the empty bench tool catalog builds");
let env = bench_env(picker);
c.bench_function("compactors_fail", |b| {
b.iter(|| {
let error = runtime
.block_on(run(
let result = runtime.block_on(
env.run(
&prompt,
"",
resolution(&picker, &models, &tools),
RunConfig::new(EXECUTION)
RunContext::new(EXECUTION)
.observer(Arc::new(NullObserver::default()))
.model(bench_catalog(1).models()[0].clone())
.client(gateway.client()),
))
.expect_err("a one-token window must exhaust at the precheck");
),
);
let RunResult::Failure(error) = result else {
panic!("a one-token window must exhaust at the precheck");
};
assert_eq!(
error.kind(),
promptforge_api::RunErrorKind::ContextExhausted,
Expand Down
Loading
Loading