assistant-runtime-sdk 1.0.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.
@@ -0,0 +1,17 @@
1
+ GNU AFFERO GENERAL PUBLIC LICENSE
2
+ Version 3, 19 November 2007
3
+
4
+ Copyright (C) 2025 Paul Clinton
5
+
6
+ This program is free software: you can redistribute it and/or modify
7
+ it under the terms of the GNU Affero General Public License as published by
8
+ the Free Software Foundation, either version 3 of the License, or
9
+ (at your option) any later version.
10
+
11
+ This program is distributed in the hope that it will be useful,
12
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
+ GNU Affero General Public License for more details.
15
+
16
+ You should have received a copy of the GNU Affero General Public License
17
+ along with this program. If not, see <https://www.gnu.org/licenses/>.
@@ -0,0 +1,259 @@
1
+ Metadata-Version: 2.4
2
+ Name: assistant_runtime_sdk
3
+ Version: 1.0.0
4
+ Summary: Python SDK for Assistant Runtime - AI-powered assistant backend
5
+ Keywords: frappe,ai,assistant,llm,mcp,anthropic,openai,streaming
6
+ Author: Paul Clinton
7
+ Requires-Python: >=3.10
8
+ Description-Content-Type: text/markdown
9
+ Classifier: Development Status :: 5 - Production/Stable
10
+ Classifier: Intended Audience :: Developers
11
+ Classifier: License :: OSI Approved :: GNU Affero General Public License v3
12
+ Classifier: Programming Language :: Python :: 3
13
+ Classifier: Programming Language :: Python :: 3.10
14
+ Classifier: Programming Language :: Python :: 3.11
15
+ Classifier: Programming Language :: Python :: 3.12
16
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
17
+ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
18
+ License-File: LICENSE
19
+ Requires-Dist: requests>=2.28.0
20
+ Requires-Dist: assistant_runtime_sdk[async, dev] ; extra == "all"
21
+ Requires-Dist: aiohttp>=3.8.0 ; extra == "async"
22
+ Requires-Dist: pytest>=7.0.0 ; extra == "dev"
23
+ Requires-Dist: pytest-asyncio>=0.21.0 ; extra == "dev"
24
+ Requires-Dist: pytest-httpserver>=1.0.0 ; extra == "dev"
25
+ Requires-Dist: ruff>=0.1.0 ; extra == "dev"
26
+ Project-URL: Documentation, https://github.com/buildswithpaul/assistant_runtime_sdk#readme
27
+ Project-URL: Homepage, https://github.com/buildswithpaul/assistant_runtime_sdk
28
+ Project-URL: Issues, https://github.com/buildswithpaul/assistant_runtime_sdk/issues
29
+ Project-URL: Repository, https://github.com/buildswithpaul/assistant_runtime_sdk
30
+ Provides-Extra: all
31
+ Provides-Extra: async
32
+ Provides-Extra: dev
33
+
34
+ # FACL - Python SDK for Frappe Assistant Cloud
35
+
36
+ A Python SDK for integrating with [Frappe Assistant Cloud (FACL)](https://facl.frappe.cloud) - the AI-powered assistant backend for the Frappe ecosystem.
37
+
38
+ ## Features
39
+
40
+ - **Sync and Async Clients** - Choose between `FACLClient` (requests) or `AsyncFACLClient` (aiohttp)
41
+ - **SSE Streaming** - Real-time streaming responses with tool execution
42
+ - **HMAC Authentication** - Secure request signing
43
+ - **Auto Model Selection** - Intelligent model routing with cross-provider fallback
44
+ - **Full API Coverage** - Chat, billing, conversations, user management, and more
45
+ - **Type Hints** - Full type annotations for better IDE support
46
+
47
+ ## Installation
48
+
49
+ ```bash
50
+ # Basic installation (sync client only)
51
+ pip install facl
52
+
53
+ # With async support
54
+ pip install facl[async]
55
+
56
+ # Development installation
57
+ pip install facl[dev]
58
+
59
+ # Everything
60
+ pip install facl[all]
61
+ ```
62
+
63
+ ## Quick Start
64
+
65
+ ### Sync Client
66
+
67
+ ```python
68
+ from facl import FACLClient
69
+
70
+ client = FACLClient(
71
+ tenant_id="your-tenant-id",
72
+ tenant_secret="your-secret",
73
+ facl_url="https://facl.frappe.cloud"
74
+ )
75
+
76
+ # List available models
77
+ models = client.list_available_models()
78
+ for model in models.get("models", []):
79
+ print(f"{model['model_id']} - {model['display_name']}")
80
+
81
+ # Stream a chat response
82
+ for event in client.stream_chat(
83
+ session_id="session-123",
84
+ message="What can you help me with?",
85
+ user_id="user@example.com",
86
+ model_id="auto" # Use auto-model selection
87
+ ):
88
+ if event["event"] == "stream_chunk":
89
+ print(event["data"].get("content", ""), end="", flush=True)
90
+ elif event["event"] == "stream_complete":
91
+ print(f"\n\nTokens used: {event['data'].get('tokens_used')}")
92
+ ```
93
+
94
+ ### Async Client
95
+
96
+ ```python
97
+ import asyncio
98
+ from facl import AsyncFACLClient
99
+
100
+ async def main():
101
+ async with AsyncFACLClient(
102
+ tenant_id="your-tenant-id",
103
+ tenant_secret="your-secret"
104
+ ) as client:
105
+ async for event in client.stream_chat(
106
+ session_id="session-123",
107
+ message="Hello!",
108
+ user_id="user@example.com"
109
+ ):
110
+ if event["event"] == "stream_chunk":
111
+ print(event["data"].get("content", ""), end="")
112
+
113
+ asyncio.run(main())
114
+ ```
115
+
116
+ ### Custom Logger
117
+
118
+ ```python
119
+ import logging
120
+ from facl import FACLClient
121
+
122
+ logging.basicConfig(level=logging.DEBUG)
123
+ logger = logging.getLogger("my_app.facl")
124
+
125
+ client = FACLClient(
126
+ tenant_id="your-tenant-id",
127
+ tenant_secret="your-secret",
128
+ logger=logger
129
+ )
130
+ ```
131
+
132
+ ## SSE Event Types
133
+
134
+ When streaming, you'll receive events with these types:
135
+
136
+ | Event | Description |
137
+ |-------|-------------|
138
+ | `stream_start` | Stream initialized |
139
+ | `stream_chunk` | Text chunk from LLM |
140
+ | `stream_complete` | Full response with metrics |
141
+ | `stream_error` | Error occurred |
142
+ | `thinking` | Reasoning/thinking content |
143
+ | `tool_call_start` | Tool execution beginning |
144
+ | `tool_call_result` | Tool execution complete |
145
+ | `approval_required` | HITL approval needed |
146
+ | `tool_cancelled` | Tool was rejected |
147
+ | `model_fallback` | Auto mode selected a model |
148
+ | `rate_limited` | All models rate limited |
149
+
150
+ ## API Reference
151
+
152
+ ### FACLClient / AsyncFACLClient
153
+
154
+ #### Chat & Streaming
155
+ - `stream_chat(session_id, message, user_id, context=None, model_id=None)` - Stream chat response
156
+
157
+ #### Models
158
+ - `list_available_models()` - List available AI models
159
+ - `set_preferred_model(model_id)` - Set preferred model
160
+
161
+ #### Tenant
162
+ - `get_tenant_info()` - Get tenant information
163
+ - `accept_terms(terms_version, accepted_by)` - Accept terms and conditions
164
+
165
+ #### Conversations
166
+ - `list_conversations(user_id=None, limit=50, offset=0)` - List conversations
167
+ - `get_conversation(conversation_id)` - Get conversation details
168
+ - `get_messages(conversation_id, limit=100, offset=0)` - Get messages
169
+ - `create_message(conversation_id, message_id, role, content, ...)` - Create message
170
+ - `update_conversation(conversation_id, title=None, user_id=None)` - Update conversation
171
+ - `delete_conversation(conversation_id)` - Soft delete conversation
172
+ - `delete_message(conversation_id, message_id)` - Soft delete message
173
+
174
+ #### Billing
175
+ - `get_plan_comparison()` - Get available plans
176
+ - `get_usage_dashboard()` - Get usage statistics
177
+ - `get_usage_history(days=30)` - Get historical usage
178
+ - `initiate_checkout(plan, billing_cycle="monthly")` - Start checkout
179
+ - `verify_checkout(session_id=None)` - Verify payment
180
+ - `upgrade_plan(new_plan, billing_cycle="monthly")` - Upgrade subscription
181
+ - `cancel_subscription(cancel_immediately=False)` - Cancel subscription
182
+
183
+ #### Users & MCP Servers
184
+ - `register_user(user_id, display_name=None, custom_instructions=None)` - Register user
185
+ - `get_user(user_id)` - Get user details
186
+ - `get_user_auth_status(user_id)` - Check auth status
187
+ - `add_user_mcp_server(user_id, server_name, endpoint_url, ...)` - Add MCP server
188
+ - `get_user_mcp_servers(user_id)` - List user's MCP servers
189
+ - `update_mcp_server_tokens(user_id, server_name, access_token, ...)` - Update tokens
190
+ - `remove_user_mcp_server(user_id, server_name)` - Remove MCP server
191
+
192
+ #### Prompts
193
+ - `list_prompts(user_id, cursor=None)` - List prompt templates
194
+ - `get_prompt(prompt_name, arguments=None, user_id=None)` - Get rendered prompt
195
+
196
+ #### Tools
197
+ - `list_tools(user_id, server=None)` - List MCP tools with input schemas
198
+
199
+ ### Standalone Functions
200
+
201
+ ```python
202
+ from facl import get_terms, register_tenant
203
+
204
+ # Get current terms (no auth required)
205
+ terms = get_terms("https://facl.frappe.cloud")
206
+
207
+ # Register a new tenant
208
+ result = register_tenant(
209
+ facl_url="https://facl.frappe.cloud",
210
+ site_url="https://mysite.frappe.cloud",
211
+ terms_accepted=True,
212
+ terms_version="1.0",
213
+ accepted_by="admin@example.com"
214
+ )
215
+ ```
216
+
217
+ ## Exceptions
218
+
219
+ ```python
220
+ from facl.exceptions import (
221
+ FACLError, # Base exception
222
+ FACLAuthenticationError, # HMAC signature failed
223
+ FACLRateLimitError, # Rate limit exceeded (has retry_after)
224
+ FACLStreamError, # SSE streaming error
225
+ FACLConfigurationError, # Invalid configuration
226
+ )
227
+ ```
228
+
229
+ ## Frappe Integration
230
+
231
+ For Frappe applications, create a thin adapter:
232
+
233
+ ```python
234
+ import frappe
235
+ from facl import FACLClient
236
+
237
+ class FrappeLogger:
238
+ def error(self, msg, *args, **kwargs):
239
+ frappe.log_error(msg, kwargs.get('category', 'FACL'))
240
+
241
+ def get_facl_client():
242
+ settings = frappe.get_single("FACO Settings")
243
+ if settings.registration_status != "Registered":
244
+ return None
245
+
246
+ return FACLClient(
247
+ tenant_id=settings.tenant_id,
248
+ tenant_secret=settings.get_password("tenant_secret"),
249
+ facl_url=settings.facl_url,
250
+ logger=FrappeLogger()
251
+ )
252
+ ```
253
+
254
+ ## License
255
+
256
+ GNU Affero General Public License v3.0
257
+
258
+ Copyright (C) 2025 Paul Clinton
259
+
@@ -0,0 +1,225 @@
1
+ # FACL - Python SDK for Frappe Assistant Cloud
2
+
3
+ A Python SDK for integrating with [Frappe Assistant Cloud (FACL)](https://facl.frappe.cloud) - the AI-powered assistant backend for the Frappe ecosystem.
4
+
5
+ ## Features
6
+
7
+ - **Sync and Async Clients** - Choose between `FACLClient` (requests) or `AsyncFACLClient` (aiohttp)
8
+ - **SSE Streaming** - Real-time streaming responses with tool execution
9
+ - **HMAC Authentication** - Secure request signing
10
+ - **Auto Model Selection** - Intelligent model routing with cross-provider fallback
11
+ - **Full API Coverage** - Chat, billing, conversations, user management, and more
12
+ - **Type Hints** - Full type annotations for better IDE support
13
+
14
+ ## Installation
15
+
16
+ ```bash
17
+ # Basic installation (sync client only)
18
+ pip install facl
19
+
20
+ # With async support
21
+ pip install facl[async]
22
+
23
+ # Development installation
24
+ pip install facl[dev]
25
+
26
+ # Everything
27
+ pip install facl[all]
28
+ ```
29
+
30
+ ## Quick Start
31
+
32
+ ### Sync Client
33
+
34
+ ```python
35
+ from facl import FACLClient
36
+
37
+ client = FACLClient(
38
+ tenant_id="your-tenant-id",
39
+ tenant_secret="your-secret",
40
+ facl_url="https://facl.frappe.cloud"
41
+ )
42
+
43
+ # List available models
44
+ models = client.list_available_models()
45
+ for model in models.get("models", []):
46
+ print(f"{model['model_id']} - {model['display_name']}")
47
+
48
+ # Stream a chat response
49
+ for event in client.stream_chat(
50
+ session_id="session-123",
51
+ message="What can you help me with?",
52
+ user_id="user@example.com",
53
+ model_id="auto" # Use auto-model selection
54
+ ):
55
+ if event["event"] == "stream_chunk":
56
+ print(event["data"].get("content", ""), end="", flush=True)
57
+ elif event["event"] == "stream_complete":
58
+ print(f"\n\nTokens used: {event['data'].get('tokens_used')}")
59
+ ```
60
+
61
+ ### Async Client
62
+
63
+ ```python
64
+ import asyncio
65
+ from facl import AsyncFACLClient
66
+
67
+ async def main():
68
+ async with AsyncFACLClient(
69
+ tenant_id="your-tenant-id",
70
+ tenant_secret="your-secret"
71
+ ) as client:
72
+ async for event in client.stream_chat(
73
+ session_id="session-123",
74
+ message="Hello!",
75
+ user_id="user@example.com"
76
+ ):
77
+ if event["event"] == "stream_chunk":
78
+ print(event["data"].get("content", ""), end="")
79
+
80
+ asyncio.run(main())
81
+ ```
82
+
83
+ ### Custom Logger
84
+
85
+ ```python
86
+ import logging
87
+ from facl import FACLClient
88
+
89
+ logging.basicConfig(level=logging.DEBUG)
90
+ logger = logging.getLogger("my_app.facl")
91
+
92
+ client = FACLClient(
93
+ tenant_id="your-tenant-id",
94
+ tenant_secret="your-secret",
95
+ logger=logger
96
+ )
97
+ ```
98
+
99
+ ## SSE Event Types
100
+
101
+ When streaming, you'll receive events with these types:
102
+
103
+ | Event | Description |
104
+ |-------|-------------|
105
+ | `stream_start` | Stream initialized |
106
+ | `stream_chunk` | Text chunk from LLM |
107
+ | `stream_complete` | Full response with metrics |
108
+ | `stream_error` | Error occurred |
109
+ | `thinking` | Reasoning/thinking content |
110
+ | `tool_call_start` | Tool execution beginning |
111
+ | `tool_call_result` | Tool execution complete |
112
+ | `approval_required` | HITL approval needed |
113
+ | `tool_cancelled` | Tool was rejected |
114
+ | `model_fallback` | Auto mode selected a model |
115
+ | `rate_limited` | All models rate limited |
116
+
117
+ ## API Reference
118
+
119
+ ### FACLClient / AsyncFACLClient
120
+
121
+ #### Chat & Streaming
122
+ - `stream_chat(session_id, message, user_id, context=None, model_id=None)` - Stream chat response
123
+
124
+ #### Models
125
+ - `list_available_models()` - List available AI models
126
+ - `set_preferred_model(model_id)` - Set preferred model
127
+
128
+ #### Tenant
129
+ - `get_tenant_info()` - Get tenant information
130
+ - `accept_terms(terms_version, accepted_by)` - Accept terms and conditions
131
+
132
+ #### Conversations
133
+ - `list_conversations(user_id=None, limit=50, offset=0)` - List conversations
134
+ - `get_conversation(conversation_id)` - Get conversation details
135
+ - `get_messages(conversation_id, limit=100, offset=0)` - Get messages
136
+ - `create_message(conversation_id, message_id, role, content, ...)` - Create message
137
+ - `update_conversation(conversation_id, title=None, user_id=None)` - Update conversation
138
+ - `delete_conversation(conversation_id)` - Soft delete conversation
139
+ - `delete_message(conversation_id, message_id)` - Soft delete message
140
+
141
+ #### Billing
142
+ - `get_plan_comparison()` - Get available plans
143
+ - `get_usage_dashboard()` - Get usage statistics
144
+ - `get_usage_history(days=30)` - Get historical usage
145
+ - `initiate_checkout(plan, billing_cycle="monthly")` - Start checkout
146
+ - `verify_checkout(session_id=None)` - Verify payment
147
+ - `upgrade_plan(new_plan, billing_cycle="monthly")` - Upgrade subscription
148
+ - `cancel_subscription(cancel_immediately=False)` - Cancel subscription
149
+
150
+ #### Users & MCP Servers
151
+ - `register_user(user_id, display_name=None, custom_instructions=None)` - Register user
152
+ - `get_user(user_id)` - Get user details
153
+ - `get_user_auth_status(user_id)` - Check auth status
154
+ - `add_user_mcp_server(user_id, server_name, endpoint_url, ...)` - Add MCP server
155
+ - `get_user_mcp_servers(user_id)` - List user's MCP servers
156
+ - `update_mcp_server_tokens(user_id, server_name, access_token, ...)` - Update tokens
157
+ - `remove_user_mcp_server(user_id, server_name)` - Remove MCP server
158
+
159
+ #### Prompts
160
+ - `list_prompts(user_id, cursor=None)` - List prompt templates
161
+ - `get_prompt(prompt_name, arguments=None, user_id=None)` - Get rendered prompt
162
+
163
+ #### Tools
164
+ - `list_tools(user_id, server=None)` - List MCP tools with input schemas
165
+
166
+ ### Standalone Functions
167
+
168
+ ```python
169
+ from facl import get_terms, register_tenant
170
+
171
+ # Get current terms (no auth required)
172
+ terms = get_terms("https://facl.frappe.cloud")
173
+
174
+ # Register a new tenant
175
+ result = register_tenant(
176
+ facl_url="https://facl.frappe.cloud",
177
+ site_url="https://mysite.frappe.cloud",
178
+ terms_accepted=True,
179
+ terms_version="1.0",
180
+ accepted_by="admin@example.com"
181
+ )
182
+ ```
183
+
184
+ ## Exceptions
185
+
186
+ ```python
187
+ from facl.exceptions import (
188
+ FACLError, # Base exception
189
+ FACLAuthenticationError, # HMAC signature failed
190
+ FACLRateLimitError, # Rate limit exceeded (has retry_after)
191
+ FACLStreamError, # SSE streaming error
192
+ FACLConfigurationError, # Invalid configuration
193
+ )
194
+ ```
195
+
196
+ ## Frappe Integration
197
+
198
+ For Frappe applications, create a thin adapter:
199
+
200
+ ```python
201
+ import frappe
202
+ from facl import FACLClient
203
+
204
+ class FrappeLogger:
205
+ def error(self, msg, *args, **kwargs):
206
+ frappe.log_error(msg, kwargs.get('category', 'FACL'))
207
+
208
+ def get_facl_client():
209
+ settings = frappe.get_single("FACO Settings")
210
+ if settings.registration_status != "Registered":
211
+ return None
212
+
213
+ return FACLClient(
214
+ tenant_id=settings.tenant_id,
215
+ tenant_secret=settings.get_password("tenant_secret"),
216
+ facl_url=settings.facl_url,
217
+ logger=FrappeLogger()
218
+ )
219
+ ```
220
+
221
+ ## License
222
+
223
+ GNU Affero General Public License v3.0
224
+
225
+ Copyright (C) 2025 Paul Clinton