Implement executing tasks

This commit is contained in:
2026-03-03 16:41:28 +00:00
parent d6bd56dace
commit 9c14e51b43
42 changed files with 3410 additions and 121 deletions

View File

@@ -48,6 +48,8 @@ from core.models import (
PlatformChatLink,
WorkspaceConversation,
)
from core.presence import get_settings as get_availability_settings
from core.presence import spans_for_range
from core.realtime.typing_state import get_person_typing_state
from core.translation.engine import process_inbound_translation
from core.views.workspace import (
@@ -101,6 +103,36 @@ def _default_service(service: str | None) -> str:
return "signal"
def _identifier_variants(service: str, identifier: str) -> list[str]:
value = str(identifier or "").strip()
if not value:
return []
service_key = _default_service(service)
variants = [value]
bare = value.split("@", 1)[0].strip()
if bare and bare not in variants:
variants.append(bare)
if service_key == "signal":
digits = re.sub(r"[^0-9]", "", value)
if digits and digits not in variants:
variants.append(digits)
if digits:
plus = f"+{digits}"
if plus not in variants:
variants.append(plus)
elif service_key == "whatsapp":
if bare:
direct = f"{bare}@s.whatsapp.net"
group = f"{bare}@g.us"
if direct not in variants:
variants.append(direct)
if group not in variants:
variants.append(group)
return variants
def _safe_limit(raw) -> int:
try:
value = int(raw or 40)
@@ -136,6 +168,24 @@ def _format_ts_label(ts_value: int) -> str:
return str(ts_value or "")
def _serialize_availability_spans(spans):
rows = []
for row in list(spans or []):
rows.append(
{
"id": int(getattr(row, "id", 0) or 0),
"service": str(getattr(row, "service", "") or ""),
"state": str(getattr(row, "state", "unknown") or "unknown"),
"start_ts": int(getattr(row, "start_ts", 0) or 0),
"end_ts": int(getattr(row, "end_ts", 0) or 0),
"confidence_start": float(getattr(row, "confidence_start", 0.0) or 0.0),
"confidence_end": float(getattr(row, "confidence_end", 0.0) or 0.0),
"payload": dict(getattr(row, "payload", {}) or {}),
}
)
return rows
def _is_outgoing(msg: Message) -> bool:
is_outgoing = str(msg.custom_author or "").upper() in {"USER", "BOT"}
if not is_outgoing:
@@ -672,14 +722,19 @@ THREAD_METRIC_COPY_OVERRIDES = {
def _workspace_conversation_for_person(user, person):
if person is None:
return None
return (
WorkspaceConversation.objects.filter(
user=user,
participants=person,
try:
from core.views.workspace import _conversation_for_person
return _conversation_for_person(user, person)
except Exception:
return (
WorkspaceConversation.objects.filter(
user=user,
participants=person,
)
.order_by("-last_event_ts", "-created_at")
.first()
)
.order_by("-last_event_ts", "-created_at")
.first()
)
def _counterpart_identifiers_for_person(user, person):
@@ -1522,14 +1577,15 @@ def _engage_source_from_ref(plan, source_ref):
def _context_base(user, service, identifier, person):
identifier_variants = _identifier_variants(service, identifier)
person_identifier = None
if person is not None:
if identifier:
if identifier_variants:
person_identifier = PersonIdentifier.objects.filter(
user=user,
person=person,
service=service,
identifier=identifier,
identifier__in=identifier_variants,
).first()
if person_identifier is None:
person_identifier = (
@@ -1544,7 +1600,7 @@ def _context_base(user, service, identifier, person):
person_identifier = PersonIdentifier.objects.filter(
user=user,
service=service,
identifier=identifier,
identifier__in=identifier_variants or [identifier],
).first()
if person_identifier:
@@ -2496,6 +2552,35 @@ def _panel_context(
counterpart_identifiers=counterpart_identifiers,
conversation=conversation,
)
availability_slices = []
availability_enabled = False
availability_settings = get_availability_settings(request.user)
if (
base["person"] is not None
and availability_settings.enabled
and availability_settings.show_in_chat
):
range_start = (
int(session_bundle["messages"][0].ts or 0) if session_bundle["messages"] else 0
)
range_end = (
int(session_bundle["messages"][-1].ts or 0) if session_bundle["messages"] else 0
)
if range_start <= 0 or range_end <= 0:
now_ts = int(time.time() * 1000)
range_start = now_ts - (24 * 60 * 60 * 1000)
range_end = now_ts
availability_enabled = True
availability_slices = _serialize_availability_spans(
spans_for_range(
user=request.user,
person=base["person"],
start_ts=range_start,
end_ts=range_end,
service=base["service"],
limit=200,
)
)
glance_items = _build_glance_items(
serialized_messages,
person_id=(base["person"].id if base["person"] else None),
@@ -2665,6 +2750,22 @@ def _panel_context(
if base["person"]
else reverse("ai_workspace")
),
"ai_workspace_graphs_url": (
reverse(
"ai_workspace_insight_graphs",
kwargs={"type": "page", "person_id": base["person"].id},
)
if base["person"]
else ""
),
"ai_workspace_info_url": (
reverse(
"ai_workspace_information",
kwargs={"type": "page", "person_id": base["person"].id},
)
if base["person"]
else ""
),
"ai_workspace_widget_url": (
(
f"{reverse('ai_workspace_person', kwargs={'type': 'widget', 'person_id': base['person'].id})}"
@@ -2676,6 +2777,9 @@ def _panel_context(
"manual_icon_class": "fa-solid fa-paper-plane",
"panel_id": f"compose-panel-{unique}",
"typing_state_json": json.dumps(typing_state),
"availability_enabled": availability_enabled,
"availability_slices": availability_slices,
"availability_slices_json": json.dumps(availability_slices),
"command_options": command_options,
"bp_binding_summary": bp_binding_summary,
"platform_options": platform_options,
@@ -3133,6 +3237,31 @@ class ComposeThread(LoginRequiredMixin, View):
counterpart_identifiers = _counterpart_identifiers_for_person(
request.user, base["person"]
)
availability_slices = []
availability_settings = get_availability_settings(request.user)
if (
base["person"] is not None
and availability_settings.enabled
and availability_settings.show_in_chat
):
range_start = (
int(messages[0].ts or 0) if messages else max(0, int(after_ts or 0))
)
range_end = int(latest_ts or 0)
if range_start <= 0 or range_end <= 0:
now_ts = int(time.time() * 1000)
range_start = now_ts - (24 * 60 * 60 * 1000)
range_end = now_ts
availability_slices = _serialize_availability_spans(
spans_for_range(
user=request.user,
person=base["person"],
start_ts=range_start,
end_ts=range_end,
service=base["service"],
limit=200,
)
)
payload = {
"messages": _serialize_messages_with_artifacts(
messages,
@@ -3141,6 +3270,7 @@ class ComposeThread(LoginRequiredMixin, View):
seed_previous=seed_previous,
),
"last_ts": latest_ts,
"availability_slices": availability_slices,
"typing": get_person_typing_state(
user_id=request.user.id,
person_id=base["person"].id if base["person"] else None,