feat: add intelligent support workflow
This commit is contained in:
@@ -0,0 +1,34 @@
|
||||
# Generated by Django 5.2.15 on 2026-07-27 04:04
|
||||
|
||||
import django.db.models.deletion
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('agents', '0004_agentrunevent'),
|
||||
('tickets', '0011_alter_intakeevaluationresult_options_and_more'),
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='TicketSupportAnalysis',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('query', models.TextField()),
|
||||
('references', models.JSONField(default=list)),
|
||||
('retrieval_policy', models.JSONField(default=dict)),
|
||||
('created_at', models.DateTimeField(auto_now_add=True)),
|
||||
('created_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='created_ticket_support_analyses', to=settings.AUTH_USER_MODEL)),
|
||||
('run', models.OneToOneField(on_delete=django.db.models.deletion.PROTECT, related_name='ticket_support_analysis', to='agents.agentrun')),
|
||||
('ticket', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='support_analyses', to='tickets.ticket')),
|
||||
],
|
||||
options={
|
||||
'ordering': ['-created_at'],
|
||||
'indexes': [models.Index(fields=['ticket', 'created_at'], name='tickets_tic_ticket__d2d551_idx')],
|
||||
},
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,84 @@
|
||||
# Generated by Django 5.2.15 on 2026-07-27 04:58
|
||||
|
||||
import django.db.models.deletion
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('solutions', '0003_solution_allow_auto_resolve_and_more'),
|
||||
('tickets', '0012_ticketsupportanalysis'),
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='ticketsupportanalysis',
|
||||
name='applied_solution',
|
||||
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='applied_support_analyses', to='solutions.solution'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='ticketsupportanalysis',
|
||||
name='automation_decision',
|
||||
field=models.JSONField(blank=True, default=dict),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='ticketsupportanalysis',
|
||||
name='processed_at',
|
||||
field=models.DateTimeField(blank=True, null=True),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='CustomerReplyTask',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('status', models.CharField(default='draft', max_length=24)),
|
||||
('draft_text', models.TextField()),
|
||||
('final_text', models.TextField()),
|
||||
('revision', models.PositiveIntegerField(default=1)),
|
||||
('sent_at', models.DateTimeField(blank=True, null=True)),
|
||||
('created_at', models.DateTimeField(auto_now_add=True)),
|
||||
('updated_at', models.DateTimeField(auto_now=True)),
|
||||
('created_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='created_customer_reply_tasks', to=settings.AUTH_USER_MODEL)),
|
||||
('sent_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='sent_customer_reply_tasks', to=settings.AUTH_USER_MODEL)),
|
||||
('support_analysis', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='customer_reply_tasks', to='tickets.ticketsupportanalysis')),
|
||||
('ticket', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='customer_reply_tasks', to='tickets.ticket')),
|
||||
('updated_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='updated_customer_reply_tasks', to=settings.AUTH_USER_MODEL)),
|
||||
],
|
||||
options={
|
||||
'ordering': ['-created_at'],
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='CustomerReplyRevision',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('version', models.PositiveIntegerField()),
|
||||
('content', models.TextField()),
|
||||
('source', models.CharField(default='human', max_length=24)),
|
||||
('created_at', models.DateTimeField(auto_now_add=True)),
|
||||
('edited_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='customer_reply_revisions', to=settings.AUTH_USER_MODEL)),
|
||||
('task', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='revisions', to='tickets.customerreplytask')),
|
||||
],
|
||||
options={
|
||||
'ordering': ['version'],
|
||||
},
|
||||
),
|
||||
migrations.AddIndex(
|
||||
model_name='customerreplytask',
|
||||
index=models.Index(fields=['ticket', 'status', 'created_at'], name='tickets_cus_ticket__31c637_idx'),
|
||||
),
|
||||
migrations.AddIndex(
|
||||
model_name='customerreplytask',
|
||||
index=models.Index(fields=['status', 'updated_at'], name='tickets_cus_status_7bec2e_idx'),
|
||||
),
|
||||
migrations.AddConstraint(
|
||||
model_name='customerreplytask',
|
||||
constraint=models.UniqueConstraint(condition=models.Q(('status', 'draft')), fields=('ticket',), name='unique_active_customer_reply_task'),
|
||||
),
|
||||
migrations.AddConstraint(
|
||||
model_name='customerreplyrevision',
|
||||
constraint=models.UniqueConstraint(fields=('task', 'version'), name='unique_customer_reply_revision'),
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,41 @@
|
||||
import django.db.models.deletion
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
dependencies = [
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
("tickets", "0013_ticketsupportanalysis_applied_solution_and_more"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name="customerreplytask",
|
||||
name="approved_at",
|
||||
field=models.DateTimeField(blank=True, null=True),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name="customerreplytask",
|
||||
name="approved_by",
|
||||
field=models.ForeignKey(
|
||||
blank=True,
|
||||
null=True,
|
||||
on_delete=django.db.models.deletion.SET_NULL,
|
||||
related_name="approved_customer_reply_tasks",
|
||||
to=settings.AUTH_USER_MODEL,
|
||||
),
|
||||
),
|
||||
migrations.RemoveConstraint(
|
||||
model_name="customerreplytask",
|
||||
name="unique_active_customer_reply_task",
|
||||
),
|
||||
migrations.AddConstraint(
|
||||
model_name="customerreplytask",
|
||||
constraint=models.UniqueConstraint(
|
||||
condition=models.Q(("status__in", ["draft", "approved"])),
|
||||
fields=("ticket",),
|
||||
name="unique_active_customer_reply_task",
|
||||
),
|
||||
),
|
||||
]
|
||||
113
backend/apps/tickets/migrations/0015_support_evaluation.py
Normal file
113
backend/apps/tickets/migrations/0015_support_evaluation.py
Normal file
@@ -0,0 +1,113 @@
|
||||
import django.db.models.deletion
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
dependencies = [
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
("tickets", "0014_customerreplytask_channel_approval"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name="SupportEvaluationCase",
|
||||
fields=[
|
||||
(
|
||||
"id",
|
||||
models.BigAutoField(
|
||||
auto_created=True,
|
||||
primary_key=True,
|
||||
serialize=False,
|
||||
verbose_name="ID",
|
||||
),
|
||||
),
|
||||
("key", models.SlugField(max_length=120, unique=True)),
|
||||
("name", models.CharField(max_length=200)),
|
||||
("source_kind", models.CharField(default="synthetic", max_length=32)),
|
||||
("input_manifest", models.JSONField(default=dict)),
|
||||
("expected_solution_ids", models.JSONField(default=list)),
|
||||
("expected_auto_resolve", models.BooleanField(default=False)),
|
||||
("expected_manual_fallback", models.BooleanField(default=True)),
|
||||
("notes", models.TextField(blank=True)),
|
||||
("enabled", models.BooleanField(default=True)),
|
||||
("created_at", models.DateTimeField(auto_now_add=True)),
|
||||
("updated_at", models.DateTimeField(auto_now=True)),
|
||||
],
|
||||
options={
|
||||
"ordering": ["key"],
|
||||
"indexes": [
|
||||
models.Index(
|
||||
fields=["source_kind", "enabled"],
|
||||
name="tickets_sup_source__864444_idx",
|
||||
)
|
||||
],
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name="SupportEvaluationResult",
|
||||
fields=[
|
||||
(
|
||||
"id",
|
||||
models.BigAutoField(
|
||||
auto_created=True,
|
||||
primary_key=True,
|
||||
serialize=False,
|
||||
verbose_name="ID",
|
||||
),
|
||||
),
|
||||
("actual_candidate_solution_ids", models.JSONField(default=list)),
|
||||
("actual_citation_ranks", models.JSONField(default=list)),
|
||||
("matched_expected_solution", models.BooleanField(default=False)),
|
||||
("actual_auto_resolved", models.BooleanField(default=False)),
|
||||
("actual_manual_fallback", models.BooleanField(default=False)),
|
||||
("incorrect_auto_reuse", models.BooleanField(default=False)),
|
||||
("citations_valid", models.BooleanField(default=False)),
|
||||
("passed", models.BooleanField(default=False)),
|
||||
("details", models.JSONField(blank=True, default=dict)),
|
||||
("created_at", models.DateTimeField(auto_now_add=True)),
|
||||
("updated_at", models.DateTimeField(auto_now=True)),
|
||||
(
|
||||
"analysis",
|
||||
models.ForeignKey(
|
||||
on_delete=django.db.models.deletion.CASCADE,
|
||||
related_name="evaluation_results",
|
||||
to="tickets.ticketsupportanalysis",
|
||||
),
|
||||
),
|
||||
(
|
||||
"case",
|
||||
models.ForeignKey(
|
||||
on_delete=django.db.models.deletion.PROTECT,
|
||||
related_name="results",
|
||||
to="tickets.supportevaluationcase",
|
||||
),
|
||||
),
|
||||
(
|
||||
"reviewed_by",
|
||||
models.ForeignKey(
|
||||
blank=True,
|
||||
null=True,
|
||||
on_delete=django.db.models.deletion.SET_NULL,
|
||||
related_name="reviewed_support_evaluations",
|
||||
to=settings.AUTH_USER_MODEL,
|
||||
),
|
||||
),
|
||||
],
|
||||
options={
|
||||
"ordering": ["-updated_at"],
|
||||
"indexes": [
|
||||
models.Index(
|
||||
fields=["case", "updated_at"],
|
||||
name="tickets_sup_case_id_1fef4b_idx",
|
||||
)
|
||||
],
|
||||
"constraints": [
|
||||
models.UniqueConstraint(
|
||||
fields=("case", "analysis"),
|
||||
name="unique_support_case_analysis_result",
|
||||
)
|
||||
],
|
||||
},
|
||||
),
|
||||
]
|
||||
Reference in New Issue
Block a user