Skip to main content

agentlib_reasoning/
utils.rs

1use agentlib_core::{ModelMessage, ModelRequest, ModelResponse, ReasoningContext};
2use anyhow::{Result, anyhow};
3
4pub async fn call_model(
5    r_ctx: &mut ReasoningContext<'_>,
6    messages: Vec<ModelMessage>,
7) -> Result<ModelResponse> {
8    let tools = r_ctx.tools.list();
9    let request = ModelRequest {
10        messages,
11        tools: Some(tools),
12    };
13
14    let response = r_ctx.model.complete(request).await?;
15
16    // Accumulate usage
17    if let Some(usage) = &response.usage {
18        r_ctx.ctx.usage.prompt_tokens += usage.prompt_tokens;
19        r_ctx.ctx.usage.completion_tokens += usage.completion_tokens;
20        r_ctx.ctx.usage.total_tokens += usage.total_tokens;
21    }
22
23    Ok(response)
24}
25
26pub async fn execute_tool_calls(
27    r_ctx: &mut ReasoningContext<'_>,
28    response: &ModelResponse,
29) -> Result<()> {
30    if let Some(tool_calls) = &response.message.tool_calls {
31        for tc in tool_calls {
32            r_ctx
33                .call_tool(&tc.name, tc.arguments.clone(), tc.id.clone())
34                .await?;
35        }
36    }
37    Ok(())
38}
39
40pub fn parse_json<T: serde::de::DeserializeOwned>(text: &str) -> Result<T> {
41    let re = regex::Regex::new(r"(?s)```(?:json)?\s*(.*?)\s*```")?;
42    let raw = if let Some(caps) = re.captures(text) {
43        caps.get(1).map_or(text, |m| m.as_str())
44    } else {
45        text
46    };
47    serde_json::from_str(raw).map_err(|e| anyhow!("Failed to parse JSON: {}", e))
48}
49
50pub fn extract_text(content: &str) -> String {
51    let re_thinking = regex::Regex::new(r"(?i)<thinking>.*?</thinking>").unwrap();
52    let re_thought = regex::Regex::new(r"(?i)<thought>.*?</thought>").unwrap();
53
54    let content = re_thinking.replace_all(content, "");
55    let content = re_thought.replace_all(&content, "");
56    content.trim().to_string()
57}