rejig-ai Claude commited on
Commit
f9ff014
·
1 Parent(s): 5ff7a49

Refactor agent tools to use agents-as-tools pattern

Browse files

- Replace broken function tools with agent.as_tool() convenience methods
- Remove circular import issues from agent_tools.py
- Simplify orchestrator configuration in agents.py
- Update chat interface to use direct orchestrator calls
- Maintain all existing functionality while fixing reliability issues

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>

agent_system/agent_tools.py CHANGED
@@ -1,411 +1,3 @@
1
- """
2
- Factory functions to create context-aware tools from agents.
3
- """
4
- from typing import Dict, Any, List, Optional, Callable
5
- from agents import Agent, Runner, RunResult, function_tool
6
- from .context import ConversationContext, Artifact, ArtifactType
7
- from .agents import websearch_agent, script_agent, video_creator_agent
8
- from .video_handler import handle_script_to_video
9
- from .video_avatar_agent import VideoAvatarAgent
10
- import logging
11
- import json
12
- from datetime import datetime
13
-
14
- logger = logging.getLogger(__name__)
15
-
16
-
17
- def create_search_tool(context: ConversationContext):
18
- """Create a context-aware search tool."""
19
-
20
- @function_tool
21
- async def search_web(query: str, search_type: Optional[str] = None) -> Dict[str, Any]:
22
- """Search the web for current information.
23
-
24
- Args:
25
- query: What to search for
26
- search_type: Optional type (news, financial, weather, general)
27
-
28
- Returns:
29
- Dictionary with search results and artifact ID
30
- """
31
- logger.info(f"Searching web for: {query}")
32
-
33
- # Build input for the search agent
34
- inputs = [{
35
- "role": "user",
36
- "content": query
37
- }]
38
-
39
- # Run the search agent
40
- from .agents import run_websearch
41
- result = run_websearch(inputs) # Don't await - it returns RunResultStreaming
42
-
43
- # Process the streaming result to get the final output
44
- full_response = ""
45
- async for chunk in result.stream_events():
46
- if hasattr(chunk, 'data') and hasattr(chunk.data, 'delta'):
47
- full_response += chunk.data.delta
48
-
49
- # Extract structured data from response
50
- # Try to identify sources and key results
51
- lines = full_response.split('\n')
52
- results = []
53
- sources = []
54
-
55
- for line in lines:
56
- line = line.strip()
57
- if line.startswith('•') or line.startswith('-') or line.startswith('*'):
58
- results.append(line[1:].strip())
59
- elif 'Source:' in line or 'source:' in line:
60
- sources.append(line)
61
-
62
- # If no structured results, use the full response
63
- if not results:
64
- results = [full_response]
65
-
66
- # Create artifact
67
- search_content = {
68
- "query": query,
69
- "search_type": search_type or "general",
70
- "results": results,
71
- "sources": sources,
72
- "full_response": full_response,
73
- "timestamp": datetime.now().isoformat()
74
- }
75
-
76
- artifact = Artifact(
77
- type=ArtifactType.SEARCH_RESULTS,
78
- content=search_content,
79
- created_by="websearch_agent",
80
- metadata={
81
- "summary": f"Search for '{query}' - {len(results)} results found"
82
- }
83
- )
84
-
85
- context.add_artifact(artifact)
86
-
87
- return {
88
- "artifact_id": artifact.id,
89
- "query": query,
90
- "results": results,
91
- "sources": sources,
92
- "summary": f"Found {len(results)} results for '{query}'"
93
- }
94
-
95
- # Return the decorated function
96
- return search_web
97
-
98
-
99
- def create_script_tool(context: ConversationContext):
100
- """Create a context-aware script creation/modification tool."""
101
-
102
- @function_tool
103
- async def create_or_modify_script(
104
- topic: Optional[str] = None,
105
- style: str = "video",
106
- role: Optional[str] = None,
107
- context_artifacts: Optional[List[str]] = None,
108
- original_script_id: Optional[str] = None,
109
- modifications: Optional[str] = None,
110
- user_provided_script: Optional[str] = None
111
- ) -> Dict[str, Any]:
112
- """Create a new script or modify an existing one.
113
-
114
- Args:
115
- topic: Topic for new script (required for new scripts)
116
- style: Type of script (video, podcast, article)
117
- role: Expert role to adopt
118
- context_artifacts: List of artifact IDs to use as context
119
- original_script_id: ID of script to modify
120
- modifications: What changes to make
121
- user_provided_script: Direct script content from user
122
-
123
- Returns:
124
- Dictionary with script content and artifact ID
125
- """
126
- logger.info(f"Script operation - modify: {bool(original_script_id)}, topic: {topic}")
127
-
128
- # Get context data if provided
129
- context_data = {}
130
- if context_artifacts:
131
- context_data = context.get_artifact_content_for_agent(context_artifacts)
132
- logger.info(f"Using {len(context_data)} context artifacts")
133
-
134
- # Handle modifications
135
- if original_script_id and modifications:
136
- original_artifact = context.get_artifact_by_id(original_script_id)
137
- if not original_artifact:
138
- raise ValueError(f"Original script {original_script_id} not found")
139
-
140
- # Build input for modification
141
- original_content = original_artifact.content
142
-
143
- # Use the script agent to modify
144
- modify_prompt = f"""
145
- Please modify the following script based on these instructions: {modifications}
146
-
147
- Original script:
148
- Title: {original_content.get('title', 'Untitled')}
149
- Content: {original_content.get('script', original_content.get('content', ''))}
150
-
151
- Maintain the same format and structure, only applying the requested modifications.
152
- """
153
-
154
- inputs = [{"role": "user", "content": modify_prompt}]
155
-
156
- # Handle user-provided script
157
- elif user_provided_script:
158
- logger.info("Using user-provided script")
159
- # Create structured script data
160
- script_content = {
161
- "title": "User Provided Script",
162
- "script": user_provided_script,
163
- "style": style,
164
- "keySummaryPoints": ["Custom script provided by user"],
165
- "metadata": {
166
- "user_provided": True,
167
- "created_at": datetime.now().isoformat()
168
- }
169
- }
170
-
171
- # Create artifact
172
- artifact = Artifact(
173
- type=ArtifactType.SCRIPT,
174
- content=script_content,
175
- created_by="script_agent",
176
- metadata={
177
- "summary": f"User provided script: {user_provided_script[:100]}..."
178
- }
179
- )
180
-
181
- context.add_artifact(artifact)
182
-
183
- return {
184
- "artifact_id": artifact.id,
185
- "title": script_content["title"],
186
- "script": script_content["script"],
187
- "type": "user_provided"
188
- }
189
-
190
- # Handle new script creation
191
- else:
192
- if not topic:
193
- raise ValueError("Topic is required for new script creation")
194
-
195
- # Build enhanced prompt with professional structure
196
- prompt_parts = [
197
- f"You are a professional content specialist creating a high-quality {style} script about {topic}.",
198
- "",
199
- "Follow this structured approach:",
200
- "",
201
- "1. ANALYZE the provided information and identify 3-5 key facts or insights",
202
- "2. IDENTIFY the most compelling angle that makes this topic timely and relevant",
203
- "3. CRAFT a professional video script with:",
204
- " - A strong hook (first sentence to grab attention immediately)",
205
- " - Core message with specific facts, numbers, and impact",
206
- " - Clear relevance (why this matters now)",
207
- " - Engaging call-to-action inviting comments or interaction",
208
- "",
209
- "SCRIPT REQUIREMENTS:",
210
- "- Keep script under 45 seconds when spoken aloud",
211
- "- Include specific data points and numbers when available",
212
- "- Focus on quality, impact, and concrete information",
213
- "- Write as natural, flowing sentences (NO numbering or bullets)",
214
- "- Create a conversational tone that flows smoothly when spoken",
215
- "- End with a contextually relevant call-to-action",
216
- "",
217
- "TITLE REQUIREMENTS:",
218
- "- Create a compelling title (5-10 words)",
219
- "- Make it descriptive, informative, and engaging",
220
- "- Include a question if it fits naturally",
221
- ""
222
- ]
223
-
224
- if context_data:
225
- prompt_parts.append("INFORMATION TO USE:")
226
- for key, data in context_data.items():
227
- prompt_parts.append(f"\n{data['type'].upper()} from {data['created_by']}:")
228
- if data['type'] == 'search_results':
229
- prompt_parts.append(f"Query: {data['content'].get('query', '')}")
230
- for result in data['content'].get('results', [])[:5]:
231
- prompt_parts.append(f"- {result}")
232
- else:
233
- prompt_parts.append(json.dumps(data['content'], indent=2)[:500])
234
- prompt_parts.append("")
235
-
236
- if role:
237
- prompt_parts.append(f"EXPERT ROLE: Adopt the perspective and expertise of a {role}.")
238
- prompt_parts.append("")
239
-
240
- prompt_parts.extend([
241
- "Provide a compelling title and professional video script.",
242
- "Focus on accuracy, engagement, and clear communication."
243
- ])
244
-
245
- inputs = [{"role": "user", "content": "\n".join(prompt_parts)}]
246
-
247
- # Run the script agent
248
- result = await Runner.run(script_agent, inputs)
249
- script_output = result.final_output
250
-
251
- # Parse the output - it should be JSON from the agent
252
- try:
253
- if isinstance(script_output, str):
254
- # Try to parse JSON from the response
255
- import re
256
- json_match = re.search(r'\{.*\}', script_output, re.DOTALL)
257
- if json_match:
258
- script_data = json.loads(json_match.group())
259
- else:
260
- # Fallback: create basic structure
261
- script_data = {
262
- "title": topic or "Modified Script",
263
- "script": script_output,
264
- "keySummaryPoints": []
265
- }
266
- else:
267
- script_data = script_output
268
- except:
269
- # Fallback for non-JSON response
270
- script_data = {
271
- "title": topic or "Script",
272
- "script": str(script_output),
273
- "keySummaryPoints": []
274
- }
275
-
276
- # Create artifact
277
- script_content = {
278
- "title": script_data.get("title", "Untitled"),
279
- "script": script_data.get("script", script_data.get("content", "")),
280
- "style": style,
281
- "keySummaryPoints": script_data.get("keySummaryPoints", []),
282
- "metadata": {
283
- "role": role,
284
- "context_used": bool(context_data),
285
- "modified": bool(original_script_id)
286
- }
287
- }
288
-
289
- artifact = Artifact(
290
- type=ArtifactType.SCRIPT,
291
- content=script_content,
292
- created_by="script_agent",
293
- metadata={
294
- "summary": f"{script_content['title']} - {style} script"
295
- }
296
- )
297
-
298
- context.add_artifact(artifact)
299
-
300
- return {
301
- "artifact_id": artifact.id,
302
- "title": script_content["title"],
303
- "script": script_content["script"],
304
- "key_points": script_content["keySummaryPoints"]
305
- }
306
-
307
- # Return the decorated function
308
- return create_or_modify_script
309
-
310
-
311
- def create_video_tool(context: ConversationContext):
312
- """Create a context-aware video creation tool."""
313
-
314
- @function_tool
315
- async def create_video(
316
- script_id: Optional[str] = None,
317
- script_content: Optional[str] = None,
318
- title: Optional[str] = None
319
- ) -> Dict[str, Any]:
320
- """Create a video from a script.
321
-
322
- Args:
323
- script_id: ID of script artifact to use
324
- script_content: Direct script content (if not using artifact)
325
- title: Optional title for the video
326
-
327
- Returns:
328
- Dictionary with video URL and artifact ID
329
- """
330
- logger.info(f"Creating video - script_id: {script_id}, direct content: {bool(script_content)}")
331
-
332
- # Get script content
333
- if script_id:
334
- script_artifact = context.get_artifact_by_id(script_id)
335
- if not script_artifact:
336
- raise ValueError(f"Script {script_id} not found")
337
-
338
- script_content = script_artifact.content.get("script", "")
339
- if not title:
340
- title = script_artifact.content.get("title", "Video")
341
- elif not script_content:
342
- # Try to get the latest script
343
- latest_script = context.get_latest(ArtifactType.SCRIPT)
344
- if latest_script:
345
- script_content = latest_script.content.get("script", "")
346
- title = title or latest_script.content.get("title", "Video")
347
- else:
348
- raise ValueError("No script provided or found")
349
-
350
- if not script_content:
351
- raise ValueError("Script content is empty")
352
-
353
- # Use VideoAvatarAgent directly for proper agent-based approach
354
- try:
355
- video_agent = VideoAvatarAgent()
356
- video_result = await video_agent.generate_video_from_script(script_content)
357
- video_url = video_result.get("video_url") or video_result.get("share_url")
358
-
359
- if not video_url:
360
- raise ValueError("No video URL returned from video generation")
361
-
362
- except Exception as e:
363
- logger.error(f"Error in video creation: {e}")
364
- raise ValueError(f"Failed to create video: {str(e)}")
365
-
366
- # Create artifact
367
- video_content = {
368
- "title": title or "Generated Video",
369
- "video_url": video_url,
370
- "script_used": script_content[:200] + "..." if len(script_content) > 200 else script_content,
371
- "created_at": datetime.now().isoformat()
372
- }
373
-
374
- artifact = Artifact(
375
- type=ArtifactType.VIDEO,
376
- content=video_content,
377
- created_by="video_creator_agent",
378
- metadata={
379
- "summary": f"Video: {title or 'Generated'} - {video_url}"
380
- }
381
- )
382
-
383
- context.add_artifact(artifact)
384
-
385
- return {
386
- "artifact_id": artifact.id,
387
- "video_url": video_url,
388
- "title": video_content["title"],
389
- "status": "completed"
390
- }
391
-
392
- # Return the decorated function
393
- return create_video
394
-
395
-
396
- def create_tools_for_orchestrator(context: ConversationContext) -> List[Callable]:
397
- """Create all tools for the orchestrator with context awareness.
398
-
399
- Args:
400
- context: The conversation context to use
401
-
402
- Returns:
403
- List of tool functions
404
- """
405
- tools = [
406
- create_search_tool(context),
407
- create_script_tool(context),
408
- create_video_tool(context)
409
- ]
410
-
411
- return tools
 
