Skip to main content

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 multiple AI providers (Gemini, `OpenRouter`, Groq, Cerebras, Zenmux, Z.AI).
6
7pub mod circuit_breaker;
8pub mod client;
9pub mod context;
10pub mod dep_enrichment;
11pub mod models;
12pub mod prompts;
13pub mod provider;
14pub mod registry;
15pub mod review_context;
16pub mod types;
17
18pub use circuit_breaker::CircuitBreaker;
19pub use client::{AiClient, AuthMethod, is_free_model, resolve_anthropic_credential};
20pub use dep_enrichment::enrich_dep_releases;
21pub use models::{AiModel, ModelProvider};
22pub use provider::AiProvider;
23pub use registry::{PROVIDER_ANTHROPIC, ProviderConfig, all_providers, get_provider};
24pub use types::{CreateIssueResponse, CreditsStatus, DepReleaseNote, TriageResponse};
25
26use crate::history::AiStats;
27
28/// Response from AI analysis containing both triage data and usage stats.
29#[derive(Debug, Clone)]
30pub struct AiResponse {
31    /// The triage analysis result.
32    pub triage: TriageResponse,
33    /// AI usage statistics.
34    pub stats: AiStats,
35}
36
37/// Sets up the primary AI client with credential resolution.
38///
39/// For the Anthropic provider, attempts to use Claude OAuth credentials in this order:
40/// 1. Existing token in OS keyring
41/// 2. ~/.claude/credentials.json file
42/// 3. Environment variable (fallback)
43///
44/// For other providers, uses the standard environment variable path.
45///
46/// # Errors
47///
48/// Returns an error if client creation fails.
49pub fn setup_primary_client(config: &crate::config::AppConfig) -> anyhow::Result<AiClient> {
50    // For Anthropic, delegate to centralized credential resolution
51    if config.ai.provider == PROVIDER_ANTHROPIC
52        && let Some(client) = resolve_anthropic_credential(&config.ai)
53    {
54        return Ok(client);
55    }
56
57    // Fall back to environment variable for non-Anthropic providers
58    AiClient::new(&config.ai.provider, &config.ai)
59}
60
61/// Creates a formatted GitHub issue using AI assistance.
62///
63/// Takes raw issue title and body, formats them professionally using the configured AI provider.
64/// Returns formatted title, body, and suggested labels.
65///
66/// # Arguments
67///
68/// * `title` - Raw issue title from user
69/// * `body` - Raw issue body/description from user
70/// * `repo` - Repository name for context (owner/repo format)
71///
72/// # Errors
73///
74/// Returns an error if AI formatting fails or API is unavailable.
75#[cfg(not(target_arch = "wasm32"))]
76pub async fn create_issue(
77    title: &str,
78    body: &str,
79    repo: &str,
80) -> anyhow::Result<(CreateIssueResponse, AiStats)> {
81    let config = crate::config::load_config()?;
82
83    // Create generic client for the configured provider
84    let client = setup_primary_client(&config)?;
85    client.create_issue(title, body, repo).await
86}