mbxai 2.1.3__tar.gz → 2.3.0__tar.gz

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.
Files changed (56) hide show
  1. mbxai-2.3.0/PKG-INFO +1191 -0
  2. mbxai-2.3.0/README.md +1156 -0
  3. {mbxai-2.1.3 → mbxai-2.3.0}/pyproject.toml +1 -1
  4. {mbxai-2.1.3 → mbxai-2.3.0}/setup.py +1 -1
  5. mbxai-2.3.0/src/mbxai/__init__.py +43 -0
  6. mbxai-2.3.0/src/mbxai/agent/__init__.py +19 -0
  7. mbxai-2.3.0/src/mbxai/agent/client.py +1015 -0
  8. mbxai-2.3.0/src/mbxai/agent/models.py +364 -0
  9. mbxai-2.3.0/src/mbxai/examples/enhanced_agent_example.py +344 -0
  10. mbxai-2.3.0/src/mbxai/examples/redis_session_handler_example.py +248 -0
  11. {mbxai-2.1.3 → mbxai-2.3.0}/src/mbxai/mcp/server.py +1 -1
  12. {mbxai-2.1.3 → mbxai-2.3.0}/src/mbxai/openrouter/client.py +1 -83
  13. mbxai-2.3.0/test_enhanced_agent.py +259 -0
  14. mbxai-2.1.3/PKG-INFO +0 -346
  15. mbxai-2.1.3/README.md +0 -311
  16. mbxai-2.1.3/src/mbxai/__init__.py +0 -22
  17. mbxai-2.1.3/src/mbxai/agent/__init__.py +0 -8
  18. mbxai-2.1.3/src/mbxai/agent/models.py +0 -131
  19. {mbxai-2.1.3 → mbxai-2.3.0}/.gitignore +0 -0
  20. {mbxai-2.1.3 → mbxai-2.3.0}/LICENSE +0 -0
  21. /mbxai-2.1.3/src/mbxai/agent/client.py → /mbxai-2.3.0/src/mbxai/agent/client_legacy.py +0 -0
  22. {mbxai-2.1.3 → mbxai-2.3.0}/src/mbxai/core.py +0 -0
  23. {mbxai-2.1.3 → mbxai-2.3.0}/src/mbxai/examples/agent_example.py +0 -0
  24. {mbxai-2.1.3 → mbxai-2.3.0}/src/mbxai/examples/agent_iterations_example.py +0 -0
  25. {mbxai-2.1.3 → mbxai-2.3.0}/src/mbxai/examples/agent_logging_example.py +0 -0
  26. {mbxai-2.1.3 → mbxai-2.3.0}/src/mbxai/examples/agent_tool_registration_example.py +0 -0
  27. {mbxai-2.1.3 → mbxai-2.3.0}/src/mbxai/examples/agent_validation_example.py +0 -0
  28. {mbxai-2.1.3 → mbxai-2.3.0}/src/mbxai/examples/auto_schema_example.py +0 -0
  29. {mbxai-2.1.3 → mbxai-2.3.0}/src/mbxai/examples/conversation_history_test.py +0 -0
  30. {mbxai-2.1.3 → mbxai-2.3.0}/src/mbxai/examples/dialog_agent_example.py +0 -0
  31. {mbxai-2.1.3 → mbxai-2.3.0}/src/mbxai/examples/mcp/mcp_client_example.py +0 -0
  32. {mbxai-2.1.3 → mbxai-2.3.0}/src/mbxai/examples/mcp/mcp_server_example.py +0 -0
  33. {mbxai-2.1.3 → mbxai-2.3.0}/src/mbxai/examples/openrouter_example.py +0 -0
  34. {mbxai-2.1.3 → mbxai-2.3.0}/src/mbxai/examples/optional_prompt_example.py +0 -0
  35. {mbxai-2.1.3 → mbxai-2.3.0}/src/mbxai/examples/parse_example.py +0 -0
  36. {mbxai-2.1.3 → mbxai-2.3.0}/src/mbxai/examples/parse_tool_example.py +0 -0
  37. {mbxai-2.1.3 → mbxai-2.3.0}/src/mbxai/examples/request.json +0 -0
  38. {mbxai-2.1.3 → mbxai-2.3.0}/src/mbxai/examples/response.json +0 -0
  39. {mbxai-2.1.3 → mbxai-2.3.0}/src/mbxai/examples/send_request.py +0 -0
  40. {mbxai-2.1.3 → mbxai-2.3.0}/src/mbxai/examples/simple_agent_test.py +0 -0
  41. {mbxai-2.1.3 → mbxai-2.3.0}/src/mbxai/examples/tool_client_example.py +0 -0
  42. {mbxai-2.1.3 → mbxai-2.3.0}/src/mbxai/examples/unified_interface_example.py +0 -0
  43. {mbxai-2.1.3 → mbxai-2.3.0}/src/mbxai/mcp/__init__.py +0 -0
  44. {mbxai-2.1.3 → mbxai-2.3.0}/src/mbxai/mcp/client.py +0 -0
  45. {mbxai-2.1.3 → mbxai-2.3.0}/src/mbxai/mcp/example.py +0 -0
  46. {mbxai-2.1.3 → mbxai-2.3.0}/src/mbxai/openrouter/__init__.py +0 -0
  47. {mbxai-2.1.3 → mbxai-2.3.0}/src/mbxai/openrouter/config.py +0 -0
  48. {mbxai-2.1.3 → mbxai-2.3.0}/src/mbxai/openrouter/models.py +0 -0
  49. {mbxai-2.1.3 → mbxai-2.3.0}/src/mbxai/openrouter/schema.py +0 -0
  50. {mbxai-2.1.3 → mbxai-2.3.0}/src/mbxai/tools/__init__.py +0 -0
  51. {mbxai-2.1.3 → mbxai-2.3.0}/src/mbxai/tools/client.py +0 -0
  52. {mbxai-2.1.3 → mbxai-2.3.0}/src/mbxai/tools/example.py +0 -0
  53. {mbxai-2.1.3 → mbxai-2.3.0}/src/mbxai/tools/types.py +0 -0
  54. {mbxai-2.1.3 → mbxai-2.3.0}/tests/test_mcp_tool_registration.py +0 -0
  55. {mbxai-2.1.3 → mbxai-2.3.0}/tests/test_real_mcp_schema.py +0 -0
  56. {mbxai-2.1.3 → mbxai-2.3.0}/tests/test_schema_conversion.py +0 -0
