conversimple 0.2.0__tar.gz → 0.2.4__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 (30) hide show
  1. {conversimple-0.2.0 → conversimple-0.2.4}/PKG-INFO +338 -57
  2. {conversimple-0.2.0 → conversimple-0.2.4}/README.md +335 -54
  3. conversimple-0.2.4/conversimple/__init__.py +63 -0
  4. {conversimple-0.2.0 → conversimple-0.2.4}/conversimple/agent.py +152 -27
  5. conversimple-0.2.4/conversimple/api/__init__.py +31 -0
  6. conversimple-0.2.4/conversimple/api/client.py +284 -0
  7. conversimple-0.2.4/conversimple/api/endpoints/__init__.py +1 -0
  8. conversimple-0.2.4/conversimple/api/endpoints/agents.py +163 -0
  9. conversimple-0.2.4/conversimple/api/endpoints/api_keys.py +44 -0
  10. conversimple-0.2.4/conversimple/api/endpoints/deployments.py +171 -0
  11. conversimple-0.2.4/conversimple/api/exceptions.py +94 -0
  12. conversimple-0.2.4/conversimple/api/models.py +114 -0
  13. conversimple-0.2.4/conversimple/config.py +96 -0
  14. {conversimple-0.2.0 → conversimple-0.2.4}/conversimple/connection.py +54 -37
  15. conversimple-0.2.4/conversimple/dispatcher.py +344 -0
  16. {conversimple-0.2.0 → conversimple-0.2.4}/conversimple/tools.py +2 -2
  17. {conversimple-0.2.0 → conversimple-0.2.4}/conversimple/utils.py +5 -3
  18. {conversimple-0.2.0 → conversimple-0.2.4}/conversimple.egg-info/PKG-INFO +338 -57
  19. conversimple-0.2.4/conversimple.egg-info/SOURCES.txt +27 -0
  20. {conversimple-0.2.0 → conversimple-0.2.4}/conversimple.egg-info/entry_points.txt +1 -0
  21. {conversimple-0.2.0 → conversimple-0.2.4}/conversimple.egg-info/requires.txt +2 -0
  22. {conversimple-0.2.0 → conversimple-0.2.4}/setup.py +7 -6
  23. conversimple-0.2.4/tests/test_dispatcher_session.py +120 -0
  24. conversimple-0.2.0/conversimple/__init__.py +0 -26
  25. conversimple-0.2.0/conversimple.egg-info/SOURCES.txt +0 -15
  26. {conversimple-0.2.0 → conversimple-0.2.4}/conversimple/callbacks.py +0 -0
  27. {conversimple-0.2.0 → conversimple-0.2.4}/conversimple.egg-info/dependency_links.txt +0 -0
  28. {conversimple-0.2.0 → conversimple-0.2.4}/conversimple.egg-info/not-zip-safe +0 -0
  29. {conversimple-0.2.0 → conversimple-0.2.4}/conversimple.egg-info/top_level.txt +0 -0
  30. {conversimple-0.2.0 → conversimple-0.2.4}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: conversimple
3
- Version: 0.2.0
3
+ Version: 0.2.4
4
4
  Summary: Python SDK for Conversimple Conversational AI Platform
5
5
  Home-page: https://github.com/conversimple/conversimple-sdk
6
6
  Author: Conversimple
@@ -16,16 +16,16 @@ Classifier: Topic :: Communications :: Chat
16
16
  Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content
17
17
  Classifier: License :: OSI Approved :: MIT License
18
18
  Classifier: Programming Language :: Python :: 3
19
- Classifier: Programming Language :: Python :: 3.8
20
- Classifier: Programming Language :: Python :: 3.9
21
19
  Classifier: Programming Language :: Python :: 3.10
22
20
  Classifier: Programming Language :: Python :: 3.11
23
21
  Classifier: Programming Language :: Python :: 3.12
24
- Requires-Python: >=3.8
22
+ Requires-Python: >=3.10
25
23
  Description-Content-Type: text/markdown
26
24
  Requires-Dist: websockets>=12.0
27
25
  Requires-Dist: aiofiles>=23.0
28
26
  Requires-Dist: aiohttp>=3.8.0
