puter1 / src /main.rs
anatoli72's picture
Create src/main.rs
439182e verified
use axum::{
extract::State,
http::StatusCode,
response::Html,
routing::{get, post},
Json, Router,
};
use reqwest::Client;
use serde::{Deserialize, Serialize};
use std::env;
use tower_http::services::ServeDir;
use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt};
#[derive(Debug, Deserialize)]
struct ChatRequest {
model: String,
messages: Vec<ChatMessage>,
}
#[derive(Debug, Serialize, Deserialize, Clone)]
struct ChatMessage {
role: String,
content: String,
}
#[derive(Debug, Serialize)]
struct ChatResponse {
reply: String,
}
#[derive(Clone)]
struct AppState {
client: Client,
api_key: String,
base_url: String,
}
#[tokio::main]
async fn main() {
tracing_subscriber::registry()
.with(tracing_subscriber::EnvFilter::new("info"))
.with(tracing_subscriber::fmt::layer())
.init();
let api_key = env::var("PUTER_API_KEY").expect("❌ PUTER_API_KEY not set");
let base_url = env::var("PUTER_BASE_URL")
.unwrap_or_else(|_| "https://api.puter.com/puterai/openai/v1/".to_string());
let port = env::var("PORT").unwrap_or_else(|_| "7860".to_string());
let state = AppState {
client: Client::new(),
api_key,
base_url,
};
// Раздаём статические файлы из папки static/
let app = Router::new()
.nest_service("/", ServeDir::new("static"))
.route("/api/chat", post(handle_chat))
.with_state(state);
let addr = format!("0.0.0.0:{}", port);
tracing::info!("🚀 Server running on http://{}", addr);
let listener = tokio::net::TcpListener::bind(&addr).await.unwrap();
axum::serve(listener, app).await.unwrap();
}
async fn handle_chat(
State(state): State<AppState>,
Json(req): Json<ChatRequest>,
) -> Result<Json<ChatResponse>, (StatusCode, String)> {
let mut messages = vec![ChatMessage {
role: "system".into(),
content: "Ты полезный ассистент. Отвечай кратко и по делу.".into(),
}];
messages.extend(req.messages);
let payload = serde_json::json!({
"model": req.model,
"messages": messages,
"temperature": 0.7,
"max_tokens": 2048
});
let res = state
.client
.post(&format!("{}chat/completions", state.base_url))
.header("Authorization", format!("Bearer {}", state.api_key))
.header("Content-Type", "application/json")
.json(&payload)
.send()
.await
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, format!("Network error: {}", e)))?;
if !res.status().is_success() {
let err_text = res.text().await.unwrap_or_default();
tracing::error!("Puter API error: {}", err_text);
return Err((StatusCode::BAD_GATEWAY, format!("API error: {}", err_text)));
}
let json: serde_json::Value = res.json().await.map_err(|e| {
(StatusCode::INTERNAL_SERVER_ERROR, format!("JSON parse error: {}", e))
})?;
let reply = json["choices"][0]["message"]["content"]
.as_str()
.unwrap_or("❌ Пустой ответ от API")
.to_string();
Ok(Json(ChatResponse { reply }))
}