Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
403 changes: 403 additions & 0 deletions Cargo.lock

Large diffs are not rendered by default.

10 changes: 7 additions & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,17 +1,22 @@
[workspace]
resolver = "3"
members = ["legacy/*"]
members = ["crates/*", "llm/*", "legacy/*"]

[workspace.package]
version = "0.0.9"
edition = "2024"
authors = ["clearloop <[email protected]>"]
license = "MIT"
repository = "https://github.com/clearloop/cydonia"
documentation = "https://cydonia.docs.rs"
keywords = ["llm", "agent", "ai"]

[workspace.dependencies]
model = { path = "legacy/model", package = "cydonia-model" }
candle = { path = "crates/candle", package = "cydonia-candle" }
ucore = { path = "crates/core", package = "ullm-core" }
deepseek = { path = "llm/deepseek", package = "ullm-deepseek" }


# crates.io
anyhow = "1"
Expand All @@ -31,6 +36,7 @@ toml = "0.9.8"
tracing = "0.1"
tracing-subscriber = { version = "0.3", features = ["env-filter"] }


# legacy dependencies
candle-core = "0.8.1"
candle-nn = "0.8.1"
Expand All @@ -40,5 +46,3 @@ llamac-sys = { version = "0.1.86", package = "llama-cpp-sys-2" }
once_cell = "1.21"
rand = "0.9.2"
tokenizers = "0.21.0"


3 changes: 3 additions & 0 deletions crates/core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,6 @@ serde.workspace = true
futures-core.workspace = true
reqwest.workspace = true
schemars.workspace = true

[dev-dependencies]
serde_json.workspace = true
70 changes: 70 additions & 0 deletions crates/core/src/chat.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
//! Chat abstractions for the unified LLM Interfaces

use crate::{
LLM, Response, Role, StreamChunk,
message::{AssistantMessage, Message, ToolMessage},
};
use anyhow::Result;
use futures_core::Stream;
use serde::Serialize;

/// A chat for the LLM
pub struct Chat<P: LLM> {
/// The chat configuration
pub config: P::ChatConfig,

/// Chat history in memory
pub messages: Vec<ChatMessage>,

/// The LLM provider
pub provider: P,
}

impl<P: LLM> Chat<P> {
/// Send a message to the LLM
pub async fn send(&mut self, message: Message) -> Result<Response> {
self.messages.push(message.into());
self.provider.send(&self.config, &self.messages).await
}

/// Send a message to the LLM with streaming
pub fn stream(&mut self, message: Message) -> impl Stream<Item = Result<StreamChunk>> {
self.messages.push(message.into());
self.provider.stream(&self.config, &self.messages)
}
}

/// A chat message in memory
#[derive(Debug, Clone, Serialize)]
#[serde(untagged)]
pub enum ChatMessage {
/// A user message
User(Message),

/// An assistant message
Assistant(AssistantMessage),

/// A tool message
Tool(ToolMessage),

/// A system message
System(Message),
}

impl From<Message> for ChatMessage {
fn from(message: Message) -> Self {
match message.role {
Role::User => ChatMessage::User(message),
Role::Assistant => ChatMessage::Assistant(AssistantMessage {
message,
prefix: false,
reasoning: String::new(),
}),
Role::System => ChatMessage::System(message),
Role::Tool => ChatMessage::Tool(ToolMessage {
tool: String::new(),
message,
}),
}
}
}
84 changes: 74 additions & 10 deletions crates/core/src/config.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,11 @@
//! Configuration for a chat

use crate::{Tool, ToolChoice};
use serde::{Deserialize, Serialize};

