Skip to content
Merged

0.7 #25

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
26 changes: 24 additions & 2 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 README.md
Original file line number Diff line number Diff line change
Expand Up @@ -226,4 +226,4 @@ Also **checkout** the [TODOs](TODO.md) file for what might come next for `symbio

Copyright (C) 2026 MathisWellmann

This project is licensed under the **Mozilla Public License 2.0** — see [LICENSE](LICENSE) for details.
This project is licensed under the **Mozilla Public License 2.0** — see [LICENSE](LICENSE) for details
4 changes: 1 addition & 3 deletions examples/quantize/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -486,9 +486,7 @@ async fn main() -> symbiont::Result<()> {
.await
.expect("evolution should succeed");

prev_code = runtime
.read_clean_code()
.expect("failed to read generated code");
prev_code = runtime.current_code();

result = evaluate(runtime, &dist_data, distr);
println!("{result}");
Expand Down
4 changes: 1 addition & 3 deletions examples/rastrigin/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -207,9 +207,7 @@ async fn main() -> symbiont::Result<()> {

if new_mse < mse_threshold {
println!("Exact formula found after {round} evolution round(s)!\n");
let code = runtime
.read_clean_code()
.expect("failed to read generated code");
let code = runtime.current_code();
println!("Generated code:\n```rust\n{code}```");
return Ok(());
}
Expand Down
4 changes: 1 addition & 3 deletions examples/sort/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -304,9 +304,7 @@ async fn main() -> symbiont::Result<()> {
.await
.expect("evolution should succeed");

prev_code = runtime
.read_clean_code()
.expect("failed to read generated code");
prev_code = runtime.current_code();

results = run_benchmarks(runtime, &benches);
let new_report = format_report(&results);
Expand Down
2 changes: 1 addition & 1 deletion examples/tictactoe/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -385,7 +385,7 @@ async fn main() -> symbiont::Result<()> {
// Update best code if this round improved.
if score > best_score {
best_score = score;
best_code = runtime.read_clean_code().ok();
best_code = Some(runtime.current_code());
info!("New best score: {:.0}%", best_score * 100.0);
}

Expand Down
2 changes: 0 additions & 2 deletions flake.nix
Original file line number Diff line number Diff line change
Expand Up @@ -64,8 +64,6 @@
statix # Highlights nix antipatterns
];
in {
nixosModules.zola-serve = import ./symbiont/modules/nixos/zola-serve.nix;

devShells.${system} = {
default = pkgs.mkShell {
buildInputs =
Expand Down
2 changes: 1 addition & 1 deletion symbiont-macros/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ keywords = ["agent", "dylib", "dynamic", "harness", "llm"]
license = "MPL-2.0"
name = "symbiont-macros"
repository = "https://github.com/MathisWellmann/symbiont"
version = "0.4.0"
version = "0.7.0"

[lints]
workspace = true
Expand Down
5 changes: 3 additions & 2 deletions symbiont/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,13 @@ license = "MPL-2.0"
name = "symbiont"
readme = "README.md"
repository = "https://github.com/MathisWellmann/symbiont"
version = "0.4.0"
version = "0.7.0"

[lints]
workspace = true

[dependencies]
symbiont-macros = { version = "0.4.0", path = "../symbiont-macros" }
symbiont-macros = { version = "0.7.0", path = "../symbiont-macros" }

rig-core.workspace = true
tokio.workspace = true
Expand All @@ -32,6 +32,7 @@ tracing-subscriber = { version = "0.3", features = ["env-filter"] }

[dev-dependencies]
criterion = { version = "0.5", features = ["html_reports"] }
tracing-test = "0.2"

[[bench]]
harness = false
Expand Down
52 changes: 0 additions & 52 deletions symbiont/modules/nixos/zola-serve.nix

This file was deleted.

21 changes: 7 additions & 14 deletions symbiont/src/compiler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,7 @@ use std::path::Path;
use minstant::Instant;
use prettyplease::unparse;
use tokio::process::Command;
use tracing::{
debug,
info,
};
use tracing::info;

use crate::{
error::{
Expand Down Expand Up @@ -36,9 +33,10 @@ pub enum Profile {

impl std::fmt::Display for Profile {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
use Profile::*;
match self {
Profile::Debug => f.write_str("debug"),
Profile::Release => f.write_str("release"),
Debug => f.write_str("debug"),
Release => f.write_str("release"),
}
}
}
Expand All @@ -52,15 +50,10 @@ pub(crate) async fn compile_dylib(
crate_dir: &Path,
profile: Profile,
clean_ast: &mut syn::File,
clean_ast_str: &str,
) -> Result<()> {
let t0 = Instant::now();

let clean_code = unparse(clean_ast);
debug!("clean_code: {clean_code}");
let clean_path = crate_dir.join("src").join("clean.rs");
std::fs::write(&clean_path, &clean_code)
.map_err(|e| Error::WriteLib(format!("Failed to write clean.rs: {e}")))?;

// Wrap function bodies in catch_unwind so panics stay inside the dylib.
wrap_bodies_in_catch_unwind(clean_ast);

Expand All @@ -85,7 +78,7 @@ pub(crate) async fn compile_dylib(
.output()
.await
.map_err(|e| Error::CompilationFailed {
code: clean_code.clone(),
code: clean_ast_str.to_string(),
err: format!("Failed to spawn cargo: {e}"),
})?;

Expand All @@ -98,7 +91,7 @@ pub(crate) async fn compile_dylib(
} else {
let err = String::from_utf8_lossy(&output.stderr).to_string();
Err(Error::CompilationFailed {
code: clean_code.clone(),
code: clean_ast_str.to_string(),
err,
})
}
Expand Down
6 changes: 6 additions & 0 deletions symbiont/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,12 @@ pub enum Error {

#[error("Failed to load dylib: {0}")]
DylibLoad(String),

#[error("Evolution failed after {attempts} attempts. Last error: {last_error}")]
MaxRetriesExceeded {
attempts: usize,
last_error: Box<Error>,
},
}

/// Result type alias for symbiont operations.
Expand Down
21 changes: 21 additions & 0 deletions symbiont/src/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,27 @@ fn no_lang_marker(x: i32) -> i32 { x }
assert_eq!(code.trim(), "fn no_lang_marker(x: i32) -> i32 { x }");
}

#[test]
fn test_extract_rust_code_with_prefix_and_extra_whitespace() {
// Prefix text ensures `start > 0` and extra whitespace after the fence
// ensures the whitespace count is `> 1`, so that `code_start + count`
// differs from `code_start * count` in a way `trim()` cannot recover.
let prefix = "Here is the code you requested:\n";
let input = format!("{prefix}```rust\n\n fn foo() -> i32 {{ 42 }}\n```");
let code = extract_rust_code(&input).expect("can extract");
assert_eq!(code, "fn foo() -> i32 { 42 }");
}

#[test]
fn test_extract_rust_code_generic_fence_with_prefix() {
// Prefix ensures `start > 0` for the generic-fence branch so that
// mutations of `+ start` to `- start` or `* start` produce a wrong
// (or panicking) result.
let input = "Some explanation here:\n```\nfn no_lang(x: i32) -> i32 { x }\n```";
let code = extract_rust_code(input).expect("can extract");
assert_eq!(code, "fn no_lang(x: i32) -> i32 { x }");
}

#[test]
fn test_parse_rust_code_from_block() {
let input = "```rust
Expand Down
Loading
Loading