poe_api_process 0.4.6

Poe API for rust
Documentation
# Poe API Process

[[English](https://github.com/jeromeleong/poe_api_process/blob/master/README_EN.md)|[繁體中文](https://github.com/jeromeleong/poe_api_process/blob/master/README.md)|[简体中文](https://github.com/jeromeleong/poe_api_process/blob/master/README_CN.md)]

This is a Rust implementation of a Poe API client library. It allows you to interact with the Poe API platform, send query requests, and receive responses.

## Features
- Stream bot responses
- Get list of available models (supports traditional API and v1/models API)
- Support for Tool Calls
- Support for file uploads and attachments
- Support for XML format tool calls (optional feature)
- Flexible URL configuration

## Installation

Add this dependency to your `Cargo.toml` file:

```toml
[dependencies]
poe_api_process = "0.4.5"
```

Or use the cargo command:

```bash
cargo add poe_api_process
```

## Usage

### Create a client and send requests

```rust
use poe_api_process::{PoeClient, ChatRequest, ChatMessage, ChatEventType};
use futures_util::StreamExt;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let client = PoeClient::new(
        "Claude-3.7-Sonnet",
        "your_access_key",
        "https://api.poe.com",
        "https://www.quora.com/poe_api/file_upload_3RD_PARTY_POST"
    );
    
    let request = ChatRequest {
        version: "1.2".to_string(),
        r#type: "query".to_string(),
        query: vec![ChatMessage {
            role: "user".to_string(),
            content: "Hello".to_string(),
            content_type: "text/markdown".to_string(),
            attachments: None,
        }],
        temperature: None,
        user_id: String::new(),
        conversation_id: String::new(),
        message_id: String::new(),
        tools: None,
        tool_calls: None,
        tool_results: None,
        logit_bias: None,
        stop_sequences: None,
    };
    
    let mut stream = client.stream_request(request).await?;
    
    while let Some(response) = stream.next().await {
        match response {
            Ok(event) => match event.event {
                ChatEventType::Text => {
                    if let Some(data) = event.data {
                        if let crate::types::ChatResponseData::Text { text } = data {
                            println!("Received text: {}", text);
                        }
                    }
                },
                ChatEventType::ReplaceResponse => {
                    if let Some(data) = event.data {
                        if let crate::types::ChatResponseData::Text { text } = data {
                            println!("Replace response: {}", text);
                        }
                    }
                },
                ChatEventType::Error => {
                    if let Some(data) = event.data {
                        if let crate::types::ChatResponseData::Error { text, allow_retry } = data {
                            eprintln!("Server error: {}", text);
                            if allow_retry {
                                println!("Request can be retried");
                            }
                        }
                    }
                },
                ChatEventType::Done => {
                    println!("Conversation complete");
                    break;
                ChatEventType::Json => {
                    println!("Received JSON event");
                },
                ChatEventType::File => {
                    if let Some(data) = event.data {
                        if let crate::types::ChatResponseData::File(file_data) = data {
                            println!("Received file: {} ({})", file_data.name, file_data.url);
                        }
                    }
                },
                },
            },
            Err(e) => eprintln!("Error: {}", e),
        }
    }
    
    Ok(())
}
```

### Tool Calls

PS: Native BOT interface tool calls only support a limited number of models and have strict formatting requirements. It is recommended to use the XML Feature.

- **Tool Call**: Allows AI models to request execution of specific tools or functions. For example, AI might need to query weather, search the web, or perform calculations.
- **Tool Result**: The result returned after tool execution, which will be sent back to the AI model to continue the conversation.

When creating a request, you can specify available tools:

```rust
use serde_json::json;
use poe_api_process::{ChatTool, FunctionDefinition, FunctionParameters};

let request = ChatRequest {
    // Other fields...
    tools: Some(vec![ChatTool {
        r#type: "function".to_string(),
        function: FunctionDefinition {
            name: "get_weather".to_string(),
            description: "Get weather information for a specified city".to_string(),
            parameters: FunctionParameters {
                r#type: "object".to_string(),
                properties: json!({
                    "city": {
                        "type": "string",
                        "description": "City name"
                    }
                }),
                required: vec!["city".to_string()],
            },
        },
    }]),
    // Other fields...
};
```

When the AI model returns a tool call, you can process it and provide results:

```rust
use poe_api_process::{ChatToolResult, ChatResponseData};

while let Some(response) = stream.next().await {
    match response {
        Ok(event) => match event.event {
            ChatEventType::Json => {
                if let Some(ChatResponseData::ToolCalls(tool_calls)) = event.data {
                    println!("Received tool call request: {:?}", tool_calls);
                    
                    // Process tool call
                    let tool_results = vec![ChatToolResult {
                        role: "tool".to_string(),
                        tool_call_id: tool_calls[0].id.clone(),
                        name: tool_calls[0].function.name.clone(),
                        content: r#"{"temperature": 25, "condition": "sunny"}"#.to_string(),
                    }];
                    
                    // Send tool results back to AI
                    let mut result_stream = client.send_tool_results(
                        request.clone(),
                        tool_calls,
                        tool_results
                    ).await?;
                    
                    // Process subsequent responses...
                    while let Some(result_response) = result_stream.next().await {
                        // Handle responses...
                    }
                }
            },
            // Handle other events...
        },
        Err(e) => eprintln!("Error: {}", e),
    }
}
```

#### XML Tool Calls

Enable the xml feature to use tool calls in XML format, automatically handling XML content without changing existing code:

```toml
[dependencies]
poe_api_process = { version = "0.4.5", features = ["xml"] }
```

### File Upload and Attachments

This library supports uploading local or remote files and attaching them to requests:

```rust
use poe_api_process::{Attachment, FileUploadRequest};

// Upload a single local file
let upload_result = client.upload_local_file("path/to/document.pdf", mime_type: None).await?;
println!("File uploaded, URL: {}", upload_result.attachment_url);

// Upload a remote file (via URL)
let remote_upload = client.upload_remote_file("https://example.com/document.pdf").await?;

// Batch upload multiple files
let batch_results = client.upload_files_batch(vec![
    FileUploadRequest::LocalFile { file: "path/to/first.pdf".to_string() , mime_type: None},
    FileUploadRequest::RemoteFile { download_url: "https://example.com/second.pdf".to_string() },
]).await?;

// Attach files to a request
let request = ChatRequest {
    // Other fields...
    query: vec![ChatMessage {
        role: "user".to_string(),
        content: "Please analyze this document".to_string(),
        content_type: "text/markdown".to_string(),
        attachments: Some(vec![Attachment {
            url: upload_result.attachment_url,
            content_type: upload_result.mime_type,
            name: Some("document.pdf".to_string()),
            inline_ref: None,
            parsed_content: None,
        }]),
    }],
    // Other fields...
};
```

### Get Available Model List

#### Traditional Model List API (no token required)

```rust
use poe_api_process::get_model_list;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    // Get the model list in English
    let models = get_model_list(Some("en")).await?;
    
    println!("Available models:");
    for (index, model) in models.data.iter().enumerate() {
        println!("{}. {}", index + 1, model.id);
    }
    
    Ok(())
}
```

#### v1/models API (token required)

```rust
use poe_api_process::PoeClient;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let client = PoeClient::new(
        "Claude-3.7-Sonnet",
        "your_access_key",
        "https://api.poe.com",
        "https://www.quora.com/poe_api/file_upload_3RD_PARTY_POST"
    );
    
    // Get v1/models API model list
    let v1_models = client.get_v1_model_list().await?;
    
    println!("v1 API model list:");
    for (index, model) in v1_models.data.iter().enumerate() {
        println!("{}. {} (created: {})", index + 1, model.id, model.created);
    }
    
    Ok(())
}
```

## Debug Features

Enable trace functionality for detailed log output:

```toml
[dependencies]
poe_api_process = { version = "0.4.5", features = ["trace"] }
```

## v0.3.0 Version Changes

### Breaking Changes
- **PoeClient::new()** now requires four parameters: `bot_name`, `access_key`, `poe_base_url`, `poe_file_upload_url`
- Added **get_v1_model_list()** method as a PoeClient instance method
- Automatic URL trailing slash normalization

### Migration Guide
```rust
// v0.2.x version
let client = PoeClient::new("Claude-3.7-Sonnet", "your_access_key");

// v0.3.0+ version
let client = PoeClient::new(
    "Claude-3.7-Sonnet",
    "your_access_key",
    "https://api.poe.com",
    "https://www.quora.com/poe_api/file_upload_3RD_PARTY_POST"
);
```

## Notes

- Ensure you have a usable [Poe API access key]https://poe.com/api_key.
- When using `stream_request`, provide a valid bot name and access key.
- `get_model_list` doesn't require an access key and can be used directly.
- `get_v1_model_list` requires an access key and is called as a PoeClient method.
- File upload functionality is subject to Poe platform's file size and type limitations.
- Bot streaming requests send `Authorization: Bearer <API_KEY>` and automatically include `Accept: text/event-stream`, `Cache-Control: no-store`, and `Content-Type: application/json`.
- The file upload endpoint uses the raw API key without the `Bearer` prefix.