feat: Ipad app production readiness, Colony orchestration, Social posting (#44)
#38 Ipad app production readiness, Colony orchestration, Social posting Co-authored-by: Sayan Datta <sayan@Sayans-MacBook-Air.local> Reviewed-on: #44
This commit was merged in pull request #44.
This commit is contained in:
@@ -24,12 +24,15 @@ import logging
|
||||
import os
|
||||
import uuid
|
||||
from datetime import datetime, timezone
|
||||
from typing import Any, Set
|
||||
from typing import Any, Literal, Set
|
||||
|
||||
import httpx
|
||||
from fastapi import APIRouter, Depends, HTTPException, Request, WebSocket, WebSocketDisconnect, status
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
from backend.auth.dependencies import UserPrincipal, get_current_user
|
||||
from backend.services.colony_gateway import ColonyConfigurationError, ColonyGateway, ColonyGatewayError
|
||||
from backend.services.colony_repository import ColonyRepository
|
||||
from .canvas_service import canvas_service
|
||||
from .collaboration_service import collaboration_service
|
||||
from .action_service import oracle_action_service
|
||||
@@ -142,6 +145,263 @@ async def _resolve_page_id(request: Request, user: UserPrincipal, page_id: str)
|
||||
return str(me["defaultPageId"])
|
||||
|
||||
|
||||
def _oracle_prompt_complexity(prompt: str, conversation_context: list[dict[str, str]] | None = None) -> tuple[str, list[str]]:
|
||||
text = prompt.lower()
|
||||
reasons: list[str] = []
|
||||
complex_markers = (
|
||||
"multi-round",
|
||||
"multi round",
|
||||
"coordinate",
|
||||
"orchestrate",
|
||||
"compare",
|
||||
"writeback",
|
||||
"write back",
|
||||
"approve",
|
||||
"crm",
|
||||
"catalyst",
|
||||
"campaign",
|
||||
"social",
|
||||
"inventory",
|
||||
"next best action",
|
||||
"next-best action",
|
||||
"follow-up plan",
|
||||
"strategy",
|
||||
"across",
|
||||
)
|
||||
matched = [marker for marker in complex_markers if marker in text]
|
||||
if matched:
|
||||
reasons.append(f"complex markers: {', '.join(matched[:5])}")
|
||||
if len(prompt) > 700:
|
||||
reasons.append("long prompt")
|
||||
if conversation_context and len(conversation_context) >= 4:
|
||||
reasons.append("multi-turn context")
|
||||
return ("thinking" if reasons else "fast", reasons)
|
||||
|
||||
|
||||
def _next_canvas_order(components: list[dict[str, Any]]) -> int:
|
||||
highest = 0
|
||||
for component in components:
|
||||
try:
|
||||
highest = max(highest, int((component.get("layout") or {}).get("orderIndex", 0)))
|
||||
except (TypeError, ValueError):
|
||||
continue
|
||||
return ((highest // 100) + 1) * 100
|
||||
|
||||
|
||||
def _build_colony_status_component(
|
||||
*,
|
||||
execution_id: str,
|
||||
mission_id: str,
|
||||
prompt: str,
|
||||
actor_id: str,
|
||||
branch_id: str,
|
||||
mode: str,
|
||||
reasons: list[str],
|
||||
order_index: int,
|
||||
) -> dict[str, Any]:
|
||||
reason_text = "; ".join(reasons) if reasons else "operator selected thinking mode"
|
||||
return {
|
||||
"componentId": str(uuid.uuid4()),
|
||||
"type": "textCanvas",
|
||||
"title": "Colony Mission Dispatched",
|
||||
"description": "Oracle routed a complex request to Colony orchestration.",
|
||||
"dataSourceDescriptor": {
|
||||
"descriptorId": str(uuid.uuid4()),
|
||||
"sourceType": "api",
|
||||
"connectorId": "velocity-colony",
|
||||
"dataset": "colony_mission",
|
||||
"authContextRef": f"authctx_{actor_id}_scope",
|
||||
"queryTemplate": f"/api/colony/missions/{mission_id}",
|
||||
"queryParameters": {"missionId": mission_id},
|
||||
"rowLimit": 1,
|
||||
"privacyTier": "standard",
|
||||
"cachePolicy": {"mode": "none"},
|
||||
},
|
||||
"visualizationParameters": {
|
||||
"content": (
|
||||
f"Oracle routed this request to Colony because it needs deeper orchestration.\n\n"
|
||||
f"Mission ID: {mission_id}\n"
|
||||
f"Mode: {mode}\n"
|
||||
f"Routing reason: {reason_text}\n\n"
|
||||
f"Original request: {prompt}\n\n"
|
||||
"Colony will coordinate specialist workers and return artifacts/writeback proposals for review."
|
||||
),
|
||||
"widthMode": "full",
|
||||
"adjustableHeight": True,
|
||||
},
|
||||
"dataBindings": {"dimensions": [], "measures": [], "series": [], "filters": []},
|
||||
"version": 1,
|
||||
"lifecycleState": "active",
|
||||
"provenance": {
|
||||
"originType": "prompt_generated",
|
||||
"promptExecutionId": execution_id,
|
||||
"sourceBranchId": branch_id,
|
||||
"createdBy": actor_id,
|
||||
"createdAt": _now(),
|
||||
"colonyMissionId": mission_id,
|
||||
},
|
||||
"renderingHints": {"estimatedHeightPx": 220, "skeletonVariant": "text", "virtualizationPriority": 5},
|
||||
"layout": {
|
||||
"orderIndex": order_index,
|
||||
"sectionId": f"sec_colony_{execution_id.replace('-', '')[:12]}",
|
||||
"widthMode": "full",
|
||||
"minHeightPx": 220,
|
||||
"stickyHeader": False,
|
||||
},
|
||||
"accessControls": {
|
||||
"visibilityScope": "private",
|
||||
"allowedRoles": ["senior_broker", "sales_director", "marketing_operator", "data_steward", "compliance_reviewer", "platform_admin"],
|
||||
"redactionPolicy": "none",
|
||||
},
|
||||
"styleSignature": {
|
||||
"theme": "velocity_glass",
|
||||
"paletteToken": "ocean_signal",
|
||||
"motionProfile": "calm_reveal",
|
||||
"density": "comfortable",
|
||||
"radiusScale": "lg",
|
||||
"typographyScale": "balanced",
|
||||
},
|
||||
"validationState": {
|
||||
"schema": "pass",
|
||||
"policy": "pass",
|
||||
"a11y": "pass",
|
||||
"performance": "pass",
|
||||
"status": "validated",
|
||||
},
|
||||
"auditLog": [f"aud_{execution_id}_colony_dispatch"],
|
||||
"dataRows": [{"missionId": mission_id, "mode": mode, "routingReason": reason_text}],
|
||||
}
|
||||
|
||||
|
||||
async def _dispatch_colony_from_oracle_prompt(
|
||||
*,
|
||||
request: Request,
|
||||
ctx: PolicyContext,
|
||||
page_id: str,
|
||||
payload: PromptSubmitRequest,
|
||||
resolved_mode: str,
|
||||
routing_reasons: list[str],
|
||||
) -> dict[str, Any]:
|
||||
pool = getattr(request.app.state, "db_pool", None)
|
||||
if pool is None:
|
||||
raise HTTPException(status_code=503, detail="Database unavailable.")
|
||||
try:
|
||||
gateway = ColonyGateway()
|
||||
except ColonyConfigurationError as exc:
|
||||
raise HTTPException(status_code=503, detail=str(exc)) from exc
|
||||
|
||||
repo = ColonyRepository(pool)
|
||||
mission_id = str(uuid.uuid4())
|
||||
execution_id = str(uuid.uuid4())
|
||||
mission = {
|
||||
"mission_id": mission_id,
|
||||
"mission_type": "oracle_advisory",
|
||||
"origin_surface": "oracle_canvas",
|
||||
"tenant_id": ctx.tenant_id,
|
||||
"actor_id": ctx.actor_id,
|
||||
"actor_role": ctx.actor_role,
|
||||
"risk_level": "medium",
|
||||
"sensitivity_class": "internal",
|
||||
"time_budget_ms": 120000,
|
||||
"token_budget": 24000,
|
||||
"user_goal": payload.prompt,
|
||||
"normalized_goal": payload.prompt,
|
||||
"context_refs": {
|
||||
"page_id": page_id,
|
||||
"branch_id": payload.branchId,
|
||||
"client_request_id": payload.clientRequestId,
|
||||
"execution_mode": payload.executionMode,
|
||||
"resolved_mode": resolved_mode,
|
||||
"target_lead_id": payload.targetLeadId,
|
||||
"conversation_context": payload.conversationContext,
|
||||
},
|
||||
"requested_outputs": ["canvas_artifacts", "summary", "writeback_proposals"],
|
||||
"payload": {
|
||||
"planned_writeback": payload.plannedWriteback,
|
||||
"placement_mode": payload.placementMode,
|
||||
"routing_reasons": routing_reasons,
|
||||
},
|
||||
}
|
||||
row = await repo.create_mission(mission)
|
||||
await repo.log_event(
|
||||
mission_id=mission_id,
|
||||
tenant_id=ctx.tenant_id,
|
||||
event_type="mission_created_from_oracle_canvas",
|
||||
actor=ctx.actor_id,
|
||||
detail={"page_id": page_id, "execution_id": execution_id, "resolved_mode": resolved_mode},
|
||||
)
|
||||
try:
|
||||
dispatch = await gateway.dispatch_mission(mission)
|
||||
except (ColonyGatewayError, httpx.HTTPError) as exc:
|
||||
await repo.update_status(mission_id, ctx.tenant_id, "dispatch_failed")
|
||||
await repo.log_event(
|
||||
mission_id=mission_id,
|
||||
tenant_id=ctx.tenant_id,
|
||||
event_type="mission_dispatch_failed",
|
||||
actor=ctx.actor_id,
|
||||
detail={"error": str(exc)},
|
||||
)
|
||||
raise HTTPException(
|
||||
status_code=502,
|
||||
detail={"message": str(exc), "mission_id": str(row["mission_id"])},
|
||||
) from exc
|
||||
|
||||
queued = await repo.update_status(mission_id, ctx.tenant_id, "queued")
|
||||
await repo.log_event(
|
||||
mission_id=mission_id,
|
||||
tenant_id=ctx.tenant_id,
|
||||
event_type="mission_dispatched",
|
||||
actor=ctx.actor_id,
|
||||
detail={"dispatch": dispatch},
|
||||
)
|
||||
|
||||
page = await canvas_service.get_page(page_id, ctx.tenant_id)
|
||||
existing_components = page.get("components", []) if page else []
|
||||
component = _build_colony_status_component(
|
||||
execution_id=execution_id,
|
||||
mission_id=mission_id,
|
||||
prompt=payload.prompt,
|
||||
actor_id=ctx.actor_id,
|
||||
branch_id=payload.branchId,
|
||||
mode=resolved_mode,
|
||||
reasons=routing_reasons,
|
||||
order_index=_next_canvas_order(existing_components),
|
||||
)
|
||||
revision = await canvas_service.commit_revision(
|
||||
page_id=page_id,
|
||||
tenant_id=ctx.tenant_id,
|
||||
actor_id=ctx.actor_id,
|
||||
commit_kind="prompt",
|
||||
commit_summary=f"Colony: {payload.prompt[:80]}",
|
||||
components=existing_components + [component],
|
||||
execution_id=execution_id,
|
||||
idempotency_key=payload.clientRequestId,
|
||||
)
|
||||
execution = {
|
||||
"executionId": execution_id,
|
||||
"tenantId": ctx.tenant_id,
|
||||
"pageId": page_id,
|
||||
"branchId": payload.branchId,
|
||||
"actorId": ctx.actor_id,
|
||||
"prompt": payload.prompt,
|
||||
"intentClass": "mixed",
|
||||
"status": "executing",
|
||||
"modelRuntime": "colony_orchestrator",
|
||||
"semanticModelVersion": "oracle_colony_router_v2026_05_03",
|
||||
"retrievalPlan": {"route": "colony", "missionId": mission_id, "dispatch": dispatch},
|
||||
"visualizationPlan": {"components": [component]},
|
||||
"warnings": [],
|
||||
"summary": f"Colony mission {mission_id} queued for multi-agent orchestration.",
|
||||
"componentsCreated": [component["componentId"]],
|
||||
"clientRequestId": payload.clientRequestId,
|
||||
"createdAt": _now(),
|
||||
"completedAt": None,
|
||||
"workflowDispatch": {"type": "colony_mission", "missionId": mission_id, "status": (queued or row)["status"]},
|
||||
}
|
||||
await prompt_orchestrator._persist_execution(execution)
|
||||
return {"execution": execution, "page": await canvas_service.get_page(page_id, ctx.tenant_id)}
|
||||
|
||||
|
||||
# ── Pydantic Models ───────────────────────────────────────────────────────────
|
||||
|
||||
class PromptSubmitRequest(BaseModel):
|
||||
@@ -150,6 +410,7 @@ class PromptSubmitRequest(BaseModel):
|
||||
prompt: str = Field(..., min_length=1, max_length=4096)
|
||||
conversationContext: list[dict[str, str]] = Field(default_factory=list)
|
||||
placementMode: str = Field("append_after_last_visible_component")
|
||||
executionMode: Literal["auto", "fast", "thinking"] = "auto"
|
||||
targetLeadId: str | None = None
|
||||
plannedWriteback: dict[str, Any] = Field(default_factory=dict)
|
||||
|
||||
@@ -202,6 +463,215 @@ class PageUpdateRequest(BaseModel):
|
||||
|
||||
# ── Endpoints ─────────────────────────────────────────────────────────────────
|
||||
|
||||
@router.get("/mobile/team-performance", summary="Mobile Oracle team performance intelligence")
|
||||
async def mobile_team_performance(
|
||||
request: Request,
|
||||
limit: int = 12,
|
||||
user: UserPrincipal = Depends(get_current_user),
|
||||
) -> dict:
|
||||
pool = getattr(request.app.state, "db_pool", None)
|
||||
if pool is None:
|
||||
raise HTTPException(status_code=503, detail="Database unavailable.")
|
||||
tenant_id = user.tenant_id or _DEFAULT_TENANT_ID
|
||||
safe_limit = max(1, min(limit, 50))
|
||||
async with pool.acquire() as conn:
|
||||
rows = await conn.fetch(
|
||||
"""
|
||||
WITH team AS (
|
||||
SELECT id, full_name, email, avatar_url
|
||||
FROM users_and_roles
|
||||
WHERE COALESCE(is_active, TRUE) = TRUE
|
||||
),
|
||||
lead_rollup AS (
|
||||
SELECT assigned_user_id,
|
||||
COUNT(*)::int AS assigned_leads,
|
||||
COUNT(*) FILTER (WHERE COALESCE(status::text, '') IN ('won', 'closed_won', 'booked'))::int AS won_leads
|
||||
FROM crm_leads
|
||||
WHERE tenant_id = $1
|
||||
GROUP BY assigned_user_id
|
||||
),
|
||||
opportunity_rollup AS (
|
||||
SELECT cl.assigned_user_id,
|
||||
COUNT(co.opportunity_id)::int AS active_opportunities,
|
||||
COALESCE(SUM(co.value) FILTER (WHERE COALESCE(co.stage::text, '') NOT IN ('closed_lost')), 0)::float AS pipeline_value,
|
||||
COALESCE(SUM(co.value) FILTER (WHERE COALESCE(co.stage::text, '') IN ('closed_won', 'won')), 0)::float AS closed_won_value
|
||||
FROM crm_opportunities co
|
||||
INNER JOIN crm_leads cl ON cl.lead_id = co.lead_id
|
||||
WHERE cl.tenant_id = $1
|
||||
GROUP BY cl.assigned_user_id
|
||||
),
|
||||
task_rollup AS (
|
||||
SELECT cl.assigned_user_id,
|
||||
COUNT(ir.reminder_id) FILTER (WHERE COALESCE(ir.status, '') IN ('pending', 'open', 'scheduled', 'snoozed'))::int AS open_tasks,
|
||||
COUNT(ir.reminder_id) FILTER (WHERE COALESCE(ir.status, '') = 'done')::int AS done_tasks
|
||||
FROM intel_reminders ir
|
||||
LEFT JOIN crm_leads cl ON cl.lead_id = ir.lead_id
|
||||
WHERE COALESCE(ir.tenant_id, $1) = $1
|
||||
GROUP BY cl.assigned_user_id
|
||||
),
|
||||
activity_rollup AS (
|
||||
SELECT cl.assigned_user_id, MAX(ii.happened_at) AS last_activity_at
|
||||
FROM intel_interactions ii
|
||||
LEFT JOIN crm_leads cl ON cl.lead_id = ii.lead_id
|
||||
WHERE COALESCE(ii.tenant_id, $1) = $1
|
||||
GROUP BY cl.assigned_user_id
|
||||
)
|
||||
SELECT
|
||||
t.id::text AS user_id,
|
||||
COALESCE(t.full_name, t.email, t.id::text) AS name,
|
||||
COALESCE(t.email, '') AS email,
|
||||
t.avatar_url,
|
||||
COALESCE(l.assigned_leads, 0)::int AS assigned_leads,
|
||||
COALESCE(tr.open_tasks, 0)::int AS open_tasks,
|
||||
COALESCE(tr.done_tasks, 0)::int AS done_tasks,
|
||||
COALESCE(o.active_opportunities, 0)::int AS active_opportunities,
|
||||
COALESCE(o.pipeline_value, 0)::float AS pipeline_value,
|
||||
COALESCE(o.closed_won_value, 0)::float AS closed_won_value,
|
||||
CASE WHEN COALESCE(l.assigned_leads, 0) = 0 THEN 0
|
||||
ELSE ROUND((COALESCE(l.won_leads, 0)::numeric / NULLIF(l.assigned_leads, 0)) * 100, 1)::float
|
||||
END AS conversion_rate,
|
||||
a.last_activity_at
|
||||
FROM team t
|
||||
LEFT JOIN lead_rollup l ON l.assigned_user_id = t.id
|
||||
LEFT JOIN opportunity_rollup o ON o.assigned_user_id = t.id
|
||||
LEFT JOIN task_rollup tr ON tr.assigned_user_id = t.id
|
||||
LEFT JOIN activity_rollup a ON a.assigned_user_id = t.id
|
||||
WHERE COALESCE(l.assigned_leads, 0) > 0
|
||||
OR COALESCE(o.active_opportunities, 0) > 0
|
||||
OR COALESCE(tr.open_tasks, 0) > 0
|
||||
ORDER BY COALESCE(o.closed_won_value, 0) DESC,
|
||||
COALESCE(o.pipeline_value, 0) DESC,
|
||||
COALESCE(l.assigned_leads, 0) DESC,
|
||||
name ASC
|
||||
LIMIT $2
|
||||
""",
|
||||
tenant_id,
|
||||
safe_limit,
|
||||
)
|
||||
performers = [
|
||||
{
|
||||
"userId": row["user_id"],
|
||||
"name": row["name"],
|
||||
"email": row["email"],
|
||||
"avatarUrl": row["avatar_url"],
|
||||
"assignedLeads": row["assigned_leads"],
|
||||
"openTasks": row["open_tasks"],
|
||||
"doneTasks": row["done_tasks"],
|
||||
"activeOpportunities": row["active_opportunities"],
|
||||
"pipelineValue": row["pipeline_value"],
|
||||
"closedWonValue": row["closed_won_value"],
|
||||
"conversionRate": row["conversion_rate"],
|
||||
"lastActivityAt": row["last_activity_at"].isoformat() if row["last_activity_at"] else None,
|
||||
}
|
||||
for row in rows
|
||||
]
|
||||
return _ok(
|
||||
{
|
||||
"summary": {
|
||||
"teamMembers": len(performers),
|
||||
"assignedLeads": sum(item["assignedLeads"] for item in performers),
|
||||
"openTasks": sum(item["openTasks"] for item in performers),
|
||||
"pipelineValue": sum(item["pipelineValue"] for item in performers),
|
||||
"closedWonValue": sum(item["closedWonValue"] for item in performers),
|
||||
},
|
||||
"performers": performers,
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
@router.get("/mobile/lead-map", summary="Mobile Oracle lead geo-interest intelligence")
|
||||
async def mobile_lead_map(
|
||||
request: Request,
|
||||
limit: int = 24,
|
||||
user: UserPrincipal = Depends(get_current_user),
|
||||
) -> dict:
|
||||
pool = getattr(request.app.state, "db_pool", None)
|
||||
if pool is None:
|
||||
raise HTTPException(status_code=503, detail="Database unavailable.")
|
||||
tenant_id = user.tenant_id or _DEFAULT_TENANT_ID
|
||||
safe_limit = max(1, min(limit, 100))
|
||||
async with pool.acquire() as conn:
|
||||
has_rollup = await conn.fetchval("SELECT to_regclass('public.lead_geo_interest_rollup') IS NOT NULL")
|
||||
if has_rollup:
|
||||
rows = await conn.fetch(
|
||||
"""
|
||||
SELECT district AS label,
|
||||
district AS district,
|
||||
NULL::text AS city,
|
||||
lat::float AS latitude,
|
||||
lng::float AS longitude,
|
||||
x::float AS x,
|
||||
y::float AS y,
|
||||
COALESCE(lead_count, 0)::int AS lead_count,
|
||||
COALESCE(avg_qd_score, 0)::float AS avg_qd_score,
|
||||
0::int AS hot_lead_count
|
||||
FROM lead_geo_interest_rollup
|
||||
WHERE tenant_id = $1
|
||||
ORDER BY lead_count DESC, avg_qd_score DESC, district ASC
|
||||
LIMIT $2
|
||||
""",
|
||||
tenant_id,
|
||||
safe_limit,
|
||||
)
|
||||
else:
|
||||
rows = await conn.fetch(
|
||||
"""
|
||||
SELECT
|
||||
COALESCE(NULLIF(p.city, ''), 'Unknown') AS label,
|
||||
COALESCE(NULLIF(p.city, ''), 'Unknown') AS city,
|
||||
NULL::text AS district,
|
||||
NULL::float AS latitude,
|
||||
NULL::float AS longitude,
|
||||
ROW_NUMBER() OVER (ORDER BY COUNT(*) DESC, COALESCE(NULLIF(p.city, ''), 'Unknown'))::float AS x,
|
||||
ROUND(AVG(COALESCE(q.current_value, 0.0))::numeric, 3)::float AS y,
|
||||
COUNT(DISTINCT p.person_id)::int AS lead_count,
|
||||
ROUND(AVG(COALESCE(q.current_value, 0.0))::numeric, 3)::float AS avg_qd_score,
|
||||
COUNT(DISTINCT p.person_id) FILTER (WHERE COALESCE(q.current_value, 0.0) >= 0.70)::int AS hot_lead_count
|
||||
FROM crm_people p
|
||||
LEFT JOIN crm_leads cl ON cl.person_id = p.person_id AND cl.tenant_id = $1
|
||||
LEFT JOIN LATERAL (
|
||||
SELECT current_value
|
||||
FROM intel_qd_scores q
|
||||
WHERE q.person_id = p.person_id
|
||||
ORDER BY q.computed_at DESC
|
||||
LIMIT 1
|
||||
) q ON TRUE
|
||||
WHERE p.tenant_id = $1
|
||||
GROUP BY COALESCE(NULLIF(p.city, ''), 'Unknown')
|
||||
ORDER BY lead_count DESC, avg_qd_score DESC, label ASC
|
||||
LIMIT $2
|
||||
""",
|
||||
tenant_id,
|
||||
safe_limit,
|
||||
)
|
||||
points = [
|
||||
{
|
||||
"label": row["label"],
|
||||
"city": row["city"],
|
||||
"district": row["district"],
|
||||
"latitude": row["latitude"],
|
||||
"longitude": row["longitude"],
|
||||
"x": row["x"],
|
||||
"y": row["y"],
|
||||
"leadCount": row["lead_count"],
|
||||
"avgQdScore": row["avg_qd_score"],
|
||||
"hotLeadCount": row["hot_lead_count"],
|
||||
}
|
||||
for row in rows
|
||||
]
|
||||
return _ok(
|
||||
{
|
||||
"summary": {
|
||||
"locations": len(points),
|
||||
"leadCount": sum(point["leadCount"] for point in points),
|
||||
"hotLeadCount": sum(point["hotLeadCount"] for point in points),
|
||||
},
|
||||
"points": points,
|
||||
},
|
||||
meta={"source": "lead_geo_interest_rollup" if has_rollup else "crm_people"},
|
||||
)
|
||||
|
||||
|
||||
@router.get("/me", summary="Get current user profile")
|
||||
async def get_me(request: Request, user: UserPrincipal = Depends(get_current_user)) -> dict:
|
||||
return _ok(await _get_current_user_profile(request, user))
|
||||
@@ -298,6 +768,50 @@ async def submit_prompt(
|
||||
) -> dict:
|
||||
page_id = await _resolve_page_id(request, user, page_id)
|
||||
ctx = await _ctx_from_request(request, user)
|
||||
complexity_route, routing_reasons = _oracle_prompt_complexity(payload.prompt, payload.conversationContext)
|
||||
resolved_mode = payload.executionMode
|
||||
if payload.executionMode == "auto":
|
||||
resolved_mode = complexity_route
|
||||
|
||||
if resolved_mode == "thinking":
|
||||
colony_result = await _dispatch_colony_from_oracle_prompt(
|
||||
request=request,
|
||||
ctx=ctx,
|
||||
page_id=page_id,
|
||||
payload=payload,
|
||||
resolved_mode=resolved_mode,
|
||||
routing_reasons=routing_reasons,
|
||||
)
|
||||
execution = colony_result["execution"]
|
||||
page = colony_result["page"]
|
||||
action = await oracle_action_service.create_from_execution(
|
||||
execution=execution,
|
||||
target_entity_type="lead" if payload.targetLeadId else "canvas_page",
|
||||
target_entity_id=payload.targetLeadId or page_id,
|
||||
action_type="oracle_colony_mission_dispatch",
|
||||
writeback_payload={
|
||||
**payload.plannedWriteback,
|
||||
"colonyMissionId": execution["workflowDispatch"]["missionId"],
|
||||
"executionMode": payload.executionMode,
|
||||
"resolvedMode": resolved_mode,
|
||||
},
|
||||
)
|
||||
return _ok({
|
||||
"executionId": execution["executionId"],
|
||||
"actionId": action["actionId"],
|
||||
"status": execution["status"],
|
||||
"executionMode": payload.executionMode,
|
||||
"resolvedMode": resolved_mode,
|
||||
"pageId": page_id,
|
||||
"branchId": payload.branchId,
|
||||
"headRevision": execution.get("headRevision", page.get("headRevision", 0) if page else 0),
|
||||
"componentsCreated": execution.get("componentsCreated", []),
|
||||
"summary": execution.get("summary", ""),
|
||||
"warnings": execution.get("warnings", []),
|
||||
"components": page.get("components", []) if page else [],
|
||||
"colonyMissionId": execution["workflowDispatch"]["missionId"],
|
||||
})
|
||||
|
||||
execution = await prompt_orchestrator.execute(
|
||||
tenant_id=ctx.tenant_id,
|
||||
page_id=page_id,
|
||||
@@ -326,6 +840,8 @@ async def submit_prompt(
|
||||
"executionId": execution["executionId"],
|
||||
"actionId": action["actionId"],
|
||||
"status": execution["status"],
|
||||
"executionMode": payload.executionMode,
|
||||
"resolvedMode": "fast",
|
||||
"pageId": page_id,
|
||||
"branchId": payload.branchId,
|
||||
"headRevision": execution.get("headRevision", page.get("headRevision", 0) if page else 0),
|
||||
@@ -551,4 +1067,3 @@ async def oracle_canvas_ws(ws: WebSocket, page_id: str) -> None:
|
||||
|
||||
|
||||
# ── Pre-made templates seed ───────────────────────────────────────────────────
|
||||
|
||||
|
||||
Reference in New Issue
Block a user