ape-dev
Add admin UI to tune the UCB formula live (c + reward range width). New /config/ucb GET+POST, bandit_config/ucb stored in Mongo + audited, applied to the live selector without redeploy; UCB Formula tab in admin. selection.py reads live params via get_ucb_params
2895b1a
Raw
History Blame Contribute Delete
10.8 kB
/**
* API client — thin wrapper around fetch() for FastAPI endpoints.
* All paths are relative; Vite proxies them to :7860 in dev,
* and FastAPI serves them at the same origin in production.
*/
async function request(method, path, body) {
const headers = { "Content-Type": "application/json" };
const opts = { method, headers };
if (body !== undefined) opts.body = JSON.stringify(body);
const resp = await fetch(path, opts);
if (!resp.ok) {
let detail = await resp.text();
try { detail = JSON.parse(detail).detail || detail; } catch {}
throw new Error(`${resp.status} ${resp.statusText}: ${detail}`);
}
if (resp.status === 204) return null;
return resp.json();
}
export const api = {
// Core flow
health: () => request("GET", "/health"),
postTurn: (payload) => request("POST", "/turn", payload),
postFeedback: (payload) => request("POST", "/feedback", payload),
// Conversation history (Mongo-backed)
loadSessionMessages: (sessionId, userId, limit = 200) => request("GET", `/sessions/${encodeURIComponent(sessionId)}/messages?user_id=${encodeURIComponent(userId)}&limit=${limit}`),
listUserSessions: (userId, limit = 20) => request("GET", `/users/${encodeURIComponent(userId)}/sessions?limit=${limit}`),
getLatestSession: (userId) => request("GET", `/users/${encodeURIComponent(userId)}/latest-session`),
deleteSession: (sessionId, userId) => request("DELETE", `/sessions/${encodeURIComponent(sessionId)}?user_id=${encodeURIComponent(userId)}`),
// Config — read
listIntents: () => request("GET", "/config/intents"),
listStrategies: () => request("GET", "/config/strategies"),
listPolicies: () => request("GET", "/config/policies"),
listSignalRules: () => request("GET", "/config/signal-rules"),
listRewardScale: () => request("GET", "/config/reward-scale"),
listInstructions: (strategyId = "", status = "") => request("GET", `/config/instructions${strategyId || status ? `?${[strategyId && `strategy_id=${encodeURIComponent(strategyId)}`, status && `status=${encodeURIComponent(status)}`].filter(Boolean).join("&")}` : ""}`),
// Config — write
upsertIntent: (payload) => request("POST", "/config/intents", payload),
upsertStrategy: (payload) => request("POST", "/config/strategies", payload),
upsertSignalRule: (payload) => request("POST", "/config/signal-rules", payload),
upsertRewardValue: (payload) => request("POST", "/config/reward-scale", payload),
getUcbConfig: () => request("GET", "/config/ucb"),
updateUcbConfig: (payload) => request("POST", "/config/ucb", payload),
upsertPolicy: (payload) => request("POST", "/config/policies", payload),
publishInstruction: (payload) => request("POST", "/config/instructions", payload),
activateInstruction: (strategyId, version, changedBy="admin_user") =>
request("POST", `/config/instructions/activate?strategy_id=${encodeURIComponent(strategyId)}&version=${encodeURIComponent(version)}&changed_by=${encodeURIComponent(changedBy)}`),
// Config — flip ACTIVE / INACTIVE / DRAFT on any config doc
setConfigStatus: (entityType, entityId, status, version) =>
request("POST", "/config/status", {
entity_type: entityType,
entity_id: entityId,
status,
...(version ? { version } : {}),
}),
// Config — delete (every entity type)
deleteIntent: (intentId) => request("DELETE", `/config/intents/${encodeURIComponent(intentId)}`),
deleteStrategy: (strategyId) => request("DELETE", `/config/strategies/${encodeURIComponent(strategyId)}`),
deleteSignalRule: (signalName) => request("DELETE", `/config/signal-rules/${encodeURIComponent(signalName)}`),
deleteRewardValue: (category) => request("DELETE", `/config/reward-scale/${encodeURIComponent(category)}`),
deletePolicy: (intent, topic, strategyId) =>
request("DELETE", `/config/policies?intent=${encodeURIComponent(intent)}&topic=${encodeURIComponent(topic)}&strategy_id=${encodeURIComponent(strategyId)}`),
deleteInstruction: (strategyId, version) =>
request("DELETE", `/config/instructions/${encodeURIComponent(strategyId)}/${encodeURIComponent(version)}`),
// Offer policies — full CRUD
listOffers: () => request("GET", "/config/offers"),
upsertOffer: (payload) => request("POST", "/config/offers", payload),
deleteOffer: (topic) => request("DELETE", `/config/offers/${encodeURIComponent(topic)}`),
// Admin / ops
clearUser: (userId) => request("DELETE", `/admin/clear-user/${encodeURIComponent(userId)}`),
clearAll: () => request("DELETE", "/admin/clear-all"),
rebuildBandit: () => request("POST", "/admin/rebuild-bandit"),
dbSnapshot: (userId, limit = 30) => request("GET", `/admin/db-snapshot?user_id=${encodeURIComponent(userId || "")}&limit=${limit}`),
banditState: (userId = "", onlyPulled = true) => request("GET", `/admin/bandit-state?only_pulled=${onlyPulled}${userId ? `&user_id=${encodeURIComponent(userId)}` : ""}`),
resetBanditCell: (userId, domain, intent, topic, strategy = "") =>
request("DELETE", `/admin/bandit-state/cell?user_id=${encodeURIComponent(userId)}&domain=${encodeURIComponent(domain)}&intent=${encodeURIComponent(intent)}&topic=${encodeURIComponent(topic)}${strategy ? `&strategy=${encodeURIComponent(strategy)}` : ""}`),
listAudit: (date = "", limit = 100) => request("GET", `/admin/audit?${date ? `date=${date}&` : ""}limit=${limit}`),
seed: () => request("POST", "/admin/seed"),
// Analytics — business layer
recomputeAnalytics: (days = 14) => request("POST", `/analytics/recompute?days=${days}`),
userInterests: (userId, limit = 10, refresh = false) =>
request("GET", `/analytics/user-interests?user_id=${encodeURIComponent(userId)}&limit=${limit}&refresh=${refresh}`),
topicUsers: (topic, limit = 20, minScore = 0.5) =>
request("GET", `/analytics/topic-users?topic=${encodeURIComponent(topic)}&limit=${limit}&min_score=${minScore}`),
trends: (days = 7, limit = 30, refresh = false, domain = "") =>
request("GET", `/analytics/trends?days=${days}&limit=${limit}&refresh=${refresh}${domain ? `&domain=${encodeURIComponent(domain)}` : ""}`),
topicTimeseries: (topic, days = 30, domain = "") =>
request("GET", `/analytics/topic-timeseries?topic=${encodeURIComponent(topic)}&days=${days}${domain ? `&domain=${encodeURIComponent(domain)}` : ""}`),
platformTimeseries: (days = 30, domain = "") =>
request("GET", `/analytics/platform-timeseries?days=${days}${domain ? `&domain=${encodeURIComponent(domain)}` : ""}`),
topicsTimeseries: (days = 30, topN = 5, domain = "") =>
request("GET", `/analytics/topics-timeseries?days=${days}&top_n=${topN}${domain ? `&domain=${encodeURIComponent(domain)}` : ""}`),
userTimeseries: (userId, days = 30, domain = "") =>
request("GET", `/analytics/user-timeseries?user_id=${encodeURIComponent(userId)}&days=${days}${domain ? `&domain=${encodeURIComponent(domain)}` : ""}`),
userOffers: (userId) =>
request("GET", `/analytics/offers/${encodeURIComponent(userId)}`),
// Pass userId="" or null to get the GLOBAL aggregate view across all users.
cognitiveFacets: (userId = "", minInteractions = 1, domain = "") =>
request("GET", `/analytics/cognitive-facets?min_interactions=${minInteractions}${userId ? `&user_id=${encodeURIComponent(userId)}` : ""}${domain ? `&domain=${encodeURIComponent(domain)}` : ""}`),
activeUsers: (days = 1, minInterest = 0, limit = 100, domain = "") =>
request("GET", `/analytics/active-users?days=${days}&min_interest=${minInterest}&limit=${limit}${domain ? `&domain=${encodeURIComponent(domain)}` : ""}`),
userProfile: (userId, domain = "") =>
request("GET", `/analytics/user-profile?user_id=${encodeURIComponent(userId)}${domain ? `&domain=${encodeURIComponent(domain)}` : ""}`),
unmappedIntents: (days = 30, limit = 50, domain = "") =>
request("GET", `/analytics/unmapped-intents?days=${days}&limit=${limit}${domain ? `&domain=${encodeURIComponent(domain)}` : ""}`),
platformOverview: (days = 30, topN = 8, domain = "") =>
request("GET", `/analytics/platform-overview?days=${days}&top_n=${topN}${domain ? `&domain=${encodeURIComponent(domain)}` : ""}`),
strategyPerformance: (userId = "", minPulls = 3, domain = "") =>
request("GET", `/analytics/strategy-performance?min_pulls=${minPulls}${userId ? `&user_id=${encodeURIComponent(userId)}` : ""}${domain ? `&domain=${encodeURIComponent(domain)}` : ""}`),
instructionQuality: (days = 14, minTurns = 5, sampleLimit = 5) =>
request("GET", `/analytics/instruction-quality?days=${days}&min_turns=${minTurns}&sample_limit=${sampleLimit}`),
customerHealth: (days = 30, cohortWeeks = 4) =>
request("GET", `/analytics/customer-health?days=${days}&cohort_weeks=${cohortWeeks}`),
ragQuality: (days = 14, minTurns = 5, sampleLimit = 5) =>
request("GET", `/analytics/rag-quality?days=${days}&min_turns=${minTurns}&sample_limit=${sampleLimit}`),
};