Skip to content

Commit c493e12

Browse files
committed
ensure the final tool call result updates the UI
1 parent fc31e06 commit c493e12

File tree

1 file changed

+54
-23
lines changed

1 file changed

+54
-23
lines changed

chatlab/views/tools.py

+54-23
Original file line numberDiff line numberDiff line change
@@ -18,27 +18,6 @@
1818
from instructor.dsl.partialjson import JSONParser
1919

2020

21-
class ToolCalled(AutoUpdate):
22-
"""Once a tool has finished up, this is the view."""
23-
24-
id: str
25-
name: str
26-
arguments: str = ""
27-
verbage: str = "Called"
28-
result: str = ""
29-
30-
def render(self):
31-
return ChatFunctionComponent(name=self.name, verbage=self.verbage, input=self.arguments, output=self.result)
32-
33-
# TODO: This is only here for legacy function calling
34-
def get_function_called_message(self):
35-
return function_result(self.name, self.result)
36-
37-
def get_tool_called_message(self):
38-
# NOTE: OpenAI has mismatched types where it doesn't include the `name`
39-
# xref: https://github.com/openai/openai-python/issues/1078
40-
return tool_result(tool_call_id=self.id, content=self.result, name=self.name)
41-
4221

4322
class ToolArguments(AutoUpdate):
4423
id: str
@@ -130,11 +109,13 @@ def append_arguments(self, arguments: str):
130109

131110
def apply_result(self, result: str):
132111
"""Replaces the existing display with a new one that shows the result of the tool being called."""
133-
return ToolCalled(
112+
tc = ToolCalled(
134113
id=self.id, name=self.name, arguments=self.arguments, result=result, display_id=self.display_id
135114
)
115+
tc.update()
116+
return tc
136117

137-
async def call(self, function_registry: FunctionRegistry) -> ToolCalled:
118+
async def call(self, function_registry: FunctionRegistry) -> 'ToolCalled':
138119
"""Call the function and return a stack of messages for LLM and human consumption."""
139120
function_name = self.name
140121
function_args = self.arguments
@@ -188,3 +169,53 @@ async def call(self, function_registry: FunctionRegistry) -> ToolCalled:
188169
self.verbage = "Ran"
189170

190171
return self.apply_result(repr_llm)
172+
173+
174+
class ToolCalled(ToolArguments):
175+
"""Once a tool has finished up, this is the view."""
176+
177+
id: str
178+
name: str
179+
arguments: str = ""
180+
verbage: str = "Called"
181+
result: str = ""
182+
183+
def render(self):
184+
if self.custom_render is not None:
185+
# We use the same definition as was in the original function
186+
try:
187+
parser = JSONParser()
188+
possible_args = parser.parse(self.arguments)
189+
190+
Model = extract_model_from_function(self.name, self.custom_render)
191+
# model = Model.model_validate(possible_args)
192+
model = Model(**possible_args)
193+
194+
# Pluck the kwargs out from the crafted model, as we can't pass the pydantic model as the arguments
195+
# However any "inner" models should retain their pydantic Model nature
196+
kwargs = {k: getattr(model, k) for k in model.__dict__.keys()}
197+
198+
except FunctionArgumentError:
199+
return None
200+
except ValidationError:
201+
return None
202+
203+
try:
204+
return self.custom_render(**kwargs)
205+
except Exception as e:
206+
# Exception in userland code
207+
# Would be preferable to bubble up, however
208+
# it might be due to us passing a not-quite model
209+
warnings.warn_explicit(f"Exception in userland code: {e}", UserWarning, "chatlab", 0)
210+
raise
211+
212+
return ChatFunctionComponent(name=self.name, verbage=self.verbage, input=self.arguments, output=self.result)
213+
214+
# TODO: This is only here for legacy function calling
215+
def get_function_called_message(self):
216+
return function_result(self.name, self.result)
217+
218+
def get_tool_called_message(self):
219+
# NOTE: OpenAI has mismatched types where it doesn't include the `name`
220+
# xref: https://github.com/openai/openai-python/issues/1078
221+
return tool_result(tool_call_id=self.id, content=self.result, name=self.name)

0 commit comments

Comments
 (0)