api: singleton ApiClient, fix log closure threading
Make ApiClient a process-wide singleton via OnceLock so the connection pool is reused across agent calls. Fix the sync wrapper to properly pass the caller's log closure through thread::scope instead of dropping it. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
643f9890df
commit
b04a98c6e5
1 changed files with 17 additions and 13 deletions
|
|
@ -12,6 +12,20 @@ use poc_agent::types::*;
|
||||||
use poc_agent::tools::{self, ProcessTracker};
|
use poc_agent::tools::{self, ProcessTracker};
|
||||||
use poc_agent::ui_channel::StreamTarget;
|
use poc_agent::ui_channel::StreamTarget;
|
||||||
|
|
||||||
|
use std::sync::OnceLock;
|
||||||
|
|
||||||
|
static API_CLIENT: OnceLock<ApiClient> = OnceLock::new();
|
||||||
|
|
||||||
|
fn get_client() -> Result<&'static ApiClient, String> {
|
||||||
|
Ok(API_CLIENT.get_or_init(|| {
|
||||||
|
let config = crate::config::get();
|
||||||
|
let base_url = config.api_base_url.as_deref().unwrap_or("");
|
||||||
|
let api_key = config.api_key.as_deref().unwrap_or("");
|
||||||
|
let model = config.api_model.as_deref().unwrap_or("qwen-2.5-27b");
|
||||||
|
ApiClient::new(base_url, api_key, model)
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
/// Run an agent prompt through the direct API with tool support.
|
/// Run an agent prompt through the direct API with tool support.
|
||||||
/// Returns the final text response after all tool calls are resolved.
|
/// Returns the final text response after all tool calls are resolved.
|
||||||
pub async fn call_api_with_tools(
|
pub async fn call_api_with_tools(
|
||||||
|
|
@ -19,14 +33,7 @@ pub async fn call_api_with_tools(
|
||||||
prompt: &str,
|
prompt: &str,
|
||||||
log: &dyn Fn(&str),
|
log: &dyn Fn(&str),
|
||||||
) -> Result<String, String> {
|
) -> Result<String, String> {
|
||||||
let config = crate::config::get();
|
let client = get_client()?;
|
||||||
|
|
||||||
let base_url = config.api_base_url.as_deref()
|
|
||||||
.ok_or("api_base_url not configured")?;
|
|
||||||
let api_key = config.api_key.as_deref().unwrap_or("");
|
|
||||||
let model = config.api_model.as_deref().unwrap_or("qwen-2.5-27b");
|
|
||||||
|
|
||||||
let client = ApiClient::new(base_url, api_key, model);
|
|
||||||
|
|
||||||
// Set up a minimal UI channel (we just collect messages, no TUI)
|
// Set up a minimal UI channel (we just collect messages, no TUI)
|
||||||
let (ui_tx, _ui_rx) = poc_agent::ui_channel::channel();
|
let (ui_tx, _ui_rx) = poc_agent::ui_channel::channel();
|
||||||
|
|
@ -104,18 +111,15 @@ pub async fn call_api_with_tools(
|
||||||
pub fn call_api_with_tools_sync(
|
pub fn call_api_with_tools_sync(
|
||||||
agent: &str,
|
agent: &str,
|
||||||
prompt: &str,
|
prompt: &str,
|
||||||
log: &dyn Fn(&str),
|
log: &(dyn Fn(&str) + Sync),
|
||||||
) -> Result<String, String> {
|
) -> Result<String, String> {
|
||||||
// Run on a new thread to avoid conflicts with any existing runtime
|
|
||||||
let agent = agent.to_string();
|
|
||||||
let prompt = prompt.to_string();
|
|
||||||
std::thread::scope(|s| {
|
std::thread::scope(|s| {
|
||||||
s.spawn(|| {
|
s.spawn(|| {
|
||||||
let rt = tokio::runtime::Builder::new_current_thread()
|
let rt = tokio::runtime::Builder::new_current_thread()
|
||||||
.enable_all()
|
.enable_all()
|
||||||
.build()
|
.build()
|
||||||
.map_err(|e| format!("tokio runtime: {}", e))?;
|
.map_err(|e| format!("tokio runtime: {}", e))?;
|
||||||
rt.block_on(call_api_with_tools(&agent, &prompt, &|msg| eprintln!("[api] {}", msg)))
|
rt.block_on(call_api_with_tools(agent, prompt, log))
|
||||||
}).join().unwrap()
|
}).join().unwrap()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue