log-genius 0.1.0

An AI-powered CLI to instantly analyze and explain complex logs.
Documentation
use crate::error::{AppError, Result};
use serde::{Deserialize, Serialize};

const API_URL: &str = "https://api.openai.com/v1/chat/completions";

#[derive(Debug, Serialize)]
struct ChatRequest<'a> {
    model: &'a str,
    messages: Vec<ChatMessage<'a>>
}

#[derive(Debug, Serialize)]
struct ChatMessage<'a> {
    role: &'a str,
    content: &'a str,
}

#[derive(Debug, Deserialize)]
struct ChatResponse {
    choices: Vec<Choice>,
}

#[derive(Debug, Deserialize)]
struct Choice {
    message: Message,
}

#[derive(Debug, Deserialize)]
struct Message {
    content: String,
}

fn build_prompt(log_data: &str) -> String {
    format!(
        r#"System: You are 'Log Genius', a world-class software developer and DevOps expert. Your mission is to analyze complex log data provided by the user and offer a clear, actionable solution. Your response MUST be in English and strictly follow the format below, using Markdown.

---
[Summary] 💡
{{Provide a 1-2 sentence executive summary of the problem here.}}

[Potential Causes] 🧐
{{List the 1-3 most likely causes in a numbered list.}}
1. {{Cause 1}}
2. {{Cause 2}}

[Suggested Solutions] 🛠️
{{Provide specific, actionable solutions for each cause. If code changes are needed, show them clearly as an indented code block, specifying the file to be modified.}}
1. {{Solution for Cause 1}}
   # e.g., in docker-compose.yml
   services:
     web:
       depends_on:
         db:
           condition: service_healthy

2. {{Solution for Cause 2}}
---

If the provided log does not seem to contain an error and looks like a normal operational log, state in the [Summary] section: "This appears to be a standard operational log with no specific errors found." and omit the other sections.

User:
{}
"#,
        log_data
    )
}

pub async fn analyze_log(api_key: &str, log_data: &str) -> Result<String> {
    let client = reqwest::Client::new();

    let prompt_content = build_prompt(log_data);

    let request_body = ChatRequest {
        model: "gpt-4o",
        messages: vec![ChatMessage {
            role: "user",
            content: &prompt_content,
        }],
    };

    let response = client
        .post(API_URL)
        .bearer_auth(api_key)
        .json(&request_body)
        .send()
        .await?
        .json::<ChatResponse>()
        .await?;

    if let Some(choice) = response.choices.into_iter().next() {
        Ok(choice.message.content)
    } else {
        Err(AppError::NoResponse)
    }
}