1
+ """
2
+ Interactive web development agent supporting both XML and Standard LLM tool calling.
3
+
4
+ This agent can:
5
+ - Create and modify web projects
6
+ - Execute terminal commands
7
+ - Handle file operations
8
+ - Use either XML or Standard tool calling patterns
9
+ """
10
+
1
11
import asyncio
2
12
import json
3
13
from agentpress .thread_manager import ThreadManager
8
18
from typing import AsyncGenerator
9
19
import sys
10
20
11
- async def run_agent (thread_id : str , max_iterations : int = 5 ):
12
- thread_manager = ThreadManager ()
13
- state_manager = StateManager ()
14
-
15
- thread_manager .add_tool (FilesTool )
16
- thread_manager .add_tool (TerminalTool )
17
-
18
- async def init ():
19
- pass
20
-
21
- async def pre_iteration ():
22
- files_tool = FilesTool ()
23
- await files_tool ._init_workspace_state ()
24
-
25
- async def after_iteration ():
26
- custom_message = input ("Enter a message to send (or press Enter to use 'Continue!!!' as message): " )
27
-
28
- message_content = custom_message if custom_message else """
29
- Continue!!!
30
- """
31
- await thread_manager .add_message (thread_id , {
32
- "role" : "user" ,
33
- "content" : message_content
34
- })
35
-
36
- async def finalizer ():
37
- pass
38
-
39
- await init ()
40
-
41
- iteration = 0
42
-
43
- while iteration < max_iterations :
44
- iteration += 1
45
- await pre_iteration ()
46
-
47
- # You are a world-class web developer who can create, edit, and delete files, and execute terminal commands. You write clean, well-structured code.
48
-
49
- # RESPONSE FORMAT:
50
- # Use XML tags to specify file operations:
51
-
52
- # <create-file file_path="path/to/file">
53
- # file contents here
54
- # </create-file>
21
+ BASE_SYSTEM_MESSAGE = """
22
+ You are a world-class web developer who can create, edit, and delete files, and execute terminal commands.
23
+ You write clean, well-structured code. Keep iterating on existing files, continue working on this existing
24
+ codebase - do not omit previous progress; instead, keep iterating.
55
25
56
- # <str-replace file_path="path/to/file">
57
- # <old_str>text to replace</old_str>
58
- # <new_str>replacement text</new_str>
59
- # </str-replace>
26
+ Available tools:
27
+ - create_file: Create new files with specified content
28
+ - delete_file: Remove existing files
29
+ - str_replace: Make precise text replacements in files
30
+ - execute_command: Run terminal commands
60
31
61
- # <delete-file file_path="path/to/file">
62
- # </delete-file>
63
-
64
- system_message = {
65
- "role" : "system" ,
66
- "content" : """
67
- You are a world-class web developer who can create, edit, and delete files, and execute terminal commands. You write clean, well-structured code. Keep iterating on existing files, continue working on this existing codebase - do not omit previous progress; instead, keep iterating.
68
-
69
- RESPONSE FORMAT:
70
- Use XML tags to specify file operations:
71
-
72
- <create-file file_path="path/to/file">
73
- file contents here
74
- </create-file>
75
-
76
- <str-replace file_path="path/to/file">
77
- <old_str>text to replace</old_str>
78
- <new_str>replacement text</new_str>
79
- </str-replace>
80
-
81
- <delete-file file_path="path/to/file">
82
- </delete-file>
83
32
84
33
RULES:
85
34
- All current file contents are available to you in the <current_workspace_state> section
@@ -117,18 +66,65 @@ async def finalizer():
117
66
"content": "<!DOCTYPE html>\\ n<html>\\ n<head>..."
118
67
}
119
68
}
120
-
121
69
Think deeply and step by step.
122
- """
123
- }
124
70
125
- state = await state_manager .export_store ()
71
+ """
72
+
73
+ XML_FORMAT = """
74
+ RESPONSE FORMAT:
75
+ Use XML tags to specify file operations:
76
+
77
+ <create-file file_path="path/to/file">
78
+ file contents here
79
+ </create-file>
80
+
81
+ <str-replace file_path="path/to/file">
82
+ <old_str>text to replace</old_str>
83
+ <new_str>replacement text</new_str>
84
+ </str-replace>
85
+
86
+ <delete-file file_path="path/to/file">
87
+ </delete-file>
88
+
89
+ """
90
+
91
+ async def run_agent (thread_id : str , use_xml : bool = True , max_iterations : int = 5 ):
92
+ """Run the development agent with specified configuration."""
93
+ thread_manager = ThreadManager ()
94
+ state_manager = StateManager ()
95
+
96
+ thread_manager .add_tool (FilesTool )
97
+ thread_manager .add_tool (TerminalTool )
98
+
99
+ # Combine base message with XML format if needed
100
+ system_message = {
101
+ "role" : "system" ,
102
+ "content" : BASE_SYSTEM_MESSAGE + (XML_FORMAT if use_xml else "" )
103
+ }
104
+
105
+ async def pre_iteration ():
106
+ files_tool = FilesTool ()
107
+ await files_tool ._init_workspace_state ()
108
+
109
+ async def after_iteration ():
110
+ custom_message = input ("\n Enter a message (or press Enter to continue): " )
111
+ message_content = custom_message if custom_message else "Continue!!!"
112
+ await thread_manager .add_message (thread_id , {
113
+ "role" : "user" ,
114
+ "content" : message_content
115
+ })
126
116
117
+ iteration = 0
118
+ while iteration < max_iterations :
119
+ iteration += 1
120
+ await pre_iteration ()
121
+
122
+ state = await state_manager .export_store ()
127
123
state_message = {
128
124
"role" : "user" ,
129
125
"content" : f"""
130
126
Current development environment workspace state:
131
- <current_workspace_state>
127
+ <current_workspace_state>
132
128
{ json .dumps (state , indent = 2 )}
133
129
</current_workspace_state>
134
130
"""
@@ -137,19 +133,19 @@ async def finalizer():
137
133
model_name = "anthropic/claude-3-5-sonnet-latest"
138
134
139
135
response = await thread_manager .run_thread (
140
- thread_id = thread_id ,
141
- system_message = system_message ,
142
- model_name = model_name ,
143
- temperature = 0.1 ,
144
- max_tokens = 8096 ,
145
- tool_choice = "auto" ,
146
- temporary_message = state_message ,
147
- native_tool_calling = False ,
148
- xml_tool_calling = True ,
149
- stream = True ,
150
- execute_tools_on_stream = True ,
151
- parallel_tool_execution = True
152
- )
136
+ thread_id = thread_id ,
137
+ system_message = system_message ,
138
+ model_name = model_name ,
139
+ temperature = 0.1 ,
140
+ max_tokens = 8096 ,
141
+ tool_choice = "auto" ,
142
+ temporary_message = state_message ,
143
+ native_tool_calling = not use_xml ,
144
+ xml_tool_calling = use_xml ,
145
+ stream = True ,
146
+ execute_tools_on_stream = True ,
147
+ parallel_tool_execution = True
148
+ )
153
149
154
150
if isinstance (response , AsyncGenerator ):
155
151
print ("\n 🤖 Assistant is responding:" )
@@ -158,19 +154,14 @@ async def finalizer():
158
154
if hasattr (chunk .choices [0 ], 'delta' ):
159
155
delta = chunk .choices [0 ].delta
160
156
161
- # Handle content streaming - print immediately without buffering
162
157
if hasattr (delta , 'content' ) and delta .content is not None :
163
158
print (delta .content , end = '' , flush = True )
164
159
165
- # Handle tool calls - print immediately
166
160
if hasattr (delta , 'tool_calls' ) and delta .tool_calls :
167
161
for tool_call in delta .tool_calls :
168
162
if tool_call .function :
169
- # Print tool name immediately when received
170
163
if tool_call .function .name :
171
164
print (f"\n 🛠️ Tool Call: { tool_call .function .name } " , flush = True )
172
-
173
- # Print arguments immediately when received
174
165
if tool_call .function .arguments :
175
166
print (f" { tool_call .function .arguments } " , end = '' , flush = True )
176
167
@@ -180,25 +171,45 @@ async def finalizer():
180
171
print (f"\n ❌ Error processing stream: { e } " , file = sys .stderr )
181
172
logging .error (f"Error processing stream: { e } " )
182
173
else :
183
- print ("\n ❌ Non -streaming response received:" , response )
174
+ print ("\n Non -streaming response received:" , response )
184
175
185
176
await after_iteration ()
186
177
187
- await finalizer ()
188
-
189
- if __name__ == "__main__" :
190
- async def main ():
178
+ def main ():
179
+ """Main entry point with synchronous setup."""
180
+ print ("\n 🚀 Welcome to AgentPress Web Developer Example!" )
181
+
182
+ project_description = input ("What would you like to build? (default: Create a modern, responsive landing page)\n > " )
183
+ if not project_description .strip ():
184
+ project_description = "Create a modern, responsive landing page"
185
+
186
+ print ("\n Choose your agent type:" )
187
+ print ("1. XML-based Tool Calling" )
188
+ print (" - Structured XML format for tool execution" )
189
+ print (" - Parses tool calls using XML outputs in the LLM response" )
190
+
191
+ print ("\n 2. Standard Function Calling" )
192
+ print (" - Native LLM function calling format" )
193
+ print (" - JSON-based parameter passing" )
194
+
195
+ use_xml = input ("\n Select tool calling format [1/2] (default: 1): " ).strip () != "2"
196
+
197
+ print (f"\n { 'XML-based' if use_xml else 'Standard' } agent will help you build: { project_description } " )
198
+ print ("Use Ctrl+C to stop the agent at any time." )
199
+
200
+ async def async_main ():
191
201
thread_manager = ThreadManager ()
192
202
thread_id = await thread_manager .create_thread ()
193
-
194
203
await thread_manager .add_message (
195
204
thread_id ,
196
205
{
197
206
"role" : "user" ,
198
- "content" : "Create a modern, responsive landing page with HTML, CSS and JS."
207
+ "content" : project_description
199
208
}
200
- )
209
+ )
210
+ await run_agent (thread_id , use_xml )
201
211
202
- await run_agent (thread_id )
203
-
204
- asyncio .run (main ())
212
+ asyncio .run (async_main ())
213
+
214
+ if __name__ == "__main__" :
215
+ main ()
0 commit comments