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
roomkit/llms.txt
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# RoomKit
|
|
2
|
+
|
|
3
|
+
> RoomKit is a pure async Python library for building multi-channel conversation systems.
|
|
4
|
+
> It provides room-based abstractions for managing conversations across SMS, Email, WebSocket,
|
|
5
|
+
> AI, and other channels with pluggable storage, identity resolution, hooks, and realtime events.
|
|
6
|
+
> Python 3.12+, Pydantic 2.x, fully typed, zero required dependencies beyond Pydantic.
|
|
7
|
+
|
|
8
|
+
RoomKit follows a pluggable architecture pattern where core abstractions (ConversationStore,
|
|
9
|
+
RoomLockManager, RealtimeBackend, IdentityResolver) have in-memory defaults but can be replaced
|
|
10
|
+
with distributed implementations (Redis, PostgreSQL, etc.) for production deployments.
|
|
11
|
+
|
|
12
|
+
## Getting Started
|
|
13
|
+
|
|
14
|
+
- [Features Overview](docs/features.md): Core features, channel support matrix, hooks, AI integration, resilience, identity resolution, and usage workflows
|
|
15
|
+
- [Architecture](docs/architecture.md): System design, component relationships, and extension points
|
|
16
|
+
|
|
17
|
+
## Core API
|
|
18
|
+
|
|
19
|
+
- [RoomKit](docs/api/roomkit.md): Central orchestrator class - room lifecycle, channel management, hooks, inbound processing
|
|
20
|
+
- [Hooks](docs/api/hooks.md): HookEngine, HookRegistration - event interception at BEFORE_BROADCAST, AFTER_BROADCAST, lifecycle triggers
|
|
21
|
+
- [Routing](docs/api/routing.md): InboundRoomRouter - strategy for routing inbound messages to rooms
|
|
22
|
+
- [Store](docs/api/store.md): ConversationStore ABC and InMemoryStore - room, event, participant, task persistence
|
|
23
|
+
- [Realtime](docs/api/realtime.md): RealtimeBackend for ephemeral events - typing indicators, presence, read receipts. Key methods: `kit.publish_typing(room_id, user_id, is_typing=True)`, `kit.publish_presence(room_id, user_id, status)`, `kit.publish_read_receipt(room_id, user_id, event_id)`, `kit.subscribe_room(room_id, callback) -> subscription_id`, `kit.unsubscribe_room(subscription_id)`. EphemeralEventType enum: TYPING_START, TYPING_STOP, PRESENCE_ONLINE, PRESENCE_AWAY, PRESENCE_OFFLINE, READ_RECEIPT, CUSTOM
|
|
24
|
+
|
|
25
|
+
## Channels
|
|
26
|
+
|
|
27
|
+
- [Channel ABC](docs/api/channel.md): Base class for all channels - handle_inbound, deliver, on_event, capabilities
|
|
28
|
+
- [Built-in Channels](docs/api/channels.md): SMSChannel, EmailChannel, AIChannel, WebSocketChannel, MessengerChannel, HTTPChannel, WhatsAppChannel
|
|
29
|
+
|
|
30
|
+
## Models
|
|
31
|
+
|
|
32
|
+
- [Room & Timers](docs/api/room.md): Room model with status lifecycle (ACTIVE, PAUSED, CLOSED, ARCHIVED) and timer configuration
|
|
33
|
+
- [Events & Content](docs/api/events.md): RoomEvent, TextContent, MediaContent, RichContent, CompositeContent, TemplateContent, LocationContent
|
|
34
|
+
- [Identity](docs/api/identity.md): Identity, IdentityResult, IdentityHookResult, Participant - identification pipeline
|
|
35
|
+
- [Delivery](docs/api/delivery.md): InboundMessage, InboundResult, DeliveryResult, ProviderResult
|
|
36
|
+
- [Channel Models](docs/api/channel-models.md): ChannelBinding, ChannelCapabilities, ChannelOutput, RateLimit, RetryPolicy
|
|
37
|
+
- [Hook Models](docs/api/hook-models.md): HookResult, InjectedEvent, Task, Observation
|
|
38
|
+
|
|
39
|
+
## Providers
|
|
40
|
+
|
|
41
|
+
- [AI Providers](docs/api/providers-ai.md): AIProvider ABC, AnthropicAIProvider, OpenAIAIProvider, GeminiAIProvider, MockAIProvider - context building, vision support
|
|
42
|
+
- [SMS Providers](docs/api/providers-sms.md): SMSProvider ABC, TwilioSMSProvider, TelnyxSMSProvider, SinchSMSProvider, VoiceMeUpSMSProvider - webhook parsing, signature verification, MMS support
|
|
43
|
+
- [Email Providers](docs/api/providers-email.md): EmailProvider ABC, ElasticEmailProvider
|
|
44
|
+
- [HTTP Providers](docs/api/providers-http.md): HTTPProvider ABC, WebhookHTTPProvider - generic webhook integration
|
|
45
|
+
- [Messenger Providers](docs/api/providers-messenger.md): MessengerProvider ABC, FacebookMessengerProvider
|
|
46
|
+
|
|
47
|
+
## Optional
|
|
48
|
+
|
|
49
|
+
- [Enums](docs/api/enums.md): ChannelType, EventType, EventStatus, RoomStatus, HookTrigger, HookExecution, IdentificationStatus, ParticipantRole
|
|
50
|
+
- [Technical Details](docs/technical.md): Implementation details and internal architecture
|
|
51
|
+
- [RFC](docs/roomkit-rfc.md): Original design document and rationale
|
|
52
|
+
- [CPaaS Comparison](docs/cpaas-comparison.md): Comparison with other communication platforms
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
"""RoomKit data models."""
|
|
2
|
+
|
|
3
|
+
from roomkit.models.channel import (
|
|
4
|
+
ChannelBinding,
|
|
5
|
+
ChannelCapabilities,
|
|
6
|
+
ChannelOutput,
|
|
7
|
+
RateLimit,
|
|
8
|
+
)
|
|
9
|
+
from roomkit.models.context import RoomContext
|
|
10
|
+
from roomkit.models.delivery import (
|
|
11
|
+
DeliveryResult,
|
|
12
|
+
InboundMessage,
|
|
13
|
+
InboundResult,
|
|
14
|
+
ProviderResult,
|
|
15
|
+
)
|
|
16
|
+
from roomkit.models.enums import (
|
|
17
|
+
Access,
|
|
18
|
+
ChannelCategory,
|
|
19
|
+
ChannelDirection,
|
|
20
|
+
ChannelMediaType,
|
|
21
|
+
ChannelType,
|
|
22
|
+
DeliveryMode,
|
|
23
|
+
EventStatus,
|
|
24
|
+
EventType,
|
|
25
|
+
HookExecution,
|
|
26
|
+
HookTrigger,
|
|
27
|
+
IdentificationStatus,
|
|
28
|
+
ParticipantRole,
|
|
29
|
+
ParticipantStatus,
|
|
30
|
+
RoomStatus,
|
|
31
|
+
TaskStatus,
|
|
32
|
+
)
|
|
33
|
+
from roomkit.models.event import (
|
|
34
|
+
AudioContent,
|
|
35
|
+
ChannelData,
|
|
36
|
+
CompositeContent,
|
|
37
|
+
EventContent,
|
|
38
|
+
EventSource,
|
|
39
|
+
LocationContent,
|
|
40
|
+
MediaContent,
|
|
41
|
+
RichContent,
|
|
42
|
+
RoomEvent,
|
|
43
|
+
SystemContent,
|
|
44
|
+
TemplateContent,
|
|
45
|
+
TextContent,
|
|
46
|
+
VideoContent,
|
|
47
|
+
)
|
|
48
|
+
from roomkit.models.framework_event import FrameworkEvent
|
|
49
|
+
from roomkit.models.hook import HookResult, InjectedEvent
|
|
50
|
+
from roomkit.models.identity import Identity, IdentityHookResult, IdentityResult
|
|
51
|
+
from roomkit.models.participant import Participant
|
|
52
|
+
from roomkit.models.room import Room, RoomTimers
|
|
53
|
+
from roomkit.models.task import Observation, Task
|
|
54
|
+
|
|
55
|
+
__all__ = [
|
|
56
|
+
"Access",
|
|
57
|
+
"AudioContent",
|
|
58
|
+
"ChannelBinding",
|
|
59
|
+
"ChannelCapabilities",
|
|
60
|
+
"ChannelCategory",
|
|
61
|
+
"ChannelData",
|
|
62
|
+
"ChannelDirection",
|
|
63
|
+
"ChannelMediaType",
|
|
64
|
+
"ChannelOutput",
|
|
65
|
+
"ChannelType",
|
|
66
|
+
"CompositeContent",
|
|
67
|
+
"DeliveryMode",
|
|
68
|
+
"DeliveryResult",
|
|
69
|
+
"EventContent",
|
|
70
|
+
"EventSource",
|
|
71
|
+
"EventStatus",
|
|
72
|
+
"EventType",
|
|
73
|
+
"FrameworkEvent",
|
|
74
|
+
"HookExecution",
|
|
75
|
+
"HookResult",
|
|
76
|
+
"HookTrigger",
|
|
77
|
+
"Identity",
|
|
78
|
+
"IdentificationStatus",
|
|
79
|
+
"IdentityHookResult",
|
|
80
|
+
"IdentityResult",
|
|
81
|
+
"InboundMessage",
|
|
82
|
+
"InboundResult",
|
|
83
|
+
"InjectedEvent",
|
|
84
|
+
"LocationContent",
|
|
85
|
+
"MediaContent",
|
|
86
|
+
"Observation",
|
|
87
|
+
"Participant",
|
|
88
|
+
"ParticipantRole",
|
|
89
|
+
"ParticipantStatus",
|
|
90
|
+
"ProviderResult",
|
|
91
|
+
"RateLimit",
|
|
92
|
+
"RichContent",
|
|
93
|
+
"Room",
|
|
94
|
+
"RoomContext",
|
|
95
|
+
"RoomEvent",
|
|
96
|
+
"RoomStatus",
|
|
97
|
+
"RoomTimers",
|
|
98
|
+
"SystemContent",
|
|
99
|
+
"Task",
|
|
100
|
+
"TaskStatus",
|
|
101
|
+
"TemplateContent",
|
|
102
|
+
"TextContent",
|
|
103
|
+
"VideoContent",
|
|
104
|
+
]
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
"""Channel-related data models."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from datetime import UTC, datetime
|
|
6
|
+
from typing import Any
|
|
7
|
+
|
|
8
|
+
from pydantic import BaseModel, Field
|
|
9
|
+
|
|
10
|
+
from roomkit.models.enums import (
|
|
11
|
+
Access,
|
|
12
|
+
ChannelCategory,
|
|
13
|
+
ChannelDirection,
|
|
14
|
+
ChannelMediaType,
|
|
15
|
+
ChannelType,
|
|
16
|
+
DeliveryMode,
|
|
17
|
+
)
|
|
18
|
+
from roomkit.models.event import RoomEvent
|
|
19
|
+
from roomkit.models.task import Observation, Task
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class RateLimit(BaseModel):
|
|
23
|
+
"""Rate limiting configuration for a channel."""
|
|
24
|
+
|
|
25
|
+
max_per_second: float | None = Field(default=None, gt=0.0)
|
|
26
|
+
max_per_minute: float | None = Field(default=None, gt=0.0)
|
|
27
|
+
max_per_hour: float | None = Field(default=None, gt=0.0)
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class RetryPolicy(BaseModel):
|
|
31
|
+
"""Configures retry behaviour for channel delivery."""
|
|
32
|
+
|
|
33
|
+
max_retries: int = Field(default=3, ge=0)
|
|
34
|
+
base_delay_seconds: float = Field(default=1.0, gt=0.0)
|
|
35
|
+
max_delay_seconds: float = Field(default=60.0, gt=0.0)
|
|
36
|
+
exponential_base: float = Field(default=2.0, gt=0.0)
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
class ChannelCapabilities(BaseModel):
|
|
40
|
+
"""What a channel can do."""
|
|
41
|
+
|
|
42
|
+
media_types: list[ChannelMediaType] = Field(default_factory=lambda: [ChannelMediaType.TEXT])
|
|
43
|
+
max_length: int | None = Field(default=None, gt=0)
|
|
44
|
+
supports_threading: bool = False
|
|
45
|
+
supports_reactions: bool = False
|
|
46
|
+
supports_read_receipts: bool = False
|
|
47
|
+
supports_typing: bool = False
|
|
48
|
+
supports_templates: bool = False
|
|
49
|
+
supports_rich_text: bool = False
|
|
50
|
+
supports_buttons: bool = False
|
|
51
|
+
max_buttons: int | None = Field(default=None, gt=0)
|
|
52
|
+
supports_cards: bool = False
|
|
53
|
+
supports_quick_replies: bool = False
|
|
54
|
+
supports_media: bool = False
|
|
55
|
+
supported_media_types: list[str] = Field(default_factory=list)
|
|
56
|
+
max_media_size_bytes: int | None = Field(default=None, gt=0)
|
|
57
|
+
supports_audio: bool = False
|
|
58
|
+
max_audio_duration_seconds: int | None = Field(default=None, gt=0)
|
|
59
|
+
supported_audio_formats: list[str] = Field(default_factory=list)
|
|
60
|
+
supports_video: bool = False
|
|
61
|
+
max_video_duration_seconds: int | None = Field(default=None, gt=0)
|
|
62
|
+
supported_video_formats: list[str] = Field(default_factory=list)
|
|
63
|
+
delivery_mode: DeliveryMode = DeliveryMode.BROADCAST
|
|
64
|
+
custom: dict[str, Any] = Field(default_factory=dict)
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
class ChannelBinding(BaseModel):
|
|
68
|
+
"""A channel's attachment to a room."""
|
|
69
|
+
|
|
70
|
+
channel_id: str
|
|
71
|
+
room_id: str
|
|
72
|
+
channel_type: ChannelType
|
|
73
|
+
category: ChannelCategory = ChannelCategory.TRANSPORT
|
|
74
|
+
direction: ChannelDirection = ChannelDirection.BIDIRECTIONAL
|
|
75
|
+
access: Access = Access.READ_WRITE
|
|
76
|
+
muted: bool = False
|
|
77
|
+
visibility: str = "all"
|
|
78
|
+
participant_id: str | None = None
|
|
79
|
+
last_read_index: int | None = Field(default=None, ge=0)
|
|
80
|
+
attached_at: datetime = Field(default_factory=lambda: datetime.now(UTC))
|
|
81
|
+
capabilities: ChannelCapabilities = Field(default_factory=ChannelCapabilities)
|
|
82
|
+
rate_limit: RateLimit | None = None
|
|
83
|
+
retry_policy: RetryPolicy | None = None
|
|
84
|
+
metadata: dict[str, Any] = Field(default_factory=dict)
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
class ChannelOutput(BaseModel):
|
|
88
|
+
"""Output produced by a channel after receiving an event."""
|
|
89
|
+
|
|
90
|
+
responded: bool = False
|
|
91
|
+
response_events: list[RoomEvent] = Field(default_factory=list)
|
|
92
|
+
tasks: list[Task] = Field(default_factory=list)
|
|
93
|
+
observations: list[Observation] = Field(default_factory=list)
|
|
94
|
+
metadata_updates: dict[str, Any] = Field(default_factory=dict)
|
|
95
|
+
|
|
96
|
+
@classmethod
|
|
97
|
+
def empty(cls) -> ChannelOutput:
|
|
98
|
+
"""Create an empty output with no responses or side effects."""
|
|
99
|
+
return cls()
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
"""Room context model."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from pydantic import BaseModel, Field
|
|
6
|
+
|
|
7
|
+
from roomkit.models.channel import ChannelBinding
|
|
8
|
+
from roomkit.models.enums import ChannelType
|
|
9
|
+
from roomkit.models.event import RoomEvent
|
|
10
|
+
from roomkit.models.participant import Participant
|
|
11
|
+
from roomkit.models.room import Room
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class RoomContext(BaseModel):
|
|
15
|
+
"""Contextual information about a room for hook and channel processing."""
|
|
16
|
+
|
|
17
|
+
room: Room
|
|
18
|
+
bindings: list[ChannelBinding] = Field(default_factory=list)
|
|
19
|
+
participants: list[Participant] = Field(default_factory=list)
|
|
20
|
+
recent_events: list[RoomEvent] = Field(default_factory=list)
|
|
21
|
+
|
|
22
|
+
def other_channels(self, exclude_channel_id: str) -> list[ChannelBinding]:
|
|
23
|
+
"""Get all bindings except the specified channel."""
|
|
24
|
+
return [b for b in self.bindings if b.channel_id != exclude_channel_id]
|
|
25
|
+
|
|
26
|
+
def channels_by_type(self, channel_type: ChannelType) -> list[ChannelBinding]:
|
|
27
|
+
"""Get all bindings of a specific channel type."""
|
|
28
|
+
return [b for b in self.bindings if b.channel_type == channel_type]
|
|
29
|
+
|
|
30
|
+
def get_binding(self, channel_id: str) -> ChannelBinding | None:
|
|
31
|
+
"""Get the binding for a specific channel."""
|
|
32
|
+
for b in self.bindings:
|
|
33
|
+
if b.channel_id == channel_id:
|
|
34
|
+
return b
|
|
35
|
+
return None
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
"""Delivery and provider result models."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import Any
|
|
6
|
+
|
|
7
|
+
from pydantic import BaseModel, Field
|
|
8
|
+
|
|
9
|
+
from roomkit.models.event import EventContent, RoomEvent
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class ProviderResult(BaseModel):
|
|
13
|
+
"""Result from a provider delivery attempt."""
|
|
14
|
+
|
|
15
|
+
success: bool
|
|
16
|
+
provider_message_id: str | None = None
|
|
17
|
+
error: str | None = None
|
|
18
|
+
metadata: dict[str, Any] = Field(default_factory=dict)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class InboundMessage(BaseModel):
|
|
22
|
+
"""A message received from an external provider."""
|
|
23
|
+
|
|
24
|
+
channel_id: str
|
|
25
|
+
sender_id: str
|
|
26
|
+
content: EventContent
|
|
27
|
+
external_id: str | None = None
|
|
28
|
+
thread_id: str | None = None
|
|
29
|
+
idempotency_key: str | None = None
|
|
30
|
+
metadata: dict[str, Any] = Field(default_factory=dict)
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class InboundResult(BaseModel):
|
|
34
|
+
"""Result of processing an inbound message."""
|
|
35
|
+
|
|
36
|
+
event: RoomEvent | None = None
|
|
37
|
+
blocked: bool = False
|
|
38
|
+
reason: str | None = None
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
class DeliveryResult(BaseModel):
|
|
42
|
+
"""Result of delivering an event to a channel."""
|
|
43
|
+
|
|
44
|
+
channel_id: str
|
|
45
|
+
success: bool
|
|
46
|
+
provider_result: ProviderResult | None = None
|
|
47
|
+
error: str | None = None
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
class DeliveryStatus(BaseModel):
|
|
51
|
+
"""Status update for an outbound message from a provider webhook.
|
|
52
|
+
|
|
53
|
+
Providers send status webhooks when messages are sent, delivered, failed, etc.
|
|
54
|
+
Use this with the ON_DELIVERY_STATUS hook to track outbound message delivery.
|
|
55
|
+
|
|
56
|
+
Attributes:
|
|
57
|
+
provider: Provider name (e.g., "telnyx", "twilio").
|
|
58
|
+
message_id: Provider's unique message identifier.
|
|
59
|
+
status: Status string (e.g., "sent", "delivered", "failed").
|
|
60
|
+
recipient: Phone number/address the message was sent to.
|
|
61
|
+
sender: Phone number/address the message was sent from.
|
|
62
|
+
error_code: Provider-specific error code (if failed).
|
|
63
|
+
error_message: Human-readable error message (if failed).
|
|
64
|
+
timestamp: When the status was reported.
|
|
65
|
+
raw: Original webhook payload for debugging.
|
|
66
|
+
"""
|
|
67
|
+
|
|
68
|
+
provider: str
|
|
69
|
+
message_id: str
|
|
70
|
+
status: str
|
|
71
|
+
recipient: str = ""
|
|
72
|
+
sender: str = ""
|
|
73
|
+
error_code: str | None = None
|
|
74
|
+
error_message: str | None = None
|
|
75
|
+
timestamp: str | None = None
|
|
76
|
+
raw: dict[str, Any] = Field(default_factory=dict)
|
roomkit/models/enums.py
ADDED
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
"""All string enums for RoomKit."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from enum import StrEnum, unique
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
@unique
|
|
9
|
+
class ChannelType(StrEnum):
|
|
10
|
+
SMS = "sms"
|
|
11
|
+
MMS = "mms"
|
|
12
|
+
RCS = "rcs"
|
|
13
|
+
EMAIL = "email"
|
|
14
|
+
WHATSAPP = "whatsapp"
|
|
15
|
+
WEBSOCKET = "websocket"
|
|
16
|
+
AI = "ai"
|
|
17
|
+
VOICE = "voice"
|
|
18
|
+
PUSH = "push"
|
|
19
|
+
MESSENGER = "messenger"
|
|
20
|
+
WEBHOOK = "webhook"
|
|
21
|
+
SYSTEM = "system"
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
@unique
|
|
25
|
+
class ChannelCategory(StrEnum):
|
|
26
|
+
TRANSPORT = "transport"
|
|
27
|
+
INTELLIGENCE = "intelligence"
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
@unique
|
|
31
|
+
class ChannelDirection(StrEnum):
|
|
32
|
+
INBOUND = "inbound"
|
|
33
|
+
OUTBOUND = "outbound"
|
|
34
|
+
BIDIRECTIONAL = "bidirectional"
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
@unique
|
|
38
|
+
class ChannelMediaType(StrEnum):
|
|
39
|
+
TEXT = "text"
|
|
40
|
+
RICH = "rich"
|
|
41
|
+
MEDIA = "media"
|
|
42
|
+
AUDIO = "audio"
|
|
43
|
+
VIDEO = "video"
|
|
44
|
+
LOCATION = "location"
|
|
45
|
+
TEMPLATE = "template"
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
@unique
|
|
49
|
+
class EventType(StrEnum):
|
|
50
|
+
MESSAGE = "message"
|
|
51
|
+
SYSTEM = "system"
|
|
52
|
+
TYPING = "typing"
|
|
53
|
+
READ_RECEIPT = "read_receipt"
|
|
54
|
+
DELIVERY_RECEIPT = "delivery_receipt"
|
|
55
|
+
PRESENCE = "presence"
|
|
56
|
+
REACTION = "reaction"
|
|
57
|
+
EDIT = "edit"
|
|
58
|
+
DELETE = "delete"
|
|
59
|
+
# Participant lifecycle (RFC §5.1)
|
|
60
|
+
PARTICIPANT_JOINED = "participant_joined"
|
|
61
|
+
PARTICIPANT_LEFT = "participant_left"
|
|
62
|
+
PARTICIPANT_IDENTIFIED = "participant_identified"
|
|
63
|
+
# Channel lifecycle (RFC §3.7)
|
|
64
|
+
CHANNEL_ATTACHED = "channel_attached"
|
|
65
|
+
CHANNEL_DETACHED = "channel_detached"
|
|
66
|
+
CHANNEL_MUTED = "channel_muted"
|
|
67
|
+
CHANNEL_UNMUTED = "channel_unmuted"
|
|
68
|
+
CHANNEL_UPDATED = "channel_updated"
|
|
69
|
+
# Side effects
|
|
70
|
+
TASK_CREATED = "task_created"
|
|
71
|
+
OBSERVATION = "observation"
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
@unique
|
|
75
|
+
class EventStatus(StrEnum):
|
|
76
|
+
PENDING = "pending"
|
|
77
|
+
DELIVERED = "delivered"
|
|
78
|
+
READ = "read"
|
|
79
|
+
FAILED = "failed"
|
|
80
|
+
BLOCKED = "blocked"
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
@unique
|
|
84
|
+
class Access(StrEnum):
|
|
85
|
+
READ_WRITE = "read_write"
|
|
86
|
+
READ_ONLY = "read_only"
|
|
87
|
+
WRITE_ONLY = "write_only"
|
|
88
|
+
NONE = "none"
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
@unique
|
|
92
|
+
class IdentificationStatus(StrEnum):
|
|
93
|
+
IDENTIFIED = "identified"
|
|
94
|
+
PENDING = "pending"
|
|
95
|
+
AMBIGUOUS = "ambiguous"
|
|
96
|
+
UNKNOWN = "unknown"
|
|
97
|
+
CHALLENGE_SENT = "challenge_sent"
|
|
98
|
+
REJECTED = "rejected"
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
@unique
|
|
102
|
+
class ParticipantRole(StrEnum):
|
|
103
|
+
OWNER = "owner"
|
|
104
|
+
AGENT = "agent"
|
|
105
|
+
MEMBER = "member"
|
|
106
|
+
OBSERVER = "observer"
|
|
107
|
+
BOT = "bot"
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
@unique
|
|
111
|
+
class ParticipantStatus(StrEnum):
|
|
112
|
+
ACTIVE = "active"
|
|
113
|
+
INACTIVE = "inactive"
|
|
114
|
+
LEFT = "left"
|
|
115
|
+
BANNED = "banned"
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
@unique
|
|
119
|
+
class TaskStatus(StrEnum):
|
|
120
|
+
PENDING = "pending"
|
|
121
|
+
IN_PROGRESS = "in_progress"
|
|
122
|
+
COMPLETED = "completed"
|
|
123
|
+
FAILED = "failed"
|
|
124
|
+
CANCELLED = "cancelled"
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
@unique
|
|
128
|
+
class RoomStatus(StrEnum):
|
|
129
|
+
ACTIVE = "active"
|
|
130
|
+
PAUSED = "paused"
|
|
131
|
+
CLOSED = "closed"
|
|
132
|
+
ARCHIVED = "archived"
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
@unique
|
|
136
|
+
class DeliveryMode(StrEnum):
|
|
137
|
+
BROADCAST = "broadcast"
|
|
138
|
+
DIRECT = "direct"
|
|
139
|
+
ROUND_ROBIN = "round_robin"
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
@unique
|
|
143
|
+
class HookTrigger(StrEnum):
|
|
144
|
+
# Event pipeline (RFC §4.1)
|
|
145
|
+
BEFORE_BROADCAST = "before_broadcast"
|
|
146
|
+
AFTER_BROADCAST = "after_broadcast"
|
|
147
|
+
# Channel lifecycle (RFC §4.1)
|
|
148
|
+
ON_CHANNEL_ATTACHED = "on_channel_attached"
|
|
149
|
+
ON_CHANNEL_DETACHED = "on_channel_detached"
|
|
150
|
+
ON_CHANNEL_MUTED = "on_channel_muted"
|
|
151
|
+
ON_CHANNEL_UNMUTED = "on_channel_unmuted"
|
|
152
|
+
# Room lifecycle (RFC §4.1)
|
|
153
|
+
ON_ROOM_CREATED = "on_room_created"
|
|
154
|
+
ON_ROOM_PAUSED = "on_room_paused"
|
|
155
|
+
ON_ROOM_CLOSED = "on_room_closed"
|
|
156
|
+
# Identity (RFC §7.3)
|
|
157
|
+
ON_IDENTITY_AMBIGUOUS = "on_identity_ambiguous"
|
|
158
|
+
ON_IDENTITY_UNKNOWN = "on_identity_unknown"
|
|
159
|
+
ON_PARTICIPANT_IDENTIFIED = "on_participant_identified"
|
|
160
|
+
# Side effects
|
|
161
|
+
ON_TASK_CREATED = "on_task_created"
|
|
162
|
+
ON_ERROR = "on_error"
|
|
163
|
+
# Delivery status (outbound message tracking)
|
|
164
|
+
ON_DELIVERY_STATUS = "on_delivery_status"
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
@unique
|
|
168
|
+
class HookExecution(StrEnum):
|
|
169
|
+
SYNC = "sync"
|
|
170
|
+
ASYNC = "async"
|