diff --git a/Cargo.lock b/Cargo.lock index b8d25cf7a..dd03ef46e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -12,11 +12,11 @@ dependencies = [ "glob", "jsonschema", "libc", + "llm", "serde", "serde_json", "thiserror 2.0.18", "tokio", - "unified-llm", "uuid", ] @@ -159,6 +159,7 @@ dependencies = [ "dotenvy", "futures", "http-body-util", + "llm", "nom", "rand", "serde", @@ -168,7 +169,6 @@ dependencies = [ "tokio", "tokio-stream", "tower", - "unified-llm", "uuid", ] @@ -1188,6 +1188,31 @@ version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6373607a59f0be73a39b6fe456b8192fcc3585f602af20751600e974dd455e77" +[[package]] +name = "llm" +version = "0.1.0" +dependencies = [ + "anyhow", + "assert_cmd", + "async-trait", + "base64", + "bytes", + "clap", + "dotenvy", + "futures", + "http", + "predicates", + "rand", + "reqwest 0.12.28", + "serde", + "serde_json", + "thiserror 2.0.18", + "tokio", + "tokio-stream", + "tokio-util", + "uuid", +] + [[package]] name = "lock_api" version = "0.4.14" @@ -2298,31 +2323,6 @@ version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" -[[package]] -name = "unified-llm" -version = "0.1.0" -dependencies = [ - "anyhow", - "assert_cmd", - "async-trait", - "base64", - "bytes", - "clap", - "dotenvy", - "futures", - "http", - "predicates", - "rand", - "reqwest 0.12.28", - "serde", - "serde_json", - "thiserror 2.0.18", - "tokio", - "tokio-stream", - "tokio-util", - "uuid", -] - [[package]] name = "untrusted" version = "0.9.0" diff --git a/crates/agent/Cargo.toml b/crates/agent/Cargo.toml index 1bbfd422d..a2e4858cd 100644 --- a/crates/agent/Cargo.toml +++ b/crates/agent/Cargo.toml @@ -10,7 +10,7 @@ keywords = ["llm", "ai", "agent", "coding"] categories = ["api-bindings"] [dependencies] -unified-llm = { path = "../unified-llm" } +llm = { path = "../llm" } thiserror.workspace = true serde.workspace = true serde_json.workspace = true diff --git a/crates/agent/src/error.rs b/crates/agent/src/error.rs index 7af71778f..a3fb690bc 100644 --- a/crates/agent/src/error.rs +++ b/crates/agent/src/error.rs @@ -1,4 +1,4 @@ -use unified_llm::error::SdkError; +use llm::error::SdkError; #[derive(Debug, thiserror::Error)] pub enum AgentError { diff --git a/crates/agent/src/history.rs b/crates/agent/src/history.rs index e15a0f37b..215b6c6d7 100644 --- a/crates/agent/src/history.rs +++ b/crates/agent/src/history.rs @@ -1,5 +1,5 @@ use crate::types::Turn; -use unified_llm::types::{ContentPart, Message, Role}; +use llm::types::{ContentPart, Message, Role}; #[derive(Debug, Clone, Default)] pub struct History { @@ -29,7 +29,7 @@ impl History { let mut parts: Vec = Vec::new(); if let Some(reasoning_text) = reasoning { parts.push(ContentPart::Thinking( - unified_llm::types::ThinkingData { + llm::types::ThinkingData { text: reasoning_text.clone(), signature: None, redacted: false, @@ -79,7 +79,7 @@ impl History { mod tests { use super::*; use std::time::SystemTime; - use unified_llm::types::{ToolCall, ToolResult, Usage}; + use llm::types::{ToolCall, ToolResult, Usage}; #[test] fn empty_history_produces_empty_messages() { diff --git a/crates/agent/src/loop_detection.rs b/crates/agent/src/loop_detection.rs index 70c19c632..c48e14bc9 100644 --- a/crates/agent/src/loop_detection.rs +++ b/crates/agent/src/loop_detection.rs @@ -94,7 +94,7 @@ fn is_repeating_pattern(signatures: &[u64], pattern_len: usize) -> bool { mod tests { use super::*; use std::time::SystemTime; - use unified_llm::types::{ToolCall, Usage}; + use llm::types::{ToolCall, Usage}; fn assistant_with_tool(name: &str, args: serde_json::Value) -> Turn { Turn::Assistant { diff --git a/crates/agent/src/profiles/openai.rs b/crates/agent/src/profiles/openai.rs index 95a31a153..c5c53efc2 100644 --- a/crates/agent/src/profiles/openai.rs +++ b/crates/agent/src/profiles/openai.rs @@ -3,7 +3,7 @@ use crate::profiles::assemble_system_prompt; use crate::profiles::BaseProfile; use crate::provider_profile::{ProfileCapabilities, ProviderProfile}; use crate::tool_registry::{RegisteredTool, ToolRegistry}; -use unified_llm::types::ToolDefinition; +use llm::types::ToolDefinition; use crate::tools::{ make_glob_tool, make_grep_tool, make_read_file_tool, make_shell_tool, make_write_file_tool, }; diff --git a/crates/agent/src/provider_profile.rs b/crates/agent/src/provider_profile.rs index 8bc27ff60..9c2e8b42a 100644 --- a/crates/agent/src/provider_profile.rs +++ b/crates/agent/src/provider_profile.rs @@ -6,7 +6,7 @@ use crate::subagent::{ }; use crate::tool_registry::ToolRegistry; use std::sync::Arc; -use unified_llm::types::ToolDefinition; +use llm::types::ToolDefinition; /// Static capabilities of a provider profile. pub struct ProfileCapabilities { diff --git a/crates/agent/src/session.rs b/crates/agent/src/session.rs index 7c755e421..9083bf1de 100644 --- a/crates/agent/src/session.rs +++ b/crates/agent/src/session.rs @@ -14,9 +14,9 @@ use std::collections::VecDeque; use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::{Arc, Mutex}; use std::time::SystemTime; -use unified_llm::client::Client; -use unified_llm::error::{ProviderErrorKind, SdkError}; -use unified_llm::types::{Message, Request, ToolChoice, ToolResult}; +use llm::client::Client; +use llm::error::{ProviderErrorKind, SdkError}; +use llm::types::{Message, Request, ToolChoice, ToolResult}; pub struct Session { id: String, @@ -398,7 +398,7 @@ impl Session { async fn execute_tool_calls( &mut self, - tool_calls: &[unified_llm::types::ToolCall], + tool_calls: &[llm::types::ToolCall], ) -> Vec { if self.provider_profile.supports_parallel_tool_calls() && tool_calls.len() > 1 { self.execute_tool_calls_parallel(tool_calls).await @@ -409,7 +409,7 @@ impl Session { async fn execute_tool_calls_sequential( &self, - tool_calls: &[unified_llm::types::ToolCall], + tool_calls: &[llm::types::ToolCall], ) -> Vec { let mut results = Vec::new(); for tc in tool_calls { @@ -450,7 +450,7 @@ impl Session { async fn execute_tool_calls_parallel( &self, - tool_calls: &[unified_llm::types::ToolCall], + tool_calls: &[llm::types::ToolCall], ) -> Vec { let emitter = self.event_emitter.clone(); let env = self.execution_env.clone(); @@ -669,9 +669,9 @@ mod tests { use super::*; use crate::test_support::*; use crate::tool_registry::{RegisteredTool, ToolRegistry}; - use unified_llm::error::ProviderErrorDetail; - use unified_llm::provider::ProviderAdapter; - use unified_llm::types::ToolDefinition; + use llm::error::ProviderErrorDetail; + use llm::provider::ProviderAdapter; + use llm::types::ToolDefinition; // --- Tests --- diff --git a/crates/agent/src/subagent.rs b/crates/agent/src/subagent.rs index 5015f5ca0..934e1d06c 100644 --- a/crates/agent/src/subagent.rs +++ b/crates/agent/src/subagent.rs @@ -6,7 +6,7 @@ use crate::types::Turn; use std::collections::{HashMap, VecDeque}; use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::{Arc, Mutex}; -use unified_llm::types::ToolDefinition; +use llm::types::ToolDefinition; pub type SessionFactory = Arc Session + Send + Sync>; diff --git a/crates/agent/src/test_support.rs b/crates/agent/src/test_support.rs index b9100cd45..8d7d58d98 100644 --- a/crates/agent/src/test_support.rs +++ b/crates/agent/src/test_support.rs @@ -8,10 +8,10 @@ use async_trait::async_trait; use std::collections::HashMap; use std::sync::atomic::{AtomicUsize, Ordering}; use std::sync::{Arc, Mutex}; -use unified_llm::client::Client; -use unified_llm::error::SdkError; -use unified_llm::provider::{ProviderAdapter, StreamEventStream}; -use unified_llm::types::{FinishReason, Message, Request, Response, Usage}; +use llm::client::Client; +use llm::error::SdkError; +use llm::provider::{ProviderAdapter, StreamEventStream}; +use llm::types::{FinishReason, Message, Request, Response, Usage}; // --- MockExecutionEnvironment --- @@ -474,7 +474,7 @@ pub(crate) fn tool_call_response( tool_call_id: &str, args: serde_json::Value, ) -> Response { - use unified_llm::types::{ContentPart, Role, ToolCall}; + use llm::types::{ContentPart, Role, ToolCall}; Response { id: format!("resp_{tool_call_id}"), model: "mock-model".into(), @@ -502,7 +502,7 @@ pub(crate) fn tool_call_response( } pub(crate) fn make_echo_tool() -> crate::tool_registry::RegisteredTool { - use unified_llm::types::ToolDefinition; + use llm::types::ToolDefinition; crate::tool_registry::RegisteredTool { definition: ToolDefinition { name: "echo".into(), @@ -522,7 +522,7 @@ pub(crate) fn make_echo_tool() -> crate::tool_registry::RegisteredTool { } pub(crate) fn make_error_tool() -> crate::tool_registry::RegisteredTool { - use unified_llm::types::ToolDefinition; + use llm::types::ToolDefinition; crate::tool_registry::RegisteredTool { definition: ToolDefinition { name: "fail_tool".into(), @@ -597,7 +597,7 @@ impl ProviderAdapter for CapturingLlmProvider { pub(crate) fn multi_tool_call_response( calls: Vec<(&str, &str, serde_json::Value)>, ) -> Response { - use unified_llm::types::{ContentPart, Role, ToolCall}; + use llm::types::{ContentPart, Role, ToolCall}; let mut content = vec![ContentPart::text("Let me use multiple tools.")]; for (tool_name, tool_call_id, args) in &calls { content.push(ContentPart::ToolCall(ToolCall::new( diff --git a/crates/agent/src/tool_registry.rs b/crates/agent/src/tool_registry.rs index c62d28da6..b944e83dd 100644 --- a/crates/agent/src/tool_registry.rs +++ b/crates/agent/src/tool_registry.rs @@ -3,7 +3,7 @@ use std::collections::HashMap; use std::future::Future; use std::pin::Pin; use std::sync::Arc; -use unified_llm::types::ToolDefinition; +use llm::types::ToolDefinition; pub type ToolExecutor = Arc< dyn Fn( diff --git a/crates/agent/src/tools.rs b/crates/agent/src/tools.rs index a23f0d3e1..276fecbd1 100644 --- a/crates/agent/src/tools.rs +++ b/crates/agent/src/tools.rs @@ -3,7 +3,7 @@ use crate::execution_env::GrepOptions; use crate::tool_registry::RegisteredTool; use std::fmt::Write; use std::sync::Arc; -use unified_llm::types::ToolDefinition; +use llm::types::ToolDefinition; pub(crate) fn required_str<'a>(args: &'a serde_json::Value, key: &str) -> Result<&'a str, String> { args.get(key) diff --git a/crates/agent/src/types.rs b/crates/agent/src/types.rs index a4cb0bd7c..7b329edbf 100644 --- a/crates/agent/src/types.rs +++ b/crates/agent/src/types.rs @@ -1,5 +1,5 @@ use std::time::SystemTime; -use unified_llm::types::{ToolCall, ToolResult, Usage}; +use llm::types::{ToolCall, ToolResult, Usage}; #[derive(Debug, Clone)] pub enum Turn { diff --git a/crates/attractor/Cargo.toml b/crates/attractor/Cargo.toml index 66ded992f..63afd5611 100644 --- a/crates/attractor/Cargo.toml +++ b/crates/attractor/Cargo.toml @@ -22,7 +22,7 @@ clap.workspace = true anyhow.workspace = true dotenvy.workspace = true agent = { path = "../agent" } -unified-llm = { path = "../unified-llm" } +llm = { path = "../llm" } thiserror.workspace = true serde.workspace = true serde_json.workspace = true diff --git a/crates/attractor/src/cli/backend.rs b/crates/attractor/src/cli/backend.rs index bfddd581a..8ff96dc1a 100644 --- a/crates/attractor/src/cli/backend.rs +++ b/crates/attractor/src/cli/backend.rs @@ -7,7 +7,7 @@ use agent::{ AnthropicProfile, GeminiProfile, LocalExecutionEnvironment, OpenAiProfile, ProviderProfile, Session, SessionConfig, Turn, }; -use unified_llm::client::Client; +use llm::client::Client; use crate::context::Context; use crate::error::AttractorError; diff --git a/crates/attractor/src/cli/run.rs b/crates/attractor/src/cli/run.rs index bb3154a1d..fcbd51a4b 100644 --- a/crates/attractor/src/cli/run.rs +++ b/crates/attractor/src/cli/run.rs @@ -79,7 +79,7 @@ pub async fn run_command(args: RunArgs) -> anyhow::Result<()> { let dry_run_mode = if args.dry_run { true } else { - match unified_llm::client::Client::from_env().await { + match llm::client::Client::from_env().await { Ok(c) if c.provider_names().is_empty() => { eprintln!("Warning: No LLM providers configured. Running in dry-run mode."); true diff --git a/crates/attractor/tests/integration.rs b/crates/attractor/tests/integration.rs index 1452b819e..ae5257cf4 100644 --- a/crates/attractor/tests/integration.rs +++ b/crates/attractor/tests/integration.rs @@ -3600,8 +3600,8 @@ mod real_llm { use attractor::graph::Node; use attractor::handler::codergen::{CodergenBackend, CodergenHandler, CodergenResult}; - use unified_llm::client::Client; - use unified_llm::types::{Message, Request}; + use llm::client::Client; + use llm::types::{Message, Request}; struct LlmCodergenBackend { client: Arc, diff --git a/crates/unified-llm/Cargo.toml b/crates/llm/Cargo.toml similarity index 97% rename from crates/unified-llm/Cargo.toml rename to crates/llm/Cargo.toml index ed5bc6096..cbf38072e 100644 --- a/crates/unified-llm/Cargo.toml +++ b/crates/llm/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "unified-llm" +name = "llm" edition.workspace = true version.workspace = true license.workspace = true diff --git a/crates/unified-llm/README.md b/crates/llm/README.md similarity index 100% rename from crates/unified-llm/README.md rename to crates/llm/README.md diff --git a/crates/unified-llm/src/bin/ullm.rs b/crates/llm/src/bin/ullm.rs similarity index 97% rename from crates/unified-llm/src/bin/ullm.rs rename to crates/llm/src/bin/ullm.rs index 5e856e991..b3debcb1c 100644 --- a/crates/unified-llm/src/bin/ullm.rs +++ b/crates/llm/src/bin/ullm.rs @@ -3,8 +3,8 @@ use std::io::{self, IsTerminal, Read}; use anyhow::{bail, Context, Result}; use clap::{Parser, Subcommand}; use futures::StreamExt; -use unified_llm::catalog; -use unified_llm::generate::{self, GenerateParams}; +use llm::catalog; +use llm::generate::{self, GenerateParams}; #[derive(Parser)] #[command(name = "ullm")] @@ -69,7 +69,7 @@ fn parse_option(s: &str) -> Result<(String, String), String> { Ok((key.to_string(), value.to_string())) } -fn print_models_table(models: &[unified_llm::types::ModelInfo]) { +fn print_models_table(models: &[llm::types::ModelInfo]) { println!( "{:<30} {:<12} {:<30} {:>14}", "ID", "PROVIDER", "ALIASES", "CONTEXT" @@ -168,7 +168,7 @@ struct PromptArgs { option: Vec<(String, String)>, } -fn print_usage(usage: &unified_llm::types::Usage) { +fn print_usage(usage: &llm::types::Usage) { eprintln!( "Tokens: {} input, {} output, {} total", usage.input_tokens, usage.output_tokens, usage.total_tokens @@ -198,7 +198,7 @@ async fn run_prompt(args: PromptArgs) -> Result<()> { } else { let mut stream_result = generate::stream(params).await?; while let Some(event) = stream_result.next().await { - if let unified_llm::types::StreamEvent::TextDelta { delta, .. } = event? { + if let llm::types::StreamEvent::TextDelta { delta, .. } = event? { print!("{delta}"); } } diff --git a/crates/unified-llm/src/catalog.json b/crates/llm/src/catalog.json similarity index 100% rename from crates/unified-llm/src/catalog.json rename to crates/llm/src/catalog.json diff --git a/crates/unified-llm/src/catalog.rs b/crates/llm/src/catalog.rs similarity index 100% rename from crates/unified-llm/src/catalog.rs rename to crates/llm/src/catalog.rs diff --git a/crates/unified-llm/src/client.rs b/crates/llm/src/client.rs similarity index 100% rename from crates/unified-llm/src/client.rs rename to crates/llm/src/client.rs diff --git a/crates/unified-llm/src/error.rs b/crates/llm/src/error.rs similarity index 100% rename from crates/unified-llm/src/error.rs rename to crates/llm/src/error.rs diff --git a/crates/unified-llm/src/generate.rs b/crates/llm/src/generate.rs similarity index 100% rename from crates/unified-llm/src/generate.rs rename to crates/llm/src/generate.rs diff --git a/crates/unified-llm/src/lib.rs b/crates/llm/src/lib.rs similarity index 100% rename from crates/unified-llm/src/lib.rs rename to crates/llm/src/lib.rs diff --git a/crates/unified-llm/src/middleware.rs b/crates/llm/src/middleware.rs similarity index 100% rename from crates/unified-llm/src/middleware.rs rename to crates/llm/src/middleware.rs diff --git a/crates/unified-llm/src/provider.rs b/crates/llm/src/provider.rs similarity index 100% rename from crates/unified-llm/src/provider.rs rename to crates/llm/src/provider.rs diff --git a/crates/unified-llm/src/providers/anthropic.rs b/crates/llm/src/providers/anthropic.rs similarity index 100% rename from crates/unified-llm/src/providers/anthropic.rs rename to crates/llm/src/providers/anthropic.rs diff --git a/crates/unified-llm/src/providers/common.rs b/crates/llm/src/providers/common.rs similarity index 100% rename from crates/unified-llm/src/providers/common.rs rename to crates/llm/src/providers/common.rs diff --git a/crates/unified-llm/src/providers/gemini.rs b/crates/llm/src/providers/gemini.rs similarity index 100% rename from crates/unified-llm/src/providers/gemini.rs rename to crates/llm/src/providers/gemini.rs diff --git a/crates/unified-llm/src/providers/mod.rs b/crates/llm/src/providers/mod.rs similarity index 100% rename from crates/unified-llm/src/providers/mod.rs rename to crates/llm/src/providers/mod.rs diff --git a/crates/unified-llm/src/providers/openai.rs b/crates/llm/src/providers/openai.rs similarity index 100% rename from crates/unified-llm/src/providers/openai.rs rename to crates/llm/src/providers/openai.rs diff --git a/crates/unified-llm/src/providers/openai_compatible.rs b/crates/llm/src/providers/openai_compatible.rs similarity index 100% rename from crates/unified-llm/src/providers/openai_compatible.rs rename to crates/llm/src/providers/openai_compatible.rs diff --git a/crates/unified-llm/src/retry.rs b/crates/llm/src/retry.rs similarity index 100% rename from crates/unified-llm/src/retry.rs rename to crates/llm/src/retry.rs diff --git a/crates/unified-llm/src/tools.rs b/crates/llm/src/tools.rs similarity index 100% rename from crates/unified-llm/src/tools.rs rename to crates/llm/src/tools.rs diff --git a/crates/unified-llm/src/types.rs b/crates/llm/src/types.rs similarity index 100% rename from crates/unified-llm/src/types.rs rename to crates/llm/src/types.rs diff --git a/crates/unified-llm/tests/integration.rs b/crates/llm/tests/integration.rs similarity index 84% rename from crates/unified-llm/tests/integration.rs rename to crates/llm/tests/integration.rs index 01c58f746..6ed2bae78 100644 --- a/crates/unified-llm/tests/integration.rs +++ b/crates/llm/tests/integration.rs @@ -1,6 +1,6 @@ -use unified_llm::provider::ProviderAdapter; -use unified_llm::providers::{AnthropicAdapter, GeminiAdapter, OpenAiAdapter}; -use unified_llm::types::{Message, Request}; +use llm::provider::ProviderAdapter; +use llm::providers::{AnthropicAdapter, GeminiAdapter, OpenAiAdapter}; +use llm::types::{Message, Request}; fn make_request(model: &str) -> Request { Request { @@ -30,7 +30,7 @@ async fn anthropic_complete() { let response = adapter.complete(&request).await.unwrap(); assert!(!response.text().is_empty(), "response text should not be empty"); - assert_eq!(response.finish_reason, unified_llm::types::FinishReason::Stop); + assert_eq!(response.finish_reason, llm::types::FinishReason::Stop); assert!(response.usage.input_tokens > 0); assert!(response.usage.output_tokens > 0); assert_eq!(response.provider, "anthropic"); @@ -46,7 +46,7 @@ async fn openai_complete() { let response = adapter.complete(&request).await.unwrap(); assert!(!response.text().is_empty(), "response text should not be empty"); - assert_eq!(response.finish_reason, unified_llm::types::FinishReason::Stop); + assert_eq!(response.finish_reason, llm::types::FinishReason::Stop); assert!(response.usage.input_tokens > 0); assert!(response.usage.output_tokens > 0); assert_eq!(response.provider, "openai"); @@ -62,7 +62,7 @@ async fn gemini_complete() { let response = adapter.complete(&request).await.unwrap(); assert!(!response.text().is_empty(), "response text should not be empty"); - assert_eq!(response.finish_reason, unified_llm::types::FinishReason::Stop); + assert_eq!(response.finish_reason, llm::types::FinishReason::Stop); assert!(response.usage.input_tokens > 0); assert!(response.usage.output_tokens > 0); assert_eq!(response.provider, "gemini");