aiclient-api 0.1.0

A unified AI gateway daemon exposing OpenAI-compatible and Anthropic-compatible API endpoints, backed by GitHub Copilot and Kiro (AWS CodeWhisperer)
Documentation
use anyhow::{bail, Context, Result};
use reqwest::header::HeaderMap;

use crate::config::types::AccountType;

pub struct CopilotClient {
    client: reqwest::Client,
    base_url: String,
}

impl CopilotClient {
    pub fn new(account_type: &AccountType) -> Self {
        let base_url = match account_type {
            AccountType::Individual => "https://api.githubcopilot.com",
            AccountType::Business => "https://api.business.githubcopilot.com",
            AccountType::Enterprise => "https://api.enterprise.githubcopilot.com",
        }
        .to_string();

        Self {
            client: reqwest::Client::new(),
            base_url,
        }
    }

    pub fn base_url(&self) -> &str {
        &self.base_url
    }

    pub fn http_client(&self) -> &reqwest::Client {
        &self.client
    }

    pub async fn chat_completions(
        &self,
        headers: HeaderMap,
        body: serde_json::Value,
        _stream: bool,
    ) -> Result<reqwest::Response> {
        let url = format!("{}/chat/completions", self.base_url);
        let resp = self
            .client
            .post(&url)
            .headers(headers)
            .json(&body)
            .send()
            .await
            .context("Failed to send chat completions request")?;

        if !resp.status().is_success() {
            let status = resp.status();
            let err_body = resp.text().await.unwrap_or_default();
            bail!("Chat completions request failed: HTTP {} - {}", status, err_body);
        }

        Ok(resp)
    }

    pub async fn messages(
        &self,
        headers: HeaderMap,
        body: serde_json::Value,
        _stream: bool,
    ) -> Result<reqwest::Response> {
        let url = format!("{}/v1/messages", self.base_url);
        let resp = self
            .client
            .post(&url)
            .headers(headers)
            .json(&body)
            .send()
            .await
            .context("Failed to send messages request")?;

        if !resp.status().is_success() {
            let status = resp.status();
            let err_body = resp.text().await.unwrap_or_default();
            bail!("Messages request failed: HTTP {} - {}", status, err_body);
        }

        Ok(resp)
    }
}