generate_client

Macro generate_client 

Source
generate_client!() { /* proc-macro */ }
Expand description

Generate a type-safe API client for an OpenAPI endpoint. This macro is designed to work within an agent-based API automation system that uses RAG (Retrieval Augmented Generation) to find and execute relevant API endpoints.

§System Overview

The typical workflow:

  1. OpenAPI specs are downloaded and stored in the database
  2. Endpoints are extracted and embedded for RAG retrieval
  3. When a natural language task arrives, relevant endpoints are retrieved
  4. This macro generates type-safe clients for those endpoints

§Usage Example

use serde::{Serialize, Deserialize};
use pgvector::Vector;
use uuid::Uuid;

// Define your custom error type
#[derive(Debug, thiserror::Error)]
pub enum AgentError {
    #[error("API request failed: {0}")]
    Request(#[from] reqwest::Error),
    #[error("JSON error: {0}")]
    Json(#[from] serde_json::Error),
    // ... other error variants as needed
}

// Define your request and response types
#[derive(Debug, Serialize)]
struct SearchUsersParams {
    query: String,
    max_results: i32,
    include_inactive: bool,
}

#[derive(Debug, Deserialize)]
struct UserSearchResponse {
    users: Vec<User>,
    total_count: i32,
    page_token: Option<String>,
}

// Generate the client with your custom error type
generate_client!(
    UserSearchClient,                // Name for the generated client
    "/api/v1/users/search",         // Endpoint path
    "POST",                         // HTTP method
    SearchUsersParams,              // Parameters type
    UserSearchResponse,             // Response type
    AgentError                      // Your custom error type
);

// Example usage in an agent system
struct Agent {
    openai: OpenAIClient,
    db: PgPool,
}

impl Agent {
    async fn execute_task(&self, task: &str) -> Result<serde_json::Value, AgentError> {
        // Find relevant endpoint using RAG
        let endpoint = find_relevant_endpoint(&self.db, task).await?;
         
        // Generate parameters using LLM
        let params = self.generate_parameters(task).await?;
         
        // Execute the API call using our generated client
        let client = UserSearchClient::new("https://api.example.com".to_string());
        let response = client.execute(params).await?;
         
        Ok(serde_json::to_value(response)?)
    }
}

§Parameters

  • client_name: The name of the generated client struct
  • path: The endpoint path template (e.g., “/users/{id}/posts”)
  • method: The HTTP method as a string (e.g., “GET”, “POST”)
  • params_type: The request parameters type (must implement Serialize)
  • response_type: The response type (must implement Deserialize)
  • error_type: Your custom error type that implements Fromreqwest::Error and From<serde_json::Error>

§Generated Client

The macro generates a client struct with:

  • Constructor for base URL configuration
  • Type-safe execute method that handles:
    • Path parameter substitution
    • Request body serialization
    • Response deserialization
    • Error conversion to your custom type

§Error Handling

The generated client returns Result<T, E> where E is your custom error type. Your error type must implement:

impl From<reqwest::Error> for YourErrorType { ... }
impl From<serde_json::Error> for YourErrorType { ... }

Common error cases that will be converted to your error type:

  • URL construction failures
  • Network errors from reqwest
  • Non-200 HTTP responses
  • JSON serialization/deserialization errors