### Start Backend Server Source: https://github.com/lazy-hq/aisdk/blob/main/examples/react-axum-agent/frontend/README.md Starts the Axum backend server. Navigate to the backend directory and run the cargo command. ```bash cd ../backend && cargo run ``` -------------------------------- ### Install Frontend Dependencies Source: https://github.com/lazy-hq/aisdk/blob/main/examples/react-axum-agent/frontend/README.md Installs project dependencies using pnpm. Ensure you have pnpm installed before running this command. ```bash pnpm install ``` -------------------------------- ### Start Frontend Development Server Source: https://github.com/lazy-hq/aisdk/blob/main/examples/react-axum-agent/frontend/README.md Starts the development server for the React frontend. Access the application at http://localhost:5173. ```bash pnpm dev ``` -------------------------------- ### Backend and Frontend Setup Source: https://github.com/lazy-hq/aisdk/blob/main/examples/react-axum-agent/README.md Instructions for setting up and running the backend Axum server and the frontend React application. ```bash cd backend cp .env.example .env # Add API_KEY cargo run ``` ```bash cd frontend pnpm install pnpm dev ``` -------------------------------- ### Add AISDK to Project Source: https://github.com/lazy-hq/aisdk/blob/main/README.md Install the core AISDK library using Cargo. ```bash cargo add aisdk ``` -------------------------------- ### Add OpenAI Provider Feature Source: https://github.com/lazy-hq/aisdk/blob/main/README.md Install the AISDK with the OpenAI provider feature enabled. ```bash cargo add aisdk --features openai ``` -------------------------------- ### Agent Loop with Tool Calling in Rust Source: https://context7.com/lazy-hq/aisdk/llms.txt This example demonstrates setting up an agent loop with multiple tools and custom stop conditions. It shows how to define tools using the `#[tool]` macro, configure the request with tools and system prompts, and process the results, including inspecting tool calls and step-by-step execution. ```rust use aisdk::core::{LanguageModelRequest, utils::step_count_is}; use aisdk::core::tools::Tool; use aisdk::macros::tool; use aisdk::providers::OpenAI; #[tool] /// Returns the current Unix timestamp in seconds. pub fn current_time() -> Tool { let ts = std::time::SystemTime::now() .duration_since(std::time::UNIX_EPOCH) .unwrap() .as_secs(); Ok(ts.to_string()) } #[tool] /// Converts Celsius temperature to Fahrenheit. pub fn celsius_to_fahrenheit(celsius: f64) -> Tool { Ok(format!({" {:.1}"}, celsius * 9.0 / 5.0 + 32.0)) } #[tokio::main] async fn main() -> Result<(), Box> { let mut request = LanguageModelRequest::builder() .model(OpenAI::gpt_4o()) .system("You are a helpful assistant with access to tools.") .prompt("What time is it now, and what is 25°C in Fahrenheit?") .with_tool(current_time()) .with_tool(celsius_to_fahrenheit()) .stop_when(step_count_is(5)) .on_step_finish(|opts| { println!("Step {} finished, {} messages so far", opts.current_step_id, opts.messages().len()); }) .build(); let result = request.generate_text().await?; println!("Answer: {}", result.text().unwrap()); // Inspect all tool calls made during the run if let Some(calls) = result.tool_calls() { for call in &calls { println!("Tool called: {} with input: {}", call.tool.name, call.input); } } // Inspect per-step breakdown for step in result.steps() { println!("Step {}: {} messages", step.step_id, step.messages().len()); } Ok(()) } ``` -------------------------------- ### Use Tools in an Agent Source: https://github.com/lazy-hq/aisdk/blob/main/README.md Register tools with an agent to allow the model to call them during its reasoning loop. This example uses OpenAI's gpt-4o and limits the agent loop to 3 steps. ```rust use aisdk::core::{LanguageModelRequest, utils::step_count_is}; use aisdk::providers::OpenAI; #[tokio::main] async fn main() -> Result<(), Box> { let result = LanguageModelRequest::builder() .model(OpenAI::gpt_4o()) .system("You are a helpful assistant.") .prompt("What is the weather in New York?") .with_tool(get_weather()) .stop_when(step_count_is(3)) // Limit agent loop to 3 steps .build() .generate_text() .await?; println!("Response: {:?}", result.text()); Ok(()) } ``` -------------------------------- ### Generate Providers in CI/CD Source: https://github.com/lazy-hq/aisdk/blob/main/scripts/README.md Use this command in CI/CD workflows like GitHub Actions to generate providers. Ensure uv is installed and configured for the specified Python version and dependencies. ```yaml - name: Generate providers run: uv run --python 3.13 --with typer --with requests ./scripts/provider-codegen.py openai-compatible --with-capabilities ``` -------------------------------- ### Hierarchical Agent Delegation with Subagents Source: https://context7.com/lazy-hq/aisdk/llms.txt This example illustrates creating and using subagents for hierarchical agent delegation. A parent agent can invoke a subagent as a tool, passing tasks for the subagent to handle. The subagent runs its own agent loop and returns the result to the parent. ```rust use aisdk::core::LanguageModelRequest; use aisdk::macros::tool; use aisdk::core::tools::Tool; use aisdk::providers::OpenAI; #[tool] /// Search the web for recent information on a topic. pub fn web_search(query: String) -> Tool { Ok(format!("Search results for '{query}': [result 1, result 2, result 3]")) } #[tokio::main] async fn main() -> Result<(), Box> { // Define a specialized research subagent let research_agent = LanguageModelRequest::builder() .model(OpenAI::gpt_4o()) .subagent( "research-agent", "A research agent that searches the web and summarizes findings on any topic.", ) .system("You are a research assistant. Use web_search to find information, then summarize concisely.") .with_tool(web_search()) .build(); // Returns Tool, not LanguageModelRequest // Parent orchestrator uses the subagent as a tool let mut orchestrator = LanguageModelRequest::builder() .model(OpenAI::gpt_4o()) .system("You are an orchestrator. Delegate research tasks to the research-agent.") .prompt("Research the latest developments in Rust async runtimes and give me a summary.") .with_tool(research_agent) .build(); let result = orchestrator.generate_text().await?; println!("{}", result.text().unwrap()); Ok(()) } ``` -------------------------------- ### Build Multi-turn Conversations with Message::builder() Source: https://context7.com/lazy-hq/aisdk/llms.txt Use `Message::builder()` for sequential message construction, starting with `Initial` state. `Message::conversation_builder()` starts directly in `Conversation` state for dynamic history building. ```rust use aisdk::core::{LanguageModelRequest, Message}; use aisdk::providers::Anthropic; #[tokio::main] async fn main() -> Result<(), Box> { // Build a conversation with history let messages = Message::builder() .system("You are a helpful coding assistant.") .user("How do I read a file in Rust?") .assistant("Use `std::fs::read_to_string(\"path\")` or `std::fs::File::open`.") .user("Show me the error-handling version.") .build(); let result = LanguageModelRequest::builder() .model(Anthropic::claude_4_sonnet()) .system("You are a helpful coding assistant.") .messages(messages) .build() .generate_text() .await?; println!("{}", result.text().unwrap()); // Dynamic conversation with conversation_builder let mut builder = Message::conversation_builder(); let history = vec![("user", "Hello"), ("assistant", "Hi!"), ("user", "What is 2+2?")]; for (role, content) in history { builder = match role { "user" => builder.user(content), "assistant" => builder.assistant(content), _ => builder, }; } let msgs = builder.build(); assert_eq!(msgs.len(), 3); Ok(()) } ``` -------------------------------- ### Control Agent Loop with `step_count_is` and Lifecycle Hooks Source: https://context7.com/lazy-hq/aisdk/llms.txt This snippet illustrates controlling the agent's generation loop using `step_count_is(n)` to stop after a specific number of steps. It also shows how to use `on_step_start` and `on_step_finish` hooks to inspect or modify `LanguageModelOptions` during each agent step. The example logs step information and the final stop reason. ```rust use aisdk::core::{LanguageModelRequest, utils::step_count_is}; use aisdk::providers::OpenAI; use std::sync::{Arc, Mutex}; #[tokio::main] async fn main() -> Result<(), Box> { let step_log = Arc::new(Mutex::new(Vec::::new())); let log_clone = step_log.clone(); let result = LanguageModelRequest::builder() .model(OpenAI::gpt_4o()) .system("You are an agent that counts steps.") .prompt("Count to three, pausing to think after each number.") .stop_when(step_count_is(3)) .on_step_start(|opts| { println!("→ Step {} starting, {} messages in context", opts.current_step_id, opts.messages().len()); }) .on_step_finish(move |opts| { let entry = format!("step={} stop_reason={:?}", opts.current_step_id, opts.stop_reason()); log_clone.lock().unwrap().push(entry); }) .build() .generate_text() .await?; println!("Final answer: {}", result.text().unwrap_or_default()); println!("Step log: {:?}", step_log.lock().unwrap()); println!("Total steps: {}", result.steps().len()); println!("Stop reason: {:?}", result.stop_reason()); Ok(()) } ``` -------------------------------- ### Content Generation Data Flow Source: https://github.com/lazy-hq/aisdk/blob/main/scripts/README.md Illustrates the data flow during the content generation phase, starting from fetching JSON data from the models.dev API to preparing provider-specific objects for writing. ```plaintext models.dev API JSON ↓ fetch_models_dev_json() ↓ prepare_openai_compatible_provider() / prepare_provider_capabilities() ↓ PendingWrite objects (content + path, no I/O) ``` -------------------------------- ### Inject Custom HTTP Headers and Body for API Requests Source: https://context7.com/lazy-hq/aisdk/llms.txt This example demonstrates how to override default provider settings or inject custom fields into API requests. It's useful for scenarios like passthrough proxies, per-request API keys, or provider-specific options not directly exposed by the SDK. The code shows adding custom headers and a JSON body to an OpenAI request. ```rust use aisdk::core::LanguageModelRequest; use aisdk::providers::OpenAI; use std::collections::HashMap; #[tokio::main] async fn main() -> Result<(), Box> { let custom_headers = HashMap::from([ ("X-Custom-Trace-Id".to_string(), "req-abc123".to_string()), ("Authorization".to_string(), "Bearer custom-key-override".to_string()), ]); let result = LanguageModelRequest::builder() .model(OpenAI::gpt_4o()) .prompt("What is 42?") .headers(custom_headers) .body(serde_json::json!({ "store": false, "user": "user-123" })) .build() .generate_text() .await?; println!("{}", result.text().unwrap()); Ok(()) } ``` -------------------------------- ### Message::builder() / Message::conversation_builder() Source: https://context7.com/lazy-hq/aisdk/llms.txt Constructs multi-turn conversations using type-state builders. `Message::builder()` requires an initial system or user message, while `Message::conversation_builder()` starts directly in the conversation state. ```APIDOC ## `Message::builder()` / `Message::conversation_builder()` — Multi-turn conversation construction Type-state builders for assembling `Messages` (a `Vec`). `Message::builder()` starts at `Initial` state requiring a `system` or `user` first message, then transitions to `Conversation` state. `Message::conversation_builder()` starts directly in `Conversation` state. ```rust use aisdk::core::{LanguageModelRequest, Message}; use aisdk::providers::Anthropic; #[tokio::main] async fn main() -> Result<(), Box> { // Build a conversation with history let messages = Message::builder() .system("You are a helpful coding assistant.") .user("How do I read a file in Rust?") .assistant("Use `std::fs::read_to_string(\"path\")` or `std::fs::File::open`.") .user("Show me the error-handling version.") .build(); let result = LanguageModelRequest::builder() .model(Anthropic::claude_4_sonnet()) .system("You are a helpful coding assistant.") .messages(messages) .build() .generate_text() .await?; println!("{}", result.text().unwrap()); // Dynamic conversation with conversation_builder let mut builder = Message::conversation_builder(); let history = vec![("user", "Hello"), ("assistant", "Hi!"), ("user", "What is 2+2?")]; for (role, content) in history { builder = match role { "user" => builder.user(content), "assistant" => builder.assistant(content), _ => builder, }; } let msgs = builder.build(); assert_eq!(msgs.len(), 3); Ok(()) } ``` ``` -------------------------------- ### LanguageModelRequest::builder() - Type-state request builder Source: https://context7.com/lazy-hq/aisdk/llms.txt The entry point for all text generation using a type-state builder pattern. It guides the user through different stages of request construction, ensuring compile-time correctness. ```APIDOC ## LanguageModelRequest::builder() ### Description The entry point for all text generation. A compile-time-enforced builder that transitions through `ModelStage → SystemStage → ConversationStage → OptionsStage` states. Calling `.build()` on `OptionsStage` produces a `LanguageModelRequest` ready for `.generate_text()` or `.stream_text()`. ### Method Rust Builder Pattern ### Endpoint N/A (Rust SDK) ### Parameters N/A (Builder methods are called sequentially) ### Request Example ```rust use aisdk::core::{LanguageModelRequest, Message}; use aisdk::providers::OpenAI; #[tokio::main] async fn main() -> Result<(), Box> { // Simple prompt let result = LanguageModelRequest::builder() .model(OpenAI::gpt_4o()) .prompt("Summarize Rust\'s ownership model in two sentences.") .build() .generate_text() .await?; println!("{}", result.text().unwrap()); // With system prompt and multi-turn messages let messages = Message::builder() .system("You are a concise Rust expert.") .user("What is a lifetime?") .assistant("A lifetime annotates how long a reference is valid.") .user("Give me a one-line example.") .build(); let result = LanguageModelRequest::builder() .model(OpenAI::gpt_4o()) .system("You are a concise Rust expert.") .messages(messages) .temperature(30u32) // 30 = 0.30, range 0–100 .max_retries(3u32) .seed(42u32) .build() .generate_text() .await?; println!("{}", result.text().unwrap()); // Expected: a short Rust lifetime example, e.g. "fn longest<'a>(x: &'a str, y: &'a str) -> &'a str { ... }" Ok(()) } ``` ### Response N/A (This is a builder pattern, the final output is from `generate_text` or `stream_text`) ``` -------------------------------- ### Type-State Request Builder for Text Generation Source: https://context7.com/lazy-hq/aisdk/llms.txt Use `LanguageModelRequest::builder()` to construct text generation requests. The builder enforces type states (`ModelStage → SystemStage → ConversationStage → OptionsStage`) at compile time. Call `.build()` on `OptionsStage` to get a request ready for `.generate_text()` or `.stream_text()`. ```rust use aisdk::core::{LanguageModelRequest, Message}; use aisdk::providers::OpenAI; #[tokio::main] async fn main() -> Result<(), Box> { // Simple prompt let result = LanguageModelRequest::builder() .model(OpenAI::gpt_4o()) .prompt("Summarize Rust's ownership model in two sentences.") .build() .generate_text() .await?; println!("{}", result.text().unwrap()); // With system prompt and multi-turn messages let messages = Message::builder() .system("You are a concise Rust expert.") .user("What is a lifetime?") .assistant("A lifetime annotates how long a reference is valid.") .user("Give me a one-line example.") .build(); let result = LanguageModelRequest::builder() .model(OpenAI::gpt_4o()) .system("You are a concise Rust expert.") .messages(messages) .temperature(30u32) // 30 = 0.30, range 0–100 .max_retries(3u32) .seed(42u32) .build() .generate_text() .await?; println!("{}", result.text().unwrap()); // Expected: a short Rust lifetime example, e.g. "fn longest<'a>(x: &'a str, y: &'a str) -> &'a str { ... }" Ok(()) } ``` -------------------------------- ### Manual Tool Construction with `Tool::builder()` Source: https://context7.com/lazy-hq/aisdk/llms.txt Shows how to construct tools manually using `Tool::builder()` when more control is needed, such as custom input schemas or runtime closures. It demonstrates using `ToolExecute::from_sync` for synchronous execution and defining input parameters with `schemars`. ```rust use aisdk::core::tools::{Tool, ToolExecute, ToolContext}; use schemars::schema_for; use serde::{Deserialize, Serialize}; use serde_json::Value; #[derive(Serialize, Deserialize, schemars::JsonSchema)] struct CalcInput { a: f64, b: f64, op: String, // "+", "-", "*", "/" } let calculator = Tool::builder() .name("calculator") .description("Performs basic arithmetic operations on two numbers.") .input_schema(schema_for!(CalcInput)) .execute(ToolExecute::from_sync(|_ctx: ToolContext, params: Value| { let a = params["a"].as_f64().unwrap_or(0.0); let b = params["b"].as_f64().unwrap_or(0.0); let result = match params["op"].as_str().unwrap_or("+") { "+" => a + b, "-" => a - b, "*" => a * b, "/" if b != 0.0 => a / b, "/" => return Err("Division by zero".to_string()), op => return Err(format!("Unknown operator: {op}")), }; Ok(format!("{result}")) })) .build() .expect("tool build failed"); assert_eq!(calculator.name, "calculator"); ``` -------------------------------- ### Run All Tests Source: https://github.com/lazy-hq/aisdk/blob/main/CONTRIBUTING.md Execute this command to run the full test suite with all features enabled, ensuring new code is well-tested. ```bash cargo test --all-features ``` -------------------------------- ### Basic Text Generation with OpenAI Source: https://github.com/lazy-hq/aisdk/blob/main/README.md Perform basic text generation using the OpenAI provider. Ensure the 'openai' feature is enabled. ```rust use aisdk::core::LanguageModelRequest; use aisdk::providers::OpenAI; #[tokio::main] async fn main() -> Result<(), Box> { let openai = OpenAI::gpt_5(); let result = LanguageModelRequest::builder() .model(openai) .prompt("What is the meaning of life?") .build() .generate_text() // or stream_text() for streaming .await?; println!("Response: {:?}", result.text()); Ok(()) } ``` -------------------------------- ### File-based Prompt Templating with PromptEnv Source: https://context7.com/lazy-hq/aisdk/llms.txt Loads prompt templates from a directory using the Tera engine. Templates support variables, conditionals, loops, and includes. Enable with `cargo add aisdk --features prompt`. ```rust use aisdk::prompt::PromptEnv; use aisdk::core::LanguageModelRequest; use aisdk::providers::OpenAI; use std::collections::HashMap; use std::path::PathBuf; // prompts/system/assistant.md: // "You are a {{ role }} assistant specializing in {{ domain }}." // // prompts/user/question.md: // "Question: {{ question }}\nAnswer in {{ language }}." #[tokio::main] async fn main() -> Result<(), Box> { let env = PromptEnv::new(PathBuf::from("prompts")); let system_prompt = env.generate_prompt( "system/assistant.md", HashMap::from([ ("role".to_string(), "senior".to_string()), ("domain".to_string(), "Rust programming".to_string()), ]), )?; let user_prompt = env.generate_prompt( "user/question.md", HashMap::from([ ("question".to_string(), "What is a trait object?".to_string()), ("language".to_string(), "English".to_string()), ]), )?; let result = LanguageModelRequest::builder() .model(OpenAI::gpt_4o()) .system(system_prompt) .prompt(user_prompt) .build() .generate_text() .await?; println!("{}", result.text().unwrap()); Ok(()) } ``` -------------------------------- ### Run Provider Code Generator with uv Source: https://github.com/lazy-hq/aisdk/blob/main/scripts/README.md Execute the provider code generator script using `uv` for dependency management. Ensure Python 3.13+ and the `typer` and `requests` packages are available. ```bash uv run --python 3.13 --with typer --with requests provider-codegen.py openai-compatible uv run --python 3.13 --with typer --with requests provider-codegen.py capabilities ``` -------------------------------- ### `Tool::builder()` — Manual tool construction Source: https://context7.com/lazy-hq/aisdk/llms.txt Provides more control for tool creation than the `#[tool]` macro, allowing for custom input schemas and runtime closures. Use `ToolBuilder` with `ToolExecute::from_sync` or `ToolExecute::from_async` for advanced configurations. ```APIDOC ## `Tool::builder()` — Manual tool construction When you need more control than `#[tool]` provides—custom input schemas, runtime closures—use the `ToolBuilder` directly with `ToolExecute::from_sync` or `ToolExecute::from_async`. ```rust use aisdk::core::tools::{Tool, ToolExecute, ToolContext}; use schemars::schema_for; use serde::{Deserialize, Serialize}; use serde_json::Value; #[derive(Serialize, Deserialize, schemars::JsonSchema)] struct CalcInput { a: f64, b: f64, op: String, // "+", "-", "*", "/" } let calculator = Tool::builder() .name("calculator") .description("Performs basic arithmetic operations on two numbers.") .input_schema(schema_for!(CalcInput)) .execute(ToolExecute::from_sync(|_ctx: ToolContext, params: Value| { let a = params["a"].as_f64().unwrap_or(0.0); let b = params["b"].as_f64().unwrap_or(0.0); let result = match params["op"].as_str().unwrap_or("+") { "+" => a + b, "-" => a - b, "*" => a * b, "/" if b != 0.0 => a / b, "/" => return Err("Division by zero".to_string()), op => return Err(format!("Unknown operator: {op}")), }; Ok(format!("{result}")) })) .build() .expect("tool build failed"); assert_eq!(calculator.name, "calculator"); ``` ``` -------------------------------- ### Set up Shell Alias for Provider Code Generator Source: https://github.com/lazy-hq/aisdk/blob/main/scripts/README.md Create a shell alias for `provider-codegen.py` for convenient development use. Add the alias to your shell configuration file (`.bashrc`, `.zshrc`, etc.) and reload your shell. ```bash alias provider-codegen='uv run --python 3.13 --with typer --with requests ./scripts/provider-codegen.py' source ~/.bashrc # or source ~/.zshrc ``` -------------------------------- ### Generate OpenAI-compatible Providers Source: https://github.com/lazy-hq/aisdk/blob/main/scripts/README.md Use the `provider-codegen` command to generate OpenAI-compatible provider modules. Specify a single provider or use the `--with-capabilities` flag for additional capability generation. ```bash # All OpenAI-compatible providers provider-codegen openai-compatible # Single provider provider-codegen openai-compatible deepseek # With capabilities provider-codegen openai-compatible --with-capabilities provider-codegen openai-compatible deepseek -c ``` -------------------------------- ### Generate Provider Capabilities Source: https://github.com/lazy-hq/aisdk/blob/main/scripts/README.md Generate capability files for providers using the `provider-codegen` command. This can be done for all providers, a single provider, or multiple specified providers. ```bash # All provider capabilities provider-codegen capabilities # Single provider provider-codegen capabilities openai # Multiple providers provider-codegen capabilities anthropic ``` -------------------------------- ### Format and Lint Code Source: https://github.com/lazy-hq/aisdk/blob/main/CONTRIBUTING.md Run these commands to format your code according to Rust conventions and check for common issues. ```bash cargo fmt --all cargo clippy --all-features ``` -------------------------------- ### PromptEnv Source: https://context7.com/lazy-hq/aisdk/llms.txt Enables file-based prompt templating using the Tera engine. Prompts can include variables, conditionals, loops, and include other templates. ```APIDOC ## `PromptEnv` — File-based prompt templating Loads all prompt templates from a directory tree using the Tera engine. Templates can include variables (`{{ name }}`), conditionals, loops, and `{% include %}` directives. Enable with `cargo add aisdk --features prompt`. ```rust use aisdk::prompt::PromptEnv; use aisdk::core::LanguageModelRequest; use aisdk::providers::OpenAI; use std::collections::HashMap; use std::path::PathBuf; // prompts/system/assistant.md: // "You are a {{ role }} assistant specializing in {{ domain }}". // // prompts/user/question.md: // "Question: {{ question }}\nAnswer in {{ language }}". #[tokio::main] async fn main() -> Result<(), Box> { let env = PromptEnv::new(PathBuf::from("prompts")); let system_prompt = env.generate_prompt( "system/assistant.md", HashMap::from([ ("role".to_string(), "senior".to_string()), ("domain".to_string(), "Rust programming".to_string()), ]), )?; let user_prompt = env.generate_prompt( "user/question.md", HashMap::from([ ("question".to_string(), "What is a trait object?".to_string()), ("language".to_string(), "English".to_string()), ]), )?; let result = LanguageModelRequest::builder() .model(OpenAI::gpt_4o()) .system(system_prompt) .prompt(user_prompt) .build() .generate_text() .await?; println!("{}", result.text().unwrap()); Ok(()) } ``` ``` -------------------------------- ### Stream Text Generation with `stream_text()` Source: https://context7.com/lazy-hq/aisdk/llms.txt Demonstrates streaming text generation using `stream_text()`. It shows how to consume chunks of text and reasoning deltas from the stream and access the full response asynchronously after the stream is closed. ```rust use aisdk::core::{LanguageModelRequest, LanguageModelStreamChunkType}; use aisdk::providers::Google; use futures::StreamExt; #[tokio::main] async fn main() -> Result<(), Box> { let mut response = LanguageModelRequest::builder() .model(Google::gemini_2_5_flash()) .system("You are a helpful assistant.") .prompt("Explain async/await in Rust step by step.") .build() .stream_text() .await?; // Consume the stream chunk by chunk while let Some(chunk) = response.stream.next().await { match chunk { LanguageModelStreamChunkType::TextDelta(text) => print!("{}", text), LanguageModelStreamChunkType::ReasoningDelta(reasoning) => { eprint!("[thinking: {}]", reasoning) } LanguageModelStreamChunkType::Failed(err) => eprintln!("\nStream error: {err}"), LanguageModelStreamChunkType::TextEnd => println!(), _ => {} } } // Async accessors available after stream is consumed let full_text = response.text().await; let usage = response.usage().await; println!("Total output tokens: {:?}", usage.output_tokens); // Expected output: streamed explanation printed token-by-token Ok(()) } ``` -------------------------------- ### Add AISDK to Project with Provider Features Source: https://context7.com/lazy-hq/aisdk/llms.txt Add the `aisdk` crate to your `Cargo.toml` file, specifying the desired provider feature flags. Alternatively, use `cargo add`. ```toml # Cargo.toml [dependencies] aisdk = { version = "0.5.2", features = ["openai", "anthropic", "google"] } tokio = { version = "1", features = ["full"] } # Other available features: groq, deepseek, mistral, xai, openrouter, amazon-bedrock, togetherai, vercel, prompt, axum, and 60+ more OpenAI-compatible providers. ``` ```bash # Or via cargo add ``` ```bash cargo add aisdk --features openai,anthropic ``` -------------------------------- ### `#[tool]` macro — Defining tools from functions Source: https://context7.com/lazy-hq/aisdk/llms.txt A procedural macro that converts Rust functions into `Tool` structs. It automatically generates a name, description from doc comments, and a JSON Schema for inputs based on function parameters. Async functions are supported, and a `ToolContext` parameter can receive runtime context. ```APIDOC ## `#[tool]` macro — Defining tools from functions A procedural macro that transforms a plain Rust function into a `Tool` struct with name, description (from doc comment), and a JSON Schema input derived from the function parameters. The resulting zero-argument factory function (e.g., `get_weather()`) returns the `Tool`. Async functions are fully supported. A parameter typed as `ToolContext` receives runtime context and is excluded from the schema. ```rust use aisdk::core::tools::{Tool, ToolContext}; use aisdk::macros::tool; #[tool] /// Fetches current weather for a city. Returns temperature in Fahrenheit. pub fn get_weather(location: String, units: Option) -> Tool { let units = units.unwrap_or_else(|| "fahrenheit".to_string()); let temp = match location.as_str() { "New York" => 72, "Tokyo" => 85, "London" => 58, _ => 70, }; Ok(format!("{temp}°{} in {location}", if units == "celsius" { "C" } else { "F" })) } #[tool] /// Asynchronously queries a database for a user record. pub async fn get_user(ctx: ToolContext, user_id: String) -> Tool { // ctx.options() exposes the current LanguageModelOptions let system = ctx.options().system.clone().unwrap_or_default(); tokio::time::sleep(std::time::Duration::from_millis(10)).await; Ok(format!("User {user_id} found. System context: '{}'", &system[..20.min(system.len())])) } #[tool(name = "search-web", desc = "Search the internet for recent information")] pub fn search(query: String) -> Tool { Ok(format!("Results for: {query}")) } // Usage verification let tool = get_weather(); assert_eq!(tool.name, "get_weather"); assert_eq!(tool.description, " Fetches current weather for a city. Returns temperature in Fahrenheit."); ``` ``` -------------------------------- ### Generate Providers with Alias in CI/CD Source: https://github.com/lazy-hq/aisdk/blob/main/scripts/README.md This snippet shows how to set up an alias for the provider-codegen script within a CI/CD environment before executing it. This can simplify repeated commands. ```yaml - name: Generate providers run: | alias provider-codegen='uv run --python 3.13 --with typer --with requests ./scripts/provider-codegen.py' provider-codegen openai-compatible --with-capabilities ``` -------------------------------- ### Check Available Providers with models.dev API Source: https://github.com/lazy-hq/aisdk/blob/main/scripts/README.md A `curl` command to fetch provider information from the models.dev API and `jq` to extract the keys (provider names). Useful for troubleshooting 'Provider not found' errors. ```bash curl https://models.dev/api.json | jq 'keys' ``` -------------------------------- ### Define Tools with `#[tool]` Macro Source: https://context7.com/lazy-hq/aisdk/llms.txt Illustrates defining tools using the `#[tool]` procedural macro. This macro automatically generates a `Tool` struct from a Rust function, including its name, description from doc comments, and a JSON schema for inputs derived from function parameters. Async functions and `ToolContext` are supported. ```rust use aisdk::core::tools::{Tool, ToolContext}; use aisdk::macros::tool; #[tool] /// Fetches current weather for a city. Returns temperature in Fahrenheit. pub fn get_weather(location: String, units: Option) -> Tool { let units = units.unwrap_or_else(|| "fahrenheit".to_string()); let temp = match location.as_str() { "New York" => 72, "Tokyo" => 85, "London" => 58, _ => 70, }; Ok(format!("{temp}°{} in {location}", if units == "celsius" { "C" } else { "F" })) } ``` ```rust use aisdk::core::tools::{Tool, ToolContext}; use aisdk::macros::tool; #[tool] /// Asynchronously queries a database for a user record. pub async fn get_user(ctx: ToolContext, user_id: String) -> Tool { // ctx.options() exposes the current LanguageModelOptions let system = ctx.options().system.clone().unwrap_or_default(); tokio::time::sleep(std::time::Duration::from_millis(10)).await; Ok(format!("User {user_id} found. System context: '{}'", &system[..20.min(system.len())])) } ``` ```rust use aisdk::core::tools::{Tool, ToolContext}; use aisdk::macros::tool; #[tool(name = "search-web", desc = "Search the internet for recent information")] pub fn search(query: String) -> Tool { Ok(format!("Results for: {query}")) } ``` ```rust // Usage verification let tool = get_weather(); assert_eq!(tool.name, "get_weather"); assert_eq!(tool.description, " Fetches current weather for a city. Returns temperature in Fahrenheit."); ``` -------------------------------- ### Compile and Test Provider Code Generator Source: https://github.com/lazy-hq/aisdk/blob/main/scripts/README.md Commands for compiling the Python script and testing its functionality. Use `py_compile` for syntax checks and `uv run` for executing commands with help or specific arguments. ```bash # Syntax check python3 -m py_compile provider-codegen.py # Help uv run --python 3.13 --with typer --with requests provider-codegen.py --help ``` -------------------------------- ### Provider Code Generator Architecture Overview Source: https://github.com/lazy-hq/aisdk/blob/main/scripts/README.md A high-level view of the `provider-codegen.py` script's structure, highlighting its main components including utilities, file writing, generation modules, and CLI commands. ```plaintext provider-codegen.py ├── Utilities (log, fetch_models_dev_json, to_pascal_case, etc.) ├── File Writing (PendingWrite, batch_write_files) ├── Capabilities Generation (generate_capabilities_rs, prepare_provider_capabilities) ├── OpenAI-compatible Generation (generate_openai_compatible_content, prepare_openai_compatible_provider) └── CLI Commands (openai_compatible, capabilities commands) ``` -------------------------------- ### Axum Server for Vercel AI SDK UI Streaming Chat Source: https://context7.com/lazy-hq/aisdk/llms.txt This snippet sets up an Axum server to handle chat requests from the Vercel AI SDK UI. It converts incoming JSON requests into `aisdk::core::Message` objects, uses a `LanguageModelRequest` with a tool (`fetch_docs`), and streams the response back in the Vercel UI protocol format. Ensure `aisdk` is added with `axum` and `google` features. ```rust use aisdk::core::{LanguageModelRequest, Tool}; use aisdk::integrations::{axum::AxumSseResponse, vercel_aisdk_ui::VercelUIRequest}; use aisdk::macros::tool; use aisdk::providers::Google; #[tool] /// Fetch documentation from aisdk.rs for a given path. async fn fetch_docs(path: String) -> Tool { let url = format!("https://aisdk.rs/docs/{path}.mdx"); let body = reqwest::get(&url).await.map_err(|e| e.to_string())? .text().await.map_err(|e| e.to_string())?; Ok(body) } async fn chat_handler( axum::Json(request): axum::Json, ) -> AxumSseResponse { // Deserialize Vercel useChat messages to aisdk Messages let messages: Vec = request.into(); let response = LanguageModelRequest::builder() .model(Google::gemini_2_5_flash()) .system("You are an aisdk.rs expert. Use fetch_docs to look up documentation.") .messages(messages) .with_tool(fetch_docs()) .build() .stream_text() .await; match response { Ok(r) => r .to_axum_vercel_ui_stream() .send_reasoning() .send_start() .send_finish() .build(), Err(e) => panic!("stream error: {e}"), } } #[tokio::main] async fn main() { dotenv::dotenv().ok(); let app = axum::Router::new() .route("/api/chat", axum::routing::post(chat_handler)) .layer(tower_http::cors::CorsLayer::permissive()); let listener = tokio::net::TcpListener::bind("127.0.0.1:8080").await.unwrap(); println!("Listening on http://127.0.0.1:8080"); axum::serve(listener, app).await.unwrap(); } // Frontend: useChat({ api: "/api/chat" }) in Next.js/React connects directly ``` -------------------------------- ### Define a Tool with `#[tool]` Macro Source: https://github.com/lazy-hq/aisdk/blob/main/README.md Expose a Rust function as a callable tool using the `#[tool]` macro. The function should return a `Tool` type. ```rust use aisdk::core::Tool; use aisdk::macros::tool; #[tool] /// Get the weather information given a location pub fn get_weather(location: String) -> Tool { let weather = match location.as_str() { "New York" => 75, "Tokyo" => 80, _ => 70, }; Ok(weather.to_string()) } ``` -------------------------------- ### Non-Streaming Text Generation with Structured Output Source: https://context7.com/lazy-hq/aisdk/llms.txt Use `generate_text()` for non-streaming requests that run the full agent loop. The `GenerateTextResponse` provides access to `.text()`, `.usage()`, `.tool_calls()`, `.tool_results()`, `.steps()`, and can be deserialized into a typed struct using `.into_schema::()` if the model supports structured output. ```rust use aisdk::core::{LanguageModelRequest, utils::step_count_is}; use aisdk::providers::Anthropic; use serde::Deserialize; use schemars::JsonSchema; #[derive(Debug, Deserialize, JsonSchema)] struct Sentiment { label: String, // "positive", "negative", or "neutral" score: f64, // 0.0–1.0 } #[tokio::main] async fn main() -> Result<(), Box> { let mut request = LanguageModelRequest::builder() .model(Anthropic::claude_4_sonnet()) .system("You are a sentiment analysis engine. Respond only with valid JSON.") .prompt("I absolutely love writing Rust code!") .schema::() // requires StructuredOutputSupport capability .temperature(0u32) .build(); let result = request.generate_text().await?; // Access raw text println!("Raw: {}", result.text().unwrap()); // Deserialize into typed struct let sentiment: Sentiment = result.into_schema()?; println!("Label: {}, Score: {}", sentiment.label, sentiment.score); // Expected: Label: positive, Score: ~0.97 // Token usage across all steps let usage = result.usage(); println!("Input tokens: {:?}, Output tokens: {:?}", usage.input_tokens, usage.output_tokens); Ok(()) } ``` -------------------------------- ### Generate Rust Function Source: https://github.com/lazy-hq/aisdk/blob/main/examples/prompts/examples/code_generation.txt Use this template to generate a Rust function. Replace placeholders with your specific requirements. ```rust fn {{ function_name }}({{ params }}) { // Your code here } ``` -------------------------------- ### Generate Python Function Source: https://github.com/lazy-hq/aisdk/blob/main/examples/prompts/examples/code_generation.txt Use this template to generate a Python function. Replace placeholders with your specific requirements. ```python def {{ function_name }}({{ params }}): # Your code here ``` -------------------------------- ### stream_text() - Streaming text generation Source: https://context7.com/lazy-hq/aisdk/llms.txt Generates text in a streaming fashion, returning a `StreamTextResponse`. The response stream provides chunks of text, reasoning, and other events. Post-stream accessors allow retrieving the full text, usage, and steps asynchronously. ```APIDOC ## `stream_text()` — Streaming text generation Returns a `StreamTextResponse` immediately. The `.stream` field is a `LanguageModelStream` implementing `futures::Stream`. Chunks include `TextStart`, `TextDelta(String)`, `TextEnd`, `ReasoningDelta(String)`, `ToolCallStart`, `ToolCallAvailable`, `ToolCallEnd`, `Failed`, and `Incomplete`. Post-stream accessors (`messages()`, `text()`, `usage()`, `steps()`) are all async. ```rust use aisdk::core::{LanguageModelRequest, LanguageModelStreamChunkType}; use aisdk::providers::Google; use futures::StreamExt; #[tokio::main] async fn main() -> Result<(), Box> { let mut response = LanguageModelRequest::builder() .model(Google::gemini_2_5_flash()) .system("You are a helpful assistant.") .prompt("Explain async/await in Rust step by step.") .build() .stream_text() .await?; // Consume the stream chunk by chunk while let Some(chunk) = response.stream.next().await { match chunk { LanguageModelStreamChunkType::TextDelta(text) => print!("{}", text), LanguageModelStreamChunkType::ReasoningDelta(reasoning) => { eprint!("[thinking: {})", reasoning) } LanguageModelStreamChunkType::Failed(err) => eprintln!("\nStream error: {err}"), LanguageModelStreamChunkType::TextEnd => println!(), _ => {{}} } } // Async accessors available after stream is consumed let full_text = response.text().await; let usage = response.usage().await; println!("Total output tokens: {:?}", usage.output_tokens); // Expected output: streamed explanation printed token-by-token Ok(()) } ``` ```