abstract the claude module out of the signal-gateway and put interface and ipml in their own crates
This commit is contained in:
@@ -0,0 +1,32 @@
|
||||
use crate::{AssistantResponse, ChatMessage};
|
||||
use std::error::Error;
|
||||
use tokio_util::sync::CancellationToken;
|
||||
|
||||
/// Assistant trait which handles chat messages and commands.
|
||||
///
|
||||
/// Implementations handle the actual LLM API calls, message history management,
|
||||
/// and tool execution.
|
||||
#[async_trait::async_trait]
|
||||
pub trait Assistant: Send {
|
||||
/// Record a chat message in the assistant's history without expecting a response.
|
||||
async fn record_message(&mut self, message: ChatMessage);
|
||||
|
||||
/// Process a chat message and generate a response.
|
||||
///
|
||||
/// The cancellation token can be used to interrupt long-running operations.
|
||||
/// Returns `Ok(None)` if the operation was cancelled.
|
||||
async fn prompt(
|
||||
&mut self,
|
||||
message: ChatMessage,
|
||||
cancel: CancellationToken,
|
||||
) -> Result<Option<AssistantResponse>, Box<dyn Error + Send + Sync>>;
|
||||
|
||||
/// Compact the assistant's message history (e.g., by summarizing).
|
||||
///
|
||||
/// This is called when the user explicitly requests compaction.
|
||||
/// Automatic compaction is an internal implementation detail.
|
||||
async fn compact(&mut self);
|
||||
|
||||
/// Log the assistant's current state for debugging.
|
||||
fn debug_log(&mut self);
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
use chrono::{DateTime, Utc};
|
||||
|
||||
/// Indicates the "role" i.e. the manner in which a particular message was sent
|
||||
#[non_exhaustive]
|
||||
pub enum SentBy {
|
||||
/// User message directed at the system (commands, etc.)
|
||||
UserToSystem,
|
||||
/// User message directed at assistant (prompts)
|
||||
UserToAssistant,
|
||||
/// Response from assistant
|
||||
Assistant,
|
||||
/// System-generated message
|
||||
System,
|
||||
/// Alert from alertmanager
|
||||
AlertManager,
|
||||
}
|
||||
|
||||
impl SentBy {
|
||||
/// Returns the API role: "assistant" for Assistant, "user" for everything else.
|
||||
pub fn api_role(&self) -> &'static str {
|
||||
match self {
|
||||
Self::Assistant => "assistant",
|
||||
_ => "user",
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a prefix to prepend to message text for context.
|
||||
pub fn prefix(&self) -> Option<&'static str> {
|
||||
match self {
|
||||
Self::UserToSystem => Some("[user to system]"),
|
||||
Self::UserToAssistant => None, // No prefix needed for direct user messages
|
||||
Self::Assistant => None,
|
||||
Self::System => Some("[system]"),
|
||||
Self::AlertManager => Some("[alertmanager]"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A chat message
|
||||
pub struct ChatMessage {
|
||||
pub sent_by: SentBy,
|
||||
pub timestamp: DateTime<Utc>,
|
||||
pub text: Box<str>,
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
//! Assistant API used by signal-gateway.
|
||||
//!
|
||||
//! This crate provides abstract types for LLM assistant interactions,
|
||||
//! making it easy to swap in different LLM implementations.
|
||||
|
||||
mod assistant;
|
||||
mod chat_message;
|
||||
mod response;
|
||||
mod tools;
|
||||
|
||||
pub use assistant::Assistant;
|
||||
pub use chat_message::{ChatMessage, SentBy};
|
||||
pub use response::AssistantResponse;
|
||||
pub use tools::{Tool, ToolExecutor, ToolResult};
|
||||
@@ -0,0 +1,49 @@
|
||||
//! Response type for assistant interactions.
|
||||
|
||||
use std::path::PathBuf;
|
||||
|
||||
/// Response from an assistant interaction.
|
||||
#[derive(Clone, Debug, Default)]
|
||||
#[non_exhaustive]
|
||||
pub struct AssistantResponse {
|
||||
/// Text response from the assistant.
|
||||
pub text: String,
|
||||
/// Optional file attachments generated during the interaction.
|
||||
pub attachments: Vec<PathBuf>,
|
||||
}
|
||||
|
||||
impl AssistantResponse {
|
||||
/// Create a new response with just text.
|
||||
pub fn new(text: impl Into<String>) -> Self {
|
||||
Self {
|
||||
text: text.into(),
|
||||
attachments: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a response with text and attachments.
|
||||
pub fn with_attachments(text: impl Into<String>, attachments: Vec<PathBuf>) -> Self {
|
||||
Self {
|
||||
text: text.into(),
|
||||
attachments,
|
||||
}
|
||||
}
|
||||
|
||||
/// Add attachments to this response.
|
||||
pub fn add_attachments(mut self, attachments: Vec<PathBuf>) -> Self {
|
||||
self.attachments.extend(attachments);
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl From<String> for AssistantResponse {
|
||||
fn from(text: String) -> Self {
|
||||
Self::new(text)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&str> for AssistantResponse {
|
||||
fn from(text: &str) -> Self {
|
||||
Self::new(text)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
//! Tool definitions and executor trait for the LLM API.
|
||||
|
||||
use async_trait::async_trait;
|
||||
use serde::Serialize;
|
||||
use serde_json::Value;
|
||||
use std::path::PathBuf;
|
||||
|
||||
/// Result of executing a tool.
|
||||
#[derive(Clone, Debug, Default)]
|
||||
pub struct ToolResult {
|
||||
/// Text result to return to LLM.
|
||||
pub text: String,
|
||||
/// Optional file attachments generated by the tool.
|
||||
pub attachments: Vec<PathBuf>,
|
||||
}
|
||||
|
||||
impl ToolResult {
|
||||
/// Create a new tool result with just text.
|
||||
pub fn new(text: impl Into<String>) -> Self {
|
||||
Self {
|
||||
text: text.into(),
|
||||
attachments: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a tool result with text and an attachment.
|
||||
pub fn with_attachment(text: impl Into<String>, path: impl Into<PathBuf>) -> Self {
|
||||
Self {
|
||||
text: text.into(),
|
||||
attachments: vec![path.into()],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<String> for ToolResult {
|
||||
fn from(text: String) -> Self {
|
||||
Self::new(text)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&str> for ToolResult {
|
||||
fn from(text: &str) -> Self {
|
||||
Self::new(text)
|
||||
}
|
||||
}
|
||||
|
||||
/// Trait for executing tools. Implement this to provide tool capabilities.
|
||||
#[async_trait]
|
||||
pub trait ToolExecutor: Send + Sync {
|
||||
/// Get the list of available tools as JSON tool definitions.
|
||||
fn tools(&self) -> Vec<Tool>;
|
||||
|
||||
/// Check if this executor handles a tool with the given name.
|
||||
fn has_tool(&self, name: &str) -> bool {
|
||||
self.tools().iter().any(|t| t.name == name)
|
||||
}
|
||||
|
||||
/// Execute a tool by name with the given input arguments.
|
||||
/// Returns the result to be sent back to LLM, potentially with attachments.
|
||||
async fn execute(&self, name: &str, input: &Value) -> Result<ToolResult, String>;
|
||||
}
|
||||
|
||||
/// A tool definition for the LLM API.
|
||||
#[derive(Clone, Debug, Serialize)]
|
||||
pub struct Tool {
|
||||
/// The name of the tool.
|
||||
pub name: &'static str,
|
||||
/// A description of what the tool does.
|
||||
pub description: &'static str,
|
||||
/// JSON schema for the tool's input parameters.
|
||||
pub input_schema: Value,
|
||||
}
|
||||
Reference in New Issue
Block a user