1use anyhow::Result;
2use gpui::{AnyElement, AnyView, AppContext, IntoElement as _, Render, Task, View, WindowContext};
3use schemars::{schema::RootSchema, schema_for, JsonSchema};
4use serde::Deserialize;
5use std::fmt::Display;
6
7#[derive(Default, Deserialize)]
8pub struct ToolFunctionCall {
9 pub id: String,
10 pub name: String,
11 pub arguments: String,
12 #[serde(skip)]
13 pub result: Option<ToolFunctionCallResult>,
14}
15
16pub enum ToolFunctionCallResult {
17 NoSuchTool,
18 ParsingFailed,
19 Finished { for_model: String, view: AnyView },
20}
21
22impl ToolFunctionCallResult {
23 pub fn format(&self, name: &String) -> String {
24 match self {
25 ToolFunctionCallResult::NoSuchTool => format!("No tool for {name}"),
26 ToolFunctionCallResult::ParsingFailed => {
27 format!("Unable to parse arguments for {name}")
28 }
29 ToolFunctionCallResult::Finished { for_model, .. } => for_model.clone(),
30 }
31 }
32
33 pub fn into_any_element(&self, name: &String) -> AnyElement {
34 match self {
35 ToolFunctionCallResult::NoSuchTool => {
36 format!("Language Model attempted to call {name}").into_any_element()
37 }
38 ToolFunctionCallResult::ParsingFailed => {
39 format!("Language Model called {name} with bad arguments").into_any_element()
40 }
41 ToolFunctionCallResult::Finished { view, .. } => view.clone().into_any_element(),
42 }
43 }
44}
45
46#[derive(Clone)]
47pub struct ToolFunctionDefinition {
48 pub name: String,
49 pub description: String,
50 pub parameters: RootSchema,
51}
52
53impl Display for ToolFunctionDefinition {
54 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
55 let schema = serde_json::to_string(&self.parameters).ok();
56 let schema = schema.unwrap_or("None".to_string());
57 write!(f, "Name: {}:\n", self.name)?;
58 write!(f, "Description: {}\n", self.description)?;
59 write!(f, "Parameters: {}", schema)
60 }
61}
62
63pub trait LanguageModelTool {
64 /// The input type that will be passed in to `execute` when the tool is called
65 /// by the language model.
66 type Input: for<'de> Deserialize<'de> + JsonSchema;
67
68 /// The output returned by executing the tool.
69 type Output: 'static;
70
71 type View: Render;
72
73 /// The name of the tool is exposed to the language model to allow
74 /// the model to pick which tools to use. As this name is used to
75 /// identify the tool within a tool registry, it should be unique.
76 fn name(&self) -> String;
77
78 /// A description of the tool that can be used to _prompt_ the model
79 /// as to what the tool does.
80 fn description(&self) -> String;
81
82 /// The OpenAI Function definition for the tool, for direct use with OpenAI's API.
83 fn definition(&self) -> ToolFunctionDefinition {
84 let root_schema = schema_for!(Self::Input);
85
86 ToolFunctionDefinition {
87 name: self.name(),
88 description: self.description(),
89 parameters: root_schema,
90 }
91 }
92
93 /// Execute the tool
94 fn execute(&self, input: &Self::Input, cx: &AppContext) -> Task<Result<Self::Output>>;
95
96 fn format(input: &Self::Input, output: &Result<Self::Output>) -> String;
97
98 fn new_view(
99 tool_call_id: String,
100 input: Self::Input,
101 output: Result<Self::Output>,
102 cx: &mut WindowContext,
103 ) -> View<Self::View>;
104}