27
+ Requires-Dist: httpx>=0.24.0
28
+ Requires-Dist: pydantic>=2.0
29
29
  Provides-Extra: dev
30
30
  Requires-Dist: pytest>=7.0; extra == "dev"
31
31
  Requires-Dist: pytest-asyncio>=0.21.0; extra == "dev"
@@ -68,52 +68,165 @@ This SDK enables customers to build and deploy AI agents that integrate with the
68
68
  ### Installation
69
69
 
70
70
  ```bash
71
- pip install conversimple-sdk
71
+ pip install conversimple
72
72
  ```
73
73
 
74
- ### Basic Usage
74
+ ### Define an Agent
75
75
 
76
76
  ```python
77
- import asyncio
78
77
  from conversimple import ConversimpleAgent, tool
79
78
 
80
79
  class MyAgent(ConversimpleAgent):
80
+ agent_id = "1b2bb22f-1c3d-4e5f-6789-abcdef012345"
81
+
81
82
  @tool("Get current weather for a location")
82
83
  def get_weather(self, location: str) -> dict:
83
84
  return {"location": location, "temperature": 72, "condition": "sunny"}
84
85
 
85
86
  def on_conversation_started(self, conversation_id: str):
86
87
  print(f"Conversation started: {conversation_id}")
88
+ ```
87
89
 
88
- async def main():
89
- agent = MyAgent(
90
- api_key="your-api-key",
91
- customer_id="your-customer-id"
92
- )
93
-
94
- await agent.start()
95
-
96
- # Keep running
97
- while True:
98
- await asyncio.sleep(1)
90
+ ### Run the Dispatcher
99
91
 
100
- if __name__ == "__main__":
101
- asyncio.run(main())
92
+ Use the dispatcher to discover your agent modules and launch per-conversation instances automatically:
93
+
94
+ ```bash
95
+ conversimple-dispatcher \
96
+ --api-key "$CONVERSIMPLE_API_KEY" \
97
+ --platform-url "$CONVERSIMPLE_PLATFORM_URL" \
98
+ --search-path ./agents
99
+ ```
100
+
101
+ The dispatcher keeps a single control-plane connection, listens for `conversation_ready` events, and spawns a dedicated `ConversimpleAgent` for each active conversation. This applies even if you only have one agent—the dispatcher guarantees safe concurrency and simplifies redeployments.
102
+
103
+ ## Platform API Client
104
+
105
+ Programmatically manage agents and deployments on the platform using the `PlatformClient`:
106
+
107
+ ### Create an Agent
108
+
109
+ ```python
110
+ from conversimple import PlatformClient
111
+
112
+ # Uses Config.API_ENDPOINT by default (set via CONVERSIMPLE_API_ENDPOINT env var)
113
+ client = PlatformClient(api_key="your-api-key")
114
+
115
+ # Create a new agent
116
+ agent = client.agents.create_agent(
117
+ name="Support Bot",
118
+ description="Handles customer support queries"
119
+ )
120
+
121
+ print(f"Created agent: {agent.id}")
122
+ ```
123
+
124
+ ### List Agents
125
+
126
+ ```python
127
+ # List all agents
128
+ agents, meta = client.agents.list_agents(page=1, per_page=20)
129
+
130
+ # Filter by status
131
+ draft_agents, meta = client.agents.list_agents(status="draft")
132
+
133
+ # Search by name
134
+ found_agents, meta = client.agents.list_agents(search="support")
135
+
136
+ for agent in agents:
137
+ print(f"{agent.name} ({agent.status}) - v{agent.version}")
138
+ ```
139
+
140
+ ### Publish an Agent
141
+
142
+ ```python
143
+ # Get agent
144
+ agent = client.agents.get_agent("agent-id")
145
+
146
+ # Publish to production
147
+ published = client.agents.publish_agent("agent-id")
148
+
149
+ print(f"Agent published: {published.status}")
150
+ ```
151
+
152
+ ### Create a Deployment
153
+
154
+ ```python
155
+ # Create deployment for a widget
156
+ deployment = client.deployments.create_deployment(
157
+ name="Support Widget",
158
+ agent_id="agent-id",
159
+ channel="widget",
160
+ channel_config={
161
+ "widget_position": "bottom-right"
162
+ },
163
+ greeting_message="How can we help?"
164
+ )
165
+
166
+ print(f"Created deployment: {deployment.id}")
167
+ ```
168
+
169
+ ### Activate/Deactivate Deployments
170
+
171
+ ```python
172
+ # Activate deployment
173
+ active = client.deployments.activate_deployment("deployment-id")
174
+
175
+ # Deactivate deployment
176
+ inactive = client.deployments.deactivate_deployment("deployment-id")
177
+ ```
178
+
179
+ ### Check API Key Usage
180
+
181
+ ```python
182
+ # Get API key information
183
+ key_info = client.api_keys.get_api_key_info()
184
+ print(f"Status: {key_info.status}")
185
+ print(f"Last 4: {key_info.last_4_chars}")
186
+
187
+ # Get usage statistics
188
+ usage = client.api_keys.get_api_key_usage()
189
+ print(f"Requests (24h): {usage.requests_24h}")
190
+ print(f"Rate limit: {usage.rate_limit}")
102
191
  ```
