Multi-turn memory
Keep a conversation across turns. JavaScript / Python / Go use a conversation
object whose send retains history; Java / C# / Elixir use ask with a stable id
whose transcript lives in the client’s conversation store. Both remember turn 1 at
turn 2.
const convo = agent.conversation({ toolkit: tk })await convo.send("My name is Muthu. Remember it.")const r = await convo.send("What's my name?")console.log(r.text) // recalls "Muthu"convo = agent.conversation(tk)await convo.send("My name is Muthu. Remember it.")r = await convo.send("What's my name?")print(r.text) # recalls "Muthu"convo := agent.Conversation(tk)convo.Send(ctx, "My name is Muthu. Remember it.")r, _ := convo.Send(ctx, "What's my name?")fmt.Println(r.Text) // recalls "Muthu"agent.ask("My name is Muthu. Remember it.", tk, "user-42");LlmClient.RunResult r = agent.ask("What's my name?", tk, "user-42");System.out.println(r.text); // recalls "Muthu"await agent.AskAsync("My name is Muthu. Remember it.", tk, "user-42");var r = await agent.AskAsync("What's my name?", tk, "user-42");Console.WriteLine(r.Text); // recalls "Muthu"Toolnexus.Client.ask(client, "My name is Muthu. Remember it.", tk, "user-42")r = Toolnexus.Client.ask(client, "What's my name?", tk, "user-42")IO.puts(r.text) # recalls "Muthu";; the client carries a per-client store (in-memory by default); a :conversation-id;; makes run load + save that thread's transcript(client/run agent "My name is Muthu. Remember it." {:toolkit tk :conversation-id "user-42"})(def r (client/run agent "What's my name?" {:toolkit tk :conversation-id "user-42"}))(println (:text r)) ; recalls "Muthu"use std::sync::Arc;use toolnexus::client::{GenerateFn, InProcessOptions, InProcessResponse, create_in_process_client};use toolnexus::{BuiltinsConfig, ToolkitOptions, create_toolkit};
#[tokio::main]async fn main() -> Result<(), Box<dyn std::error::Error>> { // A stand-in model that answers from the transcript it is sent. let generate: GenerateFn = Arc::new(|req| Box::pin(async move { let said = req.messages.iter().filter_map(|m| m["content"].as_str()) .find_map(|c| c.strip_prefix("My name is ")).map(|n| n.trim_end_matches('.').to_string()); Ok(InProcessResponse { content: format!("You are {}", said.unwrap_or("unknown".into())), ..Default::default() }) })); let client = create_in_process_client(InProcessOptions { generate: Some(generate), ..Default::default() }); let tk = create_toolkit(ToolkitOptions { builtins: Some(BuiltinsConfig::off()), ..Default::default() }).await?;
client.ask("My name is Ada.", &tk, "user-42").await?; let again = client.ask("What is my name?", &tk, "user-42").await?; // same id => same transcript let other = client.ask("What is my name?", &tk, "user-7").await?; // new id => fresh transcript assert_eq!(again.text, "You are Ada"); assert_eq!(other.text, "You are unknown"); // ClientOptions { store: Some(Arc::new(FileConversationStore::new("./convos")?)), .. } persists it println!("ok: {}", again.text); Ok(())}The transcript store is pluggable (in-memory by default; bring your own to persist to Redis, Postgres, …). See Memory & conversations.
More recipes coming — streaming, human-in-the-loop suspension, and A2A serve, each
in all eight languages. Runnable versions live in every port’s examples/ folder.