///|
pub struct ToolDesc {
description : String
name : String
schema : JsonSchema
} derive(ToJson, Eq, Debug)
///|
pub fn ToolDesc::to_openai(
tool_desc : ToolDesc,
) -> @openai.ChatCompletionToolParam {
@openai.tool(
name=tool_desc.name,
description=tool_desc.description,
parameters=tool_desc.schema.to_json(),
)
}
///|
pub fn tool_desc(
description~ : String,
name~ : String,
schema~ : JsonSchema,
) -> ToolDesc {
{ description, name, schema }
}
///|
pub struct Tool[Output] {
desc : ToolDesc
priv f : ToolFn[Output]
}
///|
pub(all) struct ToolFn[Output](async (Json) -> ToolResult[Output] noraise)
///|
pub enum ToolResult[Output] {
Ok(Output)
Error(Error, String)
} derive(ToJson)
///|
#as_free_fn
pub fn[Output] ToolResult::ok(output : Output) -> ToolResult[Output] {
Ok(output)
}
///|
pub impl[Output : Show] Show for ToolResult[Output] with fn output(
self : ToolResult[Output],
logger : &Logger,
) -> Unit {
match self {
Ok(output) => logger.write_string(output.to_string())
Error(ToolFailure(_), message) => logger.write_string(message)
Error(error, message) =>
logger.write_string("\{message}\n\{error.to_json().stringify(indent=2)}")
}
}
///|
priv suberror ToolFailure {
ToolFailure(String)
} derive(ToJson)
///|
#as_free_fn
pub fn[Output] ToolResult::error(
output : String,
error? : Error = ToolFailure(output),
) -> ToolResult[Output] {
Error(error, output)
}
///|
///
/// - `description`: Tool description that can be read by the LLM.
/// - `name`: Tool name used by the LLM to call the tool.
/// - `schema`: JSON schema to validate LLM-generated output and provide type information to the LLM.
/// `schema` should be an *object type*. Related issue: [#258](https://github.com/vectie/moonclaw/issues/258)
/// - `f`: Function whose `input` must conform to the `schema` defined above.
///
#as_free_fn
pub fn[Output] Tool::new(
description~ : String,
name~ : String,
schema~ : JsonSchema,
f : ToolFn[Output],
) -> Tool[Output] {
{ desc: { description, name, schema }, f }
}
///|
// TODO(upstream): `as_free_fn` completion not need parens
#as_free_fn
pub async fn[Output] Tool::call(
tool : Tool[Output],
args : Json,
) -> ToolResult[Output] noraise {
(tool.f)(args)
}
///|
struct AgentTool(Tool[(Json, String)])
///|
pub fn AgentTool::desc(self : AgentTool) -> ToolDesc {
self.0.desc
}
///|
pub async fn AgentTool::call(
self : AgentTool,
args : Json,
) -> ToolResult[(Json, String)] noraise {
self.0.call(args)
}
///|
/// Convert a Tool[Output] to Tool[Json] by converting outputs to Json
pub fn[Output : ToJson] Tool::to_agent_tool(self : Tool[Output]) -> AgentTool {
{
desc: self.desc,
f: args => {
match self.call(args) {
Ok(output) => {
let json = output.to_json()
Ok((json, json.stringify()))
}
Error(error, output) => Error(error, output)
}
},
}
}