use std::sync::Mutex;
use fluers_core::{AgentMessage, ContentBlock, ModelProvider, ModelRequest, ModelResponse, Role};
use fluers_runtime::{define_agent, local, JsonFileAdapter};
struct LocalScriptedProvider {
replies: Mutex<Vec<String>>,
}
impl LocalScriptedProvider {
fn new(replies: Vec<String>) -> Self {
Self {
replies: Mutex::new(replies),
}
}
}
#[async_trait::async_trait]
impl ModelProvider for LocalScriptedProvider {
async fn invoke(&self, _request: ModelRequest) -> fluers_core::Result<ModelResponse> {
let body = self
.replies
.lock()
.map_err(|e| fluers_core::CoreError::ModelProvider(format!("provider lock: {e}")))?
.pop()
.unwrap_or_else(|| "(no scripted reply left)".to_string());
Ok(ModelResponse {
messages: vec![AgentMessage {
role: Role::Assistant,
content: vec![ContentBlock::Text { text: body }],
}],
})
}
}
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let provider: std::sync::Arc<dyn ModelProvider> =
std::sync::Arc::new(LocalScriptedProvider::new(vec![
"Hello from the embedded runtime.".into(),
]));
let request = ModelRequest {
model: fluers_core::Model::new("local/embedded"),
messages: Vec::new(),
tools: Vec::new(),
thinking: fluers_core::ThinkingLevel::default(),
params: std::collections::BTreeMap::new(),
};
let response = provider.invoke(request).await?;
let reply = response
.messages
.into_iter()
.next()
.ok_or("provider returned no message")?;
println!("embedded provider reply: {reply:?}");
let tmp = std::env::temp_dir().join("fluers-embedding-example");
let sandbox: std::sync::Arc<dyn fluers_runtime::Sandbox> = std::sync::Arc::new(local());
let agent = define_agent(|b| {
b.model("local/embedded")
.instructions("You are an embedded demo.")
.sandbox(sandbox.clone());
Ok(())
})
.await?;
let _store = JsonFileAdapter::new(tmp);
println!(
"agent constructed in-process; model = {:?}, {} tools wired",
agent.profile.model.id,
agent.profile.tools.len()
);
println!("embedding proof OK — zero network, zero HTTP types.");
Ok(())
}