lellm_agent/runtime/
config.rs1use lellm_core::{ChatRequest, LlmError, Message, ToolDefinition};
8use lellm_provider::ResolvedModel;
9
10use super::context::ContextBudget;
11use super::fallback::FallbackStrategy;
12use super::request_opts::RequestOptions;
13use super::retry::RetryPolicy;
14use std::sync::Arc;
15
16#[derive(Debug, Clone)]
24pub struct ToolUseConfig {
25 pub system_prompt: Option<String>,
27 pub max_iterations: usize,
29 pub max_output_tokens: u32,
37 pub max_total_output_tokens: Option<u32>,
46 pub max_total_reasoning_tokens: Option<u32>,
53 pub context_budget: ContextBudget,
60 pub request_options: RequestOptions,
68 pub stream_thinking: bool,
76 pub retry_policy: RetryPolicy,
78}
79
80impl Default for ToolUseConfig {
81 fn default() -> Self {
82 Self {
83 system_prompt: None,
84 max_iterations: 10,
85 max_output_tokens: 4_000,
86 max_total_output_tokens: None,
87 max_total_reasoning_tokens: None,
88 context_budget: ContextBudget::default(),
89 request_options: RequestOptions::default(),
90 stream_thinking: false,
91 retry_policy: RetryPolicy::default(),
92 }
93 }
94}
95
96#[derive(Clone)]
102pub struct ToolUseDeps {
103 pub fallback: Arc<dyn FallbackStrategy>,
105}
106
107impl Default for ToolUseDeps {
108 fn default() -> Self {
109 Self {
110 fallback: Arc::new(super::fallback::DefaultFallback::default()),
111 }
112 }
113}
114
115pub(super) fn has_system_message(messages: &[Message]) -> bool {
119 messages.iter().any(|m| matches!(m, Message::System { .. }))
120}
121
122pub(super) fn build_request_messages_inner(
124 config: &ToolUseConfig,
125 messages: &[Message],
126) -> Result<Vec<Message>, LlmError> {
127 if let Some(ref sp) = config.system_prompt {
128 if has_system_message(messages) {
129 return Err(LlmError::DuplicateSystemPrompt);
130 }
131 let mut result = vec![Message::System {
132 content: lellm_core::text_block(sp.clone()),
133 }];
134 result.extend(messages.iter().cloned());
135 Ok(result)
136 } else {
137 Ok(messages.to_vec())
138 }
139}
140
141pub(super) fn build_request_inner(
148 model: &ResolvedModel,
149 messages: &[Message],
150 max_output_tokens: u32,
151 request_options: &RequestOptions,
152 definitions: &[ToolDefinition],
153) -> ChatRequest {
154 let tools = if definitions.is_empty() {
155 None
156 } else {
157 Some(definitions.to_vec())
158 };
159
160 let mut req = ChatRequest {
161 model: model.model.clone(),
162 messages: messages.to_vec(),
163 tools,
164 max_tokens: Some(max_output_tokens),
165 temperature: None,
166 top_p: None,
167 seed: None,
168 tool_choice: None,
169 stop_sequences: None,
170 prefill: None,
171 reasoning: None,
172 max_reasoning_tokens: None,
173 extra: None,
174 };
175
176 request_options.apply(&mut req);
178
179 req
180}
181
182pub(super) fn build_request_inner_with_round(
189 model: &ResolvedModel,
190 messages: &[Message],
191 max_output_tokens: u32,
192 request_options: &RequestOptions,
193 iteration: usize,
194 definitions: &[ToolDefinition],
195) -> ChatRequest {
196 let mut req = build_request_inner(
197 model,
198 messages,
199 max_output_tokens,
200 request_options,
201 definitions,
202 );
203
204 if iteration > 0 && request_options.tool_choice.is_some() {
207 req.tool_choice = None;
208 }
209
210 req
211}
212
213pub(super) fn empty_response() -> lellm_core::ChatResponse {
215 lellm_core::ChatResponse::new(
216 lellm_core::text_block(String::new()),
217 lellm_core::TokenUsage::default(),
218 serde_json::Value::Null,
219 )
220}