-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathhandoff.py
More file actions
executable file
Β·264 lines (216 loc) Β· 9.73 KB
/
Copy pathhandoff.py
File metadata and controls
executable file
Β·264 lines (216 loc) Β· 9.73 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
from agents import Agent, Runner
from openai import AsyncOpenAI
from agents import set_default_openai_api, set_default_openai_client, set_tracing_disabled, enable_verbose_stdout_logging, function_tool
from dotenv import load_dotenv
from contextManager import ProjectContext
import subprocess as subprocess
import os
import json
import time
context = ProjectContext
load_dotenv()
enable_verbose_stdout_logging()
# setting up endpoint
GEMINI_API_KEY =os.getenv("gemni_API_KEY")
if GEMINI_API_KEY is None :
raise ValueError("enviroment variable for api key is not set")
set_tracing_disabled(True)
set_default_openai_api("chat_completions")
externalClient = AsyncOpenAI(
api_key=GEMINI_API_KEY,
base_url="https://generativelanguage.googleapis.com/v1beta/openai/",
)
set_default_openai_client(externalClient)
# user's dynamic prompts
prompt = input("enter any task related to web development you want ai to do: ")
# ------------------instructions-----------------------------
def load_agent_prompt(filepath: str) -> str:
"""
Loads a structured agent prompt (list of dicts) from a JSON file,
and flattens it into a single instruction string.
"""
with open(filepath, "r", encoding="utf-8") as f:
data = json.load(f)
# If it's a list, merge each section into readable text
if isinstance(data, list):
merged_text = ""
for section in data:
for key, value in section.items():
merged_text += f"\n### {key.upper()} ###\n{value}\n"
return merged_text.strip()
# If it's already a string or object with `instructions`
elif isinstance(data, dict):
return data.get("instructions", "")
else:
raise TypeError("Invalid JSON format: must be list or dict")
# ----------------------------------------------------------
# --------------------comman instruction---------------------
default_instructions = """
Always respond with code or direct implementation unless the query is explicitly theoretical.
Use markdown formatting for all code blocks.
"""
# ----------TOOLS-----------------------------
@function_tool
async def run_cli_batch(commands: list[str]) -> str:
"""
Executes multiple CLI commands in sequence.
Detects if a long-running server starts or fails (non-blocking).
"""
results = []
print("\nπ οΈ Starting command batch...\n")
for cmd in commands:
print(f"β‘οΈ Running: {cmd}")
results.append(f"$ {cmd}")
try:
process = await asyncio.create_subprocess_shell(
cmd,
stdout=asyncio.subprocess.PIPE,
stderr=asyncio.subprocess.PIPE,
)
start_time = time.time()
output_buffer = ""
# Read logs in real-time for a few seconds (like a health check)
while True:
line = await process.stdout.readline()
if not line:
break
decoded = line.decode().strip()
output_buffer += decoded + "\n"
print(decoded)
# β
Detect successful startup
if any(keyword in decoded.lower() for keyword in ["localhost", "vite", "listening", "server running", "compiled successfully"]):
print("β
Server seems to have started successfully.")
results.append("β
Server started successfully.")
break
# β Detect error in logs
if any(keyword in decoded.lower() for keyword in ["error", "failed", "exception", "crash"]):
print("β Error detected during startup.")
results.append("β Error detected:\n" + decoded)
break
# β±οΈ Timeout check
if time.time() - start_time > 15:
print("β³ Timeout waiting for server output.")
results.append("β³ Timeout waiting for server output.")
break
# Kill process if still running
if process.returncode is None:
process.terminate()
try:
await asyncio.wait_for(process.wait(), timeout=3)
except asyncio.TimeoutError:
process.kill()
except Exception as e:
print(f"β οΈ Exception running '{cmd}': {e}\n")
results.append(f"β οΈ Error: {str(e)}")
print("π Command batch completed.\n")
return "\n".join(results)
# ------------------SUB-AGENTS-------
front_end_Agent = Agent(
name="front_end_Agnent",
model="gemini-2.5-flash",
instructions=f"""{default_instructions}
You are a frontend developer agent with expertise in React.js, Next.js, HTML, CSS, Bootstrap, and TailwindCSS.
- Always start by implementing the complete frontend portion of the project.
- you have acess to a tool called run_cli_command to run the command to create an deletefiles and directory an update them.
- If the project also includes backend, API, or database work,
first write the entire frontend code (folder structure + files),
THEN hand off to "Back_end_Agnent" using:
( "handoff_to": "Back_end_Agnent", "reason":"<reason>")
- Always include:
* Full React project structure (e.g., src/, components/, pages/, etc.)
* Working code for each file
* Minimal inline comments
- Never skip your part or just describe what to do β always provide runnable frontend code.
""",
tools=[run_cli_batch],
handoffs=[]
)
back_end_Agent = Agent(
name="Back_end_Agnent",
model="gemini-2.5-flash",
instructions= default_instructions + """
You are a backend developer agent with expertise in Node.js, Express.js, MongoDB, Mongoose, JWT, cookies, and RESTful APIs.
Your job is to write actual backend code β not outlines or plans.
https://www.youtube.com/watch?v=4bb5K9bN-0Y - you have acess to a tool called run_cli_command to run the command to create an deletefiles and directory an update them.
- Always produce executable Express.js code, including routes, models, and controllers.
- Include comments and minimal setup instructions.
- Do NOT ask the user if they want to proceed β just write the code.
- If the request involves frontend, UI, or design, handoff to "front_end_Agnent" using:
{"handoff_to": "front_end_Agnent", "reason": "<reason>"}
""",
tools=[run_cli_batch],
handoffs=[]
)
front_end_Agent.handoffs.append(back_end_Agent)
back_end_Agent.handoffs.append(front_end_Agent)
prompt_path = os.path.join("instructions", "mainAgent.json")
main_agent_instructions = load_agent_prompt(prompt_path)
# ------main agent -------------------------
webDevAgent = Agent(
name="full-stack developer agent",
model="gemini-2.5-flash",
instructions = main_agent_instructions,
tools=[run_cli_batch],
handoffs=[front_end_Agent, back_end_Agent],
)
# -----runner class-----------------
# async def main():
# print("here is the availible context for agent: ", context)
# result = await Runner.run(webDevAgent, input=prompt, max_turns=50, context=context)
# print("\n--- π§ AGENT CHAIN COMPLETE ---\n")
# print("πΉ Last agent:", result.last_agent.name)
# print("πΉ Final output:\n", result.final_output)
# # π§© Optional debugging info
# # If history is not supported, print raw data
# history = getattr(result, "history", None)
# if history:
# print("\n--- π AGENT CHAIN DETAILS (history) ---")
# for step in history:
# print(f"\nAgent: {step.agent.name}")
# print("Output:\n", step.output)
# else:
# print("\n(No detailed handoff history available in this version of the library.)")
async def main():
print("here is the available context for agent:", context)
current_agent = webDevAgent
turn = 1
while True:
print(f"\nπ Running {current_agent.name} (turn {turn})\n")
result = await Runner.run(current_agent, input=prompt, max_turns=50, context=context)
print("\n--- π§ AGENT CHAIN COMPLETE ---\n")
print("πΉ Last agent:", result.last_agent.name)
print("πΉ Final output:\n", result.final_output)
# π§© Optional debugging info
if hasattr(result, "history"):
print("\n--- π AGENT CHAIN DETAILS (history) ---")
for step in result.history:
print(f"\nAgent: {step.agent.name}")
print("Output:\n", step.output)
else:
print("\n(No detailed handoff history available in this version of the library.)")
# π§ Detect handoff from final output
if isinstance(result.final_output, dict) and "handoff_to" in result.final_output:
next_agent_name = result.final_output["handoff_to"]
reason = result.final_output.get("reason", "No reason provided")
print(f"\nπ€ Handoff detected: {current_agent.name} β {next_agent_name}")
print(f"Reason: {reason}\n")
# Lookup agent object by name (assuming you have them defined)
agent_map = {
"front_end_Agnent": front_end_Agent,
"Back_end_Agnent": back_end_Agent,
"Main_Agent": webDevAgent,
"full-stack developer agent": webDevAgent,
}
next_agent = agent_map.get(next_agent_name)
if not next_agent:
print(f"β οΈ Unknown agent '{next_agent_name}'. Stopping chain.")
break
current_agent = next_agent
turn += 1
continue # Run next agent
else:
print("\nπ No further handoffs. Task chain completed.\n")
break
if __name__ == "__main__":
import asyncio
asyncio.run(main())