103
192
 
104
193
  ## Core Concepts
105
194
 
106
- ### Agent Session Model
195
+ ### Dispatcher-Orchestrated Sessions
196
+
197
+ All deployments (even single-agent) must run through the dispatcher so each conversation is isolated in its own `ConversimpleAgent` instance. The dispatcher:
198
+
199
+ - Watches for `conversation_ready` events over a persistent control connection
200
+ - Scans a directory for `ConversimpleAgent` subclasses that expose an `agent_id`
201
+ - Spawns one agent instance per conversation and stops it when the session ends
107
202
 
108
- Each `ConversimpleAgent` instance handles a single conversation session. For multiple concurrent conversations, create multiple agent instances:
203
+ #### Declaring Agent Identity
204
+
205
+ Embed the platform agent UUID (or a stable identifier) on each agent class so the dispatcher can auto-register it:
109
206
 
110
207
  ```python
111
- # Per-conversation agent instances
112
- async def handle_conversation(conversation_id):
113
- agent = MyAgent(api_key=api_key, customer_id=customer_id)
114
- await agent.start(conversation_id=conversation_id)
208
+ from conversimple import ConversimpleAgent, tool
209
+
210
+ class BillingAgent(ConversimpleAgent):
211
+ agent_id = "7f1f28f7-4c4c-4a77-8f7a-9cf6580e3f32"
212
+
213
+ @tool("Lookup outstanding invoices")
214
+ def lookup_invoices(self, customer_id: str) -> dict:
215
+ ...
115
216
  ```
116
217
 
218
+ #### Running the Dispatcher
219
+
220
+ Point the dispatcher at a directory of agent modules (typically your project root):
221
+
222
+ ```bash
223
+ conversimple-dispatcher --api-key "$CONVERSIMPLE_API_KEY" \
224
+ --platform-url "$CONVERSIMPLE_PLATFORM_URL" \
225
+ --search-path ./agents
226
+ ```
227
+
228
+ The dispatcher discovers all agents under `--search-path`, matches incoming `agent_id` values from the platform, and launches dedicated `ConversimpleAgent` instances for each conversation. Because the dispatcher brokers every session, you get safe concurrency, easy hot-reloads, and no need to hand-wire credentials inside individual agents. Existing conversations keep running during redeploys; new sessions pick up the latest code automatically.
229
+
117
230
  ### Tool Registration
118
231
 
119
232
  Define tools using the `@tool` and `@tool_async` decorators:
@@ -134,6 +247,15 @@ class BusinessAgent(ConversimpleAgent):
134
247
  return {"sent": True, "message_id": result.id}
