5809ad3adb
Shared runtime - One agent entry point (runtime.invoke_agent_for_user / prepare_agent_turn) for web, email, iMessage, WhatsApp, scheduled and cron, so identity, permissions, memories, summaries and metrics match across channels. - Serialize turns per checkpoint thread; /query validates input and returns 409 while a durable run owns the conversation. Reconnectable chat (durable runs) - POST /runs creates an idempotent, owner-scoped run; GET .../events replays an ordered SSE log by cursor, so generation survives refresh and device switches. Stop cancels; disconnect only detaches. /stream-query is a compatibility adapter. - Coalesce streamed deltas (150 ms) and wake same-process subscribers instead of polling; keep buffered text when a run is stopped. - Drain runs on shutdown; serve via Hypercorn with a shutdown timeout covering the drain (and no longer in debug mode). CHAT_RUN_* settings are forwarded. Scheduled messages and cron - Atomic delivery claims, attempt history and explicit retries; ambiguous sends become "uncertain" and are never resent automatically. - Cron triggers need a run_id/Idempotency-Key; legacy jobs need an explicit owner. - Cancelling never rewrites a started delivery; series stop via tombstones. Legacy sent rows are not forked into new series. Malformed bodies return 400. History, integrations and retrieval - Message-content search, pinned conversations, JSON/Markdown export and a memory editor. - Integration status panel and YNAB data-freshness provenance. - Date-filtered document/note search with stable citations and a source viewer. Migration 20 is generated by aerich. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
43 lines
1.6 KiB
Python
43 lines
1.6 KiB
Python
"""Durable turn ownership and append-only replay log (shared PostgreSQL/SQLite)."""
|
|
|
|
from tortoise import fields
|
|
from tortoise.models import Model
|
|
|
|
|
|
class ChatRun(Model):
|
|
id = fields.UUIDField(primary_key=True)
|
|
conversation = fields.ForeignKeyField(
|
|
"models.Conversation", related_name="chat_runs"
|
|
)
|
|
owner = fields.ForeignKeyField("models.User", related_name="chat_runs")
|
|
request_key = fields.CharField(max_length=128)
|
|
payload = fields.JSONField()
|
|
# Unique nullable slot: only one generation can write a conversation checkpoint.
|
|
active_key = fields.CharField(max_length=36, null=True, unique=True)
|
|
status = fields.CharField(max_length=20, default="running")
|
|
worker_token = fields.UUIDField()
|
|
lease_until = fields.DatetimeField(index=True)
|
|
cancel_requested = fields.BooleanField(default=False)
|
|
sequence = fields.IntField(default=0)
|
|
partial = fields.TextField(default="")
|
|
user_saved = fields.BooleanField(default=False)
|
|
answer_saved = fields.BooleanField(default=False)
|
|
created_at = fields.DatetimeField(auto_now_add=True)
|
|
updated_at = fields.DatetimeField(auto_now=True)
|
|
|
|
class Meta:
|
|
table = "chat_runs"
|
|
unique_together = (("owner", "conversation", "request_key"),)
|
|
|
|
|
|
class ChatRunEvent(Model):
|
|
id = fields.BigIntField(primary_key=True)
|
|
run = fields.ForeignKeyField("models.ChatRun", related_name="events")
|
|
sequence = fields.IntField()
|
|
payload = fields.JSONField()
|
|
created_at = fields.DatetimeField(auto_now_add=True)
|
|
|
|
class Meta:
|
|
table = "chat_run_events"
|
|
unique_together = (("run", "sequence"),)
|