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.
- assistant_runtime_sdk-1.0.0/LICENSE +17 -0
- assistant_runtime_sdk-1.0.0/PKG-INFO +259 -0
- assistant_runtime_sdk-1.0.0/README.md +225 -0
- assistant_runtime_sdk-1.0.0/assistant_runtime_sdk/__init__.py +235 -0
- assistant_runtime_sdk-1.0.0/assistant_runtime_sdk/async_client.py +1989 -0
- assistant_runtime_sdk-1.0.0/assistant_runtime_sdk/auth.py +161 -0
- assistant_runtime_sdk-1.0.0/assistant_runtime_sdk/base.py +2277 -0
- assistant_runtime_sdk-1.0.0/assistant_runtime_sdk/client.py +3039 -0
- assistant_runtime_sdk-1.0.0/assistant_runtime_sdk/exceptions.py +146 -0
- assistant_runtime_sdk-1.0.0/assistant_runtime_sdk/skills.py +328 -0
- assistant_runtime_sdk-1.0.0/assistant_runtime_sdk/streaming.py +223 -0
- assistant_runtime_sdk-1.0.0/assistant_runtime_sdk/types.py +546 -0
- assistant_runtime_sdk-1.0.0/pyproject.toml +65 -0
|
@@ -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
|