prism-models 0.7.5__tar.gz → 0.7.7__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.
- {prism_models-0.7.5 → prism_models-0.7.7}/PKG-INFO +1 -1
- {prism_models-0.7.5 → prism_models-0.7.7}/prism_models/__init__.py +3 -0
- {prism_models-0.7.5 → prism_models-0.7.7}/prism_models/config.py +1 -1
- prism_models-0.7.7/prism_models/migration/versions/2026_01_29_1312_ed197421f25a_add_notification_tables.py +87 -0
- prism_models-0.7.7/prism_models/notification.py +142 -0
- {prism_models-0.7.5 → prism_models-0.7.7}/prism_models.egg-info/PKG-INFO +1 -1
- {prism_models-0.7.5 → prism_models-0.7.7}/prism_models.egg-info/SOURCES.txt +3 -1
- {prism_models-0.7.5 → prism_models-0.7.7}/pyproject.toml +1 -1
- {prism_models-0.7.5 → prism_models-0.7.7}/README.md +0 -0
- {prism_models-0.7.5 → prism_models-0.7.7}/prism_models/agent_profile.py +0 -0
- {prism_models-0.7.5 → prism_models-0.7.7}/prism_models/base.py +0 -0
- {prism_models-0.7.5 → prism_models-0.7.7}/prism_models/chat.py +0 -0
- {prism_models-0.7.5 → prism_models-0.7.7}/prism_models/content.py +0 -0
- {prism_models-0.7.5 → prism_models-0.7.7}/prism_models/feedback.py +0 -0
- {prism_models-0.7.5 → prism_models-0.7.7}/prism_models/migration/README +0 -0
- {prism_models-0.7.5 → prism_models-0.7.7}/prism_models/migration/__init__.py +0 -0
- {prism_models-0.7.5 → prism_models-0.7.7}/prism_models/migration/env.py +0 -0
- {prism_models-0.7.5 → prism_models-0.7.7}/prism_models/migration/script.py.mako +0 -0
- {prism_models-0.7.5 → prism_models-0.7.7}/prism_models/migration/versions/2025_09_11_1516_161f8829d93f_initial_schema.py +0 -0
- {prism_models-0.7.5 → prism_models-0.7.7}/prism_models/migration/versions/2025_09_11_1558_5e011849ea76_changes_for_feedback.py +0 -0
- {prism_models-0.7.5 → prism_models-0.7.7}/prism_models/migration/versions/2025_09_14_2243_059af231c2b2_profile_entities.py +0 -0
- {prism_models-0.7.5 → prism_models-0.7.7}/prism_models/migration/versions/2025_09_15_1646_3219fec0bb10_agent_changes.py +0 -0
- {prism_models-0.7.5 → prism_models-0.7.7}/prism_models/migration/versions/2025_09_16_1627_f2013b08daac_rename_metadata_to_additional_data.py +0 -0
- {prism_models-0.7.5 → prism_models-0.7.7}/prism_models/migration/versions/2025_09_17_1147_327febbf555f_display_name_added.py +0 -0
- {prism_models-0.7.5 → prism_models-0.7.7}/prism_models/migration/versions/2025_09_18_1106_b0bcb7ca1dc9_add_support_for_profile_on_create_convo.py +0 -0
- {prism_models-0.7.5 → prism_models-0.7.7}/prism_models/migration/versions/2025_09_18_1511_bbc1955191e6_preview_mode.py +0 -0
- {prism_models-0.7.5 → prism_models-0.7.7}/prism_models/migration/versions/2025_09_26_1115_6eb70e848451_added_publish_status_to_document.py +0 -0
- {prism_models-0.7.5 → prism_models-0.7.7}/prism_models/migration/versions/2025_09_26_1240_f8b0ea2e743c_drop_unique_title_version_on_document.py +0 -0
- {prism_models-0.7.5 → prism_models-0.7.7}/prism_models/migration/versions/2025_09_26_1505_07dc8c2589e0_added_chunk_id_and_vector_embeddings_to_.py +0 -0
- {prism_models-0.7.5 → prism_models-0.7.7}/prism_models/migration/versions/2025_09_29_1220_46ba2693b883_add_markdown_markdown_file_path_s3_.py +0 -0
- {prism_models-0.7.5 → prism_models-0.7.7}/prism_models/migration/versions/2025_10_02_1520_bf1472a9b021_removed_doc_id_from_config_table_and_.py +0 -0
- {prism_models-0.7.5 → prism_models-0.7.7}/prism_models/migration/versions/2025_10_02_1525_6c0e63e0fef8_removed_doc_id_from_config_table_.py +0 -0
- {prism_models-0.7.5 → prism_models-0.7.7}/prism_models/migration/versions/2025_10_02_1608_1b3eb48f5017_config_id_on_delete_will_be_set_to_null.py +0 -0
- {prism_models-0.7.5 → prism_models-0.7.7}/prism_models/migration/versions/2025_10_03_1109_ac85b606d8a4_added_docling_hybrid_to_chunkstrategy.py +0 -0
- {prism_models-0.7.5 → prism_models-0.7.7}/prism_models/migration/versions/2025_10_03_1204_7d1cb343a63f_added_s3_bucket_and_s3_dir_in_source_.py +0 -0
- {prism_models-0.7.5 → prism_models-0.7.7}/prism_models/migration/versions/2025_10_03_1452_f9c750ec2a0b_1_to_1_relationship_between_collection_.py +0 -0
- {prism_models-0.7.5 → prism_models-0.7.7}/prism_models/migration/versions/2025_10_07_1722_5cfa0c462948_added_travel_advisory_enum.py +0 -0
- {prism_models-0.7.5 → prism_models-0.7.7}/prism_models/migration/versions/2025_10_08_1304_c91eb8e38cc7_added_destination_report_and_event_.py +0 -0
- {prism_models-0.7.5 → prism_models-0.7.7}/prism_models/migration/versions/2025_10_09_1308_796b720ea35f_added_qa_id_to_vovetor.py +0 -0
- {prism_models-0.7.5 → prism_models-0.7.7}/prism_models/migration/versions/2025_10_16_1611_663c66268631_added_sharepoint_drive_item_id_as_an_.py +0 -0
- {prism_models-0.7.5 → prism_models-0.7.7}/prism_models/migration/versions/2025_10_23_1228_919d07a93f83_added_sharepoint_directory_in_source.py +0 -0
- {prism_models-0.7.5 → prism_models-0.7.7}/prism_models/migration/versions/2025_10_27_1331_ff3be2c4311f_added_agentcollectionaccess_table.py +0 -0
- {prism_models-0.7.5 → prism_models-0.7.7}/prism_models/migration/versions/2025_10_28_1333_b789e61df26a_ad_enum_and_field_added.py +0 -0
- {prism_models-0.7.5 → prism_models-0.7.7}/prism_models/migration/versions/2025_11_03_1633_cc27b20a106e_removed_collection_from_source_.py +0 -0
- {prism_models-0.7.5 → prism_models-0.7.7}/prism_models/migration/versions/2025_11_03_1653_bf5c2ce928e3_removed_collection_from_source_.py +0 -0
- {prism_models-0.7.5 → prism_models-0.7.7}/prism_models/migration/versions/2025_11_03_1738_b733b48d78b5_remove_source.py +0 -0
- {prism_models-0.7.5 → prism_models-0.7.7}/prism_models/migration/versions/2025_11_06_1450_8a7e56260eba_added_collection_ids_sync_enabled_and_.py +0 -0
- {prism_models-0.7.5 → prism_models-0.7.7}/prism_models/migration/versions/2025_11_06_1626_4c4148ac2d21_removed_qna_related_columns_from_.py +0 -0
- {prism_models-0.7.5 → prism_models-0.7.7}/prism_models/migration/versions/2025_11_06_1648_b228888a01ee_added_chunk_config_to_source.py +0 -0
- {prism_models-0.7.5 → prism_models-0.7.7}/prism_models/migration/versions/2025_11_06_1656_7d0982bae4db_added_chunk_config_to_source_again_last_.py +0 -0
- {prism_models-0.7.5 → prism_models-0.7.7}/prism_models/migration/versions/2025_11_12_1727_b9663951b5c1_removed_un_used_fields_from_feedback.py +0 -0
- {prism_models-0.7.5 → prism_models-0.7.7}/prism_models/migration/versions/2025_11_12_1749_3d37806383a2_simplified_feedback.py +0 -0
- {prism_models-0.7.5 → prism_models-0.7.7}/prism_models/migration/versions/2025_11_12_1838_8c3c862b565e_removed_is_upvoted_from_message.py +0 -0
- {prism_models-0.7.5 → prism_models-0.7.7}/prism_models/migration/versions/2025_11_12_1841_54a2f12a1573_added_vote_field.py +0 -0
- {prism_models-0.7.5 → prism_models-0.7.7}/prism_models/migration/versions/2025_11_13_1215_fa00286fe6cd_add_type_column_to_chunk_table.py +0 -0
- {prism_models-0.7.5 → prism_models-0.7.7}/prism_models/migration/versions/2025_11_14_1216_2c62317520f9_add_rerieved_and_created_chunks_and_.py +0 -0
- {prism_models-0.7.5 → prism_models-0.7.7}/prism_models/migration/versions/2025_11_17_1259_a3bc4e4c08a0_agent_and_profile_entity_changes.py +0 -0
- {prism_models-0.7.5 → prism_models-0.7.7}/prism_models/migration/versions/2025_11_17_1325_df5b1cdc2c36_agentprofile_contact_entity_changes.py +0 -0
- {prism_models-0.7.5 → prism_models-0.7.7}/prism_models/migration/versions/2025_11_17_1716_ed91cdf82ef3_adding_feedback_enum.py +0 -0
- {prism_models-0.7.5 → prism_models-0.7.7}/prism_models/migration/versions/2025_11_20_0008_80f5954656f0_feedback_chunk_entity_added.py +0 -0
- {prism_models-0.7.5 → prism_models-0.7.7}/prism_models/migration/versions/2025_11_20_0109_58be389d0ed7_feedback_chunks_changes.py +0 -0
- {prism_models-0.7.5 → prism_models-0.7.7}/prism_models/migration/versions/2025_11_20_0158_98eb0c87e65e_feedback_chunk_chnages.py +0 -0
- {prism_models-0.7.5 → prism_models-0.7.7}/prism_models/migration/versions/2025_11_21_1207_bbee88729aae_add_feedback_columns.py +0 -0
- {prism_models-0.7.5 → prism_models-0.7.7}/prism_models/migration/versions/2025_11_21_1625_8c50f64287fe_added_sequence_column_to_chunk_table_.py +0 -0
- {prism_models-0.7.5 → prism_models-0.7.7}/prism_models/migration/versions/2025_11_21_1654_e7ae98ce2121_removed_sync_enabled.py +0 -0
- {prism_models-0.7.5 → prism_models-0.7.7}/prism_models/migration/versions/2025_11_21_1758_24b3f480777b_refactor_feedback_analysis_for_unified_.py +0 -0
- {prism_models-0.7.5 → prism_models-0.7.7}/prism_models/migration/versions/2025_11_21_1801_5a52fb845dc0_refactor_feedback_analysis_for_unified_.py +0 -0
- {prism_models-0.7.5 → prism_models-0.7.7}/prism_models/migration/versions/2025_11_24_1124_dc57fb290621_added_conversation_context_to_.py +0 -0
- {prism_models-0.7.5 → prism_models-0.7.7}/prism_models/migration/versions/2025_11_25_1847_c6c36663acd5_added_user_preferences_table.py +0 -0
- {prism_models-0.7.5 → prism_models-0.7.7}/prism_models/migration/versions/2025_12_07_2236_88f01df468f2_added_sparse_embeddings_field_in_vector_.py +0 -0
- {prism_models-0.7.5 → prism_models-0.7.7}/prism_models/migration/versions/2025_12_09_1146_6abdd3f001ab_adding_orchestrator_model_to_profile_.py +0 -0
- {prism_models-0.7.5 → prism_models-0.7.7}/prism_models/migration/versions/2025_12_17_1220_4d9291a3acb1_removed_qa_pair_chunk.py +0 -0
- {prism_models-0.7.5 → prism_models-0.7.7}/prism_models/migration/versions/2026_01_14_1742_147509a82af8_changed_user_role_enums.py +0 -0
- {prism_models-0.7.5 → prism_models-0.7.7}/prism_models/migration/versions/2026_01_15_1010_3b7d0b2e9f5a_add_contact_profile_table.py +0 -0
- {prism_models-0.7.5 → prism_models-0.7.7}/prism_models/migration/versions/2026_01_26_1648_53d6d0b55f11_feedback_changes.py +0 -0
- {prism_models-0.7.5 → prism_models-0.7.7}/prism_models/qdrant.py +0 -0
- {prism_models-0.7.5 → prism_models-0.7.7}/prism_models.egg-info/dependency_links.txt +0 -0
- {prism_models-0.7.5 → prism_models-0.7.7}/prism_models.egg-info/requires.txt +0 -0
- {prism_models-0.7.5 → prism_models-0.7.7}/prism_models.egg-info/top_level.txt +0 -0
- {prism_models-0.7.5 → prism_models-0.7.7}/setup.cfg +0 -0
|
@@ -17,6 +17,7 @@ from prism_models.chat import Contact, Conversation, ConversationMessage, Conver
|
|
|
17
17
|
from prism_models.content import Chunk, ChunkConfig, Collection, CollectionDocument, Document, IntegrationConfig, Source, Vector
|
|
18
18
|
from prism_models.feedback import Augmentation, Feedback, FeedbackAnalysis, FeedbackStatus, FeedbackType
|
|
19
19
|
from prism_models.qdrant import QdrantVectorPayload, DestinationVectorPayload, PydanticType
|
|
20
|
+
from prism_models.notification import NotificationTarget, NotificationOutbox
|
|
20
21
|
|
|
21
22
|
__all__ = [
|
|
22
23
|
"POSTGRES_NAMING_CONVENTION",
|
|
@@ -51,4 +52,6 @@ __all__ = [
|
|
|
51
52
|
"Source",
|
|
52
53
|
"TimestampMixin",
|
|
53
54
|
"Vector",
|
|
55
|
+
"NotificationTarget",
|
|
56
|
+
"NotificationOutbox",
|
|
54
57
|
]
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
"""add notification tables
|
|
2
|
+
|
|
3
|
+
Revision ID: ed197421f25a
|
|
4
|
+
Revises: 53d6d0b55f11
|
|
5
|
+
Create Date: 2026-01-29 13:12:33.275063
|
|
6
|
+
|
|
7
|
+
"""
|
|
8
|
+
from typing import Sequence, Union
|
|
9
|
+
|
|
10
|
+
from alembic import op
|
|
11
|
+
import sqlalchemy as sa
|
|
12
|
+
from sqlalchemy.dialects import postgresql
|
|
13
|
+
|
|
14
|
+
# revision identifiers, used by Alembic.
|
|
15
|
+
revision: str = 'ed197421f25a'
|
|
16
|
+
down_revision: Union[str, Sequence[str], None] = '53d6d0b55f11'
|
|
17
|
+
branch_labels: Union[str, Sequence[str], None] = None
|
|
18
|
+
depends_on: Union[str, Sequence[str], None] = None
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def upgrade() -> None:
|
|
22
|
+
"""Upgrade schema."""
|
|
23
|
+
# ### commands auto generated by Alembic - please adjust! ###
|
|
24
|
+
op.create_table('notification_target',
|
|
25
|
+
sa.Column('provider', sa.String(length=50), nullable=False),
|
|
26
|
+
sa.Column('target_key', sa.String(length=100), nullable=False),
|
|
27
|
+
sa.Column('display_name', sa.String(length=255), nullable=True),
|
|
28
|
+
sa.Column('webhook_url', sa.Text(), nullable=False),
|
|
29
|
+
sa.Column('is_enabled', sa.Boolean(), nullable=False),
|
|
30
|
+
sa.Column('id', sa.Integer(), nullable=False),
|
|
31
|
+
sa.Column('created_at', sa.DateTime(timezone=True), server_default=sa.text('now()'), nullable=False),
|
|
32
|
+
sa.Column('updated_at', sa.DateTime(timezone=True), server_default=sa.text('now()'), nullable=False),
|
|
33
|
+
sa.Column('deleted_at', sa.DateTime(timezone=True), nullable=True),
|
|
34
|
+
sa.PrimaryKeyConstraint('id', name=op.f('notification_target_pkey')),
|
|
35
|
+
sa.UniqueConstraint('provider', 'target_key', name='uq_notification_target_provider_key')
|
|
36
|
+
)
|
|
37
|
+
op.create_index(op.f('notification_target_id_idx'), 'notification_target', ['id'], unique=False)
|
|
38
|
+
op.create_table('notification_outbox',
|
|
39
|
+
sa.Column('provider', sa.String(length=50), nullable=False),
|
|
40
|
+
sa.Column('target_key', sa.String(length=100), nullable=False),
|
|
41
|
+
sa.Column('event_type', sa.String(length=100), nullable=False),
|
|
42
|
+
sa.Column('severity', sa.String(length=20), nullable=True),
|
|
43
|
+
sa.Column('dedupe_key', sa.String(length=255), nullable=False),
|
|
44
|
+
sa.Column('correlation_id', sa.String(length=100), nullable=True),
|
|
45
|
+
sa.Column('entity_type', sa.String(length=50), nullable=True),
|
|
46
|
+
sa.Column('entity_id', sa.String(length=100), nullable=True),
|
|
47
|
+
sa.Column('payload', postgresql.JSONB(astext_type=sa.Text()), nullable=False),
|
|
48
|
+
sa.Column('status', sa.String(length=20), nullable=False),
|
|
49
|
+
sa.Column('priority', sa.Integer(), nullable=False),
|
|
50
|
+
sa.Column('attempts', sa.Integer(), nullable=False),
|
|
51
|
+
sa.Column('max_attempts', sa.Integer(), nullable=False),
|
|
52
|
+
sa.Column('next_attempt_at', sa.DateTime(timezone=True), server_default=sa.text('now()'), nullable=False),
|
|
53
|
+
sa.Column('last_error', sa.Text(), nullable=True),
|
|
54
|
+
sa.Column('sent_at', sa.DateTime(timezone=True), nullable=True),
|
|
55
|
+
sa.Column('expires_at', sa.DateTime(timezone=True), nullable=True),
|
|
56
|
+
sa.Column('locked_by', sa.String(length=100), nullable=True),
|
|
57
|
+
sa.Column('locked_at', sa.DateTime(timezone=True), nullable=True),
|
|
58
|
+
sa.Column('id', sa.Integer(), nullable=False),
|
|
59
|
+
sa.Column('created_at', sa.DateTime(timezone=True), server_default=sa.text('now()'), nullable=False),
|
|
60
|
+
sa.Column('updated_at', sa.DateTime(timezone=True), server_default=sa.text('now()'), nullable=False),
|
|
61
|
+
sa.Column('deleted_at', sa.DateTime(timezone=True), nullable=True),
|
|
62
|
+
sa.ForeignKeyConstraint(['provider', 'target_key'], ['notification_target.provider', 'notification_target.target_key'], name='fk_notification_outbox_target'),
|
|
63
|
+
sa.PrimaryKeyConstraint('id', name=op.f('notification_outbox_pkey')),
|
|
64
|
+
sa.UniqueConstraint('dedupe_key', name=op.f('notification_outbox_dedupe_key_key'))
|
|
65
|
+
)
|
|
66
|
+
op.create_index('ix_notification_outbox_entity', 'notification_outbox', ['entity_type', 'entity_id'], unique=False)
|
|
67
|
+
op.create_index('ix_notification_outbox_ready', 'notification_outbox', ['status', 'priority', 'next_attempt_at'], unique=False)
|
|
68
|
+
op.create_index(op.f('notification_outbox_correlation_id_idx'), 'notification_outbox', ['correlation_id'], unique=False)
|
|
69
|
+
op.create_index(op.f('notification_outbox_expires_at_idx'), 'notification_outbox', ['expires_at'], unique=False)
|
|
70
|
+
op.create_index(op.f('notification_outbox_id_idx'), 'notification_outbox', ['id'], unique=False)
|
|
71
|
+
op.create_index(op.f('notification_outbox_status_idx'), 'notification_outbox', ['status'], unique=False)
|
|
72
|
+
# ### end Alembic commands ###
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def downgrade() -> None:
|
|
76
|
+
"""Downgrade schema."""
|
|
77
|
+
# ### commands auto generated by Alembic - please adjust! ###
|
|
78
|
+
op.drop_index(op.f('notification_outbox_status_idx'), table_name='notification_outbox')
|
|
79
|
+
op.drop_index(op.f('notification_outbox_id_idx'), table_name='notification_outbox')
|
|
80
|
+
op.drop_index(op.f('notification_outbox_expires_at_idx'), table_name='notification_outbox')
|
|
81
|
+
op.drop_index(op.f('notification_outbox_correlation_id_idx'), table_name='notification_outbox')
|
|
82
|
+
op.drop_index('ix_notification_outbox_ready', table_name='notification_outbox')
|
|
83
|
+
op.drop_index('ix_notification_outbox_entity', table_name='notification_outbox')
|
|
84
|
+
op.drop_table('notification_outbox')
|
|
85
|
+
op.drop_index(op.f('notification_target_id_idx'), table_name='notification_target')
|
|
86
|
+
op.drop_table('notification_target')
|
|
87
|
+
# ### end Alembic commands ###
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
"""Notification models for outbox-based delivery to Teams channels."""
|
|
2
|
+
|
|
3
|
+
import enum
|
|
4
|
+
from datetime import datetime
|
|
5
|
+
from typing import Optional
|
|
6
|
+
|
|
7
|
+
from sqlalchemy import (
|
|
8
|
+
Boolean,
|
|
9
|
+
DateTime,
|
|
10
|
+
ForeignKeyConstraint,
|
|
11
|
+
Index,
|
|
12
|
+
Integer,
|
|
13
|
+
String,
|
|
14
|
+
Text,
|
|
15
|
+
UniqueConstraint,
|
|
16
|
+
func,
|
|
17
|
+
)
|
|
18
|
+
from sqlalchemy.dialects.postgresql import JSONB
|
|
19
|
+
from sqlalchemy.orm import Mapped, mapped_column, relationship
|
|
20
|
+
|
|
21
|
+
from prism_models.base import Base, BaseModel, TimestampMixin
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
# ─────────────────────────────────────────────────────────────────────────────
|
|
25
|
+
# Enums
|
|
26
|
+
# ─────────────────────────────────────────────────────────────────────────────
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class NotificationStatus(str, enum.Enum):
|
|
30
|
+
"""Lifecycle states for outbox notifications."""
|
|
31
|
+
|
|
32
|
+
PENDING = "pending"
|
|
33
|
+
SENDING = "sending"
|
|
34
|
+
SENT = "sent"
|
|
35
|
+
FAILED = "failed"
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
class NotificationSeverity(str, enum.Enum):
|
|
39
|
+
"""Severity levels for notification prioritization and formatting."""
|
|
40
|
+
|
|
41
|
+
INFO = "info"
|
|
42
|
+
WARN = "warn"
|
|
43
|
+
ERROR = "error"
|
|
44
|
+
CRITICAL = "critical"
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
class NotificationProvider(str, enum.Enum):
|
|
48
|
+
"""Supported notification providers (only Teams for now)."""
|
|
49
|
+
|
|
50
|
+
TEAMS = "teams"
|
|
51
|
+
|
|
52
|
+
class NotificationTarget(BaseModel):
|
|
53
|
+
"""
|
|
54
|
+
Webhook destinations for notifications.
|
|
55
|
+
|
|
56
|
+
Each target represents a Teams channel webhook.
|
|
57
|
+
Multiple event types can route to the same target.
|
|
58
|
+
"""
|
|
59
|
+
|
|
60
|
+
__tablename__ = "notification_target"
|
|
61
|
+
|
|
62
|
+
provider: Mapped[str] = mapped_column(String(50), nullable=False)
|
|
63
|
+
target_key: Mapped[str] = mapped_column(String(100), nullable=False)
|
|
64
|
+
display_name: Mapped[str | None] = mapped_column(String(255), nullable=True)
|
|
65
|
+
webhook_url: Mapped[str] = mapped_column(Text, nullable=False)
|
|
66
|
+
is_enabled: Mapped[bool] = mapped_column(Boolean, default=True, nullable=False)
|
|
67
|
+
|
|
68
|
+
outbox_entries: Mapped[list["NotificationOutbox"]] = relationship(
|
|
69
|
+
back_populates="target",
|
|
70
|
+
foreign_keys="[NotificationOutbox.provider, NotificationOutbox.target_key]",
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
__table_args__ = (
|
|
74
|
+
UniqueConstraint("provider", "target_key", name="uq_notification_target_provider_key"),
|
|
75
|
+
)
|
|
76
|
+
|
|
77
|
+
def __repr__(self):
|
|
78
|
+
return f"<NotificationTarget(id={self.id}, provider='{self.provider}', key='{self.target_key}')>"
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
class NotificationOutbox(BaseModel):
|
|
82
|
+
"""
|
|
83
|
+
Transactional outbox for reliable notification delivery.
|
|
84
|
+
|
|
85
|
+
Design notes:
|
|
86
|
+
- dedupe_key: Prevents duplicate sends at insert time
|
|
87
|
+
- entity_type + entity_id: Polymorphic reference to source entity
|
|
88
|
+
- locked_by + locked_at: Enables safe multi-worker polling
|
|
89
|
+
- priority: Critical alerts jump ahead of digests
|
|
90
|
+
- max_attempts: Per-notification retry limit
|
|
91
|
+
- expires_at: Auto-expire stale notifications
|
|
92
|
+
"""
|
|
93
|
+
|
|
94
|
+
__tablename__ = "notification_outbox"
|
|
95
|
+
|
|
96
|
+
provider: Mapped[str] = mapped_column(String(50), nullable=False)
|
|
97
|
+
target_key: Mapped[str] = mapped_column(String(100), nullable=False)
|
|
98
|
+
event_type: Mapped[str] = mapped_column(String(100), nullable=False)
|
|
99
|
+
severity: Mapped[str | None] = mapped_column(String(20), nullable=True)
|
|
100
|
+
|
|
101
|
+
dedupe_key: Mapped[str] = mapped_column(String(255), nullable=False, unique=True)
|
|
102
|
+
correlation_id: Mapped[str | None] = mapped_column(String(100), nullable=True, index=True)
|
|
103
|
+
|
|
104
|
+
entity_type: Mapped[str | None] = mapped_column(String(50), nullable=True)
|
|
105
|
+
entity_id: Mapped[str | None] = mapped_column(String(100), nullable=True)
|
|
106
|
+
|
|
107
|
+
payload: Mapped[dict] = mapped_column(JSONB, nullable=False)
|
|
108
|
+
|
|
109
|
+
status: Mapped[str] = mapped_column(
|
|
110
|
+
String(20), default=NotificationStatus.PENDING.value, nullable=False, index=True
|
|
111
|
+
)
|
|
112
|
+
priority: Mapped[int] = mapped_column(Integer, default=0, nullable=False)
|
|
113
|
+
attempts: Mapped[int] = mapped_column(Integer, default=0, nullable=False)
|
|
114
|
+
max_attempts: Mapped[int] = mapped_column(Integer, default=5, nullable=False)
|
|
115
|
+
next_attempt_at: Mapped[datetime] = mapped_column(
|
|
116
|
+
DateTime(timezone=True), server_default=func.now(), nullable=False
|
|
117
|
+
)
|
|
118
|
+
last_error: Mapped[str | None] = mapped_column(Text, nullable=True)
|
|
119
|
+
sent_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True)
|
|
120
|
+
|
|
121
|
+
expires_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True, index=True)
|
|
122
|
+
|
|
123
|
+
locked_by: Mapped[str | None] = mapped_column(String(100), nullable=True)
|
|
124
|
+
locked_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True)
|
|
125
|
+
|
|
126
|
+
target: Mapped[Optional["NotificationTarget"]] = relationship(
|
|
127
|
+
back_populates="outbox_entries",
|
|
128
|
+
foreign_keys=[provider, target_key],
|
|
129
|
+
)
|
|
130
|
+
|
|
131
|
+
__table_args__ = (
|
|
132
|
+
ForeignKeyConstraint(
|
|
133
|
+
["provider", "target_key"],
|
|
134
|
+
["notification_target.provider", "notification_target.target_key"],
|
|
135
|
+
name="fk_notification_outbox_target",
|
|
136
|
+
),
|
|
137
|
+
Index("ix_notification_outbox_ready", "status", "priority", "next_attempt_at"),
|
|
138
|
+
Index("ix_notification_outbox_entity", "entity_type", "entity_id"),
|
|
139
|
+
)
|
|
140
|
+
|
|
141
|
+
def __repr__(self):
|
|
142
|
+
return f"<NotificationOutbox(id={self.id}, event='{self.event_type}', status='{self.status}')>"
|
|
@@ -7,6 +7,7 @@ prism_models/chat.py
|
|
|
7
7
|
prism_models/config.py
|
|
8
8
|
prism_models/content.py
|
|
9
9
|
prism_models/feedback.py
|
|
10
|
+
prism_models/notification.py
|
|
10
11
|
prism_models/qdrant.py
|
|
11
12
|
prism_models.egg-info/PKG-INFO
|
|
12
13
|
prism_models.egg-info/SOURCES.txt
|
|
@@ -73,4 +74,5 @@ prism_models/migration/versions/2025_12_09_1146_6abdd3f001ab_adding_orchestrator
|
|
|
73
74
|
prism_models/migration/versions/2025_12_17_1220_4d9291a3acb1_removed_qa_pair_chunk.py
|
|
74
75
|
prism_models/migration/versions/2026_01_14_1742_147509a82af8_changed_user_role_enums.py
|
|
75
76
|
prism_models/migration/versions/2026_01_15_1010_3b7d0b2e9f5a_add_contact_profile_table.py
|
|
76
|
-
prism_models/migration/versions/2026_01_26_1648_53d6d0b55f11_feedback_changes.py
|
|
77
|
+
prism_models/migration/versions/2026_01_26_1648_53d6d0b55f11_feedback_changes.py
|
|
78
|
+
prism_models/migration/versions/2026_01_29_1312_ed197421f25a_add_notification_tables.py
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|