1+ import textwrap
2+ from datetime import date
3+
14from writer .abstract import register_abstract_template
25from writer .blocks .base_block import WriterBlock
36from writer .ss_types import AbstractTemplate
47
58DEFAULT_MODEL = "palmyra-x5"
69
10+
711class WriterToolCalling (WriterBlock ):
812 @classmethod
913 def register (cls , type : str ):
@@ -17,16 +21,17 @@ def register(cls, type: str):
1721 "description" : "Connects the Agent to external tools to complete tasks it cannot handle directly." ,
1822 "category" : "Writer" ,
1923 "fields" : {
20- "prompt" : {"name" : "Prompt" , "type" : "Text" , "control" : "Textarea" , "desc" : "The task that needs to be carried out." },
21- "modelId " : {
22- "name " : "Model " ,
23- "type " : "Model Id " ,
24- "default " : DEFAULT_MODEL
24+ "prompt" : {
25+ "name " : "Prompt" ,
26+ "type " : "Text " ,
27+ "control " : "Textarea " ,
28+ "desc " : "The task that needs to be carried out." ,
2529 },
30+ "modelId" : {"name" : "Model" , "type" : "Model Id" , "default" : DEFAULT_MODEL },
2631 "maxIterations" : {
2732 "name" : "Max iterations" ,
2833 "type" : "Number" ,
29- "default" : 10
34+ "default" : 10 ,
3035 },
3136 "tools" : {
3237 "name" : "Tools" ,
@@ -63,27 +68,35 @@ def _make_callable(self, tool_name: str):
6368
6469 def callable (** args ):
6570 expanded_execution_environment = self .execution_environment | args
66- return_value = repr (
67- self .runner .run_branch (
68- self .component .id ,
69- f"tools_{ tool_name } " ,
70- expanded_execution_environment ,
71- f"Blueprint branch execution (tool { tool_name } )" ,
72- )
71+ raw_return_value = self .runner .run_branch (
72+ self .component .id ,
73+ f"tools_{ tool_name } " ,
74+ expanded_execution_environment ,
75+ f"Blueprint branch execution (tool { tool_name } )" ,
7376 )
74- if return_value is None :
77+
78+ if raw_return_value is None :
7579 self .outcome = "error"
7680 raise ValueError (
7781 f'No value has been returned for the outcome branch "{ tool_name } ". Use the block "Return value" to specify one.'
7882 )
79- self .execution_environment .get ("trace" ).append (
80- {
81- "type" : "functionCall" ,
82- "time" : time .time (),
83- "name" : tool_name ,
84- "parameters" : args ,
85- }
86- )
83+
84+ transformed_result = self ._project_common_tools_result (raw_return_value )
85+ if transformed_result is None :
86+ # Fallback avoids returning the literal string "None"
87+ transformed_result = raw_return_value
88+ return_value = repr (transformed_result )
89+
90+ trace = self .execution_environment .get ("trace" )
91+ if trace is not None :
92+ trace .append (
93+ {
94+ "type" : "functionCall" ,
95+ "time" : time .time (),
96+ "name" : tool_name ,
97+ "parameters" : args ,
98+ }
99+ )
87100 return return_value
88101
89102 return callable
@@ -123,15 +136,13 @@ def reasoning_callable(**kwargs):
123136 action = kwargs .get ("action" )
124137 status = kwargs .get ("status" )
125138
126- self .execution_environment .get ("trace" ).append ({
127- "type" : "reasoning" ,
128- "time" : time .time (),
129- "thought" : thought ,
130- "action" : action
131- })
139+ trace = self .execution_environment .get ("trace" )
140+ if trace is not None :
141+ trace .append (
142+ {"type" : "reasoning" , "time" : time .time (), "thought" : thought , "action" : action }
143+ )
132144 if status == "DONE" :
133145 self .is_complete = True
134-
135146
136147 reasoning_tool = {
137148 "type" : "function" ,
@@ -149,8 +160,8 @@ def reasoning_callable(**kwargs):
149160 },
150161 "status" : {
151162 "type" : "string" ,
152- "description" : "Set to DONE if you consider the task complete. Set to INCOMPLETE if you wish to keep iterating."
153- }
163+ "description" : "Set to DONE if you consider the task complete. Set to INCOMPLETE if you wish to keep iterating." ,
164+ },
154165 },
155166 }
156167
@@ -159,31 +170,36 @@ def reasoning_callable(**kwargs):
159170 return tools
160171
161172 def _get_react_prompt (self , base_prompt : str ):
162- return f"""
163- You're a ReAct agent.
164- Disclose your reasoning using the provided function.
165-
166- Task: { base_prompt }
167- """
168-
173+ return textwrap .dedent (f"""
174+ You're a ReAct agent. Your knowledge cut-off date is 2024, but today is { str (date .today ())} .
175+ Disclose your reasoning using "disclose_reasoning" function.
176+
177+ Task: { base_prompt .strip ()}
178+ """ ).strip ()
179+
180+ def _project_common_tools_result (self , tool_result ):
181+ # Handle common HTTP-like shapes safely; pass through other types.
182+ if isinstance (tool_result , dict ):
183+ if tool_result .get ("request" ) and "body" in tool_result :
184+ return tool_result ["body" ]
185+ return tool_result
169186
170187 def run (self ):
171188 import writer .ai
189+
172190 self .is_complete = False
173191
174192 try :
175193 prompt = self ._get_field ("prompt" )
176194 model_id = self ._get_field ("modelId" , False , default_field_value = DEFAULT_MODEL )
177- max_iterations = int (self ._get_field ("maxIterations" , False , "10" ))
195+ max_iterations = max ( 1 , int (self ._get_field ("maxIterations" , False , "10" ) ))
178196 conversation = writer .ai .Conversation ()
179197 tools = self ._get_tools ()
180198
199+ conversation += {"role" : "user" , "content" : self ._get_react_prompt (prompt )}
200+
181201 for i in range (max_iterations ):
182- conversation += {
183- "role" : "user" ,
184- "content" : self ._get_react_prompt (prompt )
185- }
186- config = {"model" : model_id }
202+ config = {"model" : model_id , "temperature" : 0.1 }
187203 msg = conversation .complete (tools = tools , config = config )
188204 conversation += msg
189205 if self .is_complete :
0 commit comments