feat(l1): AI decision-tree builder — Phase 2A #193
@@ -1,7 +1,10 @@
|
||||
import { apiClient } from './client'
|
||||
import type {
|
||||
IntakeRequest,
|
||||
IntakeResponse,
|
||||
IntakeResult,
|
||||
L1Categories,
|
||||
NextNodeRequest,
|
||||
NextNodeResult,
|
||||
QueueRow,
|
||||
WalkSession,
|
||||
AdhocNote,
|
||||
@@ -9,7 +12,23 @@ import type {
|
||||
|
||||
export const l1Api = {
|
||||
intake: (body: IntakeRequest) =>
|
||||
apiClient.post<IntakeResponse>('/l1/intake', body).then(r => r.data),
|
||||
apiClient.post<IntakeResult>('/l1/intake', body).then(r => r.data),
|
||||
|
||||
nextNode: (sessionId: string, body: NextNodeRequest) =>
|
||||
apiClient
|
||||
.post<NextNodeResult>(`/l1/sessions/${sessionId}/next-node`, body)
|
||||
.then(r => r.data),
|
||||
|
||||
escalations: () =>
|
||||
apiClient.get<WalkSession[]>('/l1/escalations').then(r => r.data),
|
||||
|
||||
getCategories: () =>
|
||||
apiClient.get<L1Categories>('/accounts/me/l1-categories').then(r => r.data),
|
||||
|
||||
setCategories: (enabled: string[]) =>
|
||||
apiClient
|
||||
.patch<L1Categories>('/accounts/me/l1-categories', { enabled })
|
||||
.then(r => r.data),
|
||||
|
||||
queue: (statusFilter?: string) =>
|
||||
apiClient.get<QueueRow[]>('/l1/queue', {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
export type SessionKind = 'flow' | 'proposal' | 'adhoc'
|
||||
export type SessionKind = 'flow' | 'proposal' | 'adhoc' | 'ai_build'
|
||||
export type SessionStatus = 'active' | 'resolved' | 'escalated' | 'abandoned'
|
||||
export type TicketKind = 'psa' | 'internal'
|
||||
|
||||
@@ -42,11 +42,53 @@ export interface IntakeRequest {
|
||||
customer_name?: string
|
||||
customer_contact?: string
|
||||
flow_id?: string
|
||||
force_build?: boolean
|
||||
}
|
||||
|
||||
export interface IntakeResponse {
|
||||
session_id: string
|
||||
session_kind: SessionKind
|
||||
ticket_id: string
|
||||
ticket_kind: TicketKind
|
||||
export type IntakeOutcome = 'matched' | 'suggest' | 'out_of_scope' | 'build'
|
||||
|
||||
export interface NearMiss {
|
||||
flow_id: string
|
||||
flow_name: string
|
||||
score: number
|
||||
}
|
||||
|
||||
/** Phase 2A intake response — `outcome` drives the frontend dispatch.
|
||||
* Session fields are present only for `matched` / `build`. */
|
||||
export interface IntakeResult {
|
||||
outcome: IntakeOutcome
|
||||
session_id?: string
|
||||
session_kind?: SessionKind
|
||||
ticket_id?: string
|
||||
ticket_kind?: TicketKind
|
||||
flow_id?: string // for 'matched'
|
||||
near_miss?: NearMiss // for 'suggest'
|
||||
category?: string // for 'out_of_scope'
|
||||
}
|
||||
|
||||
/** A single node of an AI-built decision tree, returned by /next-node. */
|
||||
export type TreeNode =
|
||||
| { node_type: 'question'; id: string; text: string }
|
||||
| { node_type: 'instruction'; id: string; text: string }
|
||||
| { node_type: 'resolved'; id: string; text: string }
|
||||
| { node_type: 'escalate'; id: string; reason_category?: string; text: string }
|
||||
| { node_type: 'needs_review'; id: string; text: string }
|
||||
|
||||
export interface NextNodeRequest {
|
||||
node_id?: string
|
||||
node_text?: string // rendered text of the node being answered
|
||||
answer?: 'yes' | 'no'
|
||||
acknowledged?: boolean
|
||||
note?: string
|
||||
}
|
||||
|
||||
export interface NextNodeResult {
|
||||
node: TreeNode
|
||||
session_status: string
|
||||
}
|
||||
|
||||
export interface L1Categories {
|
||||
enabled: string[]
|
||||
available: string[]
|
||||
hard_floor: string[]
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user