/// Chat configuration
#[derive(Debug, Clone)]
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct Config {
/// The model to use
pub model: &'static str,

/// Whether to enable thinking
pub think: bool,

/// The frequency penalty of the model
pub frequency: i8,

Expand All @@ -18,15 +15,27 @@ pub struct Config {
/// Whether to return the log probabilities
pub logprobs: bool,

/// The model to use
pub model: String,

/// The presence penalty of the model
pub presence: i8,

/// Whether to stream the response
pub stream: bool,
/// Stop sequences to halt generation
pub stop: Vec<String>,

/// The temperature of the model
pub temperature: f32,

/// Whether to enable thinking
pub think: bool,

/// Controls which tool is called by the model
pub tool_choice: ToolChoice,

/// A list of tools the model may call
pub tools: Vec<Tool>,

/// The top probability of the model
pub top_p: f32,

Expand All @@ -36,6 +45,61 @@ pub struct Config {
/// The number of max tokens to generate
pub tokens: usize,

/// Whether to return the usage information
/// Whether to return the usage information in stream mode
pub usage: bool,
}

impl Config {
/// Create a new configuration
pub fn new(model: impl Into<String>) -> Self {
Self {
model: model.into(),
..Default::default()
}
}

/// Add a tool to the configuration
pub fn tool(mut self, tool: Tool) -> Self {
self.tools.push(tool);
self
}

/// Set tools for the configuration
pub fn tools(mut self, tools: Vec<Tool>) -> Self {
self.tools = tools;
self
}

/// Set the tool choice for the configuration
pub fn tool_choice(mut self, choice: ToolChoice) -> Self {
self.tool_choice = choice;
self
}

/// Set stop sequences for the configuration
pub fn stop(mut self, sequences: Vec<String>) -> Self {
self.stop = sequences;
self
}
}

impl Default for Config {
fn default() -> Self {
Self {
frequency: 0,
json: false,
logprobs: false,
model: "deepseek-chat".into(),
presence: 0,
stop: Vec::new(),
temperature: 1.0,
think: false,
tool_choice: ToolChoice::None,
tools: Vec::new(),
top_logprobs: 0,
top_p: 1.0,
tokens: 1000,
usage: true,
}
}
}
14 changes: 13 additions & 1 deletion crates/core/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,25 @@
//! Core abstractions for Unified LLM Interface

pub use {
chat::{Chat, ChatMessage},
config::Config,
message::{Message, Role},
provider::LLM,
tool::Tool,
reqwest::{self, Client},
response::{
Choice, CompletionTokensDetails, FinishReason, LogProb, LogProbs, Response,
ResponseMessage, TopLogProb, Usage,
},
stream::{Delta, StreamChoice, StreamChunk},
template::Template,
tool::{FunctionCall, Tool, ToolCall, ToolChoice},
};

mod chat;
mod config;
mod message;
mod provider;
mod response;
mod stream;
mod template;
mod tool;
59 changes: 42 additions & 17 deletions crates/core/src/message.rs
Original file line number Diff line number Diff line change
@@ -1,22 +1,26 @@
//! Turbofish LLM message

use derive_more::Display;
use serde::{Deserialize, Serialize};

/// A message in the chat
#[derive(Debug, Clone)]
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct Message {
/// The role of the message
pub role: Role,

/// The content of the message
pub content: String,

/// The name of the message
pub name: String,

/// The role of the message
pub role: Role,
}

impl Message {
/// Create a new system message
pub fn system(content: impl Into<String>) -> Self {
Self {
role: Role::System,
name: String::new(),
content: content.into(),
}
}
Expand All @@ -25,6 +29,7 @@ impl Message {
pub fn user(content: impl Into<String>) -> Self {
Self {
role: Role::User,
name: String::new(),
content: content.into(),
}
}
Expand All @@ -33,32 +38,52 @@ impl Message {
pub fn assistant(content: impl Into<String>) -> Self {
Self {
role: Role::Assistant,
name: String::new(),
content: content.into(),
}
}
}

/// Create a new tool message
pub fn tool(content: impl Into<String>) -> Self {
Self {
role: Role::Tool,
content: content.into(),
}
}
/// A tool message in the chat
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct ToolMessage {
/// The message
#[serde(flatten)]
pub message: Message,

/// The tool call id
#[serde(alias = "tool_call_id")]
pub tool: String,
}

/// An assistant message in the chat
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct AssistantMessage {
/// The message
#[serde(flatten)]
pub message: Message,

/// Whether to prefix the message
pub prefix: bool,

/// The reasoning content
#[serde(alias = "reasoning_content")]
pub reasoning: String,
}

/// The role of a message
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Display)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Deserialize, Serialize)]
pub enum Role {
/// The user role
#[display("user")]
#[serde(rename = "user")]
User,
/// The assistant role
#[display("assistant")]
#[serde(rename = "assistant")]
Assistant,
/// The system role
#[display("system")]
#[serde(rename = "system")]
System,
/// The tool role
#[display("tool")]
#[serde(rename = "tool")]
Tool,
}
Loading