mirror of
https://dev.azure.com/globalhealthx/EMR/_git/helix-engage-server
synced 2026-05-18 20:08:19 +00:00
feat(agent-lookup): resolve by Ozonetel display name too
Inbound webhook rows store agentName as Ozonetel's display string
("Ganesh Bandi", "GlobalHealthX") which doesn't match either
ozonetelAgentId or platform Agent.name. Add ozonetelDisplayName as a
third index — populated on each platform Agent with the Full Name from
the Ozonetel admin UI.
After this + setting display names on Global + Ramaiah agents:
backfill matched 122/136 (~90%) on Global; remaining are calls handled
by agents that don't exist on this workspace.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -611,16 +611,21 @@ export class MaintController {
|
||||
this.logger.log('[MAINT] Backfill call agents by name — matching agentName last-segment to Agent entity');
|
||||
|
||||
// Pull all active agents — cheap, cached at service level but we
|
||||
// also need a name→UUID map for this pass.
|
||||
// also need name → UUID maps for this pass. Three indexes:
|
||||
// - ozonetelAgentId (e.g. "globalhealthx") — matches outbound dispose rows
|
||||
// - ozonetelDisplayName (e.g. "Ganesh Bandi") — matches inbound webhook rows
|
||||
// - platform Agent.name (e.g. "Ganesh Iyer") — last-resort fallback
|
||||
const agentData = await this.platform.query<any>(
|
||||
`{ agents(first: 100) { edges { node { id name ozonetelAgentId } } } }`,
|
||||
`{ agents(first: 100) { edges { node { id name ozonetelAgentId ozonetelDisplayName } } } }`,
|
||||
);
|
||||
const agentUuidByName = new Map<string, string>(); // lowercase name → UUID
|
||||
const agentUuidByOzonetelId = new Map<string, string>(); // lowercase ozonetelAgentId → UUID
|
||||
const agentUuidByName = new Map<string, string>();
|
||||
const agentUuidByOzonetelId = new Map<string, string>();
|
||||
const agentUuidByDisplayName = new Map<string, string>();
|
||||
for (const edge of agentData?.agents?.edges ?? []) {
|
||||
const a = edge.node;
|
||||
if (a.name) agentUuidByName.set(a.name.toLowerCase().trim(), a.id);
|
||||
if (a.ozonetelAgentId) agentUuidByOzonetelId.set(a.ozonetelAgentId.toLowerCase().trim(), a.id);
|
||||
if (a.ozonetelDisplayName) agentUuidByDisplayName.set(a.ozonetelDisplayName.toLowerCase().trim(), a.id);
|
||||
}
|
||||
|
||||
let scanned = 0;
|
||||
@@ -654,9 +659,14 @@ export class MaintController {
|
||||
const last = segments[segments.length - 1];
|
||||
if (!last) { skipped++; continue; }
|
||||
|
||||
// Prefer ozonetelAgentId match; fall back to display name.
|
||||
// Prefer ozonetelAgentId match (outbound rows store
|
||||
// agentName=agentId); fall back to ozonetelDisplayName
|
||||
// (inbound webhook rows store the Ozonetel display string);
|
||||
// last-resort match on platform Agent.name.
|
||||
const key = last.toLowerCase();
|
||||
const uuid = agentUuidByOzonetelId.get(key) ?? agentUuidByName.get(key);
|
||||
const uuid = agentUuidByOzonetelId.get(key)
|
||||
?? agentUuidByDisplayName.get(key)
|
||||
?? agentUuidByName.get(key);
|
||||
if (!uuid) {
|
||||
unmatched++;
|
||||
if (unmatchedSamples.size < 10) unmatchedSamples.add(last);
|
||||
|
||||
@@ -16,6 +16,7 @@ import { PlatformGraphqlService } from './platform-graphql.service';
|
||||
export class AgentLookupService implements OnModuleInit {
|
||||
private readonly logger = new Logger(AgentLookupService.name);
|
||||
private readonly uuidByOzonetelId = new Map<string, string>();
|
||||
private readonly uuidByDisplayName = new Map<string, string>();
|
||||
|
||||
constructor(private readonly platform: PlatformGraphqlService) {}
|
||||
|
||||
@@ -26,17 +27,21 @@ export class AgentLookupService implements OnModuleInit {
|
||||
async refresh(): Promise<void> {
|
||||
try {
|
||||
const data = await this.platform.query<any>(
|
||||
`{ agents(first: 100) { edges { node { id ozonetelAgentId } } } }`,
|
||||
`{ agents(first: 100) { edges { node { id ozonetelAgentId ozonetelDisplayName } } } }`,
|
||||
);
|
||||
const edges = data?.agents?.edges ?? [];
|
||||
this.uuidByOzonetelId.clear();
|
||||
this.uuidByDisplayName.clear();
|
||||
for (const edge of edges) {
|
||||
const n = edge.node;
|
||||
if (n.ozonetelAgentId) {
|
||||
this.uuidByOzonetelId.set(n.ozonetelAgentId.toLowerCase(), n.id);
|
||||
}
|
||||
if (n.ozonetelDisplayName) {
|
||||
this.uuidByDisplayName.set(n.ozonetelDisplayName.toLowerCase().trim(), n.id);
|
||||
}
|
||||
this.logger.log(`[AGENT-LOOKUP] Loaded ${this.uuidByOzonetelId.size} agents`);
|
||||
}
|
||||
this.logger.log(`[AGENT-LOOKUP] Loaded ${this.uuidByOzonetelId.size} agents (${this.uuidByDisplayName.size} with display name)`);
|
||||
} catch (err) {
|
||||
this.logger.warn(`[AGENT-LOOKUP] Refresh failed: ${err}`);
|
||||
}
|
||||
@@ -51,4 +56,15 @@ export class AgentLookupService implements OnModuleInit {
|
||||
await this.refresh();
|
||||
return this.uuidByOzonetelId.get(key) ?? null;
|
||||
}
|
||||
|
||||
// Resolve by Ozonetel display name (e.g. "Ganesh Bandi") — used by
|
||||
// missed-call webhook backfill where only AgentName (display) is available.
|
||||
async resolveByDisplayName(displayName: string | null | undefined): Promise<string | null> {
|
||||
if (!displayName) return null;
|
||||
const key = displayName.toLowerCase().trim();
|
||||
const cached = this.uuidByDisplayName.get(key);
|
||||
if (cached) return cached;
|
||||
await this.refresh();
|
||||
return this.uuidByDisplayName.get(key) ?? null;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user