135
248
  ```
136
249
 
250
+ The 0.2.4 source re-registers conversation tools after a WebSocket reconnect.
251
+ Tool calls run independently of the receive loop, so one slow call does not
252
+ block the next message. A repeated call ID returns its previous result without
253
+ re-executing the function; the cache is bounded to 1,024 results per agent
254
+ process. Tool execution has a default 30-second timeout, capped at five
255
+ minutes. Synchronous functions run in a worker thread, so a timed-out function
256
+ may finish its side effect after the timeout response; design externally
257
+ mutating tools to be idempotent. Python 3.10 or newer is required.
258
+
137
259
  ### Event Callbacks
138
260
 
139
261
  Handle conversation lifecycle events:
@@ -155,13 +277,112 @@ class MyAgent(ConversimpleAgent):
155
277
 
156
278
  ## Configuration
157
279
 
280
+ The SDK provides centralized configuration through the `Config` class with full environment variable support. All settings can be overridden via environment variables.
281
+
282
+ ### Using the Config Class
283
+
284
+ ```python
285
+ from conversimple import Config
286
+
287
+ # Read current configuration
288
+ print(Config.API_ENDPOINT) # API endpoint from env var or default
289
+ print(Config.PLATFORM_URL) # WebSocket URL from env var or default
290
+ print(Config.API_TIMEOUT) # 30 (seconds)
291
+ print(Config.VERBOSE) # False
292
+
293
+ # Update configuration at runtime
294
+ Config.update(
295
+ API_ENDPOINT="http://api.example.com",
296
+ VERBOSE=True
297
+ )
298
+ ```
299
+
158
300
  ### Environment Variables
159
301
 
302
+ Configure the SDK using environment variables:
303
+
160
304
  ```bash
305
+ # Platform URLs
306
+ export CONVERSIMPLE_API_ENDPOINT="http://api.example.com"
307
+ export CONVERSIMPLE_PLATFORM_URL="ws://api.example.com/sdk/websocket"
308
+
309
+ # Authentication
161
310
  export CONVERSIMPLE_API_KEY="your-api-key"
162
311
  export CONVERSIMPLE_CUSTOMER_ID="your-customer-id"
163
- export CONVERSIMPLE_PLATFORM_URL="ws://localhost:4000/sdk/websocket"
312
+
313
+ # Client Configuration
314
+ export CONVERSIMPLE_API_TIMEOUT="30"
315
+ export CONVERSIMPLE_VERBOSE="true"
316
+
317
+ # Logging
164
318
  export CONVERSIMPLE_LOG_LEVEL="INFO"
