aptu_core/ai/mod.rs
1// SPDX-License-Identifier: Apache-2.0
2
3//! AI integration module.
4//!
5//! Provides AI-assisted issue triage using `OpenRouter` API.
6
7pub mod models;
8pub mod openrouter;
9pub mod types;
10
11pub use models::{AiModel, ModelProvider};
12pub use openrouter::OpenRouterClient;
13pub use types::{CreateIssueResponse, TriageResponse};
14
15use crate::history::AiStats;
16
17/// `OpenRouter` API base URL.
18pub const OPENROUTER_API_URL: &str = "https://openrouter.ai/api/v1/chat/completions";
19
20/// Environment variable for `OpenRouter` API key.
21pub const OPENROUTER_API_KEY_ENV: &str = "OPENROUTER_API_KEY";
22
23/// Response from AI analysis containing both triage data and usage stats.
24#[derive(Debug, Clone)]
25pub struct AiResponse {
26 /// The triage analysis result.
27 pub triage: TriageResponse,
28 /// AI usage statistics.
29 pub stats: AiStats,
30}
31
32/// Checks if a model is in the free tier (no cost).
33/// Free models on `OpenRouter` always have the `:free` suffix.
34#[must_use]
35pub fn is_free_model(model: &str) -> bool {
36 model.ends_with(":free")
37}
38
39/// Creates a formatted GitHub issue using AI assistance.
40///
41/// Takes raw issue title and body, formats them professionally using `OpenRouter` API.
42/// Returns formatted title, body, and suggested labels.
43///
44/// # Arguments
45///
46/// * `title` - Raw issue title from user
47/// * `body` - Raw issue body/description from user
48/// * `repo` - Repository name for context (owner/repo format)
49///
50/// # Errors
51///
52/// Returns an error if AI formatting fails or API is unavailable.
53pub async fn create_issue(
54 title: &str,
55 body: &str,
56 repo: &str,
57) -> anyhow::Result<CreateIssueResponse> {
58 let config = crate::config::load_config()?;
59 let client = OpenRouterClient::new(&config.ai)?;
60 client.create_issue(title, body, repo).await
61}