openai-interface 0.7.0

A low-level Rust interface for the OpenAI API
Documentation
//! This module provides functionality for listing files from OpenAI-compatible APIs.
//!
//! # Examples
//!
//! ```rust,no_run
//!
//! use openai_interface::files::list::request::ListFilesRequest;
//! use openai_interface::rest::{default_client, get::{Get, GetNoStream}};
//!
//! const QWEN_BASE_URL: &'static str = "https://dashscope.aliyuncs.com/compatible-mode/v1/";
//!
//! #[tokio::main]
//! async fn main() -> Result<(), anyhow::Error> {
//!    let request = ListFilesRequest {
//!        purpose: Some("batch".to_string()),
//!        limit: Some(1),
//!        // after: Some("file_123".to_string()),
//!        ..Default::default()
//!    };
//!    let response = request
//!        .get_response(&default_client(), QWEN_BASE_URL, "YOUR_API_KEY")
//!        .await?;
//!
//!    println!("Response: {:?}", response);
//!
//!    Ok(())
//! }
//! ```

pub mod request;
pub mod response;

#[cfg(test)]
mod tests {
    use super::request::*;
    use crate::rest::{
        default_client,
        get::{Get, GetNoStream},
    };

    const QWEN_BASE_URL: &str = "https://dashscope.aliyuncs.com/compatible-mode/v1/";

    fn qwen_api_key() -> Option<String> {
        std::env::var("QWEN_API_KEY")
            .ok()
            .map(|key| key.trim().to_string())
            .filter(|key| !key.is_empty())
    }

    #[test]
    fn test_build_url_no_params() {
        let request = ListFilesRequest::default();
        let base_url = "https://api.openai.com/v1/";
        let url = request.build_url(base_url).expect("Failed to build url");
        assert_eq!(url, "https://api.openai.com/v1/files");
    }

    #[test]
    fn test_build_url_with_purpose() {
        let request = ListFilesRequest {
            purpose: Some("fine-tune".to_string()),
            ..Default::default()
        };
        let base_url = "https://api.openai.com/v1/";
        let url = request.build_url(base_url).expect("Failed to build url");
        assert_eq!(url, "https://api.openai.com/v1/files?purpose=fine-tune");
    }

    #[test]
    fn test_build_url_with_limit() {
        let request = ListFilesRequest {
            limit: Some(100),
            ..Default::default()
        };
        let base_url = "https://api.openai.com/v1/";
        let url = request.build_url(base_url).expect("Failed to build url");
        assert_eq!(url, "https://api.openai.com/v1/files?limit=100");
    }

    #[test]
    fn test_build_url_with_after() {
        let request = ListFilesRequest {
            after: Some("file_123".to_string()),
            ..Default::default()
        };
        let base_url = "https://api.openai.com/v1/";
        let url = request.build_url(base_url).expect("Failed to build url");
        assert_eq!(url, "https://api.openai.com/v1/files?after=file_123");
    }

    #[test]
    fn test_build_url_with_all_params() {
        let request = ListFilesRequest {
            purpose: Some("assistants".to_string()),
            limit: Some(50),
            after: Some("file_abc".to_string()),
            order: Some(ListOrder::Asc),
        };
        let base_url = "https://api.openai.com/v1/";
        let url = request.build_url(base_url).expect("Failed to build url");
        // Note: URL parameters are ordered alphabetically by the url crate
        assert!(url.contains("after=file_abc"));
        assert!(url.contains("limit=50"));
        assert!(url.contains("order=asc"));
        assert!(url.contains("purpose=assistants"));
        assert!(url.starts_with("https://api.openai.com/v1/files?"));
    }

    #[test]
    fn test_build_url_encoding() {
        let request = ListFilesRequest {
            purpose: Some("fine tune".to_string()), // Space should be encoded
            ..Default::default()
        };
        let base_url = "https://api.openai.com/v1/";
        let url = request.build_url(base_url).expect("Failed to build url");
        assert_eq!(url, "https://api.openai.com/v1/files?purpose=fine+tune");
    }

    #[tokio::test]
    async fn test_query_qwen_no_params() -> Result<(), anyhow::Error> {
        let Some(api_key) = qwen_api_key() else {
            println!("Skipping: set QWEN_API_KEY to run this test");
            return Ok(());
        };

        let request = ListFilesRequest::default();
        let response = request
            .get_response(&default_client(), QWEN_BASE_URL, &api_key)
            .await?;

        println!("Response: {:?}", response);

        Ok(())
    }

    #[tokio::test]
    async fn test_query_qwen_with_params() -> Result<(), anyhow::Error> {
        let Some(api_key) = qwen_api_key() else {
            println!("Skipping: set QWEN_API_KEY to run this test");
            return Ok(());
        };

        let request = ListFilesRequest {
            purpose: Some("batch".to_string()),
            limit: Some(1),
            // after: Some("file_123".to_string()),
            ..Default::default()
        };
        let response = request
            .get_response(&default_client(), QWEN_BASE_URL, &api_key)
            .await?;

        println!("Response: {:?}", response);

        Ok(())
    }
}