319
+
320
+ # Connection Settings
321
+ export CONVERSIMPLE_HEARTBEAT_INTERVAL="30"
322
+ export CONVERSIMPLE_RECONNECT_BACKOFF="2.0"
323
+ export CONVERSIMPLE_MAX_BACKOFF="300.0"
324
+ export CONVERSIMPLE_ENABLE_CIRCUIT_BREAKER="true"
325
+ ```
326
+
327
+ ### Configuration Reference
328
+
329
+ **Platform URLs:**
330
+ - `CONVERSIMPLE_API_ENDPOINT` - Platform API endpoint
331
+ - `CONVERSIMPLE_PLATFORM_URL` - Platform WebSocket URL
332
+
333
+ **Authentication:**
334
+ - `CONVERSIMPLE_API_KEY` - API key for platform communication
335
+ - `CONVERSIMPLE_CUSTOMER_ID` - Customer identifier (optional)
336
+
337
+ **Client Settings:**
338
+ - `CONVERSIMPLE_API_TIMEOUT` (default: `30`) - HTTP request timeout in seconds
339
+ - `CONVERSIMPLE_VERBOSE` (default: `false`) - Enable verbose logging
340
+ - `CONVERSIMPLE_LOG_LEVEL` (default: `INFO`) - Log level (DEBUG, INFO, WARNING, ERROR)
341
+
342
+ **Connection Resilience:**
343
+ - `CONVERSIMPLE_HEARTBEAT_INTERVAL` (default: `30`) - WebSocket heartbeat interval in seconds
344
+ - `CONVERSIMPLE_RECONNECT_BACKOFF` (default: `2.0`) - Exponential backoff multiplier
345
+ - `CONVERSIMPLE_MAX_BACKOFF` (default: `300`) - Maximum backoff time in seconds
346
+ - `CONVERSIMPLE_ENABLE_CIRCUIT_BREAKER` (default: `true`) - Enable circuit breaker for permanent failures
347
+
348
+ ### Configuration Priority
349
+
350
+ Configuration is loaded in this order (first match wins):
351
+
352
+ 1. **Runtime updates** via `Config.update()`
353
+ 2. **Environment variables**
354
+ 3. **Default values** in `Config` class
355
+
356
+ ### Configuration Examples
357
+
358
+ **Remote Platform Configuration:**
359
+ ```bash
360
+ export CONVERSIMPLE_API_ENDPOINT="https://api.conversimple.com"
361
+ export CONVERSIMPLE_PLATFORM_URL="wss://app.conversimple.com/customer_sdk/websocket"
362
+ export CONVERSIMPLE_API_KEY="prod-api-key"
363
+ export CONVERSIMPLE_ENABLE_CIRCUIT_BREAKER="true"
364
+ ```
365
+
366
+ **Custom Runtime Configuration:**
367
+ ```python
368
+ from conversimple import Config, PlatformClient, ConversimpleAgent
369
+
370
+ # Update configuration for this session
371
+ Config.update(
372
+ API_ENDPOINT="http://api.example.com",
373
+ PLATFORM_URL="ws://api.example.com/sdk/websocket",
374
+ VERBOSE=True
375
+ )
376
+
377
+ # All subsequent clients use the updated config
378
+ client = PlatformClient(api_key="your-api-key")
379
+ agent = ConversimpleAgent(api_key="your-api-key")
380
+
381
+ # Or override for specific client instances
382
+ agent = ConversimpleAgent(
383
+ api_key="your-api-key",
384
+ platform_url="ws://other-server.com/sdk/websocket" # Override config
385
+ )
165
386
  ```
166
387
 
167
388
  ### Basic Configuration
@@ -217,7 +438,7 @@ agent = ConversimpleAgent(
217
438
  # Good for testing - fails quickly
218
439
  agent = ConversimpleAgent(
219
440
  api_key="test-key",
220
- platform_url="ws://localhost:4000/sdk/websocket",
441
+ # Uses Config.PLATFORM_URL by default (set via CONVERSIMPLE_PLATFORM_URL env var)
221
442
  max_reconnect_attempts=5, # Only 5 attempts
222
443
  reconnect_backoff=1.5, # Faster backoff
223
444
  max_backoff=30, # Max 30 seconds between retries
@@ -248,40 +469,29 @@ agent = ConversimpleAgent(
248
469
 
249
470
  ## Examples
250
471
 
251
- The SDK includes several example implementations:
472
+ Example agents live in `examples/` and are discovered automatically when you point the dispatcher at that directory:
252
473
 
253
- ### Simple Weather Agent
254
474
  ```bash
255
- python examples/simple_agent.py
475
+ conversimple-dispatcher \
476
+ --api-key "$CONVERSIMPLE_API_KEY" \
477
+ --platform-url "$CONVERSIMPLE_PLATFORM_URL" \
478
+ --search-path ./examples
256
479
  ```
257
480
 
258
- A basic agent that provides weather information, demonstrating:
259
- - Tool registration with `@tool` decorator
481
+ ### Simple Weather Agent (`examples/simple_agent.py`)
482
+ - Basic weather information tools
260
483
  - Conversation lifecycle callbacks
261
- - Basic agent structure
484
+ - Good starting point for lightweight agents
262
485
 
263
- ### Customer Service Agent
264
- ```bash
265
- python examples/customer_service.py
266
- ```
486
+ ### Customer Service Agent (`examples/customer_service.py`)
487
+ - Customer lookup, balance, and ticket management tools
488
+ - Mix of synchronous and asynchronous operations
489
+ - Demonstrates stateful workflows and external API usage
267
490
 
268
- Advanced customer service agent with multiple tools:
269
- - Customer lookup and account management
270
- - Support ticket creation
271
- - Email notifications
272
- - Refund processing
273
- - Async tool execution
274
-
275
- ### Multi-Step Booking Agent
276
- ```bash
277
- python examples/booking_agent.py
278
- ```
279
-
280
- Complex booking workflow demonstrating:
281
- - Multi-turn conversation state management
282
- - Booking creation, confirmation, and cancellation
283
- - Business rule validation
284
- - Transaction-like processes
491
+ ### Multi-Step Booking Agent (`examples/booking_agent.py`)
492
+ - Availability checks, booking creation, confirmation, and cancellation
493
+ - Stateful booking sessions with validations
494
+ - Shows how to manage multi-turn transactional flows
285
495
 
286
496
  ## API Reference
287
497
 
@@ -295,7 +505,7 @@ Main agent class for platform integration.
295
505
  ConversimpleAgent(
296
506
  api_key: str,
297
507
  customer_id: Optional[str] = None,
298
- platform_url: str = "ws://localhost:4000/sdk/websocket",
508
+ platform_url: Optional[str] = None, # Defaults to Config.PLATFORM_URL
299
509
  max_reconnect_attempts: Optional[int] = None,
300
510
  reconnect_backoff: float = 2.0,
301
511
  max_backoff: float = 300.0,
@@ -335,7 +545,7 @@ def my_tool(self, param1: str, param2: int = 10) -> dict:
335
545
  return {"result": "success"}
336
546
  ```
