#![allow(dead_code)]
#![allow(clippy::redundant_pattern_matching)]
#![allow(clippy::unwrap_or_default)]
use serde::{Deserialize, Serialize};
use siumai::prelude::*;
use std::collections::HashMap;
use std::sync::Arc;
use std::time::{Duration, Instant};
use tokio::sync::RwLock;
type HttpRequest = String;
type HttpResponse = String;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
println!("🌐 API Integration - REST API with AI capabilities\n");
let server = ApiServer::new().await?;
println!("🚀 AI API Server started on http://localhost:8080");
println!("📖 Available endpoints:");
println!(" POST /api/chat - Chat with AI");
println!(" POST /api/generate - Generate content");
println!(" POST /api/analyze - Analyze text");
println!(" GET /api/health - Health check");
println!(" GET /api/models - List available models");
println!(" GET /api/stats - Usage statistics");
println!("\n💡 Test with:");
println!(" curl -X POST http://localhost:8080/api/chat \\");
println!(" -H \"Content-Type: application/json\" \\");
println!(" -H \"Authorization: Bearer demo-key-123\" \\");
println!(" -d '{{\"message\": \"Hello, how are you?\"}}'");
println!("\n🛑 Press Ctrl+C to stop the server\n");
server.simulate_requests().await?;
Ok(())
}
struct ApiServer {
ai: Arc<dyn ChatCapability + Send + Sync>,
rate_limiter: Arc<RwLock<RateLimiter>>,
stats: Arc<RwLock<ApiStats>>,
auth_tokens: Vec<String>,
}
impl ApiServer {
async fn new() -> Result<Self, Box<dyn std::error::Error>> {
let api_key = std::env::var("GROQ_API_KEY")
.or_else(|_| std::env::var("OPENAI_API_KEY"))
.unwrap_or_else(|_| "demo-key".to_string());
let ai = Siumai::builder()
.openai()
.api_key(&api_key)
.model("gpt-4o-mini")
.temperature(0.7)
.max_tokens(1000)
.build()
.await?;
let rate_limiter = Arc::new(RwLock::new(RateLimiter::new()));
let stats = Arc::new(RwLock::new(ApiStats::new()));
let auth_tokens = vec![
"demo-key-123".to_string(),
"test-key-456".to_string(),
"api-key-789".to_string(),
];
Ok(Self {
ai: Arc::new(ai),
rate_limiter,
stats,
auth_tokens,
})
}
async fn handle_chat(&self, request: ChatRequest) -> Result<ChatResponse, ApiError> {
let start_time = Instant::now();
if request.message.trim().is_empty() {
return Err(ApiError::BadRequest("Message cannot be empty".to_string()));
}
let mut messages = Vec::new();
if let Some(system) = &request.system {
messages.push(ChatMessage::system(system).build());
}
messages.push(ChatMessage::user(&request.message).build());
let response = self
.ai
.chat(messages)
.await
.map_err(|e| ApiError::InternalError(format!("AI error: {e}")))?;
let response_time = start_time.elapsed();
{
let mut stats = self.stats.write().await;
stats.record_request("chat", response_time, true);
}
Ok(ChatResponse {
response: response.text().unwrap_or_default(),
model: "llama-3.1-8b-instant".to_string(),
response_time_ms: response_time.as_millis() as u64,
usage: response.usage.as_ref().map(|u| UsageInfo {
prompt_tokens: u.prompt_tokens,
completion_tokens: u.completion_tokens,
total_tokens: u.total_tokens,
}),
timestamp: chrono::Utc::now().to_rfc3339(),
})
}
async fn handle_generate(
&self,
request: GenerateRequest,
) -> Result<GenerateResponse, ApiError> {
let start_time = Instant::now();
if request.prompt.trim().is_empty() {
return Err(ApiError::BadRequest("Prompt cannot be empty".to_string()));
}
let system_prompt = match request.content_type.as_str() {
"blog" => {
"You are a professional blog writer. Create engaging, well-structured content."
}
"email" => {
"You are a professional email writer. Create clear, concise, and appropriate emails."
}
"marketing" => "You are a marketing copywriter. Create compelling, persuasive content.",
"technical" => {
"You are a technical writer. Create clear, accurate technical documentation."
}
_ => "You are a helpful content generator. Create high-quality content.",
};
let messages = vec![
ChatMessage::system(system_prompt).build(),
ChatMessage::user(&request.prompt).build(),
];
let response = self
.ai
.chat(messages)
.await
.map_err(|e| ApiError::InternalError(format!("AI error: {e}")))?;
let response_time = start_time.elapsed();
{
let mut stats = self.stats.write().await;
stats.record_request("generate", response_time, true);
}
Ok(GenerateResponse {
content: response.text().unwrap_or_default(),
content_type: request.content_type,
model: "llama-3.1-8b-instant".to_string(),
response_time_ms: response_time.as_millis() as u64,
timestamp: chrono::Utc::now().to_rfc3339(),
})
}
async fn handle_analyze(&self, request: AnalyzeRequest) -> Result<AnalyzeResponse, ApiError> {
let start_time = Instant::now();
if request.text.trim().is_empty() {
return Err(ApiError::BadRequest("Text cannot be empty".to_string()));
}
let system_prompt =
"You are a text analysis expert. Analyze the provided text and provide insights.";
let user_prompt = format!(
"Analyze this text for:\n\
1. Sentiment (positive/negative/neutral)\n\
2. Key topics and themes\n\
3. Writing style and tone\n\
4. Readability level\n\
5. Suggestions for improvement\n\n\
Text to analyze:\n{}",
request.text
);
let messages = vec![
ChatMessage::system(system_prompt).build(),
ChatMessage::user(&user_prompt).build(),
];
let response = self
.ai
.chat(messages)
.await
.map_err(|e| ApiError::InternalError(format!("AI error: {e}")))?;
let response_time = start_time.elapsed();
{
let mut stats = self.stats.write().await;
stats.record_request("analyze", response_time, true);
}
Ok(AnalyzeResponse {
analysis: response.text().unwrap_or_default(),
text_length: request.text.len(),
model: "llama-3.1-8b-instant".to_string(),
response_time_ms: response_time.as_millis() as u64,
timestamp: chrono::Utc::now().to_rfc3339(),
})
}
async fn get_health(&self) -> HealthResponse {
let ai_healthy = match self
.ai
.chat(vec![ChatMessage::user("Health check").build()])
.await
{
Ok(_) => true,
Err(_) => false,
};
let stats = self.stats.read().await;
HealthResponse {
status: if ai_healthy {
"healthy".to_string()
} else {
"unhealthy".to_string()
},
ai_provider: "groq".to_string(),
model: "llama-3.1-8b-instant".to_string(),
ai_responsive: ai_healthy,
total_requests: stats.total_requests,
uptime_seconds: stats.start_time.elapsed().as_secs(),
timestamp: chrono::Utc::now().to_rfc3339(),
}
}
async fn get_stats(&self) -> StatsResponse {
let stats = self.stats.read().await;
StatsResponse {
total_requests: stats.total_requests,
requests_by_endpoint: stats.requests_by_endpoint.clone(),
average_response_time_ms: stats.average_response_time.as_millis() as u64,
success_rate: stats.success_rate(),
uptime_seconds: stats.start_time.elapsed().as_secs(),
timestamp: chrono::Utc::now().to_rfc3339(),
}
}
fn authenticate(&self, token: &str) -> bool {
self.auth_tokens.contains(&token.to_string())
}
async fn check_rate_limit(&self, client_id: &str) -> bool {
let mut limiter = self.rate_limiter.write().await;
limiter.check_limit(client_id)
}
async fn simulate_requests(&self) -> Result<(), Box<dyn std::error::Error>> {
println!("🔄 Simulating API requests...\n");
println!("📨 Simulating chat request...");
let chat_request = ChatRequest {
message: "What is artificial intelligence?".to_string(),
system: Some("You are a helpful AI assistant.".to_string()),
};
match self.handle_chat(chat_request).await {
Ok(response) => {
println!(
"✅ Chat response: {}",
&response.response[..100.min(response.response.len())]
);
println!(" Response time: {}ms", response.response_time_ms);
}
Err(e) => println!("❌ Chat error: {e:?}"),
}
println!();
println!("📝 Simulating content generation...");
let generate_request = GenerateRequest {
prompt: "Write a short introduction to machine learning".to_string(),
content_type: "technical".to_string(),
};
match self.handle_generate(generate_request).await {
Ok(response) => {
println!(
"✅ Generated content: {}",
&response.content[..100.min(response.content.len())]
);
println!(" Response time: {}ms", response.response_time_ms);
}
Err(e) => println!("❌ Generate error: {e:?}"),
}
println!();
println!("🏥 Health check:");
let health = self.get_health().await;
println!(" Status: {}", health.status);
println!(" AI Responsive: {}", health.ai_responsive);
println!(" Total Requests: {}", health.total_requests);
println!();
println!("📊 Usage statistics:");
let stats = self.get_stats().await;
println!(" Total Requests: {}", stats.total_requests);
println!(
" Average Response Time: {}ms",
stats.average_response_time_ms
);
println!(" Success Rate: {:.1}%", stats.success_rate * 100.0);
Ok(())
}
}
#[derive(Debug, Deserialize)]
struct ChatRequest {
message: String,
system: Option<String>,
}
#[derive(Debug, Serialize)]
struct ChatResponse {
response: String,
model: String,
response_time_ms: u64,
usage: Option<UsageInfo>,
timestamp: String,
}
#[derive(Debug, Deserialize)]
struct GenerateRequest {
prompt: String,
content_type: String,
}
#[derive(Debug, Serialize)]
struct GenerateResponse {
content: String,
content_type: String,
model: String,
response_time_ms: u64,
timestamp: String,
}
#[derive(Debug, Deserialize)]
struct AnalyzeRequest {
text: String,
}
#[derive(Debug, Serialize)]
struct AnalyzeResponse {
analysis: String,
text_length: usize,
model: String,
response_time_ms: u64,
timestamp: String,
}
#[derive(Debug, Serialize)]
struct HealthResponse {
status: String,
ai_provider: String,
model: String,
ai_responsive: bool,
total_requests: u64,
uptime_seconds: u64,
timestamp: String,
}
#[derive(Debug, Serialize)]
struct StatsResponse {
total_requests: u64,
requests_by_endpoint: HashMap<String, u64>,
average_response_time_ms: u64,
success_rate: f64,
uptime_seconds: u64,
timestamp: String,
}
#[derive(Debug, Serialize)]
struct UsageInfo {
prompt_tokens: u32,
completion_tokens: u32,
total_tokens: u32,
}
#[derive(Debug)]
enum ApiError {
BadRequest(String),
Unauthorized,
RateLimited,
InternalError(String),
}
struct RateLimiter {
requests: HashMap<String, Vec<Instant>>,
max_requests: usize,
window_duration: Duration,
}
impl RateLimiter {
fn new() -> Self {
Self {
requests: HashMap::new(),
max_requests: 100, window_duration: Duration::from_secs(60), }
}
fn check_limit(&mut self, client_id: &str) -> bool {
let now = Instant::now();
let client_requests = self
.requests
.entry(client_id.to_string())
.or_insert_with(Vec::new);
client_requests.retain(|&time| now.duration_since(time) < self.window_duration);
if client_requests.len() < self.max_requests {
client_requests.push(now);
true
} else {
false
}
}
}
struct ApiStats {
start_time: Instant,
total_requests: u64,
successful_requests: u64,
requests_by_endpoint: HashMap<String, u64>,
total_response_time: Duration,
average_response_time: Duration,
}
impl ApiStats {
fn new() -> Self {
Self {
start_time: Instant::now(),
total_requests: 0,
successful_requests: 0,
requests_by_endpoint: HashMap::new(),
total_response_time: Duration::new(0, 0),
average_response_time: Duration::new(0, 0),
}
}
fn record_request(&mut self, endpoint: &str, response_time: Duration, success: bool) {
self.total_requests += 1;
if success {
self.successful_requests += 1;
}
*self
.requests_by_endpoint
.entry(endpoint.to_string())
.or_insert(0) += 1;
self.total_response_time += response_time;
self.average_response_time = self.total_response_time / self.total_requests as u32;
}
fn success_rate(&self) -> f64 {
if self.total_requests == 0 {
0.0
} else {
self.successful_requests as f64 / self.total_requests as f64
}
}
}
const fn _documentation() {}