1
+ # REMOVED: This file's functionality has been replaced by agent.as_tool() calls
2
+ # The broken function tools in this file were causing circular import issues
3
+ # All functionality has been moved to agents.py using the agents-as-tools pattern
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
agent_system/agents.py CHANGED
@@ -15,6 +15,8 @@ from .video_avatar_agent import VideoAvatarAgent
15
 
16
  logger = logging.getLogger(__name__)
17
 
 
 
18
  # Authentication tool for the auth_agent
19
  @function_tool
20
  def authenticate_user(email: str, conversation_history: str = "") -> Dict[str, Any]:
@@ -241,7 +243,7 @@ orchestrator_agent = Agent(
241
 
242
  AUTHENTICATION CHECK:
243
  First, check if the user is authenticated by looking for "#AUTH_STATE#" markers.
244
- If not authenticated, use the auth_handoff to route to auth_agent.
245
 
246
  For authenticated users, you have access to specialized agents as tools. You can:
247
  1. Call multiple tools in sequence to accomplish complex tasks
@@ -304,21 +306,46 @@ orchestrator_agent = Agent(
304
  → create_or_modify_script(original_script_id=last_script_id, modifications="change temperature to 75 degrees")
305
 
306
  5. "Tell me a joke about AI"
307
- → Use joke_handoff to route to joke_agent
308
 
309
  6. "Write a poem about the search results"
310
- → Use poem_handoff with context about the last search
311
 
312
  IMPORTANT: Think step by step. For requests involving current data, always search first.
313
- For creative content (jokes, poems), use the appropriate handoff agents.
314
  """,
315
- handoffs=[
316
- auth_agent, # For authentication
317
- joke_agent, # For jokes
318
- poem_agent, # For poems
319
- ],
320
- # Tools will be added dynamically in the chat interface
321
- tools=[]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
322
  )
323
 
324
  # Keep triage_agent as alias for backward compatibility during transition
 
15
 
16
  logger = logging.getLogger(__name__)
17
 
18
+ # Removed broken function tools import - using agent.as_tool() convenience methods instead
19
+
20
  # Authentication tool for the auth_agent
21
  @function_tool
22
  def authenticate_user(email: str, conversation_history: str = "") -> Dict[str, Any]:
 
243
 
244
  AUTHENTICATION CHECK:
245
  First, check if the user is authenticated by looking for "#AUTH_STATE#" markers.
246
+ If not authenticated, use the authenticate_user tool.
247
 
248
  For authenticated users, you have access to specialized agents as tools. You can:
249
  1. Call multiple tools in sequence to accomplish complex tasks
 
306
  → create_or_modify_script(original_script_id=last_script_id, modifications="change temperature to 75 degrees")
307
 
308
  5. "Tell me a joke about AI"
309
+ → Use tell_joke tool
310
 
311
  6. "Write a poem about the search results"
312
+ → Use write_poem tool with context about the last search
313
 
314
  IMPORTANT: Think step by step. For requests involving current data, always search first.
315
+ For creative content (jokes, poems), use the appropriate tools (tell_joke, write_poem).
316
  """,
317
+ handoffs=[], # Removed - using pure agents-as-tools pattern
318
+ tools=[
319
+ # Authentication tool - validates user emails
320
+ auth_agent.as_tool(
321
+ tool_name="authenticate_user",
322
+ tool_description="Authenticate user by validating their email address against registered user accounts. Returns auth status and user profile information."
323
+ ),
324
+
325
+ # Creative content generation tools
326
+ joke_agent.as_tool(
327
+ tool_name="tell_joke",
328
+ tool_description="Generate a humorous, family-friendly joke related to the user's request or specified topic."
329
+ ),
330
+ poem_agent.as_tool(
331
+ tool_name="write_poem",
332
+ tool_description="Create a creative poem using appropriate poetic structures related to the user's request or specified topic."
333
+ ),
334
+
335
+ # Information and workflow tools
336
+ websearch_agent.as_tool(
337
+ tool_name="search_web",
338
+ tool_description="Search the web for current information on any topic. Returns factual summaries with sources and citations."
339
+ ),
340
+ script_agent.as_tool(
341
+ tool_name="create_or_modify_script",
342
+ tool_description="Create a new video script or modify an existing script based on topic, style, and context artifacts. Supports professional video script generation."
343
+ ),
344
+ video_creator_agent.as_tool(
345
+ tool_name="create_video",
346
+ tool_description="Generate an AI avatar video using HeyGen API from a provided script. Returns video URL and metadata."
347
+ )
348
+ ]
349
  )
350
 
351
  # Keep triage_agent as alias for backward compatibility during transition
ui/chat_interface.py CHANGED
@@ -10,7 +10,6 @@ from agent_system.agents import orchestrator_agent, process_agent_response
10
  from agent_system.auth import get_auth_state_from_history, AuthState
11
  from agent_system.video_handler import handle_video_with_stages, handle_script_to_video
12
  from agent_system.context import ConversationContext, Artifact, ArtifactType
13
- from agent_system.agent_tools import create_tools_for_orchestrator
14
  from typing import List, Dict, Any, Optional
15
 
16
  logger = logging.getLogger(__name__)
@@ -20,17 +19,7 @@ class ChatState:
20
  """Maintains conversation state across messages."""
21
  def __init__(self):
22
  self.context = ConversationContext()
23
- self.orchestrator = None
24
- self._setup_orchestrator()
25
-
26
- def _setup_orchestrator(self):
27
- """Initialize orchestrator with context-aware tools."""
28
- # Create tools that have access to context
29
- tools = create_tools_for_orchestrator(self.context)
30
-
31
- # Clone orchestrator and add tools
32
- self.orchestrator = orchestrator_agent.clone(tools=tools)
33
- logger.info(f"Orchestrator initialized with {len(tools)} tools")
34
 
35
  # Custom chat function for Gradio interface
36
  async def chat_with_agents(message: str, history: List[Dict[str, Any]], state: Optional[ChatState] = None):
@@ -89,26 +78,37 @@ async def chat_with_agents(message: str, history: List[Dict[str, Any]], state: O
89
  # Format the message with context information
90
  context_summary = state.context.get_context_summary()
91
 
92
- # Create a message that includes context
 
 
 
 
 
 
 
 
 
 
93
  orchestrator_message = f"""User message: {message}
94
 
95
  Current context:
96
  - Available artifacts: {len(context_summary['available_artifacts'])}
97
  - Last artifacts by type: {context_summary.get('last_artifacts', {})}
98
 
99
- Please process this request considering the available context."""
100
 
101
- orchestrator_input = [{
102
  "role": "user",
103
  "content": orchestrator_message
104
- }]
105
 
106
  logger.info(f"Sending to orchestrator - artifacts available: {len(state.context.artifacts)}")
107
 
108
  # Run orchestrator
109
  result = Runner.run_streamed(
110
- state.orchestrator,
111
- input=orchestrator_input
 
112
  )
113
 
114
  # Create a copy of history and add the user message
@@ -130,7 +130,7 @@ Please process this request considering the available context."""
130
  return
131
 
132
  # For non-authenticated users, still use orchestrator (it will route to auth)
133
- result = Runner.run_streamed(state.orchestrator, input=inputs)
134
 
135
  # Create a copy of history and add the user message
136
  new_history = history.copy()
 
10
  from agent_system.auth import get_auth_state_from_history, AuthState
11
  from agent_system.video_handler import handle_video_with_stages, handle_script_to_video
12
  from agent_system.context import ConversationContext, Artifact, ArtifactType
 
13
  from typing import List, Dict, Any, Optional
14
 
15
  logger = logging.getLogger(__name__)
 
19
  """Maintains conversation state across messages."""
20
  def __init__(self):
21
  self.context = ConversationContext()
22
+ logger.info("Created new ChatState with fresh ConversationContext")
 
 
 
 
 
 
 
 
 
 
23
 
24
  # Custom chat function for Gradio interface
25
  async def chat_with_agents(message: str, history: List[Dict[str, Any]], state: Optional[ChatState] = None):
 
78
  # Format the message with context information
79
  context_summary = state.context.get_context_summary()
80
 
81
+ # Prepare full conversation history for orchestrator
82
+ orchestrator_input = []
83
+
84
+ # Add conversation history
85
+ for msg in history:
86
+ orchestrator_input.append({
87
+ "role": msg["role"],
88
+ "content": msg["content"]
89
+ })
90
+
91
+ # Add current user message with context
92
  orchestrator_message = f"""User message: {message}
93
 
94
  Current context:
95
  - Available artifacts: {len(context_summary['available_artifacts'])}
96
  - Last artifacts by type: {context_summary.get('last_artifacts', {})}
97
 
98
+ Please process this request considering the available context and conversation history."""
99
 
100
+ orchestrator_input.append({
101
  "role": "user",
102
  "content": orchestrator_message
103
+ })
104
 
105
  logger.info(f"Sending to orchestrator - artifacts available: {len(state.context.artifacts)}")
106
 
107
  # Run orchestrator
108
  result = Runner.run_streamed(
109
+ orchestrator_agent,
110
+ input=orchestrator_input,
111
+ context=state.context
112
  )
113
 
114
  # Create a copy of history and add the user message
 
130
  return
131
 
132
  # For non-authenticated users, still use orchestrator (it will route to auth)
133
+ result = Runner.run_streamed(orchestrator_agent, input=inputs, context=state.context)
134
 
135
  # Create a copy of history and add the user message
136
  new_history = history.copy()