Skip to main content

lc_core/language_models/
chat.rs

1// src/core/language_models/chat.rs
2//! Chat model base trait.
3
4use super::BaseLanguageModel;
5use crate::tools::ToolDefinition;
6use crate::RunnableConfig;
7use async_trait::async_trait;
8use futures_util::Stream;
9use lc_schema::Message;
10use lc_shared::tools::ToolCall;
11use serde::{Deserialize, Serialize};
12use std::pin::Pin;
13
14/// LLM result containing response content and metadata.
15#[derive(Debug, Clone, Serialize, Deserialize, Default)]
16pub struct LLMResult {
17    #[serde(default)]
18    pub content: String,
19    #[serde(default)]
20    pub model: String,
21    #[serde(default)]
22    pub token_usage: Option<TokenUsage>,
23    #[serde(default)]
24    pub tool_calls: Option<Vec<ToolCall>>,
25    #[serde(default, skip_serializing_if = "Option::is_none")]
26    pub thinking_content: Option<String>,
27}
28
29/// Token usage statistics.
30#[derive(Debug, Clone, Serialize, Deserialize)]
31pub struct TokenUsage {
32    /// Input token count.
33    pub prompt_tokens: usize,
34
35    /// Output token count.
36    pub completion_tokens: usize,
37
38    /// Total token count.
39    pub total_tokens: usize,
40}
41
42/// Base trait for chat models.
43///
44/// Extends BaseLanguageModel for chat scenarios.
45/// Accepts message list as input, returns AI message.
46#[async_trait]
47pub trait BaseChatModel: BaseLanguageModel<Vec<Message>, LLMResult> {
48    /// Chat with the model.
49    ///
50    /// # Arguments
51    /// * `messages` - Message list.
52    /// * `config` - Optional configuration.
53    ///
54    /// # Returns
55    /// LLM result.
56    async fn chat(
57        &self,
58        messages: Vec<Message>,
59        config: Option<RunnableConfig>,
60    ) -> Result<LLMResult, Self::Error>;
61
62    /// Stream chat with the model.
63    ///
64    /// # Arguments
65    /// * `messages` - Message list.
66    /// * `config` - Optional configuration.
67    ///
68    /// # Returns
69    /// Stream of output chunks.
70    async fn stream_chat(
71        &self,
72        messages: Vec<Message>,
73        config: Option<RunnableConfig>,
74    ) -> Result<Pin<Box<dyn Stream<Item = Result<String, Self::Error>> + Send>>, Self::Error>;
75
76    /// Chat with system prompt.
77    ///
78    /// # Arguments
79    /// * `system` - System prompt.
80    /// * `messages` - Message list.
81    ///
82    /// # Returns
83    /// LLM result.
84    async fn chat_with_system(
85        &self,
86        system: String,
87        messages: Vec<Message>,
88    ) -> Result<LLMResult, Self::Error> {
89        let full_messages = vec![Message::system(system)]
90            .into_iter()
91            .chain(messages)
92            .collect();
93
94        self.chat(full_messages, None).await
95    }
96
97    /// Bind tool definitions for function calling.
98    ///
99    /// Returns `None` by default if the provider does not support tool binding.
100    /// Providers that support function calling (OpenAI, Ollama) override this
101    /// to return a boxed chat model with the tools attached.
102    fn bind_tools(
103        &self,
104        _tools: Vec<ToolDefinition>,
105    ) -> Option<Box<dyn BaseChatModel<Error = Self::Error> + Send + Sync>> {
106        None
107    }
108}