Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
15 changes: 9 additions & 6 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,9 @@ jobs:
# Target self-hosted runner by label
runs-on: [nixos]
needs: [rust-checks]
env:
BASE_URL: http://127.0.0.1:8231/v1
MODEL: local
# SECURITY: Require manual approval for external PRs
if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository }}
steps:
Expand All @@ -92,23 +95,23 @@ jobs:
nix develop --command bash -c '
devenv up -d
devenv processes wait llama-server --timeout 120
BASE_URL=http://127.0.0.1:8231 cargo run -p counter-example
cargo run -p counter-example
devenv processes down
'
- name: fizzbuzz-example-smoke-test
run: |
nix develop --command bash -c '
devenv up -d
devenv processes wait llama-server --timeout 120
BASE_URL=http://127.0.0.1:8231 cargo run -p fizzbuzz-example
cargo run -p fizzbuzz-example
devenv processes down
'
- name: rastrigin-example-smoke-test
run: |
nix develop --command bash -c '
devenv up -d
devenv processes wait llama-server --timeout 120
BASE_URL=http://127.0.0.1:8231 timeout 120s cargo run -p rastrigin-example
timeout 120s cargo run -p rastrigin-example
devenv processes down
'
- name: sort-example-smoke-test
Expand All @@ -119,22 +122,22 @@ jobs:
nix develop --command bash -c '
devenv up -d
devenv processes wait llama-server --timeout 120
BASE_URL=http://127.0.0.1:8231 timeout 180s cargo run -p sort-example
timeout 180s cargo run -p sort-example
devenv processes down
'
- name: struct-support-example-smoke-test
run: |
nix develop --command bash -c '
devenv up -d
devenv processes wait llama-server --timeout 120
BASE_URL=http://127.0.0.1:8231 cargo run -p struct-support-example
cargo run -p struct-support-example
devenv processes down
'
- name: tool-calling-example-smoke-test
run: |
nix develop --command bash -c '
devenv up -d
devenv processes wait llama-server --timeout 120
BASE_URL=http://127.0.0.1:8231 cargo run -p tool-calling-example
cargo run -p tool-calling-example
devenv processes down
'
24 changes: 12 additions & 12 deletions Cargo.lock

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

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ members = [
resolver = "2"

[workspace.package]
version = "0.20.0"
version = "0.21.0"

[workspace.lints.rust]
# checks for cases that are confusing between a negative literal and a negation that's not part of the literal.
Expand Down
3 changes: 2 additions & 1 deletion devenv.nix
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@ in {
processes.llama-server = {
exec = ''
llama-server \
-hf LiquidAi/LFM2.5-8B-A1B-GGUF \
-hf prism-ml/Bonsai-8B-gguf \
--alias local \
--host 127.0.0.1 \
--port ${toString port} \
--n-gpu-layers 999 \
Expand Down
3 changes: 2 additions & 1 deletion examples/counter/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,8 @@ async fn main() -> symbiont::Result<()> {
let fn_sigs = runtime.fn_sigs(); // Alternatively, `fn_full_sources` can be used to also show doc string and default function body.
info!("fn_sigs: {fn_sigs:?}");

let agent = symbiont::init_agent(None).await?;
let model = std::env::var("MODEL").expect("the MODEL env var names the model slug");
let agent = symbiont::init_agent_from_env(None, &model).await?;

let base_prompt = format!(
"Give a concise implementation for this function signature: ```{}```, \
Expand Down
3 changes: 2 additions & 1 deletion examples/evolving-trader/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -655,7 +655,8 @@ async fn main() -> symbiont::Result<()> {
// Include the host crate's documented API (Candle, AccountState, Action)
// in the system prompt. Cap generation so small local models that fail to
// stop cannot overflow the inference server's context window.
let agent = symbiont::agent_builder(Some(host_crate))
let model = std::env::var("MODEL").expect("the MODEL env var names the model slug");
let agent = symbiont::agent_builder_from_env(Some(host_crate), &model)
.await?
.max_tokens(4096)
.build();
Expand Down
3 changes: 2 additions & 1 deletion examples/fizzbuzz/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,8 @@ async fn main() -> symbiont::Result<()> {
let fn_sigs = runtime.fn_sigs();
info!("fn_sigs: {fn_sigs:?}");

let agent = symbiont::init_agent(None).await?;
let model = std::env::var("MODEL").expect("the MODEL env var names the model slug");
let agent = symbiont::init_agent_from_env(None, &model).await?;

// -- Round 0: run the default (wrong) implementation ----------------
println!("\n=== Round 0: default implementation ===");
Expand Down
6 changes: 4 additions & 2 deletions examples/fractal-studio/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -519,9 +519,11 @@ fn main() -> eframe::Result<()> {
.expect("can initialize the symbiont runtime");
info!("fn_sigs: {:?}", runtime.fn_sigs());

let model =
std::env::var("MODEL").expect("the MODEL env var names the model slug to evolve with");
let agent = tokio_rt
.block_on(symbiont::init_agent(None))
.expect("can initialize the agent; check the API_KEY, BASE_URL and MODEL env vars");
.block_on(symbiont::init_agent_from_env(None, &model))
.expect("can initialize the agent; check the API_KEY and BASE_URL env vars");
let tokio_handle = tokio_rt.handle().clone();

let options = eframe::NativeOptions {
Expand Down
3 changes: 2 additions & 1 deletion examples/quantize/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -389,7 +389,8 @@ async fn main() -> symbiont::Result<()> {
let fn_sigs = runtime.fn_sigs();
info!("fn_sigs: {fn_sigs:?}");

let agent = symbiont::init_agent(None).await?;
let model = std::env::var("MODEL").expect("the MODEL env var names the model slug");
let agent = symbiont::init_agent_from_env(None, &model).await?;

// Fixed test data — identical across rounds for fair comparison.
let rng = Rng::from_seed_with_64bit(42);
Expand Down
3 changes: 2 additions & 1 deletion examples/rastrigin/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,8 @@ async fn main() -> symbiont::Result<()> {
let fn_sigs = runtime.fn_sigs();
info!("fn_sigs: {fn_sigs:?}");

let agent = symbiont::init_agent(None).await?;
let model = std::env::var("MODEL").expect("the MODEL env var names the model slug");
let agent = symbiont::init_agent_from_env(None, &model).await?;
let samples = build_samples();

// Convergence threshold: MSE < 1e-10 means the formula is exact
Expand Down
31 changes: 25 additions & 6 deletions examples/sort/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,11 @@
//! optimization**: the LLM writes real compiled code, the harness
//! benchmarks it at native speed, and concrete timing data drives each
//! subsequent evolution.
//!
//! Small models often fail to produce a correct sort at all; that is
//! reported rather than fatal, so the example doubles as a smoke test of
//! the evolution pipeline. Set `STRICT=1` to instead require a correct
//! implementation, e.g. when measuring a capable model.

use std::time::{
Duration,
Expand Down Expand Up @@ -234,7 +239,8 @@ async fn main() -> symbiont::Result<()> {
let fn_sigs = runtime.fn_sigs();
info!("fn_sigs: {fn_sigs:?}");

let agent = symbiont::init_agent(None).await?;
let model = std::env::var("MODEL").expect("the MODEL env var names the model slug");
let agent = symbiont::init_agent_from_env(None, &model).await?;

// Fixed test data — identical across rounds for fair comparison.
let rng = Rng::from_seed_with_64bit(42);
Expand Down Expand Up @@ -300,10 +306,12 @@ async fn main() -> symbiont::Result<()> {
)
};

runtime
.evolve(&agent, &prompt)
.await
.expect("evolution should succeed");
if let Err(e) = runtime.evolve(&agent, &prompt).await {
warn!(
"Evolution failed this round: {e}. Keeping previous code; skipping to next round."
);
continue;
}

prev_code = runtime.current_code();

Expand Down Expand Up @@ -338,8 +346,19 @@ async fn main() -> symbiont::Result<()> {
report = new_report;
}

// Whether a small model invents a correct sort in `max_rounds` is a
// property of the model, not of the harness: everything this example
// exercises — dylib creation, compilation, loading, dispatch, panic
// capture, hot-swap — has already run by the time we get here. So a
// failed search is reported, not fatal, which keeps the example usable
// as a smoke test against weak local models. Set `STRICT=1` to demand
// a correct implementation, e.g. when benchmarking a capable model.
if best_code.is_empty() {
panic!("No correct sort implementation found after {max_rounds} rounds.");
let msg = format!("No correct sort implementation found after {max_rounds} rounds.");
let strict = std::env::var_os("STRICT").is_some();
assert!(!strict, "{msg} (STRICT was requested)");
warn!("{msg} The evolution pipeline itself ran fine.");
return Ok(());
}
println!(
"Best implementation found ({max_rounds} iterations, original time: {}, new time: {}):\n```rust\n{best_code}```",
Expand Down
3 changes: 2 additions & 1 deletion examples/struct-support/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,8 @@ async fn main() -> symbiont::Result<()> {
info!("fn_prelude: {fn_prelude:#?}, fn_source: {fn_source:#?}");

let doc_crate = Some(host_crate); // Include documentation of the host crate in the system prompt.
let agent = symbiont::init_agent(doc_crate).await?;
let model = std::env::var("MODEL").expect("the MODEL env var names the model slug");
let agent = symbiont::init_agent_from_env(doc_crate, &model).await?;

let base_prompt = format!(
"Give an implementation for this evolvable function:\n
Expand Down
3 changes: 2 additions & 1 deletion examples/tictactoe/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -340,7 +340,8 @@ async fn main() -> symbiont::Result<()> {
let fn_sigs = runtime.fn_sigs();
info!("fn_sigs: {fn_sigs:?}");

let agent = symbiont::init_agent(None).await?;
let model = std::env::var("MODEL").expect("the MODEL env var names the model slug");
let agent = symbiont::init_agent_from_env(None, &model).await?;

// -- Round 0: evaluate the default (first-empty-cell) strategy -----------
println!("\n=== Round 0: default implementation (first empty cell) ===");
Expand Down
3 changes: 2 additions & 1 deletion examples/tool-calling/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,8 @@ async fn main() -> symbiont::Result<()> {
// Register the `probe` tool on the pre-configured builder.
// `default_max_turns` must be >= 1, otherwise rig aborts the run with
// `MaxTurnsError` as soon as the model chains tool calls.
let agent = symbiont::agent_builder(None)
let model = std::env::var("MODEL").expect("the MODEL env var names the model slug");
let agent = symbiont::agent_builder_from_env(None, &model)
.await?
.tool(Probe)
.default_max_turns(10)
Expand Down
2 changes: 1 addition & 1 deletion symbiont/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ website = "https://symbiont.rs"
workspace = true

[dependencies]
symbiont-macros = { version = "0.20.0", path = "../symbiont-macros" }
symbiont-macros = { version = "0.21.0", path = "../symbiont-macros" }

rig-core.workspace = true
tokio.workspace = true
Expand Down
Loading
Loading