mbxai 2.1.3__py3-none-any.whl → 2.3.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
mbxai/agent/models.py CHANGED
@@ -2,8 +2,10 @@
2
2
  Pydantic models for the agent client.
3
3
  """
4
4
 
5
- from typing import Any, Optional
5
+ from typing import Any, Optional, Union, Callable, Protocol, Dict
6
6
  from pydantic import BaseModel, Field, field_validator
7
+ from enum import Enum
8
+ from abc import ABC, abstractmethod
7
9
  import uuid
8
10
  import re
9
11
 
@@ -41,20 +43,6 @@ class Result(BaseModel):
41
43
  result: str = Field(description="The result text from the AI")
42
44
 
43
45
 
44
- class AgentResponse(BaseModel):
45
- """Response from the agent that can contain questions or a final result."""
46
- agent_id: str = Field(default_factory=lambda: str(uuid.uuid4()), description="Unique identifier for this agent session")
47
- questions: list[Question] = Field(default_factory=list, description="List of questions for the user")
48
- final_response: Optional[Any] = Field(default=None, description="The final response if processing is complete")
49
- token_summary: Optional["TokenSummary"] = Field(default=None, description="Summary of token usage for this agent process")
50
-
51
- def has_questions(self) -> bool:
52
- """Check if this response has questions that need to be answered."""
53
- return len(self.questions) > 0
54
-
55
- def is_complete(self) -> bool:
56
- """Check if this response contains a final result."""
57
- return self.final_response is not None
58
46
 
59
47
 
60
48
  class QuestionList(BaseModel):
@@ -88,20 +76,24 @@ class TokenUsage(BaseModel):
88
76
 
89
77
  class TokenSummary(BaseModel):
90
78
  """Summary of token usage across all API calls in an agent process."""
91
- question_generation: TokenUsage = Field(default_factory=TokenUsage, description="Tokens used for question generation")
92
- thinking_process: TokenUsage = Field(default_factory=TokenUsage, description="Tokens used for thinking/processing")
93
- quality_checks: list[TokenUsage] = Field(default_factory=list, description="Tokens used for each quality check iteration")
94
- improvements: list[TokenUsage] = Field(default_factory=list, description="Tokens used for each improvement iteration")
79
+ requirement_analysis: TokenUsage = Field(default_factory=TokenUsage, description="Tokens used for requirement analysis")
80
+ tool_analysis: TokenUsage = Field(default_factory=TokenUsage, description="Tokens used for tool analysis")
81
+ todo_generation: TokenUsage = Field(default_factory=TokenUsage, description="Tokens used for todo list generation")
82
+ task_execution: list[TokenUsage] = Field(default_factory=list, description="Tokens used for each task execution")
83
+ dialog_interactions: list[TokenUsage] = Field(default_factory=list, description="Tokens used for human-in-the-loop interactions")
84
+ goal_evaluation: TokenUsage = Field(default_factory=TokenUsage, description="Tokens used for goal evaluation")
95
85
  final_response: TokenUsage = Field(default_factory=TokenUsage, description="Tokens used for final response generation")
96
86
 
97
87
  @property
98
88
  def total_tokens(self) -> int:
99
89
  """Calculate total tokens used across all operations."""
100
90
  total = (
101
- self.question_generation.total_tokens +
102
- self.thinking_process.total_tokens +
103
- sum(usage.total_tokens for usage in self.quality_checks) +
104
- sum(usage.total_tokens for usage in self.improvements) +
91
+ self.requirement_analysis.total_tokens +
92
+ self.tool_analysis.total_tokens +
93
+ self.todo_generation.total_tokens +
94
+ sum(usage.total_tokens for usage in self.task_execution) +
95
+ sum(usage.total_tokens for usage in self.dialog_interactions) +
96
+ self.goal_evaluation.total_tokens +
105
97
  self.final_response.total_tokens
106
98
  )
107
99
  return total
@@ -110,10 +102,12 @@ class TokenSummary(BaseModel):
110
102
  def total_prompt_tokens(self) -> int:
111
103
  """Calculate total prompt tokens used across all operations."""
112
104
  total = (
113
- self.question_generation.prompt_tokens +
114
- self.thinking_process.prompt_tokens +
115
- sum(usage.prompt_tokens for usage in self.quality_checks) +
116
- sum(usage.prompt_tokens for usage in self.improvements) +
105
+ self.requirement_analysis.prompt_tokens +
106
+ self.tool_analysis.prompt_tokens +
107
+ self.todo_generation.prompt_tokens +
108
+ sum(usage.prompt_tokens for usage in self.task_execution) +
109
+ sum(usage.prompt_tokens for usage in self.dialog_interactions) +
110
+ self.goal_evaluation.prompt_tokens +
117
111
  self.final_response.prompt_tokens
118
112
  )
119
113
  return total
@@ -122,10 +116,249 @@ class TokenSummary(BaseModel):
122
116
  def total_completion_tokens(self) -> int:
123
117
  """Calculate total completion tokens used across all operations."""
124
118
  total = (
125
- self.question_generation.completion_tokens +
126
- self.thinking_process.completion_tokens +
127
- sum(usage.completion_tokens for usage in self.quality_checks) +
128
- sum(usage.completion_tokens for usage in self.improvements) +
119
+ self.requirement_analysis.completion_tokens +
120
+ self.tool_analysis.completion_tokens +
121
+ self.todo_generation.completion_tokens +
122
+ sum(usage.completion_tokens for usage in self.task_execution) +
123
+ sum(usage.completion_tokens for usage in self.dialog_interactions) +
124
+ self.goal_evaluation.completion_tokens +
129
125
  self.final_response.completion_tokens
130
126
  )
131
127
  return total
128
+
129
+
130
+ # New models for the enhanced agent architecture
131
+
132
+ class TaskStatus(str, Enum):
133
+ """Status of a task in the todo list."""
134
+ PENDING = "pending"
135
+ IN_PROGRESS = "in_progress"
136
+ COMPLETED = "completed"
137
+ FAILED = "failed"
138
+ SKIPPED = "skipped"
139
+
140
+
141
+ class Task(BaseModel):
142
+ """A task in the agent's todo list."""
143
+ id: str = Field(default_factory=lambda: str(uuid.uuid4()), description="Unique identifier for the task")
144
+ title: str = Field(description="Short title of the task")
145
+ description: str = Field(description="Detailed description of what needs to be done")
146
+ status: TaskStatus = Field(default=TaskStatus.PENDING, description="Current status of the task")
147
+ dependencies: list[str] = Field(default_factory=list, description="List of task IDs that must be completed before this task")
148
+ tools_needed: list[str] = Field(default_factory=list, description="List of tool names needed for this task")
149
+ estimated_complexity: int = Field(default=1, ge=1, le=5, description="Complexity rating from 1-5")
150
+ result: Optional[str] = Field(default=None, description="Result of the task execution")
151
+ error_message: Optional[str] = Field(default=None, description="Error message if task failed")
152
+
153
+
154
+ class TodoList(BaseModel):
155
+ """A list of tasks to complete the user's goal."""
156
+ tasks: list[Task] = Field(description="List of tasks to complete")
157
+ estimated_total_time: Optional[str] = Field(default=None, description="Estimated total time to complete all tasks")
158
+
159
+ def get_next_task(self) -> Optional[Task]:
160
+ """Get the next pending task that has all dependencies completed."""
161
+ completed_task_ids = {task.id for task in self.tasks if task.status == TaskStatus.COMPLETED}
162
+
163
+ for task in self.tasks:
164
+ if task.status == TaskStatus.PENDING:
165
+ # Check if all dependencies are completed
166
+ if all(dep_id in completed_task_ids for dep_id in task.dependencies):
167
+ return task
168
+ return None
169
+
170
+ def get_task_by_id(self, task_id: str) -> Optional[Task]:
171
+ """Get a task by its ID."""
172
+ for task in self.tasks:
173
+ if task.id == task_id:
174
+ return task
175
+ return None
176
+
177
+
178
+ class HumanInteractionType(str, Enum):
179
+ """Type of human interaction needed."""
180
+ DECISION = "decision"
181
+ QUESTION = "question"
182
+ DIALOG_OPTION = "dialog_option"
183
+
184
+
185
+ class DialogOption(BaseModel):
186
+ """A dialog option for structured human-in-the-loop communication.
187
+
188
+ Dialog options are UI-aware patterns that tell the frontend how to handle
189
+ specific interactions (authentication, approvals, integrations, etc.).
190
+ They are NOT functions executed by the agent - they're instructions for the UI.
191
+ """
192
+ id: str = Field(description="Unique identifier for the dialog option")
193
+ title: str = Field(description="Short title of the dialog option")
194
+ description: str = Field(description="Description of what this option does")
195
+ function: Optional[Callable[..., Any]] = Field(default=None, description="Optional function for backwards compatibility - prefer UI handling")
196
+ parameters: dict[str, Any] = Field(default_factory=dict, description="Parameters to help the UI handle this dialog (URLs, scopes, etc.)")
197
+
198
+ class Config:
199
+ arbitrary_types_allowed = True
200
+
201
+
202
+ class HumanInLoopRequest(BaseModel):
203
+ """A request for human interaction."""
204
+ id: str = Field(default_factory=lambda: str(uuid.uuid4()), description="Unique identifier for this interaction")
205
+ interaction_type: HumanInteractionType = Field(description="Type of interaction needed")
206
+ prompt: str = Field(description="The prompt/question for the human")
207
+ options: list[str] = Field(default_factory=list, description="Available options for decisions")
208
+ dialog_options: list[DialogOption] = Field(default_factory=list, description="Available dialog options")
209
+ context: str = Field(default="", description="Additional context for the interaction")
210
+ required: bool = Field(default=True, description="Whether this interaction is required")
211
+
212
+
213
+ class HumanInLoopResponse(BaseModel):
214
+ """Response from human interaction."""
215
+ interaction_id: str = Field(description="ID of the interaction being responded to")
216
+ response_type: HumanInteractionType = Field(description="Type of response")
217
+ decision: Optional[str] = Field(default=None, description="Selected decision option")
218
+ answer: Optional[str] = Field(default=None, description="Answer to a question")
219
+ dialog_option_id: Optional[str] = Field(default=None, description="Selected dialog option ID")
220
+ additional_context: str = Field(default="", description="Additional context from the user")
221
+
222
+
223
+ class HumanInLoopResponseBatch(BaseModel):
224
+ """Batch of human responses for multiple interactions."""
225
+ responses: list[HumanInLoopResponse] = Field(description="List of human responses")
226
+
227
+ def get_response_by_id(self, interaction_id: str) -> Optional[HumanInLoopResponse]:
228
+ """Get a response by interaction ID."""
229
+ for response in self.responses:
230
+ if response.interaction_id == interaction_id:
231
+ return response
232
+ return None
233
+
234
+
235
+ # Session Handler Interface
236
+ class SessionHandler(Protocol):
237
+ """Protocol for custom session storage implementations."""
238
+
239
+ def get_session(self, agent_id: str) -> Optional[Dict[str, Any]]:
240
+ """Retrieve a session by agent ID."""
241
+ ...
242
+
243
+ def set_session(self, agent_id: str, session_data: Dict[str, Any]) -> None:
244
+ """Store session data for an agent ID."""
245
+ ...
246
+
247
+ def delete_session(self, agent_id: str) -> bool:
248
+ """Delete a session by agent ID. Returns True if deleted, False if not found."""
249
+ ...
250
+
251
+ def list_sessions(self) -> list[str]:
252
+ """List all active session IDs."""
253
+ ...
254
+
255
+ def session_exists(self, agent_id: str) -> bool:
256
+ """Check if a session exists."""
257
+ ...
258
+
259
+
260
+ class InMemorySessionHandler:
261
+ """Default in-memory session handler implementation."""
262
+
263
+ def __init__(self):
264
+ self._sessions: Dict[str, Dict[str, Any]] = {}
265
+
266
+ def get_session(self, agent_id: str) -> Optional[Dict[str, Any]]:
267
+ """Retrieve a session by agent ID."""
268
+ return self._sessions.get(agent_id)
269
+
270
+ def set_session(self, agent_id: str, session_data: Dict[str, Any]) -> None:
271
+ """Store session data for an agent ID."""
272
+ self._sessions[agent_id] = session_data
273
+
274
+ def delete_session(self, agent_id: str) -> bool:
275
+ """Delete a session by agent ID. Returns True if deleted, False if not found."""
276
+ if agent_id in self._sessions:
277
+ del self._sessions[agent_id]
278
+ return True
279
+ return False
280
+
281
+ def list_sessions(self) -> list[str]:
282
+ """List all active session IDs."""
283
+ return list(self._sessions.keys())
284
+
285
+ def session_exists(self, agent_id: str) -> bool:
286
+ """Check if a session exists."""
287
+ return agent_id in self._sessions
288
+
289
+
290
+ class RequirementAnalysis(BaseModel):
291
+ """Analysis of the user's requirement/goal."""
292
+ goal: str = Field(description="The main goal the user wants to achieve")
293
+ sub_goals: list[str] = Field(default_factory=list, description="Sub-goals that contribute to the main goal")
294
+ success_criteria: list[str] = Field(description="Criteria to determine if the goal is achieved")
295
+ constraints: list[str] = Field(default_factory=list, description="Any constraints or limitations to consider")
296
+ complexity_estimate: int = Field(ge=1, le=10, description="Complexity estimate from 1-10")
297
+
298
+
299
+ class ToolAnalysis(BaseModel):
300
+ """Analysis of available tools and their relevance to the goal."""
301
+ relevant_tools: list[str] = Field(description="List of tool names relevant to the goal")
302
+ tool_mapping: dict[str, str] = Field(description="Mapping of tool names to their purpose for this goal")
303
+ missing_capabilities: list[str] = Field(default_factory=list, description="Capabilities needed but not available in tools")
304
+
305
+
306
+ class GoalEvaluation(BaseModel):
307
+ """Evaluation of whether the goal has been achieved."""
308
+ goal_achieved: bool = Field(description="Whether the main goal has been achieved")
309
+ completion_percentage: int = Field(ge=0, le=100, description="Percentage of goal completion")
310
+ completed_criteria: list[str] = Field(description="Success criteria that have been met")
311
+ remaining_criteria: list[str] = Field(description="Success criteria that still need to be met")
312
+ feedback: str = Field(description="Detailed feedback on the goal achievement")
313
+ next_steps: list[str] = Field(default_factory=list, description="Next steps if goal is not fully achieved")
314
+
315
+
316
+ class AgentState(str, Enum):
317
+ """Current state of the agent."""
318
+ ANALYZING_REQUIREMENT = "analyzing_requirement"
319
+ ANALYZING_TOOLS = "analyzing_tools"
320
+ GENERATING_TODO = "generating_todo"
321
+ EXECUTING_TASKS = "executing_tasks"
322
+ WAITING_FOR_HUMAN = "waiting_for_human"
323
+ EVALUATING_GOAL = "evaluating_goal"
324
+ COMPLETED = "completed"
325
+ FAILED = "failed"
326
+
327
+
328
+ class AgentResponse(BaseModel):
329
+ """Response from the agent that can contain various states and information."""
330
+ agent_id: str = Field(default_factory=lambda: str(uuid.uuid4()), description="Unique identifier for this agent session")
331
+ state: AgentState = Field(description="Current state of the agent")
332
+
333
+ # Legacy support for existing questions-based flow
334
+ questions: list[Question] = Field(default_factory=list, description="List of questions for the user (legacy)")
335
+
336
+ # New architecture fields
337
+ requirement_analysis: Optional[RequirementAnalysis] = Field(default=None, description="Analysis of the requirement")
338
+ tool_analysis: Optional[ToolAnalysis] = Field(default=None, description="Analysis of available tools")
339
+ todo_list: Optional[TodoList] = Field(default=None, description="Current todo list")
340
+ current_task: Optional[Task] = Field(default=None, description="Currently executing task")
341
+ human_interaction_request: Optional[HumanInLoopRequest] = Field(default=None, description="Request for human interaction")
342
+ goal_evaluation: Optional[GoalEvaluation] = Field(default=None, description="Goal achievement evaluation")
343
+
344
+ # Final response
345
+ final_response: Optional[Any] = Field(default=None, description="The final response if processing is complete")
346
+
347
+ # Token usage tracking
348
+ token_summary: Optional[TokenSummary] = Field(default=None, description="Summary of token usage for this agent process")
349
+
350
+ def has_questions(self) -> bool:
351
+ """Check if this response has questions that need to be answered (legacy)."""
352
+ return len(self.questions) > 0
353
+
354
+ def needs_human_interaction(self) -> bool:
355
+ """Check if this response needs human interaction."""
356
+ return self.human_interaction_request is not None
357
+
358
+ def is_complete(self) -> bool:
359
+ """Check if this response contains a final result."""
360
+ return self.state == AgentState.COMPLETED and self.final_response is not None
361
+
362
+ def is_waiting_for_human(self) -> bool:
363
+ """Check if the agent is waiting for human input."""
364
+ return self.state == AgentState.WAITING_FOR_HUMAN
@@ -0,0 +1,344 @@
1
+ """
2
+ Example demonstrating the enhanced Agent Client with the new 6-step process.
3
+ """
4
+
5
+ import os
6
+ from pydantic import BaseModel, Field
7
+ from mbxai import AgentClient, OpenRouterClient, ToolClient
8
+ from mbxai.agent.models import (
9
+ DialogOption, HumanInLoopResponse, HumanInteractionType,
10
+ TaskStatus, AgentState
11
+ )
12
+
13
+
14
+ class ProjectPlan(BaseModel):
15
+ """A project plan response."""
16
+ project_name: str = Field(description="Name of the project")
17
+ description: str = Field(description="Project description")
18
+ technologies: list[str] = Field(description="Technologies to be used")
19
+ phases: list[str] = Field(description="Project phases")
20
+ estimated_duration: str = Field(description="Estimated project duration")
21
+ deliverables: list[str] = Field(description="Key deliverables")
22
+ risks: list[str] = Field(description="Potential risks and mitigation strategies")
23
+
24
+
25
+ class WeatherInfo(BaseModel):
26
+ """Weather information response."""
27
+ location: str = Field(description="The location")
28
+ current_weather: str = Field(description="Current weather description")
29
+ temperature: str = Field(description="Current temperature")
30
+ recommendations: list[str] = Field(description="Recommendations based on weather")
31
+
32
+
33
+ def google_auth_dialog_option() -> str:
34
+ """Simulate Google authentication."""
35
+ print("🔐 Simulating Google Authentication...")
36
+ return "Successfully authenticated with Google"
37
+
38
+
39
+ def slack_integration_dialog_option() -> str:
40
+ """Simulate Slack integration setup."""
41
+ print("💬 Simulating Slack integration setup...")
42
+ return "Slack integration configured successfully"
43
+
44
+
45
+ def example_basic_agent():
46
+ """Example of basic agent usage without human-in-the-loop."""
47
+ print("=== Basic Agent Example ===")
48
+
49
+ # Initialize client
50
+ openrouter_client = OpenRouterClient(token=os.getenv("OPENROUTER_API_KEY", "demo-token"))
51
+ agent = AgentClient(openrouter_client, human_in_loop=False)
52
+
53
+ # Create a project plan
54
+ prompt = "Create a project plan for building a simple weather app using React and Node.js"
55
+
56
+ print(f"🚀 Starting agent with prompt: {prompt}")
57
+ response = agent.agent(prompt, final_response_structure=ProjectPlan)
58
+
59
+ print(f"📊 Agent state: {response.state}")
60
+
61
+ if response.requirement_analysis:
62
+ print(f"📋 Goal: {response.requirement_analysis.goal}")
63
+ print(f"📋 Complexity: {response.requirement_analysis.complexity_estimate}/10")
64
+
65
+ if response.todo_list:
66
+ print(f"📝 Generated {len(response.todo_list.tasks)} tasks:")
67
+ for task in response.todo_list.tasks:
68
+ print(f" - {task.title} (Status: {task.status.value})")
69
+
70
+ if response.is_complete():
71
+ plan = response.final_response
72
+ print(f"\n✅ Project Plan Generated:")
73
+ print(f"📦 Project: {plan.project_name}")
74
+ print(f"📄 Description: {plan.description}")
75
+ print(f"💻 Technologies: {', '.join(plan.technologies)}")
76
+ print(f"⏱️ Duration: {plan.estimated_duration}")
77
+ print(f"📋 Phases: {', '.join(plan.phases)}")
78
+
79
+ # Print token usage
80
+ if response.token_summary:
81
+ print(f"\n📊 Token usage: {response.token_summary.total_tokens} total")
82
+
83
+ return response.agent_id
84
+
85
+
86
+ def example_human_in_loop():
87
+ """Example of agent with human-in-the-loop interactions."""
88
+ print("\n=== Human-in-the-Loop Agent Example ===")
89
+
90
+ # Initialize client with dialog options
91
+ openrouter_client = OpenRouterClient(token=os.getenv("OPENROUTER_API_KEY", "demo-token"))
92
+
93
+ dialog_options = [
94
+ DialogOption(
95
+ id="google_auth",
96
+ title="Google Authentication",
97
+ description="Authenticate with Google services",
98
+ function=google_auth_dialog_option
99
+ ),
100
+ DialogOption(
101
+ id="slack_integration",
102
+ title="Slack Integration",
103
+ description="Set up Slack integration",
104
+ function=slack_integration_dialog_option
105
+ )
106
+ ]
107
+
108
+ agent = AgentClient(
109
+ openrouter_client,
110
+ human_in_loop=True,
111
+ dialog_options=dialog_options,
112
+ max_task_iterations=5
113
+ )
114
+
115
+ # Start with a complex task
116
+ prompt = "Set up a complete CI/CD pipeline for a web application with automated testing, deployment, and monitoring"
117
+
118
+ print(f"🚀 Starting agent with human-in-loop: {prompt}")
119
+ response = agent.agent(prompt, final_response_structure=ProjectPlan)
120
+
121
+ print(f"📊 Agent state: {response.state}")
122
+
123
+ # Simulate human interactions
124
+ iteration = 0
125
+ max_iterations = 3
126
+
127
+ while not response.is_complete() and iteration < max_iterations:
128
+ iteration += 1
129
+ print(f"\n--- Iteration {iteration} ---")
130
+
131
+ if response.needs_human_interaction():
132
+ request = response.human_interaction_request
133
+ print(f"👤 Human interaction needed: {request.interaction_type.value}")
134
+ print(f"💬 Prompt: {request.prompt}")
135
+
136
+ if request.interaction_type == HumanInteractionType.DECISION:
137
+ print(f"📋 Options: {', '.join(request.options)}")
138
+ # Simulate user choosing to proceed
139
+ human_response = HumanInLoopResponse(
140
+ interaction_id=request.id,
141
+ response_type=HumanInteractionType.DECISION,
142
+ decision="proceed",
143
+ additional_context="User decided to proceed with the current approach"
144
+ )
145
+ elif request.interaction_type == HumanInteractionType.QUESTION:
146
+ # Simulate user answering a question
147
+ human_response = HumanInLoopResponse(
148
+ interaction_id=request.id,
149
+ response_type=HumanInteractionType.QUESTION,
150
+ answer="Use Docker for containerization and AWS for cloud services",
151
+ additional_context="User prefers cloud-native solutions"
152
+ )
153
+ elif request.interaction_type == HumanInteractionType.DIALOG_OPTION:
154
+ # Simulate user selecting a dialog option
155
+ print(f"🔧 Available dialog options:")
156
+ for option in request.dialog_options:
157
+ print(f" - {option.title}: {option.description}")
158
+
159
+ selected_option = request.dialog_options[0] if request.dialog_options else None
160
+ human_response = HumanInLoopResponse(
161
+ interaction_id=request.id,
162
+ response_type=HumanInteractionType.DIALOG_OPTION,
163
+ dialog_option_id=selected_option.id if selected_option else "none",
164
+ additional_context="User selected the first option"
165
+ )
166
+ else:
167
+ # Default response
168
+ human_response = HumanInLoopResponse(
169
+ interaction_id=request.id,
170
+ response_type=HumanInteractionType.QUESTION,
171
+ answer="Continue with default settings"
172
+ )
173
+
174
+ print(f"💭 Simulated user response: {human_response.answer or human_response.decision or 'dialog option selected'}")
175
+
176
+ # Continue with human response
177
+ response = agent.agent(
178
+ prompt="Continue with user input",
179
+ final_response_structure=ProjectPlan,
180
+ agent_id=response.agent_id,
181
+ human_response=human_response
182
+ )
183
+
184
+ elif response.state == AgentState.EXECUTING_TASKS:
185
+ print(f"⚡ Agent is executing tasks...")
186
+ if response.current_task:
187
+ print(f"📋 Current task: {response.current_task.title}")
188
+
189
+ # Continue execution
190
+ response = agent.agent(
191
+ prompt="Continue execution",
192
+ final_response_structure=ProjectPlan,
193
+ agent_id=response.agent_id
194
+ )
195
+
196
+ else:
197
+ print(f"📊 Agent state: {response.state}")
198
+ break
199
+
200
+ if response.is_complete():
201
+ plan = response.final_response
202
+ print(f"\n✅ CI/CD Plan Generated:")
203
+ print(f"📦 Project: {plan.project_name}")
204
+ print(f"📄 Description: {plan.description}")
205
+ print(f"💻 Technologies: {', '.join(plan.technologies)}")
206
+
207
+ if response.goal_evaluation:
208
+ print(f"\n🎯 Goal Achievement: {response.goal_evaluation.completion_percentage}%")
209
+ print(f"💭 Feedback: {response.goal_evaluation.feedback}")
210
+
211
+ return response.agent_id
212
+
213
+
214
+ def example_with_tools():
215
+ """Example of agent with tools."""
216
+ print("\n=== Agent with Tools Example ===")
217
+
218
+ # Initialize tool client
219
+ openrouter_client = OpenRouterClient(token=os.getenv("OPENROUTER_API_KEY", "demo-token"))
220
+ tool_client = ToolClient(openrouter_client)
221
+
222
+ # Register a weather tool
223
+ def get_weather(location: str) -> str:
224
+ """Get current weather for a location."""
225
+ # This is a mock implementation
226
+ return f"The weather in {location} is sunny and 72°F with light winds"
227
+
228
+ def get_forecast(location: str, days: int = 5) -> str:
229
+ """Get weather forecast for a location."""
230
+ return f"5-day forecast for {location}: Mostly sunny with temperatures ranging from 68-75°F"
231
+
232
+ agent = AgentClient(tool_client, human_in_loop=False)
233
+
234
+ # Register tools
235
+ agent.register_tool(
236
+ name="get_weather",
237
+ description="Get current weather information for a specific location",
238
+ function=get_weather
239
+ )
240
+
241
+ agent.register_tool(
242
+ name="get_forecast",
243
+ description="Get weather forecast for a location",
244
+ function=get_forecast
245
+ )
246
+
247
+ # Ask for weather information
248
+ prompt = "I'm planning a picnic in San Francisco this weekend. Can you help me understand the weather and give recommendations?"
249
+
250
+ print(f"🚀 Starting agent with tools: {prompt}")
251
+ response = agent.agent(prompt, final_response_structure=WeatherInfo)
252
+
253
+ print(f"📊 Agent state: {response.state}")
254
+
255
+ if response.tool_analysis:
256
+ print(f"🔧 Relevant tools: {', '.join(response.tool_analysis.relevant_tools)}")
257
+
258
+ if response.is_complete():
259
+ weather = response.final_response
260
+ print(f"\n🌤️ Weather Information:")
261
+ print(f"📍 Location: {weather.location}")
262
+ print(f"🌡️ Current: {weather.current_weather}")
263
+ print(f"🌡️ Temperature: {weather.temperature}")
264
+ print(f"💡 Recommendations:")
265
+ for rec in weather.recommendations:
266
+ print(f" - {rec}")
267
+
268
+ return response.agent_id
269
+
270
+
271
+ def example_session_management():
272
+ """Example of session management and continuation."""
273
+ print("\n=== Session Management Example ===")
274
+
275
+ openrouter_client = OpenRouterClient(token=os.getenv("OPENROUTER_API_KEY", "demo-token"))
276
+ agent = AgentClient(openrouter_client, human_in_loop=False)
277
+
278
+ # Start first task
279
+ prompt1 = "Create a basic project structure for a Python web API"
280
+ response1 = agent.agent(prompt1, final_response_structure=ProjectPlan)
281
+ agent_id = response1.agent_id
282
+
283
+ print(f"📋 First task completed: {response1.is_complete()}")
284
+
285
+ if response1.is_complete():
286
+ plan1 = response1.final_response
287
+ print(f"📦 First project: {plan1.project_name}")
288
+
289
+ # Continue with related task
290
+ prompt2 = "Now add authentication and user management to this API project"
291
+ response2 = agent.agent(prompt2, final_response_structure=ProjectPlan, agent_id=agent_id)
292
+
293
+ print(f"📋 Second task completed: {response2.is_complete()}")
294
+
295
+ if response2.is_complete():
296
+ plan2 = response2.final_response
297
+ print(f"📦 Enhanced project: {plan2.project_name}")
298
+ print(f"🔐 New features: {', '.join(plan2.technologies)}")
299
+
300
+ # List sessions
301
+ sessions = agent.list_sessions()
302
+ print(f"📊 Active sessions: {len(sessions)}")
303
+
304
+ # Get session info
305
+ if agent_id in sessions:
306
+ session_info = agent.get_session_info(agent_id)
307
+ print(f"💬 Conversation length: {session_info.get('conversation_length', 0)} messages")
308
+
309
+ # Clean up
310
+ deleted = agent.delete_session(agent_id)
311
+ print(f"🗑️ Session deleted: {deleted}")
312
+
313
+ return agent_id
314
+
315
+
316
+ def main():
317
+ """Run all examples."""
318
+ print("🤖 Enhanced Agent Client Examples")
319
+ print("=" * 50)
320
+
321
+ try:
322
+ # Basic agent without human interaction
323
+ basic_id = example_basic_agent()
324
+
325
+ # Agent with human-in-the-loop
326
+ hitl_id = example_human_in_loop()
327
+
328
+ # Agent with tools
329
+ tools_id = example_with_tools()
330
+
331
+ # Session management
332
+ session_id = example_session_management()
333
+
334
+ print(f"\n✅ All examples completed successfully!")
335
+ print(f"📋 Generated agent IDs: {basic_id}, {hitl_id}, {tools_id}, {session_id}")
336
+
337
+ except Exception as e:
338
+ print(f"❌ Error running examples: {e}")
339
+ import traceback
340
+ traceback.print_exc()
341
+
342
+
343
+ if __name__ == "__main__":
344
+ main()