use std::collections::BTreeMap;
use std::collections::BTreeSet;
use ferrin_spec::JsonValue;
use ferrin_spec::ToolName;
use ferrin_spec::realtime_model::RealtimeToolDefinition;
use ferrin_tool::DescriptionContext;
use ferrin_tool::ToolKind;
use ferrin_tool::ToolSet;
use crate::error::Error;
pub async fn realtime_tool_definitions(
tools: &ToolSet,
tools_context: Option<&JsonValue>,
) -> Result<Vec<RealtimeToolDefinition>, Error> {
let mut definitions = Vec::with_capacity(tools.len());
for (name, tool) in tools.iter() {
if !matches!(tool.kind(), ToolKind::Function | ToolKind::Dynamic) {
continue;
}
let tool_context = tool
.validate_context(name, tools_context.cloned())
.map_err(|error| Error::invalid_argument("tools_context", error.to_string()))?;
let ctx = DescriptionContext {
tool_context,
#[cfg(feature = "sandbox")]
sandbox: None,
};
let description = tool.resolve_description(ctx).await;
definitions.push(RealtimeToolDefinition {
name: name.as_str().to_owned(),
description,
parameters: tool.input_schema().json_schema().clone(),
});
}
Ok(definitions)
}
#[derive(Debug, Default)]
pub(super) struct ToolTurn {
in_response: BTreeSet<String>,
submitted: BTreeSet<String>,
closed: bool,
names: BTreeMap<String, ToolName>,
}
impl ToolTurn {
pub(super) fn call_started(&mut self, call_id: &str, name: &ToolName) {
self.in_response.insert(call_id.to_owned());
self.names.insert(call_id.to_owned(), name.clone());
}
pub(super) fn name(&self, call_id: &str) -> Option<&ToolName> {
self.names.get(call_id)
}
pub(super) fn output_submitted(&mut self, call_id: &str) -> bool {
self.submitted.insert(call_id.to_owned());
self.maybe_request_response()
}
pub(super) fn response_done(&mut self) -> bool {
if self.in_response.is_empty() {
return false;
}
self.closed = true;
self.maybe_request_response()
}
fn maybe_request_response(&mut self) -> bool {
if !self.closed || self.in_response.is_empty() {
return false;
}
if !self.in_response.is_subset(&self.submitted) {
return false;
}
self.in_response.clear();
self.submitted.clear();
self.closed = false;
true
}
}