Implement AI workspace and mitigation workflow
This commit is contained in:
@@ -1,7 +1,8 @@
|
||||
# Generated by Django 5.1.5 on 2025-02-06 21:57
|
||||
|
||||
import django.db.models.deletion
|
||||
import uuid
|
||||
|
||||
import django.db.models.deletion
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
# Generated by Django 5.1.5 on 2025-02-07 12:05
|
||||
|
||||
import django.db.models.deletion
|
||||
import uuid
|
||||
|
||||
import django.db.models.deletion
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
# Generated by Django 5.1.5 on 2025-02-07 13:56
|
||||
|
||||
import django.db.models.deletion
|
||||
import uuid
|
||||
|
||||
import django.db.models.deletion
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
# Generated by Django 5.1.5 on 2025-02-08 16:07
|
||||
|
||||
import django.db.models.deletion
|
||||
import uuid
|
||||
|
||||
import django.db.models.deletion
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
@@ -0,0 +1,95 @@
|
||||
# Generated by Django 5.2.11 on 2026-02-14 22:52
|
||||
|
||||
import django.db.models.deletion
|
||||
import uuid
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('core', '0015_manipulation_filter_enabled_alter_manipulation_mode'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='AIRequest',
|
||||
fields=[
|
||||
('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)),
|
||||
('window_spec', models.JSONField(default=dict)),
|
||||
('message_ids', models.JSONField(blank=True, default=list)),
|
||||
('user_notes', models.TextField(blank=True, default='')),
|
||||
('operation', models.CharField(choices=[('summarise', 'Summarise'), ('draft_reply', 'Draft Reply'), ('critique', 'Critique'), ('repair', 'Repair'), ('extract_patterns', 'Extract Patterns'), ('memory_propose', 'Memory Propose')], max_length=32)),
|
||||
('policy_snapshot', models.JSONField(blank=True, default=dict)),
|
||||
('status', models.CharField(choices=[('queued', 'Queued'), ('running', 'Running'), ('done', 'Done'), ('failed', 'Failed')], default='queued', max_length=16)),
|
||||
('error', models.TextField(blank=True, default='')),
|
||||
('created_at', models.DateTimeField(auto_now_add=True)),
|
||||
('started_at', models.DateTimeField(blank=True, null=True)),
|
||||
('finished_at', models.DateTimeField(blank=True, null=True)),
|
||||
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
|
||||
],
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='AIResult',
|
||||
fields=[
|
||||
('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)),
|
||||
('summary_m3', models.TextField(blank=True, default='')),
|
||||
('draft_replies', models.JSONField(blank=True, default=list)),
|
||||
('risk_flags', models.JSONField(blank=True, default=list)),
|
||||
('memory_proposals', models.JSONField(blank=True, default=list)),
|
||||
('citations', models.JSONField(blank=True, default=list)),
|
||||
('created_at', models.DateTimeField(auto_now_add=True)),
|
||||
('ai_request', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='result', to='core.airequest')),
|
||||
],
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='WorkspaceConversation',
|
||||
fields=[
|
||||
('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)),
|
||||
('title', models.CharField(blank=True, default='', max_length=255)),
|
||||
('platform_type', models.CharField(choices=[('signal', 'Signal'), ('instagram', 'Instagram')], default='signal', max_length=255)),
|
||||
('platform_thread_id', models.CharField(blank=True, default='', max_length=255)),
|
||||
('last_event_ts', models.BigIntegerField(blank=True, null=True)),
|
||||
('created_at', models.DateTimeField(auto_now_add=True)),
|
||||
('participants', models.ManyToManyField(blank=True, related_name='workspace_conversations', to='core.person')),
|
||||
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='workspace_conversations', to=settings.AUTH_USER_MODEL)),
|
||||
],
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='MessageEvent',
|
||||
fields=[
|
||||
('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)),
|
||||
('ts', models.BigIntegerField(db_index=True)),
|
||||
('direction', models.CharField(choices=[('in', 'Inbound'), ('out', 'Outbound')], max_length=8)),
|
||||
('sender_uuid', models.CharField(blank=True, db_index=True, default='', max_length=255)),
|
||||
('text', models.TextField(blank=True, default='')),
|
||||
('attachments', models.JSONField(blank=True, default=list)),
|
||||
('raw_payload_ref', models.JSONField(blank=True, default=dict)),
|
||||
('created_at', models.DateTimeField(auto_now_add=True)),
|
||||
('conversation', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='events', to='core.workspaceconversation')),
|
||||
],
|
||||
options={
|
||||
'ordering': ['ts'],
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='MemoryItem',
|
||||
fields=[
|
||||
('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)),
|
||||
('type', models.CharField(choices=[('M1', 'Durable Fact/Preference'), ('M2', 'Relationship State'), ('M3', 'Conversation Working Summary')], max_length=2)),
|
||||
('status', models.CharField(choices=[('proposed', 'Proposed'), ('active', 'Active'), ('deprecated', 'Deprecated')], default='proposed', max_length=16)),
|
||||
('content', models.JSONField(blank=True, default=dict)),
|
||||
('created_at', models.DateTimeField(auto_now_add=True)),
|
||||
('updated_at', models.DateTimeField(auto_now=True)),
|
||||
('source_request', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='core.airequest')),
|
||||
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
|
||||
('conversation', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='memory_items', to='core.workspaceconversation')),
|
||||
],
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='airequest',
|
||||
name='conversation',
|
||||
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='ai_requests', to='core.workspaceconversation'),
|
||||
),
|
||||
]
|
||||
359
core/migrations/0017_remove_airesult_risk_flags_and_more.py
Normal file
359
core/migrations/0017_remove_airesult_risk_flags_and_more.py
Normal file
@@ -0,0 +1,359 @@
|
||||
# Generated by Django 5.2.11 on 2026-02-15 00:14
|
||||
|
||||
import core.models
|
||||
import django.db.models.deletion
|
||||
import uuid
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('core', '0016_airequest_airesult_workspaceconversation_and_more'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RemoveField(
|
||||
model_name='airesult',
|
||||
name='risk_flags',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='airesult',
|
||||
name='summary_m3',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='memoryitem',
|
||||
name='type',
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='airesult',
|
||||
name='interaction_signals',
|
||||
field=models.JSONField(blank=True, default=list, help_text="Structured positive/neutral/risk signals inferred for this run. Example item: {'label':'repair_attempt','valence':'positive','message_event_ids':[...]}."),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='airesult',
|
||||
name='user',
|
||||
field=models.ForeignKey(default=core.models.get_default_workspace_user_pk, help_text='Owner of this AI result row (required for restricted CRUD filtering).', on_delete=django.db.models.deletion.CASCADE, related_name='workspace_ai_results', to=settings.AUTH_USER_MODEL),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='airesult',
|
||||
name='working_summary',
|
||||
field=models.TextField(blank=True, default='', help_text='Conversation working summary generated for this run.'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='memoryitem',
|
||||
name='memory_kind',
|
||||
field=models.CharField(choices=[('fact', 'Durable Fact/Preference'), ('state', 'Relationship State'), ('summary', 'Conversation Working Summary')], default=1, help_text='Memory kind: fact/state/summary.', max_length=16),
|
||||
preserve_default=False,
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='messageevent',
|
||||
name='source_system',
|
||||
field=models.CharField(choices=[('signal', 'Signal'), ('xmpp', 'XMPP'), ('workspace', 'Workspace'), ('ai', 'AI')], default='signal', help_text='System that produced this event record.', max_length=32),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='messageevent',
|
||||
name='user',
|
||||
field=models.ForeignKey(default=core.models.get_default_workspace_user_pk, help_text='Owner of this message event row (required for restricted CRUD filtering).', on_delete=django.db.models.deletion.CASCADE, related_name='workspace_message_events', to=settings.AUTH_USER_MODEL),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='workspaceconversation',
|
||||
name='commitment_confidence',
|
||||
field=models.FloatField(default=0.0, help_text='Confidence in commitment scores (0.0-1.0).'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='workspaceconversation',
|
||||
name='commitment_inbound_score',
|
||||
field=models.FloatField(blank=True, help_text='Estimated commitment score for counterpart -> user direction (0-100). Null while calibrating.', null=True),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='workspaceconversation',
|
||||
name='commitment_last_computed_at',
|
||||
field=models.DateTimeField(blank=True, help_text='Timestamp of the latest commitment computation.', null=True),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='workspaceconversation',
|
||||
name='commitment_outbound_score',
|
||||
field=models.FloatField(blank=True, help_text='Estimated commitment score for user -> counterpart direction (0-100). Null while calibrating.', null=True),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='workspaceconversation',
|
||||
name='last_ai_run_at',
|
||||
field=models.DateTimeField(blank=True, help_text='Last time any AIRequest finished for this conversation.', null=True),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='workspaceconversation',
|
||||
name='participant_feedback',
|
||||
field=models.JSONField(blank=True, default=dict, help_text="Per-person interaction feedback map keyed by person UUID. Example: {'<person_uuid>': {'state': 'withdrawing', 'note': 'short replies'}}."),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='workspaceconversation',
|
||||
name='stability_confidence',
|
||||
field=models.FloatField(default=0.0, help_text='Confidence in stability_score (0.0-1.0).'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='workspaceconversation',
|
||||
name='stability_last_computed_at',
|
||||
field=models.DateTimeField(blank=True, help_text='Timestamp of the latest stability computation.', null=True),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='workspaceconversation',
|
||||
name='stability_sample_days',
|
||||
field=models.PositiveIntegerField(default=0, help_text='How many calendar days of data were used for stability.'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='workspaceconversation',
|
||||
name='stability_sample_messages',
|
||||
field=models.PositiveIntegerField(default=0, help_text='How many messages were used to compute stability.'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='workspaceconversation',
|
||||
name='stability_score',
|
||||
field=models.FloatField(blank=True, help_text='Relationship stability score (0-100). Null while calibrating.', null=True),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='workspaceconversation',
|
||||
name='stability_state',
|
||||
field=models.CharField(choices=[('calibrating', 'Calibrating'), ('stable', 'Stable'), ('watch', 'Watch'), ('fragile', 'Fragile')], default='calibrating', help_text='UI label for relationship stability, baseline-aware.', max_length=32),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='airequest',
|
||||
name='conversation',
|
||||
field=models.ForeignKey(help_text='Conversation analyzed by this request.', on_delete=django.db.models.deletion.CASCADE, related_name='ai_requests', to='core.workspaceconversation'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='airequest',
|
||||
name='created_at',
|
||||
field=models.DateTimeField(auto_now_add=True, help_text='Request creation timestamp.'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='airequest',
|
||||
name='error',
|
||||
field=models.TextField(blank=True, default='', help_text="Error details when status='failed'."),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='airequest',
|
||||
name='finished_at',
|
||||
field=models.DateTimeField(blank=True, help_text='Worker completion timestamp.', null=True),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='airequest',
|
||||
name='id',
|
||||
field=models.UUIDField(default=uuid.uuid4, editable=False, help_text='Stable identifier for this AI request.', primary_key=True, serialize=False),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='airequest',
|
||||
name='message_ids',
|
||||
field=models.JSONField(blank=True, default=list, help_text='Resolved ordered MessageEvent IDs included in this run.'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='airequest',
|
||||
name='operation',
|
||||
field=models.CharField(choices=[('summarise', 'Summarise'), ('draft_reply', 'Draft Reply'), ('critique', 'Critique'), ('repair', 'Repair'), ('extract_patterns', 'Extract Patterns'), ('memory_propose', 'Memory Propose'), ('state_detect', 'State Detect'), ('rewrite_style', 'Rewrite Style'), ('send_readiness', 'Send Readiness'), ('timeline_brief', 'Timeline Brief')], help_text='Requested AI operation type.', max_length=32),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='airequest',
|
||||
name='policy_snapshot',
|
||||
field=models.JSONField(blank=True, default=dict, help_text='Effective manipulation/policy values captured at request time, so results remain auditable even if policies change later.'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='airequest',
|
||||
name='started_at',
|
||||
field=models.DateTimeField(blank=True, help_text='Worker start timestamp.', null=True),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='airequest',
|
||||
name='status',
|
||||
field=models.CharField(choices=[('queued', 'Queued'), ('running', 'Running'), ('done', 'Done'), ('failed', 'Failed')], default='queued', help_text='Worker lifecycle state for this request.', max_length=16),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='airequest',
|
||||
name='user',
|
||||
field=models.ForeignKey(help_text='User who initiated this request.', on_delete=django.db.models.deletion.CASCADE, related_name='workspace_ai_requests', to=settings.AUTH_USER_MODEL),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='airequest',
|
||||
name='user_notes',
|
||||
field=models.TextField(blank=True, default='', help_text='Optional user intent/context notes injected into the prompt.'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='airequest',
|
||||
name='window_spec',
|
||||
field=models.JSONField(default=dict, help_text='Selection spec (last_n/since_ts/between_ts/include_attachments/etc). Should be dynamically resolved by available context/token budget.'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='airesult',
|
||||
name='ai_request',
|
||||
field=models.OneToOneField(help_text='Owning AI request for this result.', on_delete=django.db.models.deletion.CASCADE, related_name='result', to='core.airequest'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='airesult',
|
||||
name='citations',
|
||||
field=models.JSONField(blank=True, default=list, help_text='Referenced MessageEvent IDs supporting generated claims.'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='airesult',
|
||||
name='created_at',
|
||||
field=models.DateTimeField(auto_now_add=True, help_text='Result creation timestamp.'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='airesult',
|
||||
name='draft_replies',
|
||||
field=models.JSONField(blank=True, default=list, help_text='Draft reply candidates, typically with tone and rationale.'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='airesult',
|
||||
name='id',
|
||||
field=models.UUIDField(default=uuid.uuid4, editable=False, help_text='Stable identifier for this AI result payload.', primary_key=True, serialize=False),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='airesult',
|
||||
name='memory_proposals',
|
||||
field=models.JSONField(blank=True, default=list, help_text='Proposed memory entries, typically requiring user approval.'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='memoryitem',
|
||||
name='content',
|
||||
field=models.JSONField(blank=True, default=dict, help_text='Structured memory payload (schema can evolve by type).'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='memoryitem',
|
||||
name='conversation',
|
||||
field=models.ForeignKey(help_text='Conversation scope this memory item belongs to.', on_delete=django.db.models.deletion.CASCADE, related_name='memory_items', to='core.workspaceconversation'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='memoryitem',
|
||||
name='created_at',
|
||||
field=models.DateTimeField(auto_now_add=True, help_text='Row creation timestamp.'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='memoryitem',
|
||||
name='id',
|
||||
field=models.UUIDField(default=uuid.uuid4, editable=False, help_text='Stable identifier for this memory item.', primary_key=True, serialize=False),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='memoryitem',
|
||||
name='source_request',
|
||||
field=models.ForeignKey(blank=True, help_text='AIRequest that originated this memory, if any.', null=True, on_delete=django.db.models.deletion.SET_NULL, to='core.airequest'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='memoryitem',
|
||||
name='status',
|
||||
field=models.CharField(choices=[('proposed', 'Proposed'), ('active', 'Active'), ('deprecated', 'Deprecated')], default='proposed', help_text='Lifecycle state, especially for approval-gated memories.', max_length=16),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='memoryitem',
|
||||
name='updated_at',
|
||||
field=models.DateTimeField(auto_now=True, help_text='Last update timestamp.'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='memoryitem',
|
||||
name='user',
|
||||
field=models.ForeignKey(help_text='Owner of the memory item.', on_delete=django.db.models.deletion.CASCADE, related_name='workspace_memory_items', to=settings.AUTH_USER_MODEL),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='messageevent',
|
||||
name='attachments',
|
||||
field=models.JSONField(blank=True, default=list, help_text='Attachment metadata list associated with this message.'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='messageevent',
|
||||
name='conversation',
|
||||
field=models.ForeignKey(help_text='AI workspace conversation this message belongs to. This is not the transport-native thread object.', on_delete=django.db.models.deletion.CASCADE, related_name='events', to='core.workspaceconversation'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='messageevent',
|
||||
name='created_at',
|
||||
field=models.DateTimeField(auto_now_add=True, help_text='Row creation timestamp.'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='messageevent',
|
||||
name='direction',
|
||||
field=models.CharField(choices=[('in', 'Inbound'), ('out', 'Outbound')], help_text="Direction relative to workspace owner: 'in' from counterpart(s), 'out' from user/bot side.", max_length=8),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='messageevent',
|
||||
name='id',
|
||||
field=models.UUIDField(default=uuid.uuid4, editable=False, help_text='Stable identifier for this message event.', primary_key=True, serialize=False),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='messageevent',
|
||||
name='raw_payload_ref',
|
||||
field=models.JSONField(blank=True, default=dict, help_text='Raw source payload or reference pointer for audit/debug.'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='messageevent',
|
||||
name='sender_uuid',
|
||||
field=models.CharField(blank=True, db_index=True, default='', help_text='Source sender UUID/identifier for correlation.', max_length=255),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='messageevent',
|
||||
name='text',
|
||||
field=models.TextField(blank=True, default='', help_text='Normalized message text body.'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='messageevent',
|
||||
name='ts',
|
||||
field=models.BigIntegerField(db_index=True, help_text='Event timestamp (unix ms) as reported by source_system.'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='personidentifier',
|
||||
name='service',
|
||||
field=models.CharField(choices=[('signal', 'Signal'), ('xmpp', 'XMPP'), ('instagram', 'Instagram')], max_length=255),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='workspaceconversation',
|
||||
name='created_at',
|
||||
field=models.DateTimeField(auto_now_add=True, help_text='Row creation timestamp.'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='workspaceconversation',
|
||||
name='id',
|
||||
field=models.UUIDField(default=uuid.uuid4, editable=False, help_text='Stable identifier for this workspace conversation.', primary_key=True, serialize=False),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='workspaceconversation',
|
||||
name='last_event_ts',
|
||||
field=models.BigIntegerField(blank=True, help_text='Latest message timestamp (unix ms) currently known.', null=True),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='workspaceconversation',
|
||||
name='participants',
|
||||
field=models.ManyToManyField(blank=True, help_text='Resolved people participating in this conversation.', related_name='workspace_conversations', to='core.person'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='workspaceconversation',
|
||||
name='platform_thread_id',
|
||||
field=models.CharField(blank=True, default='', help_text='Platform-native thread/group identifier when available.', max_length=255),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='workspaceconversation',
|
||||
name='platform_type',
|
||||
field=models.CharField(choices=[('signal', 'Signal'), ('xmpp', 'XMPP'), ('instagram', 'Instagram')], default='signal', help_text='Primary transport for this conversation (reuses SERVICE_CHOICES).', max_length=255),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='workspaceconversation',
|
||||
name='title',
|
||||
field=models.CharField(blank=True, default='', help_text='Human-friendly label shown in the workspace sidebar.', max_length=255),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='workspaceconversation',
|
||||
name='user',
|
||||
field=models.ForeignKey(help_text='Owner of this conversation workspace.', on_delete=django.db.models.deletion.CASCADE, related_name='workspace_conversations', to=settings.AUTH_USER_MODEL),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='AIResultSignal',
|
||||
fields=[
|
||||
('id', models.UUIDField(default=uuid.uuid4, editable=False, help_text='Stable identifier for this result signal.', primary_key=True, serialize=False)),
|
||||
('label', models.CharField(help_text="Short signal label, e.g. 'withdrawing', 'repair_attempt'.", max_length=128)),
|
||||
('valence', models.CharField(choices=[('positive', 'Positive'), ('neutral', 'Neutral'), ('risk', 'Risk')], default='neutral', help_text='Signal polarity: positive, neutral, or risk.', max_length=16)),
|
||||
('score', models.FloatField(blank=True, help_text='Optional model confidence/strength (0.0-1.0).', null=True)),
|
||||
('rationale', models.TextField(blank=True, default='', help_text='Human-readable explanation for this signal.')),
|
||||
('created_at', models.DateTimeField(auto_now_add=True, help_text='Row creation timestamp.')),
|
||||
('ai_result', models.ForeignKey(help_text='AI result that produced this signal.', on_delete=django.db.models.deletion.CASCADE, related_name='signals', to='core.airesult')),
|
||||
('message_event', models.ForeignKey(blank=True, help_text='Optional specific message event referenced by this signal.', null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='ai_signals', to='core.messageevent')),
|
||||
('user', models.ForeignKey(help_text='Owner of this signal row (required for restricted CRUD filtering).', on_delete=django.db.models.deletion.CASCADE, related_name='workspace_ai_result_signals', to=settings.AUTH_USER_MODEL)),
|
||||
],
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,84 @@
|
||||
# Generated by Django 5.2.11 on 2026-02-15 00:58
|
||||
|
||||
import django.db.models.deletion
|
||||
import uuid
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('core', '0017_remove_airesult_risk_flags_and_more'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='PatternMitigationPlan',
|
||||
fields=[
|
||||
('id', models.UUIDField(default=uuid.uuid4, editable=False, help_text='Stable identifier for this mitigation plan.', primary_key=True, serialize=False)),
|
||||
('title', models.CharField(blank=True, default='', help_text='Display title for this plan.', max_length=255)),
|
||||
('objective', models.TextField(blank=True, default='', help_text='High-level objective this plan is meant to achieve.')),
|
||||
('fundamental_items', models.JSONField(blank=True, default=list, help_text='Foundational agreed items/principles for this plan.')),
|
||||
('creation_mode', models.CharField(choices=[('auto', 'Auto'), ('guided', 'Guided')], default='auto', help_text='Whether plan artifacts were generated automatically or user-guided.', max_length=16)),
|
||||
('status', models.CharField(choices=[('draft', 'Draft'), ('active', 'Active'), ('archived', 'Archived')], default='draft', help_text='Lifecycle status of the plan.', max_length=16)),
|
||||
('created_at', models.DateTimeField(auto_now_add=True, help_text='Row creation timestamp.')),
|
||||
('updated_at', models.DateTimeField(auto_now=True, help_text='Last update timestamp.')),
|
||||
('conversation', models.ForeignKey(help_text='Workspace conversation this plan belongs to.', on_delete=django.db.models.deletion.CASCADE, related_name='mitigation_plans', to='core.workspaceconversation')),
|
||||
('source_ai_result', models.ForeignKey(blank=True, help_text='AI result that initiated this plan, if any.', null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='mitigation_plans', to='core.airesult')),
|
||||
('user', models.ForeignKey(help_text='Owner of this plan.', on_delete=django.db.models.deletion.CASCADE, related_name='pattern_mitigation_plans', to=settings.AUTH_USER_MODEL)),
|
||||
],
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='PatternMitigationMessage',
|
||||
fields=[
|
||||
('id', models.UUIDField(default=uuid.uuid4, editable=False, help_text='Stable identifier for this mitigation message.', primary_key=True, serialize=False)),
|
||||
('role', models.CharField(choices=[('user', 'User'), ('assistant', 'Assistant'), ('system', 'System')], help_text='Message speaker role.', max_length=16)),
|
||||
('text', models.TextField(help_text='Message content.')),
|
||||
('created_at', models.DateTimeField(auto_now_add=True, help_text='Row creation timestamp.')),
|
||||
('user', models.ForeignKey(help_text='Owner of this message.', on_delete=django.db.models.deletion.CASCADE, related_name='pattern_mitigation_messages', to=settings.AUTH_USER_MODEL)),
|
||||
('plan', models.ForeignKey(help_text='Parent mitigation plan.', on_delete=django.db.models.deletion.CASCADE, related_name='messages', to='core.patternmitigationplan')),
|
||||
],
|
||||
options={
|
||||
'ordering': ['created_at'],
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='PatternMitigationGame',
|
||||
fields=[
|
||||
('id', models.UUIDField(default=uuid.uuid4, editable=False, help_text='Stable identifier for this game.', primary_key=True, serialize=False)),
|
||||
('title', models.CharField(help_text='Game title.', max_length=255)),
|
||||
('instructions', models.TextField(blank=True, default='', help_text='Gameplay/instruction text.')),
|
||||
('enabled', models.BooleanField(default=True, help_text='Whether this game is currently enabled.')),
|
||||
('created_at', models.DateTimeField(auto_now_add=True, help_text='Row creation timestamp.')),
|
||||
('user', models.ForeignKey(help_text='Owner of this game.', on_delete=django.db.models.deletion.CASCADE, related_name='pattern_mitigation_games', to=settings.AUTH_USER_MODEL)),
|
||||
('plan', models.ForeignKey(help_text='Parent mitigation plan.', on_delete=django.db.models.deletion.CASCADE, related_name='games', to='core.patternmitigationplan')),
|
||||
],
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='PatternArtifactExport',
|
||||
fields=[
|
||||
('id', models.UUIDField(default=uuid.uuid4, editable=False, help_text='Stable identifier for this export artifact.', primary_key=True, serialize=False)),
|
||||
('artifact_type', models.CharField(choices=[('rulebook', 'Rulebook'), ('rules', 'Rules'), ('games', 'Games')], help_text='Artifact category being exported.', max_length=32)),
|
||||
('export_format', models.CharField(choices=[('markdown', 'Markdown'), ('json', 'JSON'), ('text', 'Text')], default='markdown', help_text='Serialized output format.', max_length=16)),
|
||||
('protocol_version', models.CharField(default='artifact-v1', help_text='Artifact export protocol version.', max_length=32)),
|
||||
('payload', models.TextField(blank=True, default='', help_text='Serialized artifact body/content.')),
|
||||
('meta', models.JSONField(blank=True, default=dict, help_text='Additional export metadata (counts, hints, source IDs).')),
|
||||
('created_at', models.DateTimeField(auto_now_add=True, help_text='Row creation timestamp.')),
|
||||
('user', models.ForeignKey(help_text='Owner of this export artifact.', on_delete=django.db.models.deletion.CASCADE, related_name='pattern_artifact_exports', to=settings.AUTH_USER_MODEL)),
|
||||
('plan', models.ForeignKey(help_text='Source mitigation plan.', on_delete=django.db.models.deletion.CASCADE, related_name='exports', to='core.patternmitigationplan')),
|
||||
],
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='PatternMitigationRule',
|
||||
fields=[
|
||||
('id', models.UUIDField(default=uuid.uuid4, editable=False, help_text='Stable identifier for this rule.', primary_key=True, serialize=False)),
|
||||
('title', models.CharField(help_text='Rule title.', max_length=255)),
|
||||
('content', models.TextField(blank=True, default='', help_text='Rule definition/details.')),
|
||||
('enabled', models.BooleanField(default=True, help_text='Whether this rule is currently enabled.')),
|
||||
('created_at', models.DateTimeField(auto_now_add=True, help_text='Row creation timestamp.')),
|
||||
('plan', models.ForeignKey(help_text='Parent mitigation plan.', on_delete=django.db.models.deletion.CASCADE, related_name='rules', to='core.patternmitigationplan')),
|
||||
('user', models.ForeignKey(help_text='Owner of this rule.', on_delete=django.db.models.deletion.CASCADE, related_name='pattern_mitigation_rules', to=settings.AUTH_USER_MODEL)),
|
||||
],
|
||||
),
|
||||
]
|
||||
28
core/migrations/0019_patternmitigationcorrection.py
Normal file
28
core/migrations/0019_patternmitigationcorrection.py
Normal file
@@ -0,0 +1,28 @@
|
||||
# Generated by Django 5.2.11 on 2026-02-15 01:13
|
||||
|
||||
import django.db.models.deletion
|
||||
import uuid
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('core', '0018_patternmitigationplan_patternmitigationmessage_and_more'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='PatternMitigationCorrection',
|
||||
fields=[
|
||||
('id', models.UUIDField(default=uuid.uuid4, editable=False, help_text='Stable identifier for this correction item.', primary_key=True, serialize=False)),
|
||||
('title', models.CharField(help_text='Correction title.', max_length=255)),
|
||||
('clarification', models.TextField(blank=True, default='', help_text='Joint clarification text intended to reduce interpretation drift.')),
|
||||
('enabled', models.BooleanField(default=True, help_text='Whether this correction item is currently enabled.')),
|
||||
('created_at', models.DateTimeField(auto_now_add=True, help_text='Row creation timestamp.')),
|
||||
('plan', models.ForeignKey(help_text='Parent mitigation plan.', on_delete=django.db.models.deletion.CASCADE, related_name='corrections', to='core.patternmitigationplan')),
|
||||
('user', models.ForeignKey(help_text='Owner of this correction item.', on_delete=django.db.models.deletion.CASCADE, related_name='pattern_mitigation_corrections', to=settings.AUTH_USER_MODEL)),
|
||||
],
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,38 @@
|
||||
# Generated by Django 5.2.11 on 2026-02-15 01:38
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('core', '0019_patternmitigationcorrection'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='patternmitigationcorrection',
|
||||
name='language_style',
|
||||
field=models.CharField(choices=[('same', 'Same Language'), ('adapted', 'Adapted Language')], default='adapted', help_text='Whether to keep wording identical or adapt it per recipient.', max_length=16),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='patternmitigationcorrection',
|
||||
name='perspective',
|
||||
field=models.CharField(choices=[('third_person', 'Third Person'), ('first_person', 'First Person')], default='third_person', help_text='Narrative perspective used when framing this correction.', max_length=32),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='patternmitigationcorrection',
|
||||
name='share_target',
|
||||
field=models.CharField(choices=[('self', 'Self'), ('other', 'Other Party'), ('both', 'Both Parties')], default='both', help_text='Who this insight is intended to be shared with.', max_length=16),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='patternmitigationcorrection',
|
||||
name='source_phrase',
|
||||
field=models.TextField(blank=True, default='', help_text="Situation/message fragment this correction responds to, e.g. 'she says ...'."),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='patternartifactexport',
|
||||
name='artifact_type',
|
||||
field=models.CharField(choices=[('rulebook', 'Rulebook'), ('rules', 'Rules'), ('games', 'Games'), ('corrections', 'Corrections')], help_text='Artifact category being exported.', max_length=32),
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,43 @@
|
||||
# Generated by Django 5.2.11 on 2026-02-15 02:01
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('core', '0020_patternmitigationcorrection_language_style_and_more'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='patternmitigationcorrection',
|
||||
name='clarification',
|
||||
field=models.TextField(blank=True, default='', help_text='Joint clarification text intended to reduce interpretation drift. Example: \'When you say "you ignore me", I hear fear of disconnection, not blame.\''),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='patternmitigationcorrection',
|
||||
name='language_style',
|
||||
field=models.CharField(choices=[('same', 'Same Language'), ('adapted', 'Adapted Language')], default='adapted', help_text='Whether to keep wording identical or adapt it per recipient. Example: same text for both parties, or softened/adapted wording for recipient.', max_length=16),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='patternmitigationcorrection',
|
||||
name='perspective',
|
||||
field=models.CharField(choices=[('third_person', 'Third Person'), ('second_person', 'Second Person'), ('first_person', 'First Person')], default='third_person', help_text="Narrative perspective used when framing this correction. Examples: third person ('she says'), second person ('you say'), first person ('I say').", max_length=32),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='patternmitigationcorrection',
|
||||
name='share_target',
|
||||
field=models.CharField(choices=[('self', 'Self'), ('other', 'Other Party'), ('both', 'Both Parties')], default='both', help_text='Who this insight is intended to be shared with. Example: self, other, or both.', max_length=16),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='patternmitigationcorrection',
|
||||
name='source_phrase',
|
||||
field=models.TextField(blank=True, default='', help_text='Situation/message fragment this correction responds to. Example: \'she says: "you never listen"\' or \'you say: "you are dismissing me"\'.'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='patternmitigationcorrection',
|
||||
name='title',
|
||||
field=models.CharField(help_text="Correction title. Example: 'Assumption vs intent mismatch'.", max_length=255),
|
||||
),
|
||||
]
|
||||
38
core/migrations/0022_patternmitigationautosettings.py
Normal file
38
core/migrations/0022_patternmitigationautosettings.py
Normal file
@@ -0,0 +1,38 @@
|
||||
# Generated by Django 5.2.11 on 2026-02-15 02:38
|
||||
|
||||
import django.db.models.deletion
|
||||
import uuid
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('core', '0021_alter_patternmitigationcorrection_clarification_and_more'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='PatternMitigationAutoSettings',
|
||||
fields=[
|
||||
('id', models.UUIDField(default=uuid.uuid4, editable=False, help_text='Stable identifier for this automation settings row.', primary_key=True, serialize=False)),
|
||||
('enabled', models.BooleanField(default=False, help_text='Master toggle for mitigation automation in this conversation.')),
|
||||
('auto_pattern_recognition', models.BooleanField(default=True, help_text='Run pattern/violation recognition automatically when triggered.')),
|
||||
('auto_create_mitigation', models.BooleanField(default=False, help_text='Create a baseline mitigation plan automatically when missing.')),
|
||||
('auto_create_corrections', models.BooleanField(default=False, help_text='Create correction items automatically from detected violations.')),
|
||||
('auto_notify_enabled', models.BooleanField(default=False, help_text='Send NTFY notifications when new violations are detected.')),
|
||||
('ntfy_topic_override', models.CharField(blank=True, help_text='Optional NTFY topic override for automation notifications.', max_length=255, null=True)),
|
||||
('ntfy_url_override', models.CharField(blank=True, help_text='Optional NTFY server URL override for automation notifications.', max_length=255, null=True)),
|
||||
('sample_message_window', models.PositiveIntegerField(default=40, help_text='How many recent messages to include in each automation check.')),
|
||||
('check_cooldown_seconds', models.PositiveIntegerField(default=300, help_text='Minimum seconds between automatic checks for this conversation.')),
|
||||
('last_checked_event_ts', models.BigIntegerField(blank=True, help_text='Latest source message timestamp included in automation checks.', null=True)),
|
||||
('last_run_at', models.DateTimeField(blank=True, help_text='Timestamp when automation last ran.', null=True)),
|
||||
('last_result_summary', models.TextField(blank=True, default='', help_text='Human-readable summary from the last automation run.')),
|
||||
('created_at', models.DateTimeField(auto_now_add=True, help_text='Row creation timestamp.')),
|
||||
('updated_at', models.DateTimeField(auto_now=True, help_text='Last update timestamp.')),
|
||||
('conversation', models.OneToOneField(help_text='Conversation scope this automation config applies to.', on_delete=django.db.models.deletion.CASCADE, related_name='mitigation_auto_settings', to='core.workspaceconversation')),
|
||||
('user', models.ForeignKey(help_text='Owner of this automation settings row.', on_delete=django.db.models.deletion.CASCADE, related_name='pattern_mitigation_auto_settings', to=settings.AUTH_USER_MODEL)),
|
||||
],
|
||||
),
|
||||
]
|
||||
Reference in New Issue
Block a user