mbxai-2.3.0/PKG-INFO ADDED
@@ -0,0 +1,1191 @@
1
+ Metadata-Version: 2.4
2
+ Name: mbxai
3
+ Version: 2.3.0
4
+ Summary: MBX AI SDK
5
+ Project-URL: Homepage, https://www.mibexx.de
6
+ Project-URL: Documentation, https://www.mibexx.de
7
+ Project-URL: Repository, https://github.com/yourusername/mbxai.git
8
+ Author: MBX AI
9
+ License: MIT
10
+ License-File: LICENSE
11
+ Classifier: Development Status :: 4 - Beta
12
+ Classifier: Operating System :: OS Independent
13
+ Classifier: Programming Language :: Python
14
+ Classifier: Programming Language :: Python :: 3.12
15
+ Requires-Python: >=3.12
16
+ Requires-Dist: fastapi>=0.115.12
17
+ Requires-Dist: httpx>=0.27.0
18
+ Requires-Dist: mcp>=1.7.1
19
+ Requires-Dist: openai>=1.77.0
20
+ Requires-Dist: pydantic-settings>=2.9.1
21
+ Requires-Dist: pydantic>=2.9.1
22
+ Requires-Dist: python-multipart>=0.0.20
23
+ Requires-Dist: sse-starlette>=2.3.4
24
+ Requires-Dist: starlette>=0.46.2
25
+ Requires-Dist: typing-inspection<=0.4.0
26
+ Requires-Dist: uvicorn>=0.34.2
27
+ Provides-Extra: dev
28
+ Requires-Dist: black>=24.3.0; extra == 'dev'
29
+ Requires-Dist: isort>=5.13.2; extra == 'dev'
30
+ Requires-Dist: mypy>=1.8.0; extra == 'dev'
31
+ Requires-Dist: pytest-asyncio>=0.26.0; extra == 'dev'
32
+ Requires-Dist: pytest-cov>=6.1.1; extra == 'dev'
33
+ Requires-Dist: pytest>=8.3.5; extra == 'dev'
34
+ Description-Content-Type: text/markdown
35
+
36
+ # MBX AI
37
+
38
+ A comprehensive Python library for building intelligent AI applications with Large Language Models (LLMs), structured responses, tool integration, and agent-based thinking.
39
+
40
+ ## 🚀 Features
41
+
42
+ - **🔗 Multiple AI Client Types**: OpenRouter integration with tool-enabled and MCP-enabled variants
43
+ - **🤖 Enhanced Agent System**: 6-step intelligent process with human-in-the-loop, task management, and goal evaluation
44
+ - **💾 Pluggable Session Storage**: Custom session handlers (Redis, Database, File System) for scalable, distributed sessions
45
+ - **🛠️ Tool Integration**: Easy function registration with automatic schema generation
46
+ - **🔌 MCP Support**: Full Model Context Protocol (MCP) client and server implementation
47
+ - **📋 Structured Responses**: Type-safe responses using Pydantic models
48
+ - **🔄 Quality Iteration**: Built-in response improvement through AI-powered quality checks
49
+ - **💬 Conversation Memory**: Persistent dialog sessions with history management
50
+ - **⚡ Automatic Retry**: Built-in retry logic with exponential backoff for robust connections
51
+
52
+ ## 📦 Installation
53
+
54
+ ```bash
55
+ pip install mbxai
56
+ ```
57
+
58
+ ## 🏗️ Architecture Overview
59
+
60
+ MBX AI provides four main client types, each building upon the previous:
61
+
62
+ 1. **OpenRouterClient** - Basic LLM interactions with structured responses
63
+ 2. **ToolClient** - Adds function calling capabilities
64
+ 3. **MCPClient** - Adds Model Context Protocol server integration
65
+ 4. **AgentClient** - Adds intelligent dialog-based thinking (wraps any of the above)
66
+
67
+ | Client | Structured Responses | Function Calling | MCP Integration | Agent Thinking |
68
+ |--------|---------------------|------------------|-----------------|----------------|
69
+ | OpenRouterClient | ✅ | ❌ | ❌ | ❌ |
70
+ | ToolClient | ✅ | ✅ | ❌ | ❌ |
71
+ | MCPClient | ✅ | ✅ | ✅ | ❌ |
72
+ | AgentClient | ✅ | ✅* | ✅* | ✅ |
73
+
74
+ *AgentClient capabilities depend on the wrapped client
75
+
76
+ ## 🚀 Quick Start
77
+
78
+ ### Basic OpenRouter Client
79
+
80
+ ```python
81
+ import os
82
+ from mbxai import OpenRouterClient
83
+ from pydantic import BaseModel, Field
84
+
85
+ # Initialize client
86
+ client = OpenRouterClient(token=os.getenv("OPENROUTER_API_KEY"))
87
+
88
+ # Simple chat
89
+ response = client.create([
90
+ {"role": "user", "content": "What is the capital of France?"}
91
+ ])
92
+ print(response.choices[0].message.content)
93
+
94
+ # Structured response
95
+ class CityInfo(BaseModel):
96
+ name: str = Field(description="City name")
97
+ population: int = Field(description="Population count")
98
+ country: str = Field(description="Country name")
99
+
100
+ response = client.parse(
101
+ messages=[{"role": "user", "content": "Tell me about Paris"}],
102
+ response_format=CityInfo
103
+ )
104
+ city = response.choices[0].message.parsed
105
+ print(f"{city.name}, {city.country} - Population: {city.population:,}")
106
+ ```
107
+
108
+ ### Tool Client with Automatic Schema Generation
109
+
110
+ ```python
111
+ import os
112
+ from mbxai import ToolClient, OpenRouterClient
113
+
114
+ # Initialize clients
115
+ openrouter_client = OpenRouterClient(token=os.getenv("OPENROUTER_API_KEY"))
116
+ tool_client = ToolClient(openrouter_client)
117
+
118
+ # Define a function - schema is auto-generated!
119
+ def get_weather(location: str, unit: str = "celsius") -> dict:
120
+ """Get weather information for a location.
121
+
122
+ Args:
123
+ location: The city or location name
124
+ unit: Temperature unit (celsius or fahrenheit)
125
+ """
126
+ return {
127
+ "location": location,
128
+ "temperature": 22,
129
+ "unit": unit,
130
+ "condition": "Sunny"
131
+ }
132
+
133
+ # Register tool (schema automatically generated from function signature)
134
+ tool_client.register_tool(
135
+ name="get_weather",
136
+ description="Get current weather for a location",
137
+ function=get_weather
138
+ # No schema needed - automatically generated!
139
+ )
140
+
141
+ # Use the tool
142
+ response = tool_client.chat([
143
+ {"role": "user", "content": "What's the weather like in Tokyo?"}
144
+ ])
145
+ print(response.choices[0].message.content)
146
+ ```
147
+
148
+ ### MCP Client for Server Integration
149
+
150
+ ```python
151
+ import os
152
+ from mbxai import MCPClient, OpenRouterClient
153
+
154
+ # Initialize MCP client
155
+ openrouter_client = OpenRouterClient(token=os.getenv("OPENROUTER_API_KEY"))
156
+ mcp_client = MCPClient(openrouter_client)
157
+
158
+ # Register MCP server (automatically loads all tools)
159
+ mcp_client.register_mcp_server("data-analysis", "http://localhost:8000")
160
+
161
+ # Chat with MCP tools available
162
+ response = mcp_client.chat([
163
+ {"role": "user", "content": "Analyze the sales data from the server"}
164
+ ])
165
+ print(response.choices[0].message.content)
166
+ ```
167
+
168
+ ### Enhanced Agent Client - 6-Step Intelligent Process
169
+
170
+ The AgentClient provides a structured 6-step process: requirement analysis, tool analysis, todo generation, task execution, human-in-the-loop interactions, and goal evaluation.
171
+
172
+ ```python
173
+ import os
174
+ from mbxai import AgentClient, OpenRouterClient
175
+ from mbxai.agent.models import DialogOption, HumanInLoopResponse, HumanInteractionType
176
+ from pydantic import BaseModel, Field
177
+
178
+ class ProjectPlan(BaseModel):
179
+ project_name: str = Field(description="Name of the project")
180
+ technologies: list[str] = Field(description="Technologies to be used")
181
+ phases: list[str] = Field(description="Project phases")
182
+ estimated_duration: str = Field(description="Estimated duration")
183
+
184
+ # Basic usage without human-in-the-loop
185
+ openrouter_client = OpenRouterClient(token=os.getenv("OPENROUTER_API_KEY"))
186
+ agent = AgentClient(openrouter_client, human_in_loop=False)
187
+
188
+ response = agent.agent(
189
+ "Create a project plan for a React web application",
190
+ final_response_structure=ProjectPlan
191
+ )
192
+
193
+ print(f"Agent State: {response.state}")
194
+ if response.requirement_analysis:
195
+ print(f"Goal: {response.requirement_analysis.goal}")
196
+ print(f"Complexity: {response.requirement_analysis.complexity_estimate}/10")
197
+
198
+ if response.todo_list:
199
+ print(f"Generated {len(response.todo_list.tasks)} tasks:")
200
+ for task in response.todo_list.tasks:
201
+ print(f" - {task.title} (Status: {task.status.value})")
202
+
203
+ if response.is_complete():
204
+ plan = response.final_response
205
+ print(f"Project: {plan.project_name}")
206
+ print(f"Technologies: {', '.join(plan.technologies)}")
207
+ ```
208
+
209
+ ### Human-in-the-Loop Agent
210
+
211
+ ```python
212
+ # Define dialog options for UI-aware authentication flows
213
+ # These don't execute functions directly - they provide structured
214
+ # communication patterns that the UI can handle
215
+
216
+ dialog_options = [
217
+ DialogOption(
218
+ id="jira_auth",
219
+ title="Jira Authentication",
220
+ description="Authenticate with Atlassian Jira",
221
+ # No function - this is handled by the UI
222
+ parameters={"jira_url": "https://company.atlassian.net"}
223
+ ),
224
+ DialogOption(
225
+ id="github_auth",
226
+ title="GitHub Authentication",
227
+ description="Authenticate with GitHub",
228
+ parameters={"scopes": ["repo", "user"]}
229
+ ),
230
+ DialogOption(
231
+ id="approval_request",
232
+ title="Deployment Approval",
233
+ description="Request approval for production deployment",
234
+ parameters={"environment": "production", "risk_level": "medium"}
235
+ )
236
+ ]
237
+
238
+ # Initialize agent with human-in-the-loop
239
+ agent = AgentClient(
240
+ openrouter_client,
241
+ human_in_loop=True,
242
+ dialog_options=dialog_options,
243
+ max_task_iterations=10
244
+ )
245
+
246
+ # Start task that requires authentication
247
+ response = agent.agent(
248
+ "Create a summary of the Jira story PROJ-123 from https://company.atlassian.net",
249
+ final_response_structure=ProjectPlan
250
+ )
251
+
252
+ # Handle human interactions
253
+ while not response.is_complete():
254
+ if response.needs_human_interaction():
255
+ request = response.human_interaction_request
256
+ print(f"Human input needed: {request.prompt}")
257
+
258
+ if request.interaction_type == HumanInteractionType.DECISION:
259
+ # Present options and get user choice
260
+ print(f"Options: {', '.join(request.options)}")
261
+ user_choice = "proceed" # Simulate user input
262
+
263
+ human_response = HumanInLoopResponse(
264
+ interaction_id=request.id,
265
+ response_type=HumanInteractionType.DECISION,
266
+ decision=user_choice
267
+ )
268
+
269
+ elif request.interaction_type == HumanInteractionType.QUESTION:
270
+ # Get user answer
271
+ user_answer = "Use AWS for deployment" # Simulate user input
272
+
273
+ human_response = HumanInLoopResponse(
274
+ interaction_id=request.id,
275
+ response_type=HumanInteractionType.QUESTION,
276
+ answer=user_answer
277
+ )
278
+
279
+ elif request.interaction_type == HumanInteractionType.DIALOG_OPTION:
280
+ # UI presents structured authentication dialog
281
+ # User sees "Login to Atlassian" button, completes OAuth flow
282
+ # UI receives auth token and sends it back to agent
283
+
284
+ # Simulate UI completing authentication
285
+ auth_token = "oauth_token_abc123" # Retrieved by UI from OAuth flow
286
+
287
+ human_response = HumanInLoopResponse(
288
+ interaction_id=request.id,
289
+ response_type=HumanInteractionType.DIALOG_OPTION,
290
+ dialog_option_id="jira_auth",
291
+ additional_context=f"auth_token:{auth_token}"
292
+ )
293
+
294
+ # Continue with human response
295
+ response = agent.agent(
296
+ "Continue with user input",
297
+ final_response_structure=ProjectPlan,
298
+ agent_id=response.agent_id,
299
+ human_response=human_response
300
+ )
301
+ else:
302
+ # Continue execution
303
+ response = agent.agent(
304
+ "Continue processing",
305
+ final_response_structure=ProjectPlan,
306
+ agent_id=response.agent_id
307
+ )
308
+
309
+ # Final result
310
+ if response.is_complete():
311
+ plan = response.final_response
312
+ print(f"✅ Project completed: {plan.project_name}")
313
+
314
+ if response.goal_evaluation:
315
+ print(f"Goal Achievement: {response.goal_evaluation.completion_percentage}%")
316
+ print(f"Feedback: {response.goal_evaluation.feedback}")
317
+ ```
318
+
319
+ ### Agent with Tool Integration
320
+
321
+ ```python
322
+ from mbxai import AgentClient, ToolClient, OpenRouterClient
323
+
324
+ # Setup tool-enabled agent
325
+ openrouter_client = OpenRouterClient(token=os.getenv("OPENROUTER_API_KEY"))
326
+ tool_client = ToolClient(openrouter_client)
327
+ agent = AgentClient(tool_client)
328
+
329
+ # Register tools via agent (proxy method)
330
+ def search_flights(origin: str, destination: str, date: str) -> dict:
331
+ """Search for flights between cities."""
332
+ return {
333
+ "flights": [
334
+ {"airline": "Example Air", "price": "$450", "duration": "3h 15m"}
335
+ ]
336
+ }
337
+
338
+ agent.register_tool(
339
+ name="search_flights",
340
+ description="Search for flights between cities",
341
+ function=search_flights
342
+ )
343
+
344
+ # Agent automatically uses tools when needed
345
+ class FlightInfo(BaseModel):
346
+ flights: list[dict] = Field(description="Available flights")
347
+ recommendation: str = Field(description="Flight recommendation")
348
+
349
+ response = agent.agent(
350
+ prompt="Find flights from New York to Los Angeles for tomorrow",
351
+ final_response_structure=FlightInfo,
352
+ ask_questions=False
353
+ )
354
+
355
+ flight_info = response.final_response
356
+ print(f"Found {len(flight_info.flights)} flights")
357
+ print(f"Recommendation: {flight_info.recommendation}")
358
+ ```
359
+
360
+ ### New Models and Types
361
+
362
+ The enhanced agent client introduces several new models and types:
363
+
364
+ ```python
365
+ # Import new agent models
366
+ from mbxai.agent.models import (
367
+ Task, TodoList, TaskStatus, # Task management
368
+ DialogOption, HumanInLoopRequest, # Human interactions
369
+ HumanInLoopResponse, HumanInteractionType,
370
+ RequirementAnalysis, ToolAnalysis, # Analysis models
371
+ GoalEvaluation, AgentState, # State management
372
+ SessionHandler, InMemorySessionHandler, # Session storage
373
+ TokenSummary, TokenUsage # Token tracking
374
+ )
375
+
376
+ # Or import from main package
377
+ from mbxai import (
378
+ Task, TodoList, DialogOption, HumanInLoopRequest,
379
+ AgentState, TaskStatus, HumanInteractionType,
380
+ SessionHandler, InMemorySessionHandler
381
+ )
382
+ ```
383
+
384
+ ## 📚 Detailed Documentation
385
+
386
+ ### OpenRouterClient
387
+
388
+ The base client for OpenRouter API integration with structured response support.
389
+
390
+ #### Key Features:
391
+ - **Multiple Models**: Support for GPT-4, Claude, Llama, and other models via OpenRouter
392
+ - **Structured Responses**: Type-safe responses using Pydantic models
393
+ - **Retry Logic**: Automatic retry with exponential backoff
394
+ - **Error Handling**: Comprehensive error handling with detailed logging
395
+
396
+ #### Methods:
397
+ - `create()` - Basic chat completion
398
+ - `parse()` - Chat completion with structured response
399
+
400
+ #### Configuration:
401
+ ```python
402
+ client = OpenRouterClient(
403
+ token="your-api-key",
404
+ model="openai/gpt-4-turbo", # or use OpenRouterModel enum
405
+ max_retries=3,
406
+ retry_initial_delay=1.0,
407
+ retry_max_delay=10.0
408
+ )
409
+ ```
410
+
411
+ ### ToolClient
412
+
413
+ Extends OpenRouterClient with function calling capabilities.
414
+
415
+ #### Key Features:
416
+ - **Automatic Schema Generation**: Generate JSON schemas from Python function signatures
417
+ - **Tool Registration**: Simple function registration
418
+ - **Tool Execution**: Automatic tool calling and response handling
419
+ - **Error Recovery**: Graceful handling of tool execution errors
420
+
421
+ #### Usage:
422
+ ```python
423
+ tool_client = ToolClient(openrouter_client)
424
+
425
+ # Register with automatic schema
426
+ tool_client.register_tool("function_name", "description", function)
427
+
428
+ # Register with custom schema
429
+ tool_client.register_tool("function_name", "description", function, custom_schema)
430
+ ```
431
+
432
+ ### MCPClient
433
+
434
+ Extends ToolClient with Model Context Protocol (MCP) server integration.
435
+
436
+ #### Key Features:
437
+ - **MCP Server Integration**: Connect to MCP servers and load their tools
438
+ - **Tool Discovery**: Automatically discover and register tools from MCP servers
439
+ - **HTTP Client Management**: Built-in HTTP client for MCP communication
440
+ - **Schema Conversion**: Convert MCP schemas to OpenAI function format
441
+
442
+ #### Usage:
443
+ ```python
444
+ mcp_client = MCPClient(openrouter_client)
445
+ mcp_client.register_mcp_server("server-name", "http://localhost:8000")
446
+ ```
447
+
448
+ ### Enhanced AgentClient
449
+
450
+ Wraps any client with a structured 6-step intelligent process and human-in-the-loop capabilities.
451
+
452
+ #### Key Features:
453
+ - **6-Step Process**: Requirement analysis → Tool analysis → Todo generation → Task execution → Human interaction → Goal evaluation
454
+ - **Human-in-the-Loop**: Support for decisions, questions, and custom dialog options
455
+ - **Task Management**: Intelligent todo list generation with dependencies and complexity assessment
456
+ - **Goal Evaluation**: Automatic assessment of goal achievement with feedback
457
+ - **Conversation Memory**: Maintains full conversation history and context
458
+ - **Tool Integration**: Seamlessly works with any underlying client (OpenRouter, Tool, MCP)
459
+
460
+ #### Configuration Options:
461
+ ```python
462
+ from mbxai import AgentClient, InMemorySessionHandler
463
+
464
+ agent = AgentClient(
465
+ ai_client=any_supported_client,
466
+ human_in_loop=False, # Enable human-in-the-loop
467
+ dialog_options=[], # Custom dialog options
468
+ max_task_iterations=10, # Max task execution cycles
469
+ session_handler=InMemorySessionHandler() # Session storage (optional)
470
+ )
471
+ ```
472
+
473
+ **Session Storage Options:**
474
+ - **InMemorySessionHandler** (default): Single-instance memory storage
475
+ - **Custom handlers**: Redis, Database, File System for distributed/persistent sessions
476
+
477
+ #### The 6-Step Process:
478
+ 1. **Requirement Analysis**: Understand goals, success criteria, constraints, and complexity
479
+ 2. **Tool Analysis**: Map available tools to goals, identify missing capabilities
480
+ 3. **Todo Generation**: Create specific, actionable tasks with dependencies
481
+ 4. **Task Execution**: Execute tasks step-by-step with status tracking
482
+ 5. **Human Interaction**: Dialog for decisions, questions, or custom actions (if enabled)
483
+ 6. **Goal Evaluation**: Assess achievement, provide feedback, generate new todos if needed
484
+
485
+ #### Human Interaction Types:
486
+ - **DECISION**: Multiple choice with predefined options
487
+ - **QUESTION**: Free-text input for clarification
488
+ - **DIALOG_OPTION**: Structured UI-aware communication patterns (authentication, integrations, etc.)
489
+
490
+ #### Tools vs Dialog Options:
491
+ - **Tools**: Functions the agent executes directly (fetch data, generate documents, setup systems)
492
+ - **Dialog Options**: Structured human-in-the-loop patterns that the UI can handle (authentication flows, approvals, integrations)
493
+
494
+ #### New Response Properties:
495
+ ```python
496
+ response = agent.agent("Create a web app", ProjectPlan)
497
+
498
+ # Enhanced state information
499
+ response.state # Current agent state
500
+ response.requirement_analysis # Goal breakdown
501
+ response.tool_analysis # Tool mapping
502
+ response.todo_list # Generated tasks
503
+ response.current_task # Currently executing task
504
+ response.human_interaction_request # Human input needed
505
+ response.goal_evaluation # Achievement assessment
506
+
507
+ # Check states
508
+ response.is_complete() # Final response ready
509
+ response.needs_human_interaction() # Human input required
510
+ response.is_waiting_for_human() # Waiting for human response
511
+ ```
512
+
513
+ #### Session Management:
514
+ ```python
515
+ # List active sessions
516
+ sessions = agent.list_sessions()
517
+
518
+ # Get session info
519
+ info = agent.get_session_info(agent_id)
520
+
521
+ # Delete session
522
+ agent.delete_session(agent_id)
523
+ ```
524
+
525
+ ## 🏃‍♂️ Advanced Examples
526
+
527
+ ### Custom Model Registration
528
+
529
+ ```python
530
+ from mbxai import OpenRouterClient, OpenRouterModel
531
+
532
+ # Register custom model
533
+ OpenRouterClient.register_model("CUSTOM_MODEL", "provider/model-name")
534
+
535
+ # Use custom model
536
+ client = OpenRouterClient(token="your-key", model="CUSTOM_MODEL")
537
+ ```
538
+
539
+ ### Enhanced Agent Session Continuation
540
+
541
+ ```python
542
+ # Start a complex project
543
+ response1 = agent.agent(
544
+ "Create a microservices architecture for an e-commerce platform",
545
+ final_response_structure=ProjectPlan
546
+ )
547
+ agent_id = response1.agent_id
548
+
549
+ # Continue with additional requirements
550
+ response2 = agent.agent(
551
+ "Add authentication and payment processing to the architecture",
552
+ final_response_structure=ProjectPlan,
553
+ agent_id=agent_id # Continues previous session
554
+ )
555
+
556
+ # The agent remembers the full context and builds upon previous work
557
+ print(f"Updated project: {response2.final_response.project_name}")
558
+ print(f"Goal achievement: {response2.goal_evaluation.completion_percentage}%")
559
+ ```
560
+
561
+ ### Complete Example: Jira Integration with Authentication
562
+
563
+ ```python
564
+ from mbxai import AgentClient, ToolClient, OpenRouterClient
565
+ from mbxai.agent.models import DialogOption, HumanInLoopResponse, HumanInteractionType
566
+ from pydantic import BaseModel, Field
567
+
568
+ class JiraSummary(BaseModel):
569
+ story_id: str = Field(description="Jira story ID")
570
+ title: str = Field(description="Story title")
571
+ description: str = Field(description="Story description")
572
+ status: str = Field(description="Current status")
573
+ assignee: str = Field(description="Assigned person")
574
+ summary: str = Field(description="AI-generated summary")
575
+
576
+ # Tool for fetching Jira data (agent executes this)
577
+ def fetch_jira_story(story_id: str, jira_url: str, auth_token: str) -> dict:
578
+ """Fetch a Jira story using the API."""
579
+ # Your Jira API integration logic here
580
+ return {
581
+ "id": story_id,
582
+ "title": "Implement user authentication",
583
+ "description": "Add OAuth2 authentication to the application",
584
+ "status": "In Progress",
585
+ "assignee": "john.doe@company.com"
586
+ }
587
+
588
+ # Dialog option for authentication (UI handles this)
589
+ dialog_options = [
590
+ DialogOption(
591
+ id="jira_auth",
592
+ title="Jira Authentication",
593
+ description="Authenticate with Atlassian Jira",
594
+ # No function - this tells the UI to handle authentication
595
+ parameters={
596
+ "jira_url": "https://company.atlassian.net",
597
+ "scopes": ["read:jira-work", "read:jira-user"]
598
+ }
599
+ )
600
+ ]
601
+
602
+ # Setup agent with both tools and dialog options
603
+ openrouter_client = OpenRouterClient(token=os.getenv("OPENROUTER_API_KEY"))
604
+ tool_client = ToolClient(openrouter_client)
605
+ agent = AgentClient(
606
+ tool_client,
607
+ human_in_loop=True,
608
+ dialog_options=dialog_options
609
+ )
610
+
611
+ # Register the Jira tool
612
+ agent.register_tool(
613
+ name="fetch_jira_story",
614
+ description="Fetch Jira story details using API",
615
+ function=fetch_jira_story
616
+ )
617
+
618
+ # Agent process
619
+ response = agent.agent(
620
+ "Create a summary of Jira story PROJ-123 from https://company.atlassian.net",
621
+ final_response_structure=JiraSummary
622
+ )
623
+
624
+ # Handle authentication dialog
625
+ if response.needs_human_interaction():
626
+ request = response.human_interaction_request
627
+
628
+ if request.interaction_type == HumanInteractionType.DIALOG_OPTION:
629
+ # UI shows "Login to Atlassian" button
630
+ # User completes OAuth flow
631
+ # UI receives auth token and sends it back
632
+
633
+ auth_token = "oauth_token_from_ui" # Provided by UI after OAuth
634
+
635
+ human_response = HumanInLoopResponse(
636
+ interaction_id=request.id,
637
+ response_type=HumanInteractionType.DIALOG_OPTION,
638
+ dialog_option_id="jira_auth",
639
+ additional_context=f"auth_token:{auth_token}"
640
+ )
641
+
642
+ # Continue with authentication
643
+ response = agent.agent(
644
+ "Continue with Jira authentication",
645
+ final_response_structure=JiraSummary,
646
+ agent_id=response.agent_id,
647
+ human_response=human_response
648
+ )
649
+
650
+ # Now agent can use the auth token with the fetch_jira_story tool
651
+ if response.is_complete():
652
+ summary = response.final_response
653
+ print(f"Story: {summary.story_id} - {summary.title}")
654
+ print(f"Status: {summary.status}")
655
+ print(f"Summary: {summary.summary}")
656
+ ```
657
+
658
+ ### Key Distinction: Tools vs Dialog Options
659
+
660
+ This example demonstrates the important difference:
661
+
662
+ **Tools** (Agent-Executed Functions):
663
+ - Functions that the agent calls directly to perform tasks
664
+ - Examples: `fetch_jira_story`, `generate_document`, `setup_database`
665
+ - Executed server-side by the agent
666
+ - Used for data processing, API calls, system operations
667
+
668
+ **Dialog Options** (UI-Handled Patterns):
669
+ - Structured communication patterns that the UI can understand and handle
670
+ - Examples: `jira_auth`, `github_oauth`, `approval_request`
671
+ - NOT executed by the agent - they're instructions for the UI
672
+ - Used for authentication flows, user approvals, secure interactions
673
+ - Parameters help the UI know how to handle the interaction
674
+ - Responses provide the agent with the results (tokens, approvals, etc.)
675
+
676
+ **Benefits**:
677
+ - **Security**: No sensitive data (auth tokens) passed as plain text
678
+ - **User Experience**: UI can provide proper authentication flows, not just text input
679
+ - **Structured**: Both agent and UI know exactly what type of interaction is needed
680
+ - **Flexible**: UI can handle complex flows (OAuth, 2FA, file uploads) appropriately
681
+
682
+ ### Multiple Human Interactions
683
+
684
+ For complex workflows requiring multiple human interactions, you can provide responses as a list:
685
+
686
+ ```python
687
+ from mbxai.agent.models import HumanInLoopResponseBatch, HumanInLoopResponse, HumanInteractionType
688
+
689
+ # Multiple individual responses
690
+ responses = [
691
+ HumanInLoopResponse(
692
+ interaction_id="auth_req_1",
693
+ response_type=HumanInteractionType.DIALOG_OPTION,
694
+ dialog_option_id="github_auth",
695
+ additional_context="auth_token:github_oauth_token_123"
696
+ ),
697
+ HumanInLoopResponse(
698
+ interaction_id="approval_req_1",
699
+ response_type=HumanInteractionType.DECISION,
700
+ decision="approve",
701
+ additional_context="approved_by:john.doe@company.com"
702
+ )
703
+ ]
704
+
705
+ # Send multiple responses at once
706
+ response = agent.agent(
707
+ prompt="Continue with multiple inputs",
708
+ final_response_structure=ProjectPlan,
709
+ agent_id=agent_id,
710
+ human_response=responses # List of responses
711
+ )
712
+
713
+ # Or use the batch model
714
+ response_batch = HumanInLoopResponseBatch(responses=responses)
715
+ response = agent.agent(
716
+ prompt="Continue with batch input",
717
+ final_response_structure=ProjectPlan,
718
+ agent_id=agent_id,
719
+ human_response=response_batch # Batch object
720
+ )
721
+ ```
722
+
723
+ This is particularly useful when:
724
+ - The agent requests multiple approvals simultaneously
725
+ - Complex workflows need multiple authentication steps
726
+ - Batch processing user decisions for efficiency
727
+
728
+ ### Custom Session Handlers
729
+
730
+ The `AgentClient` supports pluggable session storage through the `SessionHandler` protocol. This enables distributed, persistent, and scalable session management.
731
+
732
+ #### Built-in Session Handlers
733
+
734
+ **InMemorySessionHandler (Default):**
735
+ ```python
736
+ from mbxai import AgentClient, InMemorySessionHandler
737
+
738
+ # Default in-memory storage (single instance)
739
+ agent = AgentClient(ai_client, session_handler=InMemorySessionHandler())
740
+ ```
741
+
742
+ **Custom Redis Session Handler:**
743
+ ```python
744
+ import redis
745
+ from mbxai import AgentClient, SessionHandler
746
+ from typing import Dict, Any, Optional
747
+
748
+ class RedisSessionHandler:
749
+ def __init__(self, redis_client: redis.Redis = None, ttl_seconds: int = 86400):
750
+ self.redis_client = redis_client or redis.Redis()
751
+ self.ttl_seconds = ttl_seconds
752
+
753
+ def get_session(self, agent_id: str) -> Optional[Dict[str, Any]]:
754
+ session_json = self.redis_client.get(f"agent:{agent_id}")
755
+ return json.loads(session_json) if session_json else None
756
+
757
+ def set_session(self, agent_id: str, session_data: Dict[str, Any]) -> None:
758
+ session_json = json.dumps(session_data, default=str)
759
+ self.redis_client.setex(f"agent:{agent_id}", self.ttl_seconds, session_json)
760
+
761
+ def delete_session(self, agent_id: str) -> bool:
762
+ return self.redis_client.delete(f"agent:{agent_id}") > 0
763
+
764
+ def list_sessions(self) -> list[str]:
765
+ keys = self.redis_client.keys("agent:*")
766
+ return [key.decode().replace("agent:", "") for key in keys]
767
+
768
+ def session_exists(self, agent_id: str) -> bool:
769
+ return self.redis_client.exists(f"agent:{agent_id}") > 0
770
+
771
+ # Use Redis for distributed session storage
772
+ redis_handler = RedisSessionHandler(ttl_seconds=3600) # 1 hour TTL
773
+ agent = AgentClient(ai_client, session_handler=redis_handler)
774
+ ```
775
+
776
+ #### Benefits of Custom Session Handlers
777
+
778
+ - **Distributed**: Share sessions across multiple application instances
779
+ - **Persistent**: Sessions survive application restarts
780
+ - **Scalable**: Handle thousands of concurrent sessions
781
+ - **Flexible**: Database, file system, cloud storage integration
782
+ - **TTL Support**: Automatic session cleanup
783
+ - **High Availability**: Redis Sentinel/Cluster support
784
+
785
+ #### Production Setup Example
786
+
787
+ ```python
788
+ from mbxai import AgentClient
789
+ import redis.sentinel
790
+
791
+ # Redis Sentinel for high availability
792
+ sentinels = [('sentinel1', 26379), ('sentinel2', 26379)]
793
+ sentinel = redis.sentinel.Sentinel(sentinels)
794
+ redis_client = sentinel.master_for('mymaster', decode_responses=True)
795
+
796
+ class ProductionRedisHandler:
797
+ def __init__(self):
798
+ self.redis_client = redis_client
799
+ self.ttl_seconds = 24 * 60 * 60 # 24 hours
800
+
801
+ # ... implement SessionHandler methods
802
+
803
+ # Production agent with Redis clustering
804
+ agent = AgentClient(
805
+ ai_client=ai_client,
806
+ session_handler=ProductionRedisHandler()
807
+ )
808
+ ```
809
+
810
+ ### Real-World Example: Separate UI and Agent Systems
811
+
812
+ This example shows how the UI and Agent run as separate services with their own endpoints:
813
+
814
+ **UI Service (FastAPI - Container 1):**
815
+ ```python
816
+ from fastapi import FastAPI, HTTPException
817
+ from pydantic import BaseModel
818
+ import httpx
819
+ from typing import Optional
820
+
821
+ app = FastAPI()
822
+
823
+ # Models for UI service
824
+ class ChatMessage(BaseModel):
825
+ message: str
826
+ user_id: str
827
+
828
+ class AgentRequest(BaseModel):
829
+ prompt: str
830
+ final_response_structure: str = "OrderInfo"
831
+ agent_id: Optional[str] = None
832
+ human_response: Optional[dict] = None
833
+
834
+ class CredentialsForm(BaseModel):
835
+ username: str
836
+ password: str
837
+ shop_url: str
838
+
839
+ # In-memory session storage (use Redis in production)
840
+ active_sessions = {}
841
+
842
+ @app.post("/chat")
843
+ async def chat_endpoint(message: ChatMessage):
844
+ """Main chat endpoint for the UI"""
845
+
846
+ # Send user message to Agent service
847
+ agent_request = AgentRequest(
848
+ prompt=message.message,
849
+ final_response_structure="OrderInfo"
850
+ )
851
+
852
+ async with httpx.AsyncClient() as client:
853
+ response = await client.post(
854
+ "http://agent-service:8001/agent",
855
+ json=agent_request.dict()
856
+ )
857
+ agent_response = response.json()
858
+
859
+ # Check if agent needs human interaction
860
+ if agent_response.get("needs_human_interaction"):
861
+ request = agent_response["human_interaction_request"]
862
+
863
+ if request["interaction_type"] == "dialog_option" and request.get("dialog_option_id") == "shop_credentials":
864
+ # Store session and ask user for credentials
865
+ session_id = agent_response["agent_id"]
866
+ active_sessions[session_id] = {
867
+ "interaction_id": request["id"],
868
+ "shop_url": request["parameters"]["shop_url"]
869
+ }
870
+
871
+ return {
872
+ "type": "credentials_form",
873
+ "message": "Please provide your shop credentials",
874
+ "shop_url": request["parameters"]["shop_url"],
875
+ "session_id": session_id
876
+ }
877
+
878
+ # Return final result if available
879
+ if agent_response.get("is_complete"):
880
+ order = agent_response["final_response"]
881
+ return {
882
+ "type": "order_result",
883
+ "message": f"Your last order: #{order['order_number']} from {order['date']} (ID: {order['id']})"
884
+ }
885
+
886
+ return {"type": "message", "message": "Processing your request..."}
887
+
888
+ @app.post("/submit_credentials")
889
+ async def submit_credentials(credentials: CredentialsForm, session_id: str):
890
+ """Handle user credentials submission"""
891
+
892
+ if session_id not in active_sessions:
893
+ raise HTTPException(status_code=404, detail="Session not found")
894
+
895
+ session = active_sessions[session_id]
896
+
897
+ # Send credentials back to agent
898
+ human_response = {
899
+ "interaction_id": session["interaction_id"],
900
+ "response_type": "dialog_option",
901
+ "dialog_option_id": "shop_credentials",
902
+ "additional_context": f"username:{credentials.username},password:{credentials.password}"
903
+ }
904
+
905
+ agent_request = AgentRequest(
906
+ prompt="Continue with shop credentials",
907
+ final_response_structure="OrderInfo",
908
+ agent_id=session_id,
909
+ human_response=human_response
910
+ )
911
+
912
+ async with httpx.AsyncClient() as client:
913
+ response = await client.post(
914
+ "http://agent-service:8001/agent",
915
+ json=agent_request.dict()
916
+ )
917
+ agent_response = response.json()
918
+
919
+ # Clean up session
920
+ del active_sessions[session_id]
921
+
922
+ if agent_response.get("is_complete"):
923
+ order = agent_response["final_response"]
924
+ return {
925
+ "type": "order_result",
926
+ "message": f"Your last order: #{order['order_number']} from {order['date']} (ID: {order['id']})"
927
+ }
928
+
929
+ return {"type": "message", "message": "Processing your order request..."}
930
+ ```
931
+
932
+ **Agent Service (FastAPI - Container 2):**
933
+ ```python
934
+ from fastapi import FastAPI
935
+ from pydantic import BaseModel, Field
936
+ from mbxai import AgentClient, ToolClient, OpenRouterClient
937
+ from mbxai.agent.models import DialogOption, HumanInLoopResponse, HumanInteractionType
938
+ import os
939
+ from typing import Optional
940
+
941
+ app = FastAPI()
942
+
943
+ # Models for Agent service
944
+ class OrderInfo(BaseModel):
945
+ id: str = Field(description="Order ID")
946
+ order_number: str = Field(description="Order number")
947
+ date: str = Field(description="Order date")
948
+
949
+ class AgentRequest(BaseModel):
950
+ prompt: str
951
+ final_response_structure: str
952
+ agent_id: Optional[str] = None
953
+ human_response: Optional[dict] = None
954
+
955
+ # Shop integration tool (agent executes this)
956
+ def list_orders_from_shop(username: str, password: str, shop_url: str) -> list:
957
+ """Fetch orders from shop using credentials."""
958
+ # Mock shop API call
959
+ if username == "demo" and password == "demo123":
960
+ return [
961
+ {"id": "ord_001", "order_number": "ORD-2024-001", "date": "2024-01-15"},
962
+ {"id": "ord_002", "order_number": "ORD-2024-002", "date": "2024-01-20"},
963
+ {"id": "ord_003", "order_number": "ORD-2024-003", "date": "2024-01-25"}
964
+ ]
965
+ else:
966
+ return []
967
+
968
+ # Dialog option for shop credentials (UI handles this)
969
+ dialog_options = [
970
+ DialogOption(
971
+ id="shop_credentials",
972
+ title="Shop Credentials",
973
+ description="Provide shop login credentials",
974
+ parameters={"shop_url": "https://shop.example.com"}
975
+ )
976
+ ]
977
+
978
+ # Initialize agent
979
+ openrouter_client = OpenRouterClient(token=os.getenv("OPENROUTER_API_KEY"))
980
+ tool_client = ToolClient(openrouter_client)
981
+ agent = AgentClient(
982
+ tool_client,
983
+ human_in_loop=True,
984
+ dialog_options=dialog_options
985
+ )
986
+
987
+ # Register the shop tool
988
+ agent.register_tool(
989
+ name="list_orders_from_shop",
990
+ description="List orders from online shop using user credentials",
991
+ function=list_orders_from_shop
992
+ )
993
+
994
+ @app.post("/agent")
995
+ async def agent_endpoint(request: AgentRequest):
996
+ """Main agent processing endpoint"""
997
+
998
+ # Process human response if provided
999
+ human_response = None
1000
+ if request.human_response:
1001
+ human_response = HumanInLoopResponse(
1002
+ interaction_id=request.human_response["interaction_id"],
1003
+ response_type=HumanInteractionType(request.human_response["response_type"]),
1004
+ dialog_option_id=request.human_response.get("dialog_option_id"),
1005
+ additional_context=request.human_response.get("additional_context", "")
1006
+ )
1007
+
1008
+ # Call agent
1009
+ response = agent.agent(
1010
+ prompt=request.prompt,
1011
+ final_response_structure=OrderInfo,
1012
+ agent_id=request.agent_id,
1013
+ human_response=human_response
1014
+ )
1015
+
1016
+ # Convert response to JSON-serializable format
1017
+ result = {
1018
+ "agent_id": response.agent_id,
1019
+ "state": response.state.value,
1020
+ "is_complete": response.is_complete(),
1021
+ "needs_human_interaction": response.needs_human_interaction()
1022
+ }
1023
+
1024
+ if response.needs_human_interaction():
1025
+ request = response.human_interaction_request
1026
+ result["human_interaction_request"] = {
1027
+ "id": request.id,
1028
+ "interaction_type": request.interaction_type.value,
1029
+ "prompt": request.prompt,
1030
+ "dialog_option_id": "shop_credentials" if request.dialog_options else None,
1031
+ "parameters": {"shop_url": "https://shop.example.com"}
1032
+ }
1033
+
1034
+ if response.is_complete():
1035
+ # Get the last order (most recent)
1036
+ order_data = response.final_response
1037
+ result["final_response"] = {
1038
+ "id": order_data.id,
1039
+ "order_number": order_data.order_number,
1040
+ "date": order_data.date
1041
+ }
1042
+
1043
+ return result
1044
+
1045
+ @app.get("/health")
1046
+ async def health_check():
1047
+ return {"status": "healthy"}
1048
+ ```
1049
+
1050
+ **Flow Explanation:**
1051
+
1052
+ 1. **User**: "Give me my last order from this online shop"
1053
+ 2. **UI**: Sends message to Agent service `/agent` endpoint
1054
+ 3. **Agent**: Analyzes request, realizes it needs shop credentials
1055
+ 4. **Agent**: Returns `needs_human_interaction=True` with `shop_credentials` dialog option
1056
+ 5. **UI**: Recognizes dialog option, shows credentials form to user
1057
+ 6. **User**: Enters username/password in UI form
1058
+ 7. **UI**: Sends credentials to Agent via `/agent` endpoint with `human_response`
1059
+ 8. **Agent**: Uses `list_orders_from_shop` tool with provided credentials
1060
+ 9. **Agent**: Returns last order information
1061
+ 10. **UI**: Displays order to user
1062
+
1063
+ **Key Points:**
1064
+ - **Separation**: UI handles user interaction, Agent handles business logic
1065
+ - **Security**: Credentials flow through structured dialog, not plain text
1066
+ - **Scalability**: Services can be scaled independently
1067
+ - **Flexibility**: UI can customize credential forms, Agent focuses on order processing
1068
+
1069
+ ### Error Handling and Logging
1070
+
1071
+ ```python
1072
+ import logging
1073
+
1074
+ # Configure logging
1075
+ logging.basicConfig(level=logging.DEBUG)
1076
+
1077
+ try:
1078
+ response = client.create(messages)
1079
+ except OpenRouterAPIError as e:
1080
+ print(f"API Error: {e}")
1081
+ except OpenRouterConnectionError as e:
1082
+ print(f"Connection Error: {e}")
1083
+ except Exception as e:
1084
+ print(f"Unexpected Error: {e}")
1085
+ ```
1086
+
1087
+ ### Streaming Responses
1088
+
1089
+ ```python
1090
+ # Streaming with OpenRouterClient
1091
+ response = client.create(messages, stream=True)
1092
+ for chunk in response:
1093
+ if chunk.choices[0].delta.content:
1094
+ print(chunk.choices[0].delta.content, end="")
1095
+
1096
+ # Streaming with ToolClient (tools execute before streaming)
1097
+ response = tool_client.chat(messages, stream=True)
1098
+ for chunk in response:
1099
+ if chunk.choices[0].delta.content:
1100
+ print(chunk.choices[0].delta.content, end="")
1101
+ ```
1102
+
1103
+ ## 🧪 Testing
1104
+
1105
+ Run the test suite:
1106
+
1107
+ ```bash
1108
+ # Install development dependencies using uv
1109
+ uv sync
1110
+
1111
+ # Run tests with uv
1112
+ uv run pytest tests/
1113
+
1114
+ # Run with coverage
1115
+ uv run pytest tests/ --cov=mbxai --cov-report=html
1116
+
1117
+ # Test enhanced agent examples
1118
+ uv run python src/mbxai/examples/enhanced_agent_example.py
1119
+
1120
+ # Test Redis session handler (requires Redis)
1121
+ uv run python src/mbxai/examples/redis_session_handler_example.py
1122
+ ```
1123
+
1124
+ ## 🔧 Development Setup
1125
+
1126
+ 1. Clone the repository:
1127
+ ```bash
1128
+ git clone https://github.com/yourusername/mbxai.git
1129
+ cd mbxai/packages
1130
+ ```
1131
+
1132
+ 2. Install using uv (recommended):
1133
+ ```bash
1134
+ # Install uv if not already installed
1135
+ curl -LsSf https://astral.sh/uv/install.sh | sh
1136
+
1137
+ # Install dependencies and create virtual environment
1138
+ uv sync
1139
+
1140
+ # Activate virtual environment (optional, uv run handles this automatically)
1141
+ source .venv/bin/activate # On Windows: .venv\Scripts\activate
1142
+ ```
1143
+
1144
+ 3. Alternatively, use pip:
1145
+ ```bash
1146
+ python -m venv .venv
1147
+ source .venv/bin/activate # On Windows: .venv\Scripts\activate
1148
+ pip install -e ".[dev]"
1149
+ ```
1150
+
1151
+ 4. Set up environment variables:
1152
+ ```bash
1153
+ export OPENROUTER_API_KEY="your-api-key"
1154
+ ```
1155
+
1156
+ ## 📄 License
1157
+
1158
+ MIT License - see [LICENSE](LICENSE) file for details.
1159
+
1160
+ ## 🤝 Contributing
1161
+
1162
+ Contributions are welcome! Please feel free to submit a Pull Request.
1163
+
1164
+ ## 🔗 Links
1165
+
1166
+ - **Homepage**: [https://www.mibexx.de](https://www.mibexx.de)
1167
+ - **Documentation**: [https://www.mibexx.de](https://www.mibexx.de)
1168
+ - **Repository**: [https://github.com/yourusername/mbxai](https://github.com/yourusername/mbxai)
1169
+
1170
+ ## 📊 Version Information
1171
+
1172
+ Current version: **2.3.0**
1173
+
1174
+ ### What's New in 2.3.0:
1175
+ - **🎯 Enhanced Agent Client**: Complete rewrite with 6-step intelligent process
1176
+ - **💾 Pluggable Session Storage**: Custom session handlers for Redis, Database, File System
1177
+ - **👤 Human-in-the-Loop**: Interactive decision making, questions, and custom dialog options
1178
+ - **📋 Task Management**: Intelligent todo list generation with dependencies and status tracking
1179
+ - **🎯 Goal Evaluation**: Automatic assessment of goal achievement with iterative improvement
1180
+ - **🔧 Dialog Options**: Custom functions for authentication, integrations, and workflows
1181
+ - **📊 Enhanced State Management**: Full visibility into agent process and progress
1182
+ - **🧠 Requirement Analysis**: Intelligent goal breakdown and complexity assessment
1183
+ - **🛠️ Tool Analysis**: Smart mapping of available tools to goals
1184
+ - **🌐 Distributed Sessions**: Scale across multiple instances with persistent session storage
1185
+
1186
+ ### Requirements:
1187
+ - Python 3.12+ required
1188
+ - Built with modern async/await patterns
1189
+ - Type-safe with Pydantic v2
1190
+ - Compatible with OpenAI SDK v1.77+
1191
+ - Recommended: Use `uv` for dependency management