### Run Dynamic Hooks Example Source: https://github.com/minorcell/aquaregia/blob/main/README.md Execute the dynamic hooks example, illustrating 'prepare_call' and 'prepare_step'. ```bash cargo run --example prepare_hooks ``` -------------------------------- ### Run Minimal Agent Example Source: https://github.com/minorcell/aquaregia/blob/main/README.md Execute the minimal agent example, showcasing 'Agent::builder' with a single tool. ```bash cargo run --example agent_minimal ``` -------------------------------- ### Run Basic Stream Example Source: https://github.com/minorcell/aquaregia/blob/main/README.md Execute the basic stream example to demonstrate 'stream' functionality and 'StreamEvent' handling. ```bash cargo run --example basic_stream ``` -------------------------------- ### Run Basic Generation Example Source: https://github.com/minorcell/aquaregia/blob/main/README.md Execute the basic generation example to perform a one-shot 'generate' operation. ```bash cargo run --example basic_generate ``` -------------------------------- ### Run Tool Loop Guardrails Example Source: https://github.com/minorcell/aquaregia/blob/main/README.md Execute the tool loop guardrails example, demonstrating multi-step tools with 'max_steps'. ```bash cargo run --example tools_max_steps ``` -------------------------------- ### Run Multimodal Image Example Source: https://github.com/minorcell/aquaregia/blob/main/README.md Execute the multimodal image example, demonstrating 'Message::user_text_and_image_url' with vision capabilities. ```bash cargo run --example multimodal_image ``` -------------------------------- ### Check Examples Source: https://github.com/minorcell/aquaregia/blob/main/CONTRIBUTING.md Compiles and checks all examples in the project without running them. Useful for ensuring examples are up-to-date. ```bash cargo check --examples ``` -------------------------------- ### Run OpenAI Compatible Custom Example Source: https://github.com/minorcell/aquaregia/blob/main/README.md Execute the OpenAI compatible custom example, focusing on custom headers, query parameters, and path. ```bash cargo run --example openai_compatible_custom ``` -------------------------------- ### Run Mini Terminal Code Agent Example Source: https://github.com/minorcell/aquaregia/blob/main/README.md Execute the mini Claude code agent example, using 'Agent::builder', '#[tool]', and local tools. ```bash cargo run --example mini_claude_code ``` -------------------------------- ### Check Crate Features Source: https://github.com/minorcell/aquaregia/blob/main/README.md Verify crate installation and feature availability by checking the project with different feature flag combinations. ```bash cargo check --no-default-features cargo check --no-default-features --features openai cargo check --no-default-features --features anthropic cargo check --features telemetry ``` -------------------------------- ### Constructing Manual Content Parts Source: https://github.com/minorcell/aquaregia/blob/main/README_CN.md Provides examples for manually constructing messages with base64 encoded images or raw byte data. ```rust use aquaregia::{ContentPart, ImagePart, MediaData, Message, MessageRole}; // base64 编码图像 let msg = Message::new( MessageRole::User, vec![ ContentPart::Text("请描述这张图表:".into()), ContentPart::Image(ImagePart { data: MediaData::Base64("".into()), media_type: Some("image/png".into()), provider_metadata: None, }), ], )?; // 原始字节(例如从文件读取) let bytes = std::fs::read("chart.png")?; let msg = Message::user_image_bytes(bytes, "image/png"); ``` -------------------------------- ### Add Aquaregia to Project Source: https://github.com/minorcell/aquaregia/blob/main/README.md Install the Aquaregia crate using Cargo. Default features include OpenAI and Anthropic adapters. ```bash cargo add aquaregia ``` -------------------------------- ### Stream Text Output from LLM Source: https://github.com/minorcell/aquaregia/blob/main/README.md Stream text responses from an LLM, handling different event types like reasoning steps, text deltas, and usage statistics. This example uses an OpenAI-compatible provider. ```rust use aquaregia::{GenerateTextRequest, LlmClient, StreamEvent}; use futures_util::StreamExt; #[tokio::main] async fn main() -> Result<(), Box> { let client = LlmClient::openai_compatible("https://api.deepseek.com") .api_key(std::env::var("DEEPSEEK_API_KEY")) .build()?; let mut stream = client .stream(GenerateTextRequest::from_user_prompt( "deepseek-chat", "Write a short release note.", )) .await?; while let Some(event) = stream.next().await { match event? { StreamEvent::ReasoningStarted { block_id, .. } => { eprintln!("\n[reasoning:{block_id}]"); } StreamEvent::ReasoningDelta { text, .. } => { eprint!("{text}"); } StreamEvent::ReasoningDone { .. } => { eprintln!(); } StreamEvent::TextDelta { text } => print!("{text}"), StreamEvent::Usage { usage } => { eprintln!( "\nusage: in={} (no_cache={} cache_read={} cache_write={}) out={} (text={} reasoning={}) total={}", usage.input_tokens, usage.input_no_cache_tokens, usage.input_cache_read_tokens, usage.input_cache_write_tokens, usage.output_tokens, usage.output_text_tokens, usage.reasoning_tokens, usage.total_tokens ); } StreamEvent::Done => break, _ => {} } } Ok(()) } ``` -------------------------------- ### Configure Agent with Hooks Source: https://context7.com/minorcell/aquaregia/llms.txt Set up an agent with dynamic planning and lifecycle callbacks. Use `prepare_call` to adjust run-level settings and `prepare_step` to modify per-step inputs. Lifecycle callbacks like `on_start`, `on_step_start`, `on_tool_call_start`, `on_tool_call_finish`, `on_step_finish`, and `on_finish` allow for monitoring and logging. ```rust use aquaregia::{Agent, LlmClient, Message, tool}; use serde_json::json; #[tokio::main] async fn main() -> Result<(), Box> { let client = LlmClient::openai_compatible("https://api.deepseek.com") .api_key(std::env::var("DEEPSEEK_API_KEY")?) .build()?; let time_tool = tool("get_time") .description("Return current time") .raw_schema(json!({"type": "object", "properties": {}})) .execute_raw(|_| async move { Ok(json!({ "time": "2024-01-15T12:00:00Z" })) }); let agent = Agent::builder(client, "deepseek-chat") .instructions("You may call tools, then answer concisely.") .tools([time_tool]) .max_steps(4) // prepare_call: Adjust run-level settings before execution starts .prepare_call(|plan| { // Dynamically adjust based on user input let has_json_keyword = plan.messages.iter().any(|m| { m.parts().iter().any(|p| { matches!(p, aquaregia::ContentPart::Text(t) if t.contains("json")) }) }); if has_json_keyword { plan.temperature = Some(0.0); plan.max_output_tokens = Some(400); } }) // prepare_step: Modify inputs before each model call .prepare_step(|event| { let mut next = event.to_prepared(); // Add context about current step next.messages.push(Message::system_text( format!("Current step: {}. Be concise.", event.step) )); // Disable tools after step 2 to prevent infinite loops if event.step >= 2 { next.tools.clear(); } next }) // Lifecycle callbacks for monitoring .on_start(|event| { println!("[start] model={} tools={}", event.model_id, event.tool_count); }) .on_step_start(|event| { println!("[step_start] step={} messages={}", event.step, event.messages.len()); }) .on_tool_call_start(|event| { println!("[tool_start] step={} tool={}", event.step, event.tool_call.tool_name); }) .on_tool_call_finish(|event| { println!( "[tool_finish] step={} tool={} duration={}ms", event.step, event.tool_call.tool_name, event.duration_ms ); }) .on_step_finish(|step| { println!( "[step_finish] step={} tool_calls={} finish={{:?}}", step.step, step.tool_calls.len(), step.finish_reason ); }) .on_finish(|event| { println!( "[finish] output_len={} steps={} total_tokens={}", event.output_text.len(), event.step_count, event.usage_total.total_tokens ); }) // Early stop condition .stop_when(|step| { step.output_text.contains("DONE") }) .build()?; let result = agent.run("What time is it? Answer with JSON.").await?; println!("Final: {}", result.output_text); Ok(()) } ``` -------------------------------- ### LlmClient - Creating Provider Clients Source: https://context7.com/minorcell/aquaregia/llms.txt Demonstrates how to create clients for different LLM providers (OpenAI, Anthropic, Google, OpenAI-compatible) using the LlmClient entry point. It shows configuration options like base URL, timeouts, retries, API versions, and custom headers/query parameters. ```APIDOC ## LlmClient - Creating Provider Clients The entry point for creating provider-bound clients. Each provider has its own constructor method that returns a `ClientBuilder` which can be further configured before building into a reusable `BoundClient`. ```rust use aquaregia::LlmClient; use std::time::Duration; // OpenAI client let openai_client = LlmClient::openai("sk-your-api-key") .base_url("https://api.openai.com") // Optional: custom base URL .timeout(Duration::from_secs(60)) // Request timeout .max_retries(3) // Retry count for retryable errors .build()?; // Anthropic client let anthropic_client = LlmClient::anthropic("your-anthropic-key") .base_url("https://api.anthropic.com") .api_version("2023-06-01") // Anthropic API version header .build()?; // Google client let google_client = LlmClient::google("your-google-key") .base_url("https://generativelanguage.googleapis.com") .build()?; // OpenAI-compatible client (DeepSeek, local LLM servers, etc.) let compatible_client = LlmClient::openai_compatible("https://api.deepseek.com") .api_key("your-deepseek-key") .header("x-custom-header", "value") // Custom HTTP headers .query_param("source", "sdk") // Custom query params .chat_completions_path("/v1/chat/completions") // Custom endpoint path .think_tag_parsing(true) // Parse tags for reasoning .build()?; ``` ``` -------------------------------- ### Implement Multi-Step Tool-Using Agent Source: https://context7.com/minorcell/aquaregia/llms.txt Shows how to define tools using the #[tool] macro and configure an agent to execute a multi-step loop until a final answer is reached. ```rust use aquaregia::{Agent, LlmClient, tool}; use serde_json::{Value, json}; // Define a tool using the #[tool] macro #[tool(description = "Get weather information for a city")] async fn get_weather(city: String) -> Result { // In a real application, this would call a weather API Ok(json!({ "city": city, "temp_c": 23, "condition": "sunny", "humidity": 65 })) } #[tool(description = "Search for information on a topic")] async fn search(query: String, limit: Option) -> Result { let limit = limit.unwrap_or(5); Ok(json!({ "query": query, "results": ["Result 1", "Result 2", "Result 3"], "total": limit })) } #[tokio::main] async fn main() -> Result<(), Box> { let client = LlmClient::openai_compatible("https://api.deepseek.com") .api_key(std::env::var("DEEPSEEK_API_KEY")?) .build()?; let agent = Agent::builder(client, "deepseek-chat") .instructions("You are a helpful assistant. Call tools when needed.") .tools([get_weather, search]) // Register tools .max_steps(4) // Maximum tool loop iterations .temperature(0.7) .build()?; // Run with a simple prompt let result = agent.run("What is the weather in Shanghai?").await?; println!("Output: {}", result.output_text); println!("Steps executed: {}", result.steps); println!( "Total usage: in={} out={}", result.usage_total.input_tokens, result.usage_total.output_tokens ); // Access step-by-step results for step in &result.step_results { println!( "Step {}: {} tool calls, finish={:?}", step.step, step.tool_calls.len(), step.finish_reason ); } Ok(()) } ``` -------------------------------- ### Configure Dynamic Planning Source: https://github.com/minorcell/aquaregia/blob/main/README.md Use prepare_call and prepare_step to modify request parameters or agent state dynamically during execution. ```rust use aquaregia::{Agent, LlmClient}; let agent = Agent::builder(client, "deepseek-chat") .max_steps(4) .prepare_call(|plan| { plan.temperature = Some(0.2); }) .prepare_step(|event| { let mut next = event.to_prepared(); if event.step >= 2 { next.tools.clear(); } next }) .build()?; ``` -------------------------------- ### Create Provider-Bound LLM Clients Source: https://context7.com/minorcell/aquaregia/llms.txt Instantiate clients for different LLM providers like OpenAI, Anthropic, and Google. Configure options such as base URL, timeout, and retry count. ```rust use aquaregia::LlmClient; use std::time::Duration; // OpenAI client let openai_client = LlmClient::openai("sk-your-api-key") .base_url("https://api.openai.com") // Optional: custom base URL .timeout(Duration::from_secs(60)) // Request timeout .max_retries(3) // Retry count for retryable errors .build()?; // Anthropic client let anthropic_client = LlmClient::anthropic("your-anthropic-key") .base_url("https://api.anthropic.com") .api_version("2023-06-01") // Anthropic API version header .build()?; // Google client let google_client = LlmClient::google("your-google-key") .base_url("https://generativelanguage.googleapis.com") .build()?; // OpenAI-compatible client (DeepSeek, local LLM servers, etc.) let compatible_client = LlmClient::openai_compatible("https://api.deepseek.com") .api_key("your-deepseek-key") .header("x-custom-header", "value") // Custom HTTP headers .query_param("source", "sdk") // Custom query params .chat_completions_path("/v1/chat/completions") // Custom endpoint path .think_tag_parsing(true) // Parse tags for reasoning .build()? ``` -------------------------------- ### Configure OpenAI-Compatible Client Source: https://github.com/minorcell/aquaregia/blob/main/README.md Customize headers, query parameters, and parsing settings for OpenAI-compatible endpoints. ```rust use aquaregia::LlmClient; let client = LlmClient::openai_compatible("https://api.deepseek.com") .api_key(std::env::var("DEEPSEEK_API_KEY")?) .header("x-trace-source", "aquaregia") .query_param("source", "sdk") .chat_completions_path("/v1/chat/completions") .think_tag_parsing(true) .think_tag_case_insensitive(true) .build()?; ``` -------------------------------- ### Define tools using the Builder API Source: https://context7.com/minorcell/aquaregia/llms.txt Demonstrates creating a typed tool with automatic schema generation and a raw JSON tool, then registering them with an agent. ```rust use aquaregia::{tool, ToolExecError}; use schemars::JsonSchema; use serde::Deserialize; use serde_json::{Value, json}; // Typed tool with automatic JSON Schema generation #[derive(Debug, Deserialize, JsonSchema)] struct CalculatorArgs { /// First operand a: f64, /// Second operand b: f64, /// Operation: add, subtract, multiply, divide operation: String, } let calculator = tool("calculator") .description("Perform basic arithmetic operations") .execute(|args: CalculatorArgs| async move { let result = match args.operation.as_str() { "add" => args.a + args.b, "subtract" => args.a - args.b, "multiply" => args.a * args.b, "divide" => { if args.b == 0.0 { return Err(ToolExecError::Execution("Division by zero".to_string())); } args.a / args.b } _ => return Err(ToolExecError::Execution("Unknown operation".to_string())), }; Ok(json!({ "result": result })) }); // Raw JSON tool with explicit schema let time_tool = tool("get_time") .description("Return current time") .raw_schema(json!({ "type": "object", "properties": { "timezone": { "type": "string", "description": "IANA timezone name" } }, "required": [] })) .execute_raw(|args| async move { let tz = args.get("timezone") .and_then(|v| v.as_str()) .unwrap_or("UTC"); Ok(json!({ "time": "2024-01-15T12:00:00Z", "timezone": tz })) }); // Use tools with agent let agent = Agent::builder(client, "deepseek-chat") .tools([calculator, time_tool]) .build()?; ``` -------------------------------- ### Run Rust Formatting Source: https://github.com/minorcell/aquaregia/blob/main/CONTRIBUTING.md Ensures code adheres to Rust formatting standards. Run this before committing. ```bash cargo fmt ``` -------------------------------- ### Run Clippy with Warnings Source: https://github.com/minorcell/aquaregia/blob/main/README.md Run Clippy linter and treat all warnings as errors. ```bash cargo clippy -- -D warnings ``` -------------------------------- ### Check With Axum Feature Source: https://github.com/minorcell/aquaregia/blob/main/CONTRIBUTING.md Compiles the project with the 'axum' feature enabled. Use for testing Axum integration. ```bash cargo check --features axum ``` -------------------------------- ### Generate Text with OpenAI-Compatible Provider Source: https://github.com/minorcell/aquaregia/blob/main/README.md Use the LlmClient to connect to an OpenAI-compatible service (like Deepseek) and generate text based on a user prompt. Ensure the DEEPSEEK_API_KEY environment variable is set. ```rust use aquaregia::{GenerateTextRequest, LlmClient}; #[tokio::main] async fn main() -> Result<(), Box> { let client = LlmClient::openai_compatible("https://api.deepseek.com") .api_key(std::env::var("DEEPSEEK_API_KEY")) .build()?; let out = client .generate(GenerateTextRequest::from_user_prompt( "deepseek-chat", "Explain Rust ownership in 3 bullet points.", )) .await?; println!("{}", out.output_text); Ok(()) } ``` -------------------------------- ### Run All Tests Source: https://github.com/minorcell/aquaregia/blob/main/CONTRIBUTING.md Executes all unit and integration tests for the project. Essential for verifying code changes. ```bash cargo test ``` -------------------------------- ### Enable Telemetry Source: https://github.com/minorcell/aquaregia/blob/main/README.md Configure the telemetry feature and initialize a tracing subscriber to capture spans. ```toml aquaregia = { version = "*", features = ["telemetry"] } ``` ```rust tracing_subscriber::fmt::init(); // or any other subscriber let out = client.generate(req).await?; // emits a span ``` -------------------------------- ### Implement Agent with Tool Loop Source: https://github.com/minorcell/aquaregia/blob/main/README.md Define tools using the #[tool] attribute and execute an agent loop that can invoke those tools. ```rust use aquaregia::{Agent, LlmClient, tool}; use serde_json::{Value, json}; #[tool(description = "Get weather by city")] async fn get_weather(city: String) -> Result { Ok(json!({ "city": city, "temp_c": 23, "condition": "sunny" })) } #[tokio::main] async fn main() -> Result<(), Box> { let client = LlmClient::openai_compatible("https://api.deepseek.com") .api_key(std::env::var("DEEPSEEK_API_KEY")) .build()?; let agent = Agent::builder(client, "deepseek-chat") .instructions("You can call tools before answering.") .tools([get_weather]) .max_steps(4) .build()?; let out = agent.run("What is the weather in Shanghai?").await?; println!("{}", out.output_text); Ok(()) } ``` -------------------------------- ### The `#[tool]` Macro Source: https://github.com/minorcell/aquaregia/blob/main/aquaregia-macros/README.md The `#[tool]` macro transforms an `async fn` into an `aquaregia::Tool`. It automatically generates the JSON Schema for function parameters and sets up the execution handler. This macro should not be added as a direct dependency but used through the `aquaregia` crate. ```APIDOC ## `#[tool]` Macro Usage ### Description The `#[tool]` macro simplifies the creation of `aquaregia::Tool` by converting an `async fn` into a `Tool` with minimal boilerplate. It automatically generates the JSON Schema for the function's parameters and handles the execution wiring. ### Method Macro attribute ### Endpoint N/A (Macro) ### Parameters #### Macro Arguments - **description** (string) - Optional - A natural-language description of the tool, which is sent to the language model. ### Requirements for `async fn` - **Must be `async fn`**: Synchronous functions are not supported. - **Parameters must be simple identifiers**: For example, `city: String` is valid, but `(a, b): (String, String)` is not. - **No `self`**: The function must be a free function. - **No generics**: Generic parameters are not yet supported. - **Return type**: Must be `Result`. ### Request Example ```rust use aquaregia::{Agent, LlmClient, tool}; use serde_json::{Value, json}; #[tool(description = "Get current weather for a city")] async fn get_weather(city: String, unit: String) -> Result { Ok(json!({ "city": city, "unit": unit, "temp": 23 })) } ``` ### Response At compile time, the `#[tool]` macro expands to generate an `aquaregia::Tool` that can be passed to `Agent::builder`. #### Success Response (200) N/A (Macro expansion generates a `Tool` type) #### Response Example N/A (Macro expansion generates a `Tool` type) ``` -------------------------------- ### Stream Text Generation with Aquaregia Source: https://context7.com/minorcell/aquaregia/llms.txt Demonstrates how to consume a stream of events from an LLM, including reasoning deltas, text content, tool calls, and usage statistics. ```rust use aquaregia::{GenerateTextRequest, LlmClient, StreamEvent}; use futures_util::StreamExt; #[tokio::main] async fn main() -> Result<(), Box> { let client = LlmClient::openai_compatible("https://api.deepseek.com") .api_key(std::env::var("DEEPSEEK_API_KEY")?) .build()?; let mut stream = client .stream(GenerateTextRequest::from_user_prompt( "deepseek-chat", "Write a short release note.", )) .await?; let mut full_text = String::new(); while let Some(event) = stream.next().await { match event? { // Reasoning events (for models with chain-of-thought) StreamEvent::ReasoningStarted { block_id, .. } => { eprintln!("\n[reasoning:{block_id}]"); } StreamEvent::ReasoningDelta { text, .. } => { eprint!("{text}"); } StreamEvent::ReasoningDone { .. } => { eprintln!(); } // Text content events StreamEvent::TextDelta { text } => { full_text.push_str(&text); print!("{text}"); } // Tool call ready for execution StreamEvent::ToolCallReady { call } => { println!("Tool call ready: {} with args {:?}", call.tool_name, call.args_json); } // Usage statistics StreamEvent::Usage { usage } => { eprintln!( "\nUsage: in={} out={} reasoning={}", usage.input_tokens, usage.output_tokens, usage.reasoning_tokens ); } // Stream finished StreamEvent::Done => break, } } println!("\nFinal text length: {} chars", full_text.chars().count()); Ok(()) } ``` -------------------------------- ### Send Multimodal Image Inputs in Rust Source: https://context7.com/minorcell/aquaregia/llms.txt Demonstrates sending images via URL, raw bytes, or base64 to vision-capable models using the LlmClient. ```rust use aquaregia::{ ContentPart, GenerateTextRequest, ImagePart, LlmClient, MediaData, Message, MessageRole }; #[tokio::main] async fn main() -> Result<(), Box> { let client = LlmClient::anthropic(std::env::var("ANTHROPIC_API_KEY")?) .build()?; // Simple: Text + image URL in one message let response = client .generate( GenerateTextRequest::builder("claude-sonnet-4-5") .message(Message::user_text_and_image_url( "What's in this image?", "https://upload.wikimedia.org/wikipedia/commons/thumb/3/3a/Cat03.jpg/1200px-Cat03.jpg", )) .build()?, ) .await?; println!("Description: {}", response.output_text); // Image from URL only let _url_only = Message::user_image_url( "https://example.com/image.jpg" ); // Image from raw bytes (e.g., read from file) let bytes = std::fs::read("chart.png")?; let _from_bytes = Message::user_image_bytes(bytes, "image/png"); // Full control: explicit ImagePart with base64 let _explicit = Message::new( MessageRole::User, vec![ ContentPart::Text("Describe this chart:".into()), ContentPart::Image(ImagePart { data: MediaData::Base64("iVBORw0KGgoAAAANSUhEUgAAAAEAAAAB...".into()), media_type: Some("image/png".into()), provider_metadata: None, }), ], )?; // Data URL format also works let _data_url = Message::user_image_url( "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg==" ); Ok(()) } ``` -------------------------------- ### Define an Aquaregia Tool with #[tool] Macro Source: https://github.com/minorcell/aquaregia/blob/main/aquaregia-macros/README.md Use the `#[tool]` macro on an `async fn` to automatically generate a JSON Schema for parameters and set up an execution handler. Ensure the function is `async`, has simple identifier parameters, no `self`, no generics, and returns `Result`. ```rust use aquaregia::{Agent, LlmClient, tool}; use serde_json::{Value, json}; #[tool(description = "Get current weather for a city")] async fn get_weather(city: String, unit: String) -> Result { Ok(json!({ "city": city, "unit": unit, "temp": 23 })) } ``` -------------------------------- ### Run Tests with Telemetry Source: https://github.com/minorcell/aquaregia/blob/main/README.md Run project tests with the 'telemetry' feature enabled. ```bash cargo test --features telemetry ``` -------------------------------- ### Accessing Reasoning Outputs Source: https://github.com/minorcell/aquaregia/blob/main/README_CN.md Demonstrates how to retrieve reasoning text and tokens from a model response. ```rust let out = client .generate(GenerateTextRequest::from_user_prompt( "deepseek-chat", "请分步骤推理后给出答案。", )) .await?; println!("answer: {}", out.output_text); println!("reasoning text: {}", out.reasoning_text); println!("reasoning tokens: {}", out.usage.reasoning_tokens); for part in &out.reasoning_parts { println!("reasoning block: {}", part.text); } ``` -------------------------------- ### Check Without Default Features and OpenAI Source: https://github.com/minorcell/aquaregia/blob/main/CONTRIBUTING.md Compiles the project, disabling default features and enabling the 'openai' feature. Use for testing specific integrations. ```bash cargo check --no-default-features --features openai ``` -------------------------------- ### Sending Multimodal Messages with Image URLs Source: https://github.com/minorcell/aquaregia/blob/main/README.md Demonstrates sending a text prompt alongside an image URL using the Anthropic provider. ```rust use aquaregia::{GenerateTextRequest, LlmClient, Message}; #[tokio::main] async fn main() -> Result<(), Box> { let client = LlmClient::anthropic(std::env::var("ANTHROPIC_API_KEY")?) .build()?; // Image URL + text in one message let out = client .generate( GenerateTextRequest::builder("claude-sonnet-4-5") .message(Message::user_text_and_image_url( "What's in this image?", "https://upload.wikimedia.org/wikipedia/commons/thumb/3/3a/Cat03.jpg/1200px-Cat03.jpg", )) .build()?, ) .await?; println!("{}", out.output_text); Ok() } ``` -------------------------------- ### Sending Multimodal Image Inputs Source: https://github.com/minorcell/aquaregia/blob/main/README_CN.md Shows how to send text and image URLs to a model using the Anthropic client. ```rust use aquaregia::{GenerateTextRequest, LlmClient, Message}; #[tokio::main] async fn main() -> Result<(), Box> { let client = LlmClient::anthropic(std::env::var("ANTHROPIC_API_KEY")?) .build()?; // 文字 + 图像 URL,合为一条消息 let out = client .generate( GenerateTextRequest::builder("claude-sonnet-4-5") .message(Message::user_text_and_image_url( "这张图里有什么?", "https://upload.wikimedia.org/wikipedia/commons/thumb/3/3a/Cat03.jpg/1200px-Cat03.jpg", )) .build()?, ) .await?; println!("{}", out.output_text); Ok(()) } ``` -------------------------------- ### Cancel Running Requests with CancellationToken Source: https://context7.com/minorcell/aquaregia/llms.txt Demonstrates how to use CancellationToken to cancel a single generate call and an agent run. Cancellation is checked before HTTP sends, SSE chunks, and agent steps. ```rust use aquaregia::{Agent, CancellationToken, ErrorCode, GenerateTextRequest, LlmClient, tool}; use serde_json::{Value, json}; use tokio::time::{Duration, sleep}; #[tool(description = "Get weather")] async fn get_weather(city: String) -> Result { Ok(json!({ "city": city, "temp": 23 })) } #[tokio::main] async fn main() -> Result<(), Box> { let client = LlmClient::openai_compatible("https://api.deepseek.com") .api_key(std::env::var("DEEPSEEK_API_KEY")?) .build()?; // Cancel a single generate call let token = CancellationToken::new(); let token_clone = token.clone(); tokio::spawn(async move { sleep(Duration::from_millis(200)).await; token_clone.cancel(); // Cancel after 200ms }); let req = GenerateTextRequest::builder("deepseek-chat") .user_prompt("Write a 10000-word essay.") .cancellation_token(token) .build()?; match client.generate(req).await { Err(e) if e.code == ErrorCode::Cancelled => { println!("Request cancelled as expected"); } other => println!("Result: {:?}", other), } // Cancel an agent run let agent = Agent::builder(client, "deepseek-chat") .tools([get_weather]) .build()?; let token = CancellationToken::new(); token.cancel(); // Pre-cancel match agent.run_cancellable("What's the weather?", token).await { Err(e) if e.code == ErrorCode::Cancelled => { println!("Agent cancelled"); } _ => {} // Ignore other results } // Cancel with explicit message list let messages = vec![aquaregia::Message::user_text("Hello")]; let token = CancellationToken::new(); let _ = agent.run_messages_cancellable(messages, token).await; Ok(()) } ``` -------------------------------- ### Configure Aquaregia Dependencies Source: https://context7.com/minorcell/aquaregia/llms.txt Specify Aquaregia and Tokio dependencies in your Cargo.toml file. Optional features like 'telemetry' can be enabled. ```toml [dependencies] aquaregia = { version = "0.1", features = ["telemetry"] } tokio = { version = "1", features = ["macros", "rt-multi-thread"] } ``` -------------------------------- ### Constructing Multimodal Messages with Base64 and Raw Bytes Source: https://github.com/minorcell/aquaregia/blob/main/README.md Shows manual construction of messages using base64-encoded data or raw file bytes. ```rust use aquaregia::{ContentPart, ImagePart, MediaData, Message, MessageRole}; // Base64-encoded image let msg = Message::new( MessageRole::User, vec![ ContentPart::Text("Describe this chart:".into()), ContentPart::Image(ImagePart { data: MediaData::Base64("".into()), media_type: Some("image/png".into()), provider_metadata: None, }), ], )?; // Raw bytes (e.g. read from a file) let bytes = std::fs::read("chart.png")?; let msg = Message::user_image_bytes(bytes, "image/png"); ``` -------------------------------- ### Enable Telemetry with Tracing Spans Source: https://context7.com/minorcell/aquaregia/llms.txt Shows how to enable automatic tracing spans for generate, stream, agent steps, and tool calls by enabling the 'telemetry' feature and initializing a tracing subscriber. ```rust // Cargo.toml: // aquaregia = { version = "0.1", features = ["telemetry"] } // tracing-subscriber = "0.3" use aquaregia::{Agent, GenerateTextRequest, LlmClient, tool}; use serde_json::{Value, json}; #[tool(description = "Get data")] async fn get_data(id: String) -> Result { Ok(json!({ "id": id, "value": 42 })) } #[tokio::main] async fn main() -> Result<(), Box> { // Initialize your preferred tracing subscriber tracing_subscriber::fmt::init(); let client = LlmClient::openai("key").build()?; // Spans emitted automatically: // - aquaregia::generate (fields: model, provider) // - aquaregia::stream (fields: model) // - agent_step (fields: step) // - tool_call (fields: tool.name) let _response = client .generate(GenerateTextRequest::from_user_prompt("gpt-4o", "Hello")) .await?; let agent = Agent::builder(client, "gpt-4o") .tools([get_data]) .build()?; let _result = agent.run("Get data for id=123").await?; Ok(()) } ``` -------------------------------- ### Handling API Errors Source: https://github.com/minorcell/aquaregia/blob/main/README_CN.md Demonstrates how to match against specific error codes returned by the API. ```rust use aquaregia::{ErrorCode, GenerateTextRequest, LlmClient}; match client .generate(GenerateTextRequest::from_user_prompt("deepseek-chat", "hello")) .await { Ok(out) => println!("{}", out.output_text), Err(err) => match err.code { ErrorCode::RateLimited => eprintln!("触发限流,请稍后重试"), ErrorCode::AuthFailed => eprintln!("请检查 API Key"), ErrorCode::Cancelled => eprintln!("请求已取消"), _ => eprintln!("请求失败: {}", err), }, } ``` -------------------------------- ### Configure Tool Error Policy in Aquaregia Agent Source: https://context7.com/minorcell/aquaregia/llms.txt Demonstrates setting the tool error policy for an Aquaregia agent. Use `ContinueAsToolResult` to allow the model to handle errors, or `FailFast` to abort the run immediately on failure. Ensure the `LlmClient` and tools are correctly set up. ```rust use aquaregia::{Agent, LlmClient, ToolErrorPolicy, ToolExecError, tool}; use serde_json::{Value, json}; #[tool(description = "A tool that might fail")] async fn risky_operation(input: String) -> Result { if input == "fail" { Err("Simulated failure".to_string()) } else { Ok(json!({ "result": "success" })) } } #[tokio::main] async fn main() -> Result<(), Box> { let client = LlmClient::openai("key").build()?; // ContinueAsToolResult: Convert failures to error-shaped tool results // The model receives the error and can decide how to proceed let agent = Agent::builder(client.clone(), "gpt-4o") .tools([risky_operation]) .tool_error_policy(ToolErrorPolicy::ContinueAsToolResult) .build()?; // FailFast: Abort the run immediately when a tool fails let strict_agent = Agent::builder(client, "gpt-4o") .tools([risky_operation]) .tool_error_policy(ToolErrorPolicy::FailFast) .build()?; Ok(()) } ``` -------------------------------- ### GenerateTextRequest - Non-Streaming Text Generation Source: https://context7.com/minorcell/aquaregia/llms.txt Shows how to perform non-streaming text generation using the `generate` API with `GenerateTextRequest`. It covers both simple user prompts and detailed requests using a builder pattern, including message history, sampling parameters, and accessing reasoning output. ```APIDOC ## GenerateTextRequest - Non-Streaming Text Generation Creates a request for one-shot text generation. Supports both simple prompts and full message conversation history with configurable sampling parameters. ```rust use aquaregia::{GenerateTextRequest, LlmClient, Message}; #[tokio::main] async fn main() -> Result<(), Box> { let client = LlmClient::openai_compatible("https://api.deepseek.com") .api_key(std::env::var("DEEPSEEK_API_KEY")?) .build()?; // Simple one-shot request from user prompt let response = client .generate(GenerateTextRequest::from_user_prompt( "deepseek-chat", "Explain Rust ownership in 3 bullet points.", )) .await?; println!("Output: {}", response.output_text); println!("Finish reason: {:?}", response.finish_reason); println!( "Usage: input={} output={} total={}", response.usage.input_tokens, response.usage.output_tokens, response.usage.total_tokens ); // Builder pattern with full configuration let response = client .generate( GenerateTextRequest::builder("deepseek-chat") .message(Message::system_text("You are a helpful assistant.")) .message(Message::user_text("What is Rust?")) .temperature(0.7) // Sampling temperature 0.0..=2.0 .top_p(0.9) // Nucleus sampling 0.0..=1.0 .max_output_tokens(1024) // Max output token budget .stop_sequences(["END"]) .build()?, ) .await?; // Access reasoning content (for models that support it) println!("Reasoning: {}", response.reasoning_text); for part in &response.reasoning_parts { println!("Reasoning block: {}", part.text); } Ok(()) } ``` ``` -------------------------------- ### Check Without Default Features and Anthropic Source: https://github.com/minorcell/aquaregia/blob/main/CONTRIBUTING.md Compiles the project, disabling default features and enabling the 'anthropic' feature. Use for testing specific integrations. ```bash cargo check --no-default-features --features anthropic ``` -------------------------------- ### Handle Errors with Diagnostic Metadata Source: https://context7.com/minorcell/aquaregia/llms.txt Shows how to match on specific error codes and access diagnostic metadata like request IDs and retryability hints. ```rust use aquaregia::{ErrorCode, GenerateTextRequest, LlmClient}; #[tokio::main] async fn main() -> Result<(), Box> { let client = LlmClient::openai("sk-your-key").build()?; match client .generate(GenerateTextRequest::from_user_prompt("gpt-4o", "hello")) .await { Ok(out) => println!("{}", out.output_text), Err(err) => { // Match on error code for specific handling match err.code { ErrorCode::RateLimited => { eprintln!("Rate limited - implement backoff and retry"); } ErrorCode::AuthFailed => { eprintln!("Authentication failed - check API key"); } ErrorCode::Cancelled => { eprintln!("Request was cancelled"); } ErrorCode::Timeout => { eprintln!("Request timed out - consider increasing timeout"); } ErrorCode::ProviderServerError => { eprintln!("Provider server error - retry may succeed"); } ErrorCode::InvalidRequest => { eprintln!("Invalid request: {}", err.message); } ErrorCode::MaxStepsExceeded => { eprintln!("Agent exceeded max steps without final answer"); } ErrorCode::UnknownTool => { eprintln!("Model requested unknown tool"); } ErrorCode::ToolExecutionFailed => { eprintln!("Tool execution failed"); } _ => eprintln!("Error: {}", err), } // Access additional error metadata if let Some(provider) = &err.provider { eprintln!("Provider: {}", provider); } if let Some(status) = err.status { eprintln!("HTTP status: {}", status); } if let Some(request_id) = &err.request_id { eprintln!("Request ID: {}", request_id); } // Check if error is retryable if err.retryable { eprintln!("This error is retryable"); } } } Ok(()) } ``` -------------------------------- ### Create Typed Model References Source: https://context7.com/minorcell/aquaregia/llms.txt Uses helper functions to define model references with compile-time provider safety and access model metadata. ```rust use aquaregia::{openai, anthropic, google, openai_compatible, ModelRef, OpenAi}; // Helper functions for typed model references let gpt4 = openai("gpt-4o"); // openai/gpt-4o let claude = anthropic("claude-sonnet-4-5"); // anthropic/claude-sonnet-4-5 let gemini = google("gemini-2.0-flash"); // google/gemini-2.0-flash let deepseek = openai_compatible("deepseek-chat"); // openai-compatible/deepseek-chat // Access model information println!("Model ID: {}", gpt4.id()); // "openai/gpt-4o" println!("Model name: {}", gpt4.model()); // "gpt-4o" println!("Provider: {}", gpt4.provider_slug()); // "openai" // Direct ModelRef construction let model = ModelRef::::new("gpt-4o-mini"); assert_eq!(model.id(), "openai/gpt-4o-mini"); ``` -------------------------------- ### Access Usage Statistics Source: https://context7.com/minorcell/aquaregia/llms.txt Retrieves token usage metrics including cache and reasoning breakdowns, and supports accumulating usage across multiple requests. ```rust use aquaregia::{GenerateTextRequest, LlmClient, Usage}; #[tokio::main] async fn main() -> Result<(), Box> { let client = LlmClient::anthropic(std::env::var("ANTHROPIC_API_KEY")?) .build()?; let response = client .generate(GenerateTextRequest::from_user_prompt( "claude-sonnet-4-5", "Explain quantum computing.", )) .await?; let usage = &response.usage; // Basic token counts println!("Input tokens: {}", usage.input_tokens); println!("Output tokens: {}", usage.output_tokens); println!("Total tokens: {}", usage.total_tokens); // Cache information (when available) println!("Input (no cache): {}", usage.input_no_cache_tokens); println!("Cache read: {}", usage.input_cache_read_tokens); println!("Cache write: {}", usage.input_cache_write_tokens); // Output breakdown (when available) println!("Output text tokens: {}", usage.output_text_tokens); println!("Reasoning tokens: {}", usage.reasoning_tokens); // Raw provider usage for debugging if let Some(raw) = &usage.raw_usage { println!("Raw usage: {}", raw); } // Usage can be accumulated across multiple calls let mut total_usage = Usage::default(); total_usage += response.usage.clone(); // ... make more requests ... // total_usage += another_response.usage; Ok(()) } ```