337
547
 
338
- #### @tool_async(description)
548
+ #### @tool_async(description)
339
549
  Register asynchronous tool function.
340
550
 
341
551
  ```python
@@ -345,6 +555,77 @@ async def my_async_tool(self, param: str) -> dict:
345
555
  return {"result": "success"}
346
556
  ```
347
557
 
558
+ ### PlatformClient
559
+
560
+ HTTP client for managing agents and deployments on the platform.
561
+
562
+ #### Constructor
563
+
564
+ ```python
565
+ PlatformClient(
566
+ api_key: str,
567
+ api_endpoint: Optional[str] = None, # Defaults to Config.API_ENDPOINT
568
+ timeout: int = 30,
569
+ verbose: bool = False
570
+ )
571
+ ```
572
+
573
+ **Parameters:**
574
+ - `api_key` (str): API key for authentication
575
+ - `api_endpoint` (str): Platform API endpoint URL
576
+ - `timeout` (int): Request timeout in seconds (default: 30)
577
+ - `verbose` (bool): Enable verbose logging (default: False)
578
+
579
+ #### Endpoints
580
+
581
+ **Agents** (`client.agents`):
582
+ - `list_agents(page, per_page, status, search)` - List agents with pagination and filtering
583
+ - `create_agent(name, description, agent_config)` - Create new agent
584
+ - `get_agent(agent_id)` - Get agent details
585
+ - `update_agent(agent_id, name, description, status)` - Update agent
586
+ - `delete_agent(agent_id)` - Delete agent
587
+ - `publish_agent(agent_id)` - Publish agent to production
588
+ - `get_agent_spec(agent_id)` - Get agent specification
589
+
590
+ **Deployments** (`client.deployments`):
591
+ - `list_deployments(page, per_page, agent_id, status, environment)` - List deployments
592
+ - `create_deployment(name, agent_id, channel, environment, channel_config)` - Create deployment
593
+ - `get_deployment(deployment_id)` - Get deployment details
594
+ - `update_deployment(deployment_id, name, environment)` - Update deployment
595
+ - `delete_deployment(deployment_id)` - Delete deployment
596
+ - `activate_deployment(deployment_id)` - Activate deployment
597
+ - `deactivate_deployment(deployment_id)` - Deactivate deployment
598
+
599
+ **API Keys** (`client.api_keys`):
600
+ - `get_api_key_info()` - Get API key information
601
+ - `rotate_api_key()` - Rotate API key
602
+ - `get_api_key_usage()` - Get usage statistics
603
+
604
+ #### Error Handling
605
+
606
+ The PlatformClient raises specific exceptions for different error cases:
607
+
608
+ ```python
609
+ from conversimple import (
610
+ APIError, # Base API error
611
+ ValidationError, # 422 validation error
612
+ NotFoundError, # 404 not found
613
+ UnauthorizedError, # 401 authentication failed
614
+ ForbiddenError # 403 permission denied
615
+ )
616
+
617
+ try:
618
+ agent = client.agents.get_agent("invalid-id")
619
+ except NotFoundError as e:
620
+ print(f"Agent not found: {e.message}")
621
+ except UnauthorizedError as e:
622
+ print(f"Invalid API key: {e.message}")
623
+ except ValidationError as e:
624
+ print(f"Validation errors: {e.errors}")
625
+ except APIError as e:
626
+ print(f"API error: {e.message}")
627
+ ```
628
+
348
629
  ### Type Hints
349
630
 
350
631
  The SDK automatically generates JSON schemas from Python type hints: