Tori: AI agent workflow manager - initial implementation
Rust (Axum) + Vue 3 + SQLite. Features: - Project CRUD REST API with proper error handling - Per-project agent loop (mpsc + broadcast channels) - LLM-driven plan generation and replan on user feedback - SSH command execution with status streaming - WebSocket real-time updates to frontend - Four-zone UI: requirement, plan (left), execution (right), comment Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
56
src/llm.rs
Normal file
56
src/llm.rs
Normal file
@@ -0,0 +1,56 @@
|
||||
use serde::{Deserialize, Serialize};
|
||||
use crate::LlmConfig;
|
||||
|
||||
pub struct LlmClient {
|
||||
client: reqwest::Client,
|
||||
config: LlmConfig,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
struct ChatRequest {
|
||||
model: String,
|
||||
messages: Vec<ChatMessage>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct ChatMessage {
|
||||
pub role: String,
|
||||
pub content: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
struct ChatResponse {
|
||||
choices: Vec<Choice>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
struct Choice {
|
||||
message: ChatMessage,
|
||||
}
|
||||
|
||||
impl LlmClient {
|
||||
pub fn new(config: &LlmConfig) -> Self {
|
||||
Self {
|
||||
client: reqwest::Client::new(),
|
||||
config: config.clone(),
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn chat(&self, messages: Vec<ChatMessage>) -> anyhow::Result<String> {
|
||||
let resp = self.client
|
||||
.post(format!("{}/chat/completions", self.config.base_url))
|
||||
.header("Authorization", format!("Bearer {}", self.config.api_key))
|
||||
.json(&ChatRequest {
|
||||
model: self.config.model.clone(),
|
||||
messages,
|
||||
})
|
||||
.send()
|
||||
.await?
|
||||
.json::<ChatResponse>()
|
||||
.await?;
|
||||
|
||||
Ok(resp.choices.first()
|
||||
.map(|c| c.message.content.clone())
|
||||
.unwrap_or_default())
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user