correlation upgrades
This commit is contained in:
@@ -17,8 +17,9 @@ class Settings(BaseSettings):
|
||||
# Node health
|
||||
node_offline_threshold: int = 90 # seconds without checkin before marking offline
|
||||
|
||||
# OpenAI (Whisper STT)
|
||||
# OpenAI (STT + intelligence)
|
||||
openai_api_key: Optional[str] = None
|
||||
stt_model: str = "gpt-4o-transcribe" # whisper-1 | gpt-4o-mini-transcribe | gpt-4o-transcribe
|
||||
|
||||
# Gemini (intelligence extraction, embeddings, incident summaries)
|
||||
gemini_api_key: Optional[str] = None
|
||||
@@ -31,6 +32,7 @@ class Settings(BaseSettings):
|
||||
incident_auto_resolve_minutes: int = 90 # auto-resolve after N minutes with no new calls
|
||||
recorrelation_scan_minutes: int = 60 # re-examine orphaned calls ended within this window
|
||||
tg_fast_path_idle_minutes: int = 90 # fast path: max minutes since incident last updated
|
||||
tg_dispatch_thin_idle_minutes: int = 10 # dispatch channels only: thin calls only attach to incidents idle < this many minutes
|
||||
|
||||
# Vocabulary learning
|
||||
vocabulary_induction_interval_hours: int = 24 # how often the induction loop runs
|
||||
|
||||
@@ -249,16 +249,33 @@ async def correlate_call(
|
||||
|
||||
if tg_recent and is_thin_call:
|
||||
# Status/ack call — no scene data to reason about.
|
||||
# Attach to whichever recent incident was most recently active on this TGID.
|
||||
matched_incident = max(tg_recent, key=lambda inc: inc.get("updated_at", ""))
|
||||
corr_debug = {
|
||||
"corr_path": "fast/thin",
|
||||
"corr_incident_idle_min": round(_incident_idle_minutes(matched_incident, now), 1),
|
||||
}
|
||||
logger.info(
|
||||
f"Correlator fast-path (thin→last TGID incident): "
|
||||
f"call {call_id} → {matched_incident['incident_id']}"
|
||||
)
|
||||
# On dispatch channels (shared backbone), apply a much tighter idle gate so
|
||||
# a "10-4" or "Dispatch." doesn't re-activate an incident that's been quiet
|
||||
# for an hour and then absorb the next unrelated dispatch on the same TGID.
|
||||
if is_dispatch:
|
||||
thin_pool = [
|
||||
inc for inc in tg_recent
|
||||
if _incident_idle_minutes(inc, now) <= settings.tg_dispatch_thin_idle_minutes
|
||||
]
|
||||
else:
|
||||
thin_pool = tg_recent
|
||||
|
||||
if not thin_pool:
|
||||
logger.info(
|
||||
f"Correlator fast-path thin: dispatch channel idle > "
|
||||
f"{settings.tg_dispatch_thin_idle_minutes}min, skipping thin call {call_id}"
|
||||
)
|
||||
else:
|
||||
# Attach to whichever pool incident was most recently active on this TGID.
|
||||
matched_incident = max(thin_pool, key=lambda inc: inc.get("updated_at", ""))
|
||||
corr_debug = {
|
||||
"corr_path": "fast/thin",
|
||||
"corr_incident_idle_min": round(_incident_idle_minutes(matched_incident, now), 1),
|
||||
}
|
||||
logger.info(
|
||||
f"Correlator fast-path (thin→last TGID incident): "
|
||||
f"call {call_id} → {matched_incident['incident_id']}"
|
||||
)
|
||||
elif len(tg_recent) == 1:
|
||||
candidate = tg_recent[0]
|
||||
if _call_fits_incident(
|
||||
|
||||
@@ -116,7 +116,7 @@ def _sync_transcribe(
|
||||
openai_client = OpenAI(api_key=settings.openai_api_key)
|
||||
with open(tmp_path, "rb") as f:
|
||||
response = openai_client.audio.transcriptions.create(
|
||||
model="whisper-1",
|
||||
model=settings.stt_model,
|
||||
file=f,
|
||||
language="en",
|
||||
prompt=prompt,
|
||||
|
||||
@@ -18,7 +18,7 @@ import asyncio
|
||||
import difflib
|
||||
import json
|
||||
import random
|
||||
from datetime import datetime, timezone
|
||||
from datetime import datetime, timezone, timedelta
|
||||
from typing import Optional
|
||||
from app.internal.logger import logger
|
||||
from app.internal import firestore as fstore
|
||||
|
||||
@@ -85,9 +85,29 @@ function CorrelationDebugTab() {
|
||||
}
|
||||
}
|
||||
|
||||
async function handleCopy() {
|
||||
function handleCopy() {
|
||||
if (!data) return;
|
||||
await navigator.clipboard.writeText(JSON.stringify(data, null, 2));
|
||||
const text = JSON.stringify(data, null, 2);
|
||||
if (navigator.clipboard) {
|
||||
navigator.clipboard.writeText(text).then(() => {
|
||||
setCopied(true);
|
||||
setTimeout(() => setCopied(false), 2000);
|
||||
}).catch(() => fallbackCopy(text));
|
||||
} else {
|
||||
fallbackCopy(text);
|
||||
}
|
||||
}
|
||||
|
||||
function fallbackCopy(text: string) {
|
||||
const ta = document.createElement("textarea");
|
||||
ta.value = text;
|
||||
ta.style.position = "fixed";
|
||||
ta.style.opacity = "0";
|
||||
document.body.appendChild(ta);
|
||||
ta.focus();
|
||||
ta.select();
|
||||
document.execCommand("copy");
|
||||
document.body.removeChild(ta);
|
||||
setCopied(true);
|
||||
setTimeout(() => setCopied(false), 2000);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user