feat(analytics): wire flow usage tracking into session matching and resolution
- In `start_session`: increment `flow.usage_count` and set `flow.last_matched_at` when a flow is matched to a new session; errors are caught without blocking - In `resolve_session`: recalculate `flow.success_rate` as (resolved / total) across all sessions ever matched to the flow after each resolution; errors are caught without blocking the session close Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -11,7 +11,7 @@ from datetime import datetime, timezone
|
|||||||
from typing import Any, Optional
|
from typing import Any, Optional
|
||||||
from uuid import UUID
|
from uuid import UUID
|
||||||
|
|
||||||
from sqlalchemy import select, or_
|
from sqlalchemy import select, func, or_
|
||||||
from sqlalchemy.ext.asyncio import AsyncSession
|
from sqlalchemy.ext.asyncio import AsyncSession
|
||||||
from sqlalchemy.orm import selectinload
|
from sqlalchemy.orm import selectinload
|
||||||
|
|
||||||
@@ -20,6 +20,7 @@ from app.core.config import settings
|
|||||||
from app.services.notification_service import notify
|
from app.services.notification_service import notify
|
||||||
from app.models.ai_session import AISession
|
from app.models.ai_session import AISession
|
||||||
from app.models.ai_session_step import AISessionStep
|
from app.models.ai_session_step import AISessionStep
|
||||||
|
from app.models.tree import Tree
|
||||||
from app.schemas.ai_session import (
|
from app.schemas.ai_session import (
|
||||||
AISessionCreateRequest,
|
AISessionCreateRequest,
|
||||||
AISessionCreateResponse,
|
AISessionCreateResponse,
|
||||||
@@ -308,6 +309,16 @@ async def start_session(
|
|||||||
)
|
)
|
||||||
db.add(session)
|
db.add(session)
|
||||||
|
|
||||||
|
# 7a. Update matched flow usage tracking
|
||||||
|
if matched_flow_id:
|
||||||
|
try:
|
||||||
|
flow_result = await db.get(Tree, matched_flow_id)
|
||||||
|
if flow_result:
|
||||||
|
flow_result.usage_count = (flow_result.usage_count or 0) + 1
|
||||||
|
flow_result.last_matched_at = datetime.now(timezone.utc)
|
||||||
|
except Exception as e:
|
||||||
|
logger.warning("Failed to update flow usage stats for flow %s: %s", matched_flow_id, e)
|
||||||
|
|
||||||
# 7. Create first step
|
# 7. Create first step
|
||||||
step = _create_step_from_parsed(
|
step = _create_step_from_parsed(
|
||||||
session_id=session.id,
|
session_id=session.id,
|
||||||
@@ -457,6 +468,28 @@ async def resolve_session(
|
|||||||
# Queue for Knowledge Flywheel analysis
|
# Queue for Knowledge Flywheel analysis
|
||||||
session.analysis_status = "pending"
|
session.analysis_status = "pending"
|
||||||
|
|
||||||
|
# Recalculate success_rate for the matched flow
|
||||||
|
if session.matched_flow_id:
|
||||||
|
try:
|
||||||
|
flow = await db.get(Tree, session.matched_flow_id)
|
||||||
|
if flow:
|
||||||
|
total_result = await db.execute(
|
||||||
|
select(func.count(AISession.id))
|
||||||
|
.where(AISession.matched_flow_id == flow.id)
|
||||||
|
)
|
||||||
|
resolved_result = await db.execute(
|
||||||
|
select(func.count(AISession.id))
|
||||||
|
.where(
|
||||||
|
AISession.matched_flow_id == flow.id,
|
||||||
|
AISession.status == "resolved",
|
||||||
|
)
|
||||||
|
)
|
||||||
|
total = total_result.scalar() or 0
|
||||||
|
resolved_count = resolved_result.scalar() or 0
|
||||||
|
flow.success_rate = round(resolved_count / total, 3) if total else None
|
||||||
|
except Exception as e:
|
||||||
|
logger.warning("Failed to recalculate success_rate for flow %s: %s", session.matched_flow_id, e)
|
||||||
|
|
||||||
await db.flush()
|
await db.flush()
|
||||||
|
|
||||||
# Push documentation to PSA if ticket is linked
|
# Push documentation to PSA if ticket is linked
|
||||||
|
|||||||
Reference in New Issue
Block a user