feat: Unified Messaging System — kommunikation plugin, AI/Proactive/System participants, MessageSidebar, Rich Content Renderer
Phase 1: Backend plugin kommunikation (13 files, 10 tables, REST API, WebSocket, RBAC, DMS Bridge, Participant Registry, Mini-App Registry, Search Provider) Phase 2: AI plugins as participants (ai_assistant + ai_proactive dock as participants, heartbeat job) Phase 3: system_notif plugin (system events → chat messages, pinned System room) Phase 4: Frontend MessageSidebar (replaces AISidebar, same design, comm API client, WebSocket hook, commStore) Phase 5: Rich Content Block Renderer (11 components: Markdown, HTML, Image, Audio, Video, File, ActionCard, ContactCard, MiniApp, BlockRenderer) BasePlugin: added services property + _container in on_activate
This commit is contained in:
@@ -0,0 +1,177 @@
|
||||
"""Pydantic schemas for the kommunikation plugin API."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import uuid
|
||||
from datetime import datetime
|
||||
from typing import Any
|
||||
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
|
||||
# ─── Conversation Schemas ───
|
||||
|
||||
class ParticipantResponse(BaseModel):
|
||||
id: str
|
||||
conversation_id: str
|
||||
participant_id: str | None = None
|
||||
participant_type: str
|
||||
display_name: str | None = None
|
||||
role: str
|
||||
joined_at: str | None = None
|
||||
|
||||
|
||||
class ConversationCreate(BaseModel):
|
||||
title: str | None = None
|
||||
participant_ids: list[str] = Field(default_factory=list)
|
||||
participant_types: list[str] = Field(default_factory=lambda: ["user"])
|
||||
initial_message: str | None = None
|
||||
is_direct: bool = False
|
||||
|
||||
|
||||
class ConversationUpdate(BaseModel):
|
||||
title: str | None = None
|
||||
is_archived: bool | None = None
|
||||
|
||||
|
||||
class ConversationResponse(BaseModel):
|
||||
id: str
|
||||
title: str | None = None
|
||||
is_locked: bool = False
|
||||
locked_by: str | None = None
|
||||
is_direct: bool = False
|
||||
is_archived: bool = False
|
||||
is_pinned: bool = False
|
||||
created_by: str | None = None
|
||||
created_by_type: str = "user"
|
||||
last_msg_at: str | None = None
|
||||
last_msg_preview: str | None = None
|
||||
last_msg_sender_type: str | None = None
|
||||
participants: list[ParticipantResponse] = Field(default_factory=list)
|
||||
unread_count: int = 0
|
||||
metadata: dict[str, Any] = Field(default_factory=dict)
|
||||
|
||||
|
||||
class ConversationListResponse(BaseModel):
|
||||
items: list[ConversationResponse]
|
||||
total: int
|
||||
|
||||
|
||||
# ─── Participant Management ───
|
||||
|
||||
class ParticipantAdd(BaseModel):
|
||||
participant_id: str
|
||||
participant_type: str = "user"
|
||||
role: str = "member"
|
||||
|
||||
|
||||
class ParticipantRoleUpdate(BaseModel):
|
||||
role: str = Field(..., pattern="^(admin|member|reader)$")
|
||||
|
||||
|
||||
# ─── Message Schemas ───
|
||||
|
||||
class MessageBlockCreate(BaseModel):
|
||||
block_type: str
|
||||
block_data: dict[str, Any]
|
||||
|
||||
|
||||
class MessageBlockResponse(BaseModel):
|
||||
id: str
|
||||
block_type: str
|
||||
block_data: dict[str, Any]
|
||||
sort_order: int = 0
|
||||
|
||||
|
||||
class AttachmentCreate(BaseModel):
|
||||
file_id: str | None = None
|
||||
file_source: str = "comm"
|
||||
|
||||
|
||||
class AttachmentResponse(BaseModel):
|
||||
id: str
|
||||
file_id: str | None = None
|
||||
file_source: str = "comm"
|
||||
file_name: str
|
||||
file_type: str
|
||||
file_size: int | None = None
|
||||
thumbnail_path: str | None = None
|
||||
|
||||
|
||||
class MessageCreate(BaseModel):
|
||||
content: str = ""
|
||||
content_format: str = "text"
|
||||
blocks: list[MessageBlockCreate] = Field(default_factory=list)
|
||||
reply_to_id: str | None = None
|
||||
attachments: list[AttachmentCreate] = Field(default_factory=list)
|
||||
|
||||
|
||||
class MessageUpdate(BaseModel):
|
||||
content: str | None = None
|
||||
read: bool | None = None
|
||||
|
||||
|
||||
class MessageResponse(BaseModel):
|
||||
id: str
|
||||
conversation_id: str
|
||||
sender_id: str | None = None
|
||||
sender_type: str
|
||||
content: str = ""
|
||||
content_format: str = "text"
|
||||
metadata: dict[str, Any] = Field(default_factory=dict)
|
||||
reply_to_id: str | None = None
|
||||
is_pinned: bool = False
|
||||
created_at: str | None = None
|
||||
edited_at: str | None = None
|
||||
blocks: list[MessageBlockResponse] = Field(default_factory=list)
|
||||
attachments: list[AttachmentResponse] = Field(default_factory=list)
|
||||
reactions: list[dict[str, Any]] = Field(default_factory=list)
|
||||
|
||||
|
||||
class MessageListResponse(BaseModel):
|
||||
items: list[MessageResponse]
|
||||
total: int
|
||||
page: int = 1
|
||||
has_more: bool = False
|
||||
|
||||
|
||||
# ─── Reaction Schemas ───
|
||||
|
||||
class ReactionCreate(BaseModel):
|
||||
emoji: str
|
||||
|
||||
|
||||
class ReactionResponse(BaseModel):
|
||||
id: str
|
||||
message_id: str
|
||||
user_id: str
|
||||
emoji: str
|
||||
|
||||
|
||||
# ─── Read State ───
|
||||
|
||||
class ReadStateUpdate(BaseModel):
|
||||
last_read_msg_id: str | None = None
|
||||
|
||||
|
||||
# ─── Mini-App Schemas ───
|
||||
|
||||
class MiniAppResponse(BaseModel):
|
||||
app_id: str
|
||||
name: str
|
||||
icon: str
|
||||
description: str
|
||||
plugin_name: str
|
||||
render_schema: dict[str, Any] = Field(default_factory=dict)
|
||||
|
||||
|
||||
class MiniAppStartRequest(BaseModel):
|
||||
app_id: str
|
||||
config: dict[str, Any] = Field(default_factory=dict)
|
||||
|
||||
|
||||
# ─── WebSocket Message Schemas ───
|
||||
|
||||
class WSMessage(BaseModel):
|
||||
type: str
|
||||
data: dict[str, Any] = Field(default_factory=dict)
|
||||
Reference in New Issue
Block a user