roomkit 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- roomkit/AGENTS.md +362 -0
- roomkit/__init__.py +372 -0
- roomkit/_version.py +1 -0
- roomkit/ai_docs.py +93 -0
- roomkit/channels/__init__.py +194 -0
- roomkit/channels/ai.py +238 -0
- roomkit/channels/base.py +66 -0
- roomkit/channels/transport.py +115 -0
- roomkit/channels/websocket.py +85 -0
- roomkit/core/__init__.py +0 -0
- roomkit/core/_channel_ops.py +252 -0
- roomkit/core/_helpers.py +296 -0
- roomkit/core/_inbound.py +435 -0
- roomkit/core/_room_lifecycle.py +275 -0
- roomkit/core/circuit_breaker.py +84 -0
- roomkit/core/event_router.py +401 -0
- roomkit/core/framework.py +793 -0
- roomkit/core/hooks.py +232 -0
- roomkit/core/inbound_router.py +57 -0
- roomkit/core/locks.py +66 -0
- roomkit/core/rate_limiter.py +67 -0
- roomkit/core/retry.py +49 -0
- roomkit/core/router.py +24 -0
- roomkit/core/transcoder.py +85 -0
- roomkit/identity/__init__.py +0 -0
- roomkit/identity/base.py +27 -0
- roomkit/identity/mock.py +49 -0
- roomkit/llms.txt +52 -0
- roomkit/models/__init__.py +104 -0
- roomkit/models/channel.py +99 -0
- roomkit/models/context.py +35 -0
- roomkit/models/delivery.py +76 -0
- roomkit/models/enums.py +170 -0
- roomkit/models/event.py +203 -0
- roomkit/models/framework_event.py +19 -0
- roomkit/models/hook.py +68 -0
- roomkit/models/identity.py +81 -0
- roomkit/models/participant.py +34 -0
- roomkit/models/room.py +33 -0
- roomkit/models/task.py +36 -0
- roomkit/providers/__init__.py +0 -0
- roomkit/providers/ai/__init__.py +0 -0
- roomkit/providers/ai/base.py +140 -0
- roomkit/providers/ai/mock.py +33 -0
- roomkit/providers/anthropic/__init__.py +6 -0
- roomkit/providers/anthropic/ai.py +145 -0
- roomkit/providers/anthropic/config.py +14 -0
- roomkit/providers/elasticemail/__init__.py +6 -0
- roomkit/providers/elasticemail/config.py +16 -0
- roomkit/providers/elasticemail/email.py +97 -0
- roomkit/providers/email/__init__.py +0 -0
- roomkit/providers/email/base.py +46 -0
- roomkit/providers/email/mock.py +34 -0
- roomkit/providers/gemini/__init__.py +6 -0
- roomkit/providers/gemini/ai.py +153 -0
- roomkit/providers/gemini/config.py +14 -0
- roomkit/providers/http/__init__.py +15 -0
- roomkit/providers/http/base.py +33 -0
- roomkit/providers/http/config.py +14 -0
- roomkit/providers/http/mock.py +21 -0
- roomkit/providers/http/provider.py +105 -0
- roomkit/providers/http/webhook.py +33 -0
- roomkit/providers/messenger/__init__.py +15 -0
- roomkit/providers/messenger/base.py +33 -0
- roomkit/providers/messenger/config.py +17 -0
- roomkit/providers/messenger/facebook.py +95 -0
- roomkit/providers/messenger/mock.py +21 -0
- roomkit/providers/messenger/webhook.py +42 -0
- roomkit/providers/openai/__init__.py +6 -0
- roomkit/providers/openai/ai.py +155 -0
- roomkit/providers/openai/config.py +24 -0
- roomkit/providers/pydantic_ai/__init__.py +5 -0
- roomkit/providers/pydantic_ai/config.py +14 -0
- roomkit/providers/rcs/__init__.py +9 -0
- roomkit/providers/rcs/base.py +95 -0
- roomkit/providers/rcs/mock.py +78 -0
- roomkit/providers/sendgrid/__init__.py +5 -0
- roomkit/providers/sendgrid/config.py +13 -0
- roomkit/providers/sinch/__init__.py +6 -0
- roomkit/providers/sinch/config.py +22 -0
- roomkit/providers/sinch/sms.py +192 -0
- roomkit/providers/sms/__init__.py +15 -0
- roomkit/providers/sms/base.py +67 -0
- roomkit/providers/sms/meta.py +401 -0
- roomkit/providers/sms/mock.py +24 -0
- roomkit/providers/sms/phone.py +77 -0
- roomkit/providers/telnyx/__init__.py +21 -0
- roomkit/providers/telnyx/config.py +14 -0
- roomkit/providers/telnyx/rcs.py +352 -0
- roomkit/providers/telnyx/sms.py +231 -0
- roomkit/providers/twilio/__init__.py +18 -0
- roomkit/providers/twilio/config.py +19 -0
- roomkit/providers/twilio/rcs.py +183 -0
- roomkit/providers/twilio/sms.py +200 -0
- roomkit/providers/voicemeup/__init__.py +15 -0
- roomkit/providers/voicemeup/config.py +21 -0
- roomkit/providers/voicemeup/sms.py +374 -0
- roomkit/providers/whatsapp/__init__.py +0 -0
- roomkit/providers/whatsapp/base.py +44 -0
- roomkit/providers/whatsapp/mock.py +21 -0
- roomkit/py.typed +0 -0
- roomkit/realtime/__init__.py +17 -0
- roomkit/realtime/base.py +111 -0
- roomkit/realtime/memory.py +158 -0
- roomkit/sources/__init__.py +35 -0
- roomkit/sources/base.py +207 -0
- roomkit/sources/websocket.py +260 -0
- roomkit/store/__init__.py +0 -0
- roomkit/store/base.py +230 -0
- roomkit/store/memory.py +293 -0
- roomkit-0.1.0.dist-info/METADATA +567 -0
- roomkit-0.1.0.dist-info/RECORD +114 -0
- roomkit-0.1.0.dist-info/WHEEL +4 -0
- roomkit-0.1.0.dist-info/licenses/LICENSE +21 -0
|
@@ -0,0 +1,567 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: roomkit
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Pure async Python library for multi-channel conversations
|
|
5
|
+
Project-URL: Homepage, https://github.com/sboily/roomkit
|
|
6
|
+
Project-URL: Repository, https://github.com/sboily/roomkit
|
|
7
|
+
Project-URL: Issues, https://github.com/sboily/roomkit/issues
|
|
8
|
+
Author: Sylvain Boily
|
|
9
|
+
License-Expression: MIT
|
|
10
|
+
License-File: LICENSE
|
|
11
|
+
Classifier: Development Status :: 3 - Alpha
|
|
12
|
+
Classifier: Framework :: AsyncIO
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
18
|
+
Classifier: Typing :: Typed
|
|
19
|
+
Requires-Python: >=3.12
|
|
20
|
+
Requires-Dist: pydantic>=2.9
|
|
21
|
+
Provides-Extra: all
|
|
22
|
+
Requires-Dist: anthropic>=0.30; extra == 'all'
|
|
23
|
+
Requires-Dist: google-genai>=1.0.0; extra == 'all'
|
|
24
|
+
Requires-Dist: httpx>=0.27; extra == 'all'
|
|
25
|
+
Requires-Dist: openai>=1.30; extra == 'all'
|
|
26
|
+
Requires-Dist: pydantic-ai>=0.1; extra == 'all'
|
|
27
|
+
Requires-Dist: twilio>=9.0; extra == 'all'
|
|
28
|
+
Provides-Extra: anthropic
|
|
29
|
+
Requires-Dist: anthropic>=0.30; extra == 'anthropic'
|
|
30
|
+
Provides-Extra: dev
|
|
31
|
+
Requires-Dist: anthropic>=0.30; extra == 'dev'
|
|
32
|
+
Requires-Dist: google-genai>=1.0.0; extra == 'dev'
|
|
33
|
+
Requires-Dist: httpx>=0.27; extra == 'dev'
|
|
34
|
+
Requires-Dist: mkdocs-material>=9.5; extra == 'dev'
|
|
35
|
+
Requires-Dist: mkdocs>=1.6; extra == 'dev'
|
|
36
|
+
Requires-Dist: mkdocstrings[python]>=0.27; extra == 'dev'
|
|
37
|
+
Requires-Dist: mypy>=1.11; extra == 'dev'
|
|
38
|
+
Requires-Dist: openai>=1.30; extra == 'dev'
|
|
39
|
+
Requires-Dist: phonenumbers>=8.13; extra == 'dev'
|
|
40
|
+
Requires-Dist: pre-commit>=3.8; extra == 'dev'
|
|
41
|
+
Requires-Dist: pynacl>=1.5; extra == 'dev'
|
|
42
|
+
Requires-Dist: pytest-asyncio>=0.24; extra == 'dev'
|
|
43
|
+
Requires-Dist: pytest-cov>=5.0; extra == 'dev'
|
|
44
|
+
Requires-Dist: pytest>=8.0; extra == 'dev'
|
|
45
|
+
Requires-Dist: ruff>=0.6; extra == 'dev'
|
|
46
|
+
Requires-Dist: twilio>=9.0; extra == 'dev'
|
|
47
|
+
Requires-Dist: websockets>=13.0; extra == 'dev'
|
|
48
|
+
Provides-Extra: docs
|
|
49
|
+
Requires-Dist: mkdocs-material>=9.5; extra == 'docs'
|
|
50
|
+
Requires-Dist: mkdocs>=1.6; extra == 'docs'
|
|
51
|
+
Requires-Dist: mkdocstrings[python]>=0.27; extra == 'docs'
|
|
52
|
+
Provides-Extra: gemini
|
|
53
|
+
Requires-Dist: google-genai>=1.0.0; extra == 'gemini'
|
|
54
|
+
Provides-Extra: httpx
|
|
55
|
+
Requires-Dist: httpx>=0.27; extra == 'httpx'
|
|
56
|
+
Provides-Extra: openai
|
|
57
|
+
Requires-Dist: openai>=1.30; extra == 'openai'
|
|
58
|
+
Provides-Extra: phonenumbers
|
|
59
|
+
Requires-Dist: phonenumbers>=8.13; extra == 'phonenumbers'
|
|
60
|
+
Provides-Extra: providers
|
|
61
|
+
Requires-Dist: anthropic>=0.30; extra == 'providers'
|
|
62
|
+
Requires-Dist: google-genai>=1.0.0; extra == 'providers'
|
|
63
|
+
Requires-Dist: httpx>=0.27; extra == 'providers'
|
|
64
|
+
Requires-Dist: openai>=1.30; extra == 'providers'
|
|
65
|
+
Requires-Dist: twilio>=9.0; extra == 'providers'
|
|
66
|
+
Provides-Extra: pydantic-ai
|
|
67
|
+
Requires-Dist: pydantic-ai>=0.1; extra == 'pydantic-ai'
|
|
68
|
+
Provides-Extra: pynacl
|
|
69
|
+
Requires-Dist: pynacl>=1.5; extra == 'pynacl'
|
|
70
|
+
Provides-Extra: sources
|
|
71
|
+
Requires-Dist: websockets>=13.0; extra == 'sources'
|
|
72
|
+
Provides-Extra: twilio
|
|
73
|
+
Requires-Dist: twilio>=9.0; extra == 'twilio'
|
|
74
|
+
Provides-Extra: websocket
|
|
75
|
+
Requires-Dist: websockets>=13.0; extra == 'websocket'
|
|
76
|
+
Description-Content-Type: text/markdown
|
|
77
|
+
|
|
78
|
+
# RoomKit
|
|
79
|
+
|
|
80
|
+
[](https://pypi.org/project/roomkit/)
|
|
81
|
+
[](https://pypi.org/project/roomkit/)
|
|
82
|
+
[](LICENSE)
|
|
83
|
+
|
|
84
|
+
Pure async Python 3.12+ library for multi-channel conversations.
|
|
85
|
+
|
|
86
|
+
RoomKit gives you a single abstraction — the **room** — to orchestrate messages across SMS, RCS, Email, WhatsApp, Messenger, WebSocket, HTTP webhooks, and AI channels. Events flow in through any channel, get validated by hooks, and broadcast to every other channel in the room with automatic content transcoding.
|
|
87
|
+
|
|
88
|
+
```
|
|
89
|
+
Inbound ──► Hook pipeline ──► Store ──► Broadcast to all channels
|
|
90
|
+
│
|
|
91
|
+
┌──────────┬──────────┬────────┼────────┬────────┐
|
|
92
|
+
▼ ▼ ▼ ▼ ▼ ▼
|
|
93
|
+
SMS/RCS WebSocket Email Messenger AI Webhook
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
**Website:** [www.roomkit.live](https://www.roomkit.live) | **Docs:** [www.roomkit.live/docs](https://www.roomkit.live/docs/)
|
|
97
|
+
|
|
98
|
+
## Quickstart
|
|
99
|
+
|
|
100
|
+
```bash
|
|
101
|
+
pip install roomkit
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
```python
|
|
105
|
+
import asyncio
|
|
106
|
+
from roomkit import (
|
|
107
|
+
ChannelCategory, HookResult, HookTrigger,
|
|
108
|
+
InboundMessage, MockAIProvider, RoomContext,
|
|
109
|
+
RoomEvent, RoomKit, TextContent, WebSocketChannel,
|
|
110
|
+
)
|
|
111
|
+
from roomkit.channels.ai import AIChannel
|
|
112
|
+
|
|
113
|
+
async def main():
|
|
114
|
+
kit = RoomKit()
|
|
115
|
+
|
|
116
|
+
# Register channels
|
|
117
|
+
ws = WebSocketChannel("ws-user")
|
|
118
|
+
ai = AIChannel("ai-bot", provider=MockAIProvider(responses=["Hello!"]))
|
|
119
|
+
kit.register_channel(ws)
|
|
120
|
+
kit.register_channel(ai)
|
|
121
|
+
|
|
122
|
+
# Create a room and attach channels
|
|
123
|
+
await kit.create_room(room_id="room-1")
|
|
124
|
+
await kit.attach_channel("room-1", "ws-user")
|
|
125
|
+
await kit.attach_channel("room-1", "ai-bot", category=ChannelCategory.INTELLIGENCE)
|
|
126
|
+
|
|
127
|
+
# Add a pre-inbound hook
|
|
128
|
+
@kit.hook(HookTrigger.PRE_INBOUND, name="filter")
|
|
129
|
+
async def block_spam(event: RoomEvent, ctx: RoomContext) -> HookResult:
|
|
130
|
+
if isinstance(event.content, TextContent) and "spam" in event.content.body:
|
|
131
|
+
return HookResult.block("spam detected")
|
|
132
|
+
return HookResult.allow()
|
|
133
|
+
|
|
134
|
+
# Process a message — it gets stored, broadcast, and the AI responds
|
|
135
|
+
result = await kit.process_inbound(
|
|
136
|
+
InboundMessage(channel_id="ws-user", sender_id="user-1", content=TextContent(body="Hi"))
|
|
137
|
+
)
|
|
138
|
+
print(result.blocked) # False
|
|
139
|
+
|
|
140
|
+
# View conversation history
|
|
141
|
+
for event in await kit.store.list_events("room-1"):
|
|
142
|
+
print(f"[{event.source.channel_id}] {event.content.body}")
|
|
143
|
+
|
|
144
|
+
asyncio.run(main())
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
More examples in [`examples/`](examples/).
|
|
148
|
+
|
|
149
|
+
## Installation
|
|
150
|
+
|
|
151
|
+
RoomKit's core has a single dependency (pydantic). Providers that call external APIs need optional extras:
|
|
152
|
+
|
|
153
|
+
```bash
|
|
154
|
+
pip install roomkit # core only
|
|
155
|
+
pip install roomkit[httpx] # HTTP-based providers (SMS, RCS, Email)
|
|
156
|
+
pip install roomkit[websocket] # WebSocket event source
|
|
157
|
+
pip install roomkit[anthropic] # Anthropic Claude AI
|
|
158
|
+
pip install roomkit[openai] # OpenAI GPT
|
|
159
|
+
pip install roomkit[gemini] # Google Gemini AI
|
|
160
|
+
pip install roomkit[providers] # all transport providers
|
|
161
|
+
pip install roomkit[all] # everything
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
For development:
|
|
165
|
+
|
|
166
|
+
```bash
|
|
167
|
+
git clone https://github.com/sboily/roomkit.git
|
|
168
|
+
cd roomkit
|
|
169
|
+
uv sync --extra dev
|
|
170
|
+
make all # lint + typecheck + test
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
Requires **Python 3.12+**.
|
|
174
|
+
|
|
175
|
+
## Channels
|
|
176
|
+
|
|
177
|
+
Each channel is a thin adapter between the room and an external transport. All channels implement the same interface: `handle_inbound()` converts a provider message into a `RoomEvent`, and `deliver()` pushes events out.
|
|
178
|
+
|
|
179
|
+
| Channel | Type | Media | Notes |
|
|
180
|
+
|---------|------|-------|-------|
|
|
181
|
+
| **SMS** | `sms` | text, MMS | Max 1600 chars, delivery receipts |
|
|
182
|
+
| **RCS** | `rcs` | text, rich, media | Rich cards, carousels, suggested actions |
|
|
183
|
+
| **Email** | `email` | text, rich, media | Threading support |
|
|
184
|
+
| **WebSocket** | `websocket` | text, rich, media | Real-time with typing, reactions |
|
|
185
|
+
| **Messenger** | `messenger` | text, rich, media, template | Buttons, quick replies |
|
|
186
|
+
| **WhatsApp** | `whatsapp` | text, rich, media, location, template | Buttons, templates |
|
|
187
|
+
| **HTTP** | `webhook` | text, rich | Generic webhook for any system |
|
|
188
|
+
| **AI** | `ai` | text, rich | Intelligence layer (not transport) |
|
|
189
|
+
|
|
190
|
+
Channels have two categories: **transport** (delivers to external systems) and **intelligence** (generates content, like AI).
|
|
191
|
+
|
|
192
|
+
## Providers
|
|
193
|
+
|
|
194
|
+
Providers handle the actual API calls. Every provider has a mock counterpart for testing.
|
|
195
|
+
|
|
196
|
+
### SMS Providers
|
|
197
|
+
|
|
198
|
+
| Provider | Features | Dependency |
|
|
199
|
+
|----------|----------|------------|
|
|
200
|
+
| `TwilioSMSProvider` | SMS, MMS, delivery status | `roomkit[httpx]` |
|
|
201
|
+
| `TelnyxSMSProvider` | SMS, MMS, delivery status | `roomkit[httpx]` |
|
|
202
|
+
| `SinchSMSProvider` | SMS, delivery status | `roomkit[httpx]` |
|
|
203
|
+
| `VoiceMeUpSMSProvider` | SMS, MMS aggregation | `roomkit[httpx]` |
|
|
204
|
+
|
|
205
|
+
### RCS Providers
|
|
206
|
+
|
|
207
|
+
| Provider | Features | Dependency |
|
|
208
|
+
|----------|----------|------------|
|
|
209
|
+
| `TwilioRCSProvider` | Rich cards, carousels, actions | `roomkit[httpx]` |
|
|
210
|
+
| `TelnyxRCSProvider` | Rich cards, carousels, actions | `roomkit[httpx]` |
|
|
211
|
+
|
|
212
|
+
### AI Providers
|
|
213
|
+
|
|
214
|
+
| Provider | Features | Dependency |
|
|
215
|
+
|----------|----------|------------|
|
|
216
|
+
| `AnthropicAIProvider` | Claude, vision, tools | `roomkit[anthropic]` |
|
|
217
|
+
| `OpenAIAIProvider` | GPT-4, vision, tools | `roomkit[openai]` |
|
|
218
|
+
| `GeminiAIProvider` | Gemini, vision, tools | `roomkit[gemini]` |
|
|
219
|
+
|
|
220
|
+
### Other Providers
|
|
221
|
+
|
|
222
|
+
| Provider | Channel | Dependency |
|
|
223
|
+
|----------|---------|------------|
|
|
224
|
+
| `ElasticEmailProvider` | Email | `roomkit[httpx]` |
|
|
225
|
+
| `FacebookMessengerProvider` | Messenger | `roomkit[httpx]` |
|
|
226
|
+
| `WebhookHTTPProvider` | HTTP | `roomkit[httpx]` |
|
|
227
|
+
|
|
228
|
+
Each HTTP-based provider lazy-imports `httpx` so the core library stays lightweight.
|
|
229
|
+
|
|
230
|
+
## Hooks
|
|
231
|
+
|
|
232
|
+
Hooks intercept events at specific points in the pipeline. Sync hooks can block or modify events; async hooks run after the fact for logging or side effects.
|
|
233
|
+
|
|
234
|
+
```python
|
|
235
|
+
@kit.hook(HookTrigger.PRE_INBOUND, name="compliance_check")
|
|
236
|
+
async def check(event: RoomEvent, ctx: RoomContext) -> HookResult:
|
|
237
|
+
# Block, allow, or modify the event
|
|
238
|
+
return HookResult.allow()
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
**Triggers:** `PRE_INBOUND`, `POST_INBOUND`, `PRE_DELIVERY`, `POST_DELIVERY`, `BEFORE_BROADCAST`, `AFTER_BROADCAST`, `ON_ROOM_CREATED`, `ON_ROOM_PAUSED`, `ON_ROOM_CLOSED`, `ON_CHANNEL_ATTACHED`, `ON_CHANNEL_DETACHED`, `ON_IDENTITY_AMBIGUOUS`, `ON_IDENTITY_UNKNOWN`, `ON_DELIVERY_STATUS`, `ON_ERROR`.
|
|
242
|
+
|
|
243
|
+
Hooks support **filtering** by channel type, channel ID, and direction:
|
|
244
|
+
|
|
245
|
+
```python
|
|
246
|
+
@kit.hook(
|
|
247
|
+
HookTrigger.PRE_INBOUND,
|
|
248
|
+
channel_types={ChannelType.SMS},
|
|
249
|
+
directions={ChannelDirection.INBOUND},
|
|
250
|
+
)
|
|
251
|
+
async def sms_only_hook(event, ctx):
|
|
252
|
+
return HookResult.allow()
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
Hooks can also inject side-effect events, create tasks, and record observations.
|
|
256
|
+
|
|
257
|
+
## AI Integration
|
|
258
|
+
|
|
259
|
+
### Per-Room AI Configuration
|
|
260
|
+
|
|
261
|
+
Configure AI behavior per room with custom system prompts, temperature, and tools:
|
|
262
|
+
|
|
263
|
+
```python
|
|
264
|
+
from roomkit import AIConfig, AITool
|
|
265
|
+
|
|
266
|
+
room = await kit.create_room(
|
|
267
|
+
room_id="support-room",
|
|
268
|
+
ai_config=AIConfig(
|
|
269
|
+
system_prompt="You are a helpful support agent.",
|
|
270
|
+
temperature=0.7,
|
|
271
|
+
tools=[
|
|
272
|
+
AITool(
|
|
273
|
+
name="lookup_order",
|
|
274
|
+
description="Look up order status",
|
|
275
|
+
parameters={"type": "object", "properties": {"order_id": {"type": "string"}}}
|
|
276
|
+
)
|
|
277
|
+
],
|
|
278
|
+
),
|
|
279
|
+
)
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
### Function Calling
|
|
283
|
+
|
|
284
|
+
AI providers support function calling with automatic tool result handling:
|
|
285
|
+
|
|
286
|
+
```python
|
|
287
|
+
response = await ai_provider.generate(context)
|
|
288
|
+
if response.tool_calls:
|
|
289
|
+
for call in response.tool_calls:
|
|
290
|
+
result = await execute_tool(call.name, call.arguments)
|
|
291
|
+
# Feed result back to AI
|
|
292
|
+
```
|
|
293
|
+
|
|
294
|
+
## Realtime Events
|
|
295
|
+
|
|
296
|
+
Handle ephemeral events like typing indicators, presence, and read receipts:
|
|
297
|
+
|
|
298
|
+
```python
|
|
299
|
+
from roomkit import EphemeralEvent, EphemeralEventType
|
|
300
|
+
|
|
301
|
+
# Subscribe to realtime events
|
|
302
|
+
async def handle_realtime(event: EphemeralEvent):
|
|
303
|
+
if event.type == EphemeralEventType.TYPING_START:
|
|
304
|
+
print(f"{event.user_id} is typing...")
|
|
305
|
+
|
|
306
|
+
sub_id = await kit.subscribe_room("room-1", handle_realtime)
|
|
307
|
+
|
|
308
|
+
# Publish typing indicator
|
|
309
|
+
await kit.publish_typing("room-1", "user-1")
|
|
310
|
+
|
|
311
|
+
# Publish presence
|
|
312
|
+
await kit.publish_presence("room-1", "user-1", "online")
|
|
313
|
+
|
|
314
|
+
# Publish read receipt
|
|
315
|
+
await kit.publish_read_receipt("room-1", "user-1", "event-123")
|
|
316
|
+
```
|
|
317
|
+
|
|
318
|
+
For distributed deployments, implement a custom `RealtimeBackend` (e.g., Redis pub/sub).
|
|
319
|
+
|
|
320
|
+
## Identity Resolution
|
|
321
|
+
|
|
322
|
+
Resolve unknown senders to known identities with a pluggable pipeline:
|
|
323
|
+
|
|
324
|
+
```python
|
|
325
|
+
from roomkit import IdentityResolver, IdentityResult, IdentificationStatus, Identity
|
|
326
|
+
|
|
327
|
+
class MyResolver(IdentityResolver):
|
|
328
|
+
async def resolve(self, message, context):
|
|
329
|
+
user = await lookup(message.sender_id)
|
|
330
|
+
if user:
|
|
331
|
+
return IdentityResult(
|
|
332
|
+
status=IdentificationStatus.IDENTIFIED,
|
|
333
|
+
identity=Identity(id=user.id, display_name=user.name),
|
|
334
|
+
)
|
|
335
|
+
return IdentityResult(status=IdentificationStatus.UNKNOWN)
|
|
336
|
+
|
|
337
|
+
kit = RoomKit(identity_resolver=MyResolver())
|
|
338
|
+
```
|
|
339
|
+
|
|
340
|
+
### Channel Type Filtering
|
|
341
|
+
|
|
342
|
+
Restrict identity resolution to specific channel types:
|
|
343
|
+
|
|
344
|
+
```python
|
|
345
|
+
kit = RoomKit(
|
|
346
|
+
identity_resolver=MyResolver(),
|
|
347
|
+
identity_channel_types={ChannelType.SMS}, # Only resolve for SMS
|
|
348
|
+
)
|
|
349
|
+
```
|
|
350
|
+
|
|
351
|
+
Supports identified, pending, ambiguous, challenge, and rejected outcomes with hook-based customization.
|
|
352
|
+
|
|
353
|
+
## Webhook Processing
|
|
354
|
+
|
|
355
|
+
Process provider webhooks with automatic parsing and delivery status tracking:
|
|
356
|
+
|
|
357
|
+
```python
|
|
358
|
+
# Generic webhook processing
|
|
359
|
+
result = await kit.process_webhook(
|
|
360
|
+
channel_id="sms-channel",
|
|
361
|
+
raw_payload=request_body,
|
|
362
|
+
headers=request_headers,
|
|
363
|
+
)
|
|
364
|
+
|
|
365
|
+
# Handle delivery status updates
|
|
366
|
+
@kit.on_delivery_status
|
|
367
|
+
async def handle_status(status: DeliveryStatus):
|
|
368
|
+
print(f"Message {status.provider_message_id}: {status.status}")
|
|
369
|
+
```
|
|
370
|
+
|
|
371
|
+
## Event-Driven Sources
|
|
372
|
+
|
|
373
|
+
For persistent connections (WebSocket, NATS, SSE), use **SourceProviders** instead of webhooks:
|
|
374
|
+
|
|
375
|
+
```python
|
|
376
|
+
from roomkit import RoomKit, BaseSourceProvider, SourceStatus
|
|
377
|
+
from roomkit.sources import WebSocketSource
|
|
378
|
+
|
|
379
|
+
kit = RoomKit()
|
|
380
|
+
|
|
381
|
+
# Built-in WebSocket source
|
|
382
|
+
source = WebSocketSource(
|
|
383
|
+
url="wss://chat.example.com/events",
|
|
384
|
+
channel_id="websocket-chat",
|
|
385
|
+
)
|
|
386
|
+
|
|
387
|
+
# Attach with resilience options
|
|
388
|
+
await kit.attach_source(
|
|
389
|
+
"websocket-chat",
|
|
390
|
+
source,
|
|
391
|
+
auto_restart=True, # Restart on failure
|
|
392
|
+
max_restart_attempts=10, # Give up after 10 failures
|
|
393
|
+
max_concurrent_emits=20, # Backpressure control
|
|
394
|
+
)
|
|
395
|
+
|
|
396
|
+
# Monitor health
|
|
397
|
+
health = await kit.source_health("websocket-chat")
|
|
398
|
+
print(f"Status: {health.status}, Messages: {health.messages_received}")
|
|
399
|
+
|
|
400
|
+
# Detach when done
|
|
401
|
+
await kit.detach_source("websocket-chat")
|
|
402
|
+
```
|
|
403
|
+
|
|
404
|
+
**Webhook vs Event-Driven:**
|
|
405
|
+
|
|
406
|
+
| Aspect | Webhooks | Event Sources |
|
|
407
|
+
|--------|----------|---------------|
|
|
408
|
+
| Connection | Stateless HTTP | Persistent (WS, TCP, etc.) |
|
|
409
|
+
| Initiative | External system pushes | RoomKit subscribes |
|
|
410
|
+
| Use cases | Twilio, SendGrid | WebSocket, NATS, SSE |
|
|
411
|
+
|
|
412
|
+
Create custom sources by extending `BaseSourceProvider`:
|
|
413
|
+
|
|
414
|
+
```python
|
|
415
|
+
class NATSSource(BaseSourceProvider):
|
|
416
|
+
@property
|
|
417
|
+
def name(self) -> str:
|
|
418
|
+
return "nats:events"
|
|
419
|
+
|
|
420
|
+
async def start(self, emit) -> None:
|
|
421
|
+
self._set_status(SourceStatus.CONNECTED)
|
|
422
|
+
async for msg in self.subscribe():
|
|
423
|
+
await emit(parse_message(msg))
|
|
424
|
+
self._record_message()
|
|
425
|
+
```
|
|
426
|
+
|
|
427
|
+
Features: exponential backoff, max restart attempts, backpressure control, health monitoring.
|
|
428
|
+
|
|
429
|
+
## Resilience
|
|
430
|
+
|
|
431
|
+
Built-in patterns for production reliability:
|
|
432
|
+
|
|
433
|
+
- **Retry with backoff** — configurable per-channel retry policy with exponential backoff
|
|
434
|
+
- **Circuit breaker** — isolates failing providers so one broken channel doesn't bring down the room
|
|
435
|
+
- **Rate limiting** — token bucket limiter with per-second/minute/hour limits per channel
|
|
436
|
+
- **Content transcoding** — automatic conversion between channel capabilities (e.g. rich to text fallback)
|
|
437
|
+
- **Chain depth tracking** — prevents infinite event loops between channels
|
|
438
|
+
|
|
439
|
+
Configure per binding:
|
|
440
|
+
|
|
441
|
+
```python
|
|
442
|
+
from roomkit import RetryPolicy, RateLimit
|
|
443
|
+
|
|
444
|
+
await kit.attach_channel("room-1", "sms-out",
|
|
445
|
+
metadata={"phone_number": "+15551234567"},
|
|
446
|
+
retry_policy=RetryPolicy(max_retries=3, base_delay_seconds=1.0),
|
|
447
|
+
rate_limit=RateLimit(max_per_second=5.0),
|
|
448
|
+
)
|
|
449
|
+
```
|
|
450
|
+
|
|
451
|
+
## Scaling
|
|
452
|
+
|
|
453
|
+
### Per-room locking
|
|
454
|
+
|
|
455
|
+
RoomKit serializes event processing per room using a `RoomLockManager`. The default `InMemoryLockManager` works for single-process deployments. For multi-process or distributed setups, subclass `RoomLockManager` with a distributed lock (Redis, Postgres advisory locks, etc.):
|
|
456
|
+
|
|
457
|
+
```python
|
|
458
|
+
from roomkit import RoomKit, RoomLockManager
|
|
459
|
+
|
|
460
|
+
# Single process (default)
|
|
461
|
+
kit = RoomKit()
|
|
462
|
+
|
|
463
|
+
# Distributed
|
|
464
|
+
kit = RoomKit(lock_manager=MyRedisLockManager())
|
|
465
|
+
```
|
|
466
|
+
|
|
467
|
+
### Backpressure
|
|
468
|
+
|
|
469
|
+
RoomKit does not enforce a global concurrency limit on `process_inbound` calls. Each call acquires a per-room lock internally, but across different rooms, processing is fully concurrent.
|
|
470
|
+
|
|
471
|
+
To prevent resource exhaustion, add concurrency control upstream:
|
|
472
|
+
|
|
473
|
+
```python
|
|
474
|
+
import asyncio
|
|
475
|
+
from roomkit import RoomKit, InboundMessage, InboundResult
|
|
476
|
+
|
|
477
|
+
kit = RoomKit()
|
|
478
|
+
semaphore = asyncio.Semaphore(100) # max 100 concurrent rooms processing
|
|
479
|
+
|
|
480
|
+
async def handle_webhook(message: InboundMessage) -> InboundResult:
|
|
481
|
+
async with semaphore:
|
|
482
|
+
return await kit.process_inbound(message)
|
|
483
|
+
```
|
|
484
|
+
|
|
485
|
+
## Room Lifecycle
|
|
486
|
+
|
|
487
|
+
Rooms transition through states automatically based on activity timers:
|
|
488
|
+
|
|
489
|
+
```
|
|
490
|
+
ACTIVE ──(inactive timeout)──► PAUSED ──(closed timeout)──► CLOSED
|
|
491
|
+
```
|
|
492
|
+
|
|
493
|
+
```python
|
|
494
|
+
from roomkit import RoomTimers
|
|
495
|
+
|
|
496
|
+
await kit.create_room(
|
|
497
|
+
room_id="support-123",
|
|
498
|
+
timers=RoomTimers(inactive_after_seconds=300, closed_after_seconds=3600),
|
|
499
|
+
)
|
|
500
|
+
|
|
501
|
+
# Check and apply timer transitions
|
|
502
|
+
transitioned = await kit.sweep_room_timers()
|
|
503
|
+
```
|
|
504
|
+
|
|
505
|
+
## Storage
|
|
506
|
+
|
|
507
|
+
The `ConversationStore` ABC defines the persistence interface. `InMemoryStore` is included for development and testing. Implement the ABC to use any database.
|
|
508
|
+
|
|
509
|
+
```python
|
|
510
|
+
kit = RoomKit() # uses InMemoryStore by default
|
|
511
|
+
kit = RoomKit(store=MyPostgresStore()) # plug in your own
|
|
512
|
+
```
|
|
513
|
+
|
|
514
|
+
The store handles rooms, events, bindings, participants, identities, tasks, and observations.
|
|
515
|
+
|
|
516
|
+
## AI Assistant Support
|
|
517
|
+
|
|
518
|
+
RoomKit includes files to help AI coding assistants understand the library:
|
|
519
|
+
|
|
520
|
+
- **[llms.txt](https://www.roomkit.live/llms.txt)** — Structured documentation for LLM context windows
|
|
521
|
+
- **[AGENTS.md](AGENTS.md)** — Coding guidelines and patterns for AI assistants
|
|
522
|
+
- **[MCP Integration](https://www.roomkit.live/docs/mcp/)** — Model Context Protocol support
|
|
523
|
+
|
|
524
|
+
Access programmatically:
|
|
525
|
+
|
|
526
|
+
```python
|
|
527
|
+
from roomkit import get_llms_txt, get_agents_md
|
|
528
|
+
|
|
529
|
+
llms_content = get_llms_txt()
|
|
530
|
+
agents_content = get_agents_md()
|
|
531
|
+
```
|
|
532
|
+
|
|
533
|
+
## Project Structure
|
|
534
|
+
|
|
535
|
+
```
|
|
536
|
+
src/roomkit/
|
|
537
|
+
channels/ Channel implementations (sms, rcs, email, websocket, ai, ...)
|
|
538
|
+
core/ Framework, hooks, routing, retry, circuit breaker
|
|
539
|
+
identity/ Identity resolution pipeline
|
|
540
|
+
models/ Pydantic data models and enums
|
|
541
|
+
providers/ Provider implementations grouped by vendor
|
|
542
|
+
realtime/ Ephemeral events (typing, presence, read receipts)
|
|
543
|
+
sources/ Event-driven sources (WebSocket, custom)
|
|
544
|
+
store/ Storage abstraction and in-memory implementation
|
|
545
|
+
```
|
|
546
|
+
|
|
547
|
+
## Documentation
|
|
548
|
+
|
|
549
|
+
- **[Website](https://www.roomkit.live)** — Landing page and overview
|
|
550
|
+
- **[Documentation](https://www.roomkit.live/docs/)** — Full documentation
|
|
551
|
+
- **[API Reference](https://www.roomkit.live/docs/api/)** — Complete API docs
|
|
552
|
+
- **[RFC](https://www.roomkit.live/docs/roomkit-rfc/)** — Design document
|
|
553
|
+
|
|
554
|
+
## Contributing
|
|
555
|
+
|
|
556
|
+
See [CONTRIBUTING.md](CONTRIBUTING.md) and [CODE_OF_CONDUCT.md](CODE_OF_CONDUCT.md). Quick version:
|
|
557
|
+
|
|
558
|
+
```bash
|
|
559
|
+
uv sync --extra dev
|
|
560
|
+
make all # ruff check + mypy --strict + pytest
|
|
561
|
+
```
|
|
562
|
+
|
|
563
|
+
All new code needs tests. Aim for >90% coverage.
|
|
564
|
+
|
|
565
|
+
## License
|
|
566
|
+
|
|
567
|
+
[MIT](LICENSE)
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
roomkit/__init__.py,sha256=PYIvFXG8e6MEwEPXKltIaGxnLvft5mlqTcaJd1Vi2jA,10093
|
|
2
|
+
roomkit/_version.py,sha256=kUR5RAFc7HCeiqdlX36dZOHkUI5wI6V_43RpEcD8b-0,22
|
|
3
|
+
roomkit/ai_docs.py,sha256=mnKnwraTD5J38vm7Rm_q-2BYPBh2vXQ7OgT8i-B_niY,2336
|
|
4
|
+
roomkit/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
5
|
+
roomkit/channels/__init__.py,sha256=FQYB5c_y8dxyB7Os0l7o157-LVlneQrGltxF18HMugk,5023
|
|
6
|
+
roomkit/channels/ai.py,sha256=7RK-PXsPwHw4ADhJL2Z1MGlCXseKpdg8-J1bls-AoMM,8330
|
|
7
|
+
roomkit/channels/base.py,sha256=rS7vUOzLFZwjd8ulB3iihlbLQ1FhgTjj8OmYAYWhETM,2137
|
|
8
|
+
roomkit/channels/transport.py,sha256=2bnAxv1-J6h-CZcoizKd5gZghmQZms5Pyspo4pB1RuY,4744
|
|
9
|
+
roomkit/channels/websocket.py,sha256=CfidIKW_DK7Q6yljjlN5mR79OTHOIgX67IUU5Bj7CPE,2954
|
|
10
|
+
roomkit/core/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
11
|
+
roomkit/core/_channel_ops.py,sha256=SSDddZTxqcsSDCQ6PhQfQRhJs-KGGAO3moQqFQcJ-GI,10271
|
|
12
|
+
roomkit/core/_helpers.py,sha256=-PxREqVe-cV3mZhPaCsechuQs5bUfVqNHSKdu2EM8j8,11270
|
|
13
|
+
roomkit/core/_inbound.py,sha256=5PkRo6VOZKi6nJ4Kb9CJJvxPyEND6jwt8U-xDh7PrLY,18078
|
|
14
|
+
roomkit/core/_room_lifecycle.py,sha256=xo2UwDZLuZA1xXXGeg85qdj1j3_-fJNM2B_J3E5DRJU,10661
|
|
15
|
+
roomkit/core/circuit_breaker.py,sha256=kEXEI8EEkD5LZyegAaFZb5U4mrX2IDnAjI0avv46exs,2873
|
|
16
|
+
roomkit/core/event_router.py,sha256=TNweMDuzG0OPk3qvJ-CiQgtcANCMJD5FdNAtTtIRPSo,16634
|
|
17
|
+
roomkit/core/framework.py,sha256=NzWe39s-S0MH7EyuEQhAN9xteEW6ck9P_43imbSU8-A,29509
|
|
18
|
+
roomkit/core/hooks.py,sha256=OWgKOHU53Pxdfg0GH8vhvBbWGdf6xete9m4kkC-I2TE,8479
|
|
19
|
+
roomkit/core/inbound_router.py,sha256=QvuFAjWedeV02tJC6xU8JQGWk-qlhKnQl58JOL6crw4,1773
|
|
20
|
+
roomkit/core/locks.py,sha256=_2hqKbCjtWVyeKiiXD_StAHYdghW_Daw0VIPeiNfoJA,2060
|
|
21
|
+
roomkit/core/rate_limiter.py,sha256=u6AEyB4LVy85fWSOPk5FbBvV9KyRUYV87D-X7FFwaCE,2624
|
|
22
|
+
roomkit/core/retry.py,sha256=OpBLZMHisfvjGwdI05G-SAPNz48t9062V_neHLLVllA,1396
|
|
23
|
+
roomkit/core/router.py,sha256=cIxIDt9gnHswF3p9dycOo7Ls4XCzKQ3LLtbugvWZGYY,680
|
|
24
|
+
roomkit/core/transcoder.py,sha256=MLSPgf8Mj6PdZjkzX0NQHXw858Z9fBYbxyBPFieCkhg,3093
|
|
25
|
+
roomkit/identity/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
26
|
+
roomkit/identity/base.py,sha256=1BOFNm7jc90MUeRCFO4uWQBjSRJpz1j3SCGmWHLQ25I,850
|
|
27
|
+
roomkit/identity/mock.py,sha256=DNZyAZ80r2dceHKxMl-zhVe15mQemLNHNUN4UFd-ZfA,1792
|
|
28
|
+
roomkit/models/__init__.py,sha256=1xjIztFpQnOsWa6CteMIz5NaEuAr_0leq00nFdk7NDc,2242
|
|
29
|
+
roomkit/models/channel.py,sha256=70Jxynrsi-LSMtrWgtbpzysEUIFS9H_MXzjRfmyECa8,3525
|
|
30
|
+
roomkit/models/context.py,sha256=1AoTwUBhff_eSCD6tGv66Gj7qoIeGeuvjH3ZFaxsgwA,1335
|
|
31
|
+
roomkit/models/delivery.py,sha256=0HEGvkymYjocfWY52DX1kYO7pFNIEQ7rdHwhlx6WQTQ,2242
|
|
32
|
+
roomkit/models/enums.py,sha256=x_nZ-Ng13-FR-f6XNVEVsYgHdYRXpI2eJVO1vlFOC7I,3768
|
|
33
|
+
roomkit/models/event.py,sha256=n6fgTR-KWDMA2E9YE8tsAiWlltT8jCgbd5aoI0vJW_A,5887
|
|
34
|
+
roomkit/models/framework_event.py,sha256=ZVJPXymC8VjiG4pKaKw2zHq9XimJE0jOYVUw1yLY500,509
|
|
35
|
+
roomkit/models/hook.py,sha256=MnVmh5gjVBeiBZhWzpz9pmOPslQZ_N4xpzHAc7h5TPY,2192
|
|
36
|
+
roomkit/models/identity.py,sha256=l7lpHVknLGzZJwGumnGfHeI0u3bzJmPdnfK9jDkA1uA,2582
|
|
37
|
+
roomkit/models/participant.py,sha256=NX2wZylXKp9OG-4YA9Rc7Elpi7sdrHbT6gnIW2tPO7k,991
|
|
38
|
+
roomkit/models/room.py,sha256=M2XuWKqudRKErcxL1awOrFYkGMlcPbxou5gghYA3UH0,1000
|
|
39
|
+
roomkit/models/task.py,sha256=9hMfZc8Z_ccD2cV7Adb8XC39rEH6iShF3zfQ3T5jG_k,961
|
|
40
|
+
roomkit/providers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
41
|
+
roomkit/providers/ai/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
42
|
+
roomkit/providers/ai/base.py,sha256=ZqJqZ_DJpTPMInSHZcJ4-yi-JwcmOdquDYRiXMxBB2k,3949
|
|
43
|
+
roomkit/providers/ai/mock.py,sha256=aMOJwY0vX9WMc-BuCfV8e-bkp0nAjeseLdIlu5xDdJs,987
|
|
44
|
+
roomkit/providers/anthropic/__init__.py,sha256=2kwNeqgDpXwV13ksMALO8BSWnZA_yslBdPNhPrUYYH0,207
|
|
45
|
+
roomkit/providers/anthropic/ai.py,sha256=SrdLvLARHYe2EXM3JospigZ6q6wxObZD5VbMIGxvRvc,4691
|
|
46
|
+
roomkit/providers/anthropic/config.py,sha256=dcNFCtpoNPuUPn_SkG58eDg-0bb1jckr5Fes8RpWGhs,326
|
|
47
|
+
roomkit/providers/elasticemail/__init__.py,sha256=0h2RG9wt3HwDPAb3axTwN3MG9s-iuxdK7NFZqvCjtiM,228
|
|
48
|
+
roomkit/providers/elasticemail/config.py,sha256=5MLU0R2IJo-O4LFR7C-F9Nz9VOKxGWRDFynGoliUw_Y,412
|
|
49
|
+
roomkit/providers/elasticemail/email.py,sha256=xDz4gbe3izP070W_56i3P-WpPbPDF1WnPkX11IYlTTY,3365
|
|
50
|
+
roomkit/providers/email/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
51
|
+
roomkit/providers/email/base.py,sha256=Sl-A7wnlilbKDaKstM8i2T05jxYkrBATi-CktcLNEtE,1306
|
|
52
|
+
roomkit/providers/email/mock.py,sha256=r7IhMIouV9bQjZpExGyYrQ2OrHmWhGdgofOmRZLmA5A,900
|
|
53
|
+
roomkit/providers/gemini/__init__.py,sha256=rEXAnbH28iHm4agFvEqJH4LljU2d27hhdbAUt9mBMds,193
|
|
54
|
+
roomkit/providers/gemini/ai.py,sha256=_SM4j1ScIskYBazaT16cDFs-svvldpQX31oBwdE5A3k,5326
|
|
55
|
+
roomkit/providers/gemini/config.py,sha256=iBcksrmKYr2Rx9gsIwxUecN30xZP-BU23BHY2fsYh1c,341
|
|
56
|
+
roomkit/providers/http/__init__.py,sha256=y0Mr2Qu0uoTvZFBpPKCcZmGXsnA79YG1gIKvJwXLZ80,473
|
|
57
|
+
roomkit/providers/http/base.py,sha256=W4lTxUH_-IBxOqSaOpk8NWa7vc2QgSXIbDZp5g9-4kw,865
|
|
58
|
+
roomkit/providers/http/config.py,sha256=DdgmHS8c8dZMtEtp5HmxdvAU4uAEvPXcT1FRKZGiTJU,341
|
|
59
|
+
roomkit/providers/http/mock.py,sha256=7kujHCWdMP4kVH8VM9wTyhQWGz9ZrOJJjG1HitVTMEw,648
|
|
60
|
+
roomkit/providers/http/provider.py,sha256=c6u6wqd9a6Y4q6ogxAyXfnDSsDoRWx3lGsXyZhopFvM,3484
|
|
61
|
+
roomkit/providers/http/webhook.py,sha256=uEunVVwIlLsT0IlC64pKKLkJAiMEUYx4WZmuCUbD50o,886
|
|
62
|
+
roomkit/providers/messenger/__init__.py,sha256=Yp3Kn_FwdpddzysGKHArxIjB4QfjkJiPCh1OZM2VjaI,532
|
|
63
|
+
roomkit/providers/messenger/base.py,sha256=mA6pNUtQW0KhQnCFNGk9pH14KEeV_1Om6_RM6Sq-nrI,905
|
|
64
|
+
roomkit/providers/messenger/config.py,sha256=2r2QWCFJxKn3DTjlPuUCg1Jk710h4GquFWBh7sbo38k,430
|
|
65
|
+
roomkit/providers/messenger/facebook.py,sha256=iQXmEZ9qXwbhKmi3vDBovKnwDhhr90JB87Dthgdvjzo,3215
|
|
66
|
+
roomkit/providers/messenger/mock.py,sha256=iexGJRsPIUS9JxDYaRbRagRxWcRXL0W3fFw_3yY9u1A,673
|
|
67
|
+
roomkit/providers/messenger/webhook.py,sha256=zD54df12OQl3IhQxpC5xJs7UdDXnDDNb6zLZ-mWBHNg,1530
|
|
68
|
+
roomkit/providers/openai/__init__.py,sha256=rnbPVtymGeKX4jV5DlFcUOIv7csIiL7fov-Ea3m5fCo,186
|
|
69
|
+
roomkit/providers/openai/ai.py,sha256=duBPJWAGQ3r24CCqWfdvkyYeIhP4g2RlKvfV6-uFxco,4888
|
|
70
|
+
roomkit/providers/openai/config.py,sha256=3Nul5LafjJwVziEWXBd9DKpknVThADneCyNq2IJCreI,705
|
|
71
|
+
roomkit/providers/pydantic_ai/__init__.py,sha256=0TCON1PkeLymIDd3JwInn5_UTNCrPj4F9dNjr3QMkyA,127
|
|
72
|
+
roomkit/providers/pydantic_ai/config.py,sha256=h4AHprHCroZY7UVWv_Tym-yqKnxcMjZGM4Ky4SS9YtU,331
|
|
73
|
+
roomkit/providers/rcs/__init__.py,sha256=bwuwVj-eBoas_38D3ghdCOMW7YiX19Qq1gk2N7ATR4M,217
|
|
74
|
+
roomkit/providers/rcs/base.py,sha256=uduI-V6Rk8xsLkVLbWnNr5s-A_q2zoohXYXJHVs5JiI,2881
|
|
75
|
+
roomkit/providers/rcs/mock.py,sha256=F4OQpvYXRmYYhXhVeoTmTd8zu_5gXcR__jPeDPsRDJ8,2212
|
|
76
|
+
roomkit/providers/sendgrid/__init__.py,sha256=i_IP6yGn2mSVmc6TfJRyda1whDFj6dmqfTR0-2u5pyw,117
|
|
77
|
+
roomkit/providers/sendgrid/config.py,sha256=xyqqKridfUw7AThz6Z2fJPV0e4beMpHug3NcZUa3aPs,279
|
|
78
|
+
roomkit/providers/sinch/__init__.py,sha256=5sYsxkY35ldyfXVWwWX_ZV5jXVa7kGKNknm_SRu4xjI,226
|
|
79
|
+
roomkit/providers/sinch/config.py,sha256=vWBNdPYOJRJiGJX8xFc_t6FyH2OdkzzLCm0Azp_zeuY,559
|
|
80
|
+
roomkit/providers/sinch/sms.py,sha256=eNUORI6W0QKl7gxJ1UYh80OeotA_BJbPF4BWO6aVhF8,6276
|
|
81
|
+
roomkit/providers/sms/__init__.py,sha256=DUpHOvbymXxLkKGsACaAAPNicFr2W7ACDDJO2Q1UweM,414
|
|
82
|
+
roomkit/providers/sms/base.py,sha256=MJ2Wz3G_LsLrlKnhELZRrpsNaZNsh-1VqdFG7bXxNec,2081
|
|
83
|
+
roomkit/providers/sms/meta.py,sha256=uMhpaP818PcP3yoOeifWTXrsBrNO9YdrN5ccG10s6-c,13452
|
|
84
|
+
roomkit/providers/sms/mock.py,sha256=psp3GEunBX22_QeuUEpNGzQJ_wUnBafF-iVk7bwKOws,758
|
|
85
|
+
roomkit/providers/sms/phone.py,sha256=7hPhtkYNyz-gzzisaRsqpp-ETkcCe_qnN7BDHC4LM-E,2529
|
|
86
|
+
roomkit/providers/telnyx/__init__.py,sha256=gR4Wl1wBh3jHxncBz5ywBGrZ1Fk1YeSf-ifa7nfL1HQ,462
|
|
87
|
+
roomkit/providers/telnyx/config.py,sha256=IjSWQz7uHClhVdIhx22qF1ZUJijiuWzhDy3JsODvchE,309
|
|
88
|
+
roomkit/providers/telnyx/rcs.py,sha256=1PngjG5M-Z98WT5Cwxak30OV_3S68TohwDeNxQGQisQ,12091
|
|
89
|
+
roomkit/providers/telnyx/sms.py,sha256=6E3hZ_WRKw1XeJ4Wq36sE3VER87-8f0NxXRobPD8hlI,7996
|
|
90
|
+
roomkit/providers/twilio/__init__.py,sha256=xalOYLH071MDDRMHk9AextP1wIb2Yp5e96GgM2fpBYE,449
|
|
91
|
+
roomkit/providers/twilio/config.py,sha256=raqd3usAbFnN6DRuqhH82j1Fu-xHsNjLi0oORieiKgU,473
|
|
92
|
+
roomkit/providers/twilio/rcs.py,sha256=aGA8seEPXwV5KzLuSSiTLetNYd3RsGDnaJUKb7NqrPY,6162
|
|
93
|
+
roomkit/providers/twilio/sms.py,sha256=yxCyGcEOTPEpznxTYOxSesSwTi69Z8v6ViHmixqUqEk,6769
|
|
94
|
+
roomkit/providers/voicemeup/__init__.py,sha256=f9DEfqYGkkq0Gz0WyO3YZ3Z4FqMUDga_UOgbb52ZJhE,350
|
|
95
|
+
roomkit/providers/voicemeup/config.py,sha256=rlinegfi0Ui4m4ly06a_LRc15PbRmmTzSGEtc329Js4,551
|
|
96
|
+
roomkit/providers/voicemeup/sms.py,sha256=wojWXYB9zuI0VMVwaVbqOMKvi287dspESgit9CGydi4,13132
|
|
97
|
+
roomkit/providers/whatsapp/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
98
|
+
roomkit/providers/whatsapp/base.py,sha256=QyDqHG82XWIigmMaOePYOlCs2QJMzW9pT0GC6YyTvfk,1351
|
|
99
|
+
roomkit/providers/whatsapp/mock.py,sha256=i4wtsVMsBpx6LXjQpgvK7aoOEH2dMxu5W9SWaG2H0XE,668
|
|
100
|
+
roomkit/realtime/__init__.py,sha256=unS1HPdMmfYSMf1WVo9mvMDuw3ceP4bkyB60cCTcT8w,360
|
|
101
|
+
roomkit/realtime/base.py,sha256=8savEf_efHVLkmuPDwwX1IVbXru_GMhVtlfVbP1qMTg,3476
|
|
102
|
+
roomkit/realtime/memory.py,sha256=aIh2dy2g9V6n_VhEtSuzcYpB7UtDMQR6dpXK-B91Ne8,5128
|
|
103
|
+
roomkit/sources/__init__.py,sha256=Qh2QGw2k0kX5cPBbgG4zzVoDV7-SJx3XzSkrtDtPiT8,854
|
|
104
|
+
roomkit/sources/base.py,sha256=8oHjw0vdcOMYxwtenN-dwuBwCx42bmMdfC_KZwriAaE,6637
|
|
105
|
+
roomkit/sources/websocket.py,sha256=j60gsILNutqJNPdVqyOBbQ5sUV3XOes2LwhLMKZIOE0,8521
|
|
106
|
+
roomkit/store/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
107
|
+
roomkit/store/base.py,sha256=Q5p-lOQmWJqiBd7jD-1inee6qoYBhlb-C06Z2bzJ8mI,6781
|
|
108
|
+
roomkit/store/memory.py,sha256=_cAUBuC7BUwOKEa6aomr4aG7t-J0CuiK7izagQ-Daig,11932
|
|
109
|
+
roomkit/AGENTS.md,sha256=lETRMzpOTJ7ilhbm3_ufZYu5Y7Q0nTlIEU2-oT2XN8Q,10490
|
|
110
|
+
roomkit/llms.txt,sha256=QWeLJaSnt2nKUmmQxChnEFPO675ikMFm0TDHMdEKn8s,4039
|
|
111
|
+
roomkit-0.1.0.dist-info/METADATA,sha256=aA83baVrZkex6_VsyO85PSxYvjZy7Gn4Tg85aGH0VcA,19391
|
|
112
|
+
roomkit-0.1.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
113
|
+
roomkit-0.1.0.dist-info/licenses/LICENSE,sha256=GIXnbN1bOyI_7Ydnf7EXBxrFi344wlsdVV4acemzRcE,1070
|
|
114
|
+
roomkit-0.1.0.dist-info/RECORD,,
|