use std::future::Future;
use crate::{
error::{LlmError, StoreError, ToolError},
types::{
LlmRequest, LlmResponse, LlmStream, SessionId, SessionState, ToolCall, ToolDescriptor,
ToolResult,
},
};
pub trait NativeLlmPort: Send + Sync {
fn complete(
&self,
req: LlmRequest,
) -> impl Future<Output = Result<LlmResponse, LlmError>> + Send;
fn complete_stream(
&self,
req: LlmRequest,
) -> impl Future<Output = Result<LlmStream, LlmError>> + Send;
}
pub trait NativeToolPort: Send + Sync {
fn list_tools(&self) -> impl Future<Output = Result<Vec<ToolDescriptor>, ToolError>> + Send;
fn call_tool(
&self,
call: ToolCall,
) -> impl Future<Output = Result<ToolResult, ToolError>> + Send;
}
pub trait NativeSessionStore: Send + Sync {
fn load(
&self,
id: &SessionId,
) -> impl Future<Output = Result<Option<SessionState>, StoreError>> + Send;
fn save(
&self,
id: &SessionId,
state: &SessionState,
) -> impl Future<Output = Result<(), StoreError>> + Send;
}
#[cfg(test)]
mod tests {
use super::*;
struct MyTool;
impl NativeToolPort for MyTool {
async fn list_tools(&self) -> Result<Vec<ToolDescriptor>, ToolError> {
Ok(vec![])
}
async fn call_tool(&self, call: ToolCall) -> Result<ToolResult, ToolError> {
Ok(ToolResult { name: call.name, output: serde_json::json!(null), is_error: false })
}
}
struct MyLlm;
impl NativeLlmPort for MyLlm {
async fn complete(&self, _req: LlmRequest) -> Result<LlmResponse, LlmError> {
Err(LlmError::Provider("not implemented".into()))
}
async fn complete_stream(&self, _req: LlmRequest) -> Result<LlmStream, LlmError> {
Err(LlmError::Provider("not implemented".into()))
}
}
struct MyStore;
impl NativeSessionStore for MyStore {
async fn load(&self, _id: &SessionId) -> Result<Option<SessionState>, StoreError> {
Ok(None)
}
async fn save(&self, _id: &SessionId, _state: &SessionState) -> Result<(), StoreError> {
Ok(())
}
}
#[tokio::test]
async fn native_tool_port_works() {
let tool = MyTool;
let tools = tool.list_tools().await.unwrap();
assert!(tools.is_empty());
let result = tool.call_tool(ToolCall::new("test", serde_json::json!({}))).await.unwrap();
assert_eq!(result.name, "test");
}
#[tokio::test]
async fn native_llm_port_works() {
let llm = MyLlm;
let req = LlmRequest {
model: "test".into(),
messages: vec![],
tools: vec![],
output_schema: None,
};
assert!(llm.complete(req).await.is_err());
}
#[tokio::test]
async fn native_session_store_works() {
let store = MyStore;
let loaded = store.load(&"test".to_string()).await.unwrap();
assert!(loaded.is_none());
}
}