All checks were successful
CI / test (push) Successful in 4m1s
方案③的定时触发(scan loop 每 60s 入队 + 触发 worker)此前只有 Worker
消费端 API,无可查看的列表。新增:
- GET /api/intelligent-evals/tasks:任务明细(含评估名/状态)+ 状态分布统计
(注册在 /{eval_id} 之前避免被捕获为 eval_id="tasks")
- 前端 TaskQueueMonitor 组件 + 智能评估页任务队列入口:5s 轮询
(usePolling),状态卡 + 状态筛选 + 明细表
测试:+3(列表/筛选/不被 {eval_id} 遮蔽),892 passed,tsc 通过
932 lines
26 KiB
TypeScript
932 lines
26 KiB
TypeScript
import axios from 'axios'
|
||
import { message } from 'antd'
|
||
|
||
const AUTH_TOKEN_KEY = 'agenteval_auth_token'
|
||
|
||
export const authToken = {
|
||
get: () => sessionStorage.getItem(AUTH_TOKEN_KEY),
|
||
set: (token: string) => sessionStorage.setItem(AUTH_TOKEN_KEY, token),
|
||
clear: () => sessionStorage.removeItem(AUTH_TOKEN_KEY),
|
||
}
|
||
|
||
/** Registered by App to switch back to the login screen on 401. */
|
||
let onUnauthorized: (() => void) | null = null
|
||
export const setOnUnauthorized = (handler: (() => void) | null) => { onUnauthorized = handler }
|
||
|
||
const api = axios.create({
|
||
baseURL: '/api',
|
||
timeout: 30000,
|
||
})
|
||
|
||
api.interceptors.request.use((config) => {
|
||
const token = authToken.get()
|
||
if (token) config.headers['X-Auth-Token'] = token
|
||
return config
|
||
})
|
||
|
||
api.interceptors.response.use(
|
||
(response) => response,
|
||
(error) => {
|
||
const isLoginCall = error.config?.url?.includes('/auth/login')
|
||
if (error.response?.status === 401 && !isLoginCall && onUnauthorized) {
|
||
authToken.clear()
|
||
onUnauthorized()
|
||
return Promise.reject(error)
|
||
}
|
||
const msg = error.response?.data?.detail || error.message || '请求失败'
|
||
message.error(msg)
|
||
return Promise.reject(error)
|
||
},
|
||
)
|
||
|
||
export default api
|
||
|
||
export interface AuthStatus {
|
||
auth_required: boolean
|
||
authenticated: boolean
|
||
}
|
||
|
||
export const authApi = {
|
||
status: () => api.get<AuthStatus>('/auth/status'),
|
||
login: (password: string) => api.post<{ token: string }>('/auth/login', { password }),
|
||
}
|
||
|
||
export interface Target {
|
||
id: string
|
||
name: string
|
||
description: string
|
||
platform: string
|
||
channel_type: string
|
||
channel_config: Record<string, unknown>
|
||
status: string
|
||
created_at: string
|
||
updated_at: string
|
||
}
|
||
|
||
export interface Scenario {
|
||
id: string
|
||
name: string
|
||
description: string
|
||
tags: string[]
|
||
cases: any[]
|
||
model_bindings: Record<string, string>
|
||
version: number
|
||
created_at: string
|
||
updated_at: string
|
||
}
|
||
|
||
export type ModelCapability = 'chat' | 'embedding' | 'moderation'
|
||
export type ModelProtocol = 'openai_compatible' | 'anthropic' | 'google_gemini' | 'dashscope'
|
||
export type ModelModality = 'text' | 'image' | 'audio' | 'video'
|
||
|
||
export interface ModelConfig {
|
||
id: string
|
||
name: string
|
||
provider: ModelProtocol
|
||
capability: ModelCapability
|
||
endpoint_url: string
|
||
model_name: string | null
|
||
vendor_name: string
|
||
input_modalities: ModelModality[]
|
||
output_modalities: ModelModality[]
|
||
context_window: number | null
|
||
max_output_tokens: number | null
|
||
supports_streaming: boolean
|
||
supports_tool_calling: boolean
|
||
supports_structured_output: boolean
|
||
supports_reasoning: boolean
|
||
region: string
|
||
documentation_url: string | null
|
||
has_api_key: boolean
|
||
enabled: boolean
|
||
is_default: boolean
|
||
is_analysis_default: boolean
|
||
description: string
|
||
created_at: string | null
|
||
updated_at: string | null
|
||
}
|
||
|
||
export interface ModelConfigPayload {
|
||
name: string
|
||
provider: ModelProtocol
|
||
capability: ModelCapability
|
||
endpoint_url: string
|
||
model_name?: string | null
|
||
vendor_name: string
|
||
input_modalities: ModelModality[]
|
||
output_modalities: ModelModality[]
|
||
context_window?: number | null
|
||
max_output_tokens?: number | null
|
||
supports_streaming: boolean
|
||
supports_tool_calling: boolean
|
||
supports_structured_output: boolean
|
||
supports_reasoning: boolean
|
||
region: string
|
||
documentation_url?: string | null
|
||
api_key?: string | null
|
||
clear_api_key?: boolean
|
||
enabled: boolean
|
||
is_default: boolean
|
||
is_analysis_default?: boolean
|
||
description: string
|
||
}
|
||
|
||
export interface ModelConfigReference {
|
||
scenario_id: string
|
||
scenario_name: string
|
||
purpose: string
|
||
}
|
||
|
||
export type RunTrigger = 'manual' | 'ai_assistant' | 'cli' | 'campaign'
|
||
|
||
export interface RunError {
|
||
code: string
|
||
message: string
|
||
}
|
||
|
||
export interface RunSummary {
|
||
total_cases: number
|
||
passed_cases: number
|
||
failed_cases: number
|
||
total_rules: number
|
||
passed_rules: number
|
||
pass_rate: number | null
|
||
avg_latency_ms: number | null
|
||
case_outcomes: Record<string, { passed: boolean; connectivity: boolean }>
|
||
case_errors: Array<Record<string, string>>
|
||
error: RunError | null
|
||
}
|
||
|
||
export interface Run {
|
||
id: string
|
||
target_id: string
|
||
scenario_id: string
|
||
scenario_version?: number
|
||
campaign_id?: string | null
|
||
status: string
|
||
triggered_by?: RunTrigger
|
||
scenario_name?: string | null
|
||
target_name?: string | null
|
||
started_at: string
|
||
completed_at: string | null
|
||
summary: RunSummary | null
|
||
}
|
||
|
||
export interface ScenarioStat {
|
||
scenario_id: string
|
||
scenario_name: string
|
||
run_count: number
|
||
avg_pass_rate: number | null
|
||
last_run_at: string | null
|
||
}
|
||
|
||
export interface DashboardStats {
|
||
targets_count: number
|
||
scenarios_count: number
|
||
runs_count: number
|
||
model_configs_count: number
|
||
today_runs: number
|
||
running_count: number
|
||
overall_pass_rate: number | null
|
||
trigger_breakdown: Partial<Record<RunTrigger, number>>
|
||
scenario_stats: ScenarioStat[]
|
||
recent_runs: Run[]
|
||
}
|
||
|
||
export interface TrendPoint {
|
||
date: string
|
||
pass_rate: number
|
||
run_count: number
|
||
}
|
||
|
||
export const targetsApi = {
|
||
list: () => api.get<Target[]>('/targets'),
|
||
get: (id: string) => api.get<Target>(`/targets/${id}`),
|
||
create: (data: Partial<Target>) => api.post<Target>('/targets', data),
|
||
update: (id: string, data: Partial<Target>) => api.put<Target>(`/targets/${id}`, data),
|
||
delete: (id: string) => api.delete(`/targets/${id}`),
|
||
test: (id: string) => api.post<{ ok: boolean; message: string }>(`/targets/${id}/test`),
|
||
runs: (id: string) => api.get<Run[]>(`/targets/${id}/runs`),
|
||
}
|
||
|
||
export const scenariosApi = {
|
||
list: () => api.get<Scenario[]>('/scenarios'),
|
||
get: (id: string) => api.get<Scenario>(`/scenarios/${id}`),
|
||
create: (data: Partial<Scenario>) => api.post<Scenario>('/scenarios', data),
|
||
update: (id: string, data: Partial<Scenario>) => api.put<Scenario>(`/scenarios/${id}`, data),
|
||
delete: (id: string) => api.delete(`/scenarios/${id}`),
|
||
validate: (data: any) => api.post<{ valid: boolean; errors: string[] }>('/scenarios/validate', data),
|
||
listTemplates: () => api.get<any[]>('/scenarios/templates'),
|
||
getTemplate: (id: string) => api.get<any>(`/scenarios/templates/${id}`),
|
||
}
|
||
|
||
export const modelConfigsApi = {
|
||
list: (params?: { capability?: ModelCapability; enabled?: boolean }) =>
|
||
api.get<ModelConfig[]>('/model-configs', { params }),
|
||
get: (id: string) => api.get<ModelConfig>(`/model-configs/${id}`),
|
||
create: (data: ModelConfigPayload) => api.post<ModelConfig>('/model-configs', data),
|
||
update: (id: string, data: ModelConfigPayload) => api.put<ModelConfig>(`/model-configs/${id}`, data),
|
||
delete: (id: string) => api.delete(`/model-configs/${id}`),
|
||
test: (id: string) => api.post<{ ok: boolean; message: string; tested_at: string }>(`/model-configs/${id}/test`),
|
||
references: (id: string) => api.get<ModelConfigReference[]>(`/model-configs/${id}/references`),
|
||
}
|
||
|
||
export const runsApi = {
|
||
list: () => api.get<Run[]>('/runs'),
|
||
start: (target_id: string, scenario_id: string) =>
|
||
api.post<Run>('/runs', { target_id, scenario_id }),
|
||
get: (id: string) => api.get<Run>(`/runs/${id}`),
|
||
logs: (id: string) => api.get<RunLogsResponse>(`/runs/${id}/logs`),
|
||
cancel: (id: string) => api.post(`/runs/${id}/cancel`),
|
||
}
|
||
|
||
export interface RunLogsTurn {
|
||
id: string
|
||
case_id: string
|
||
round_index: number
|
||
latency_ms: number | null
|
||
sent_text: string
|
||
reply_text: string
|
||
sent_at: string | null
|
||
received_at: string | null
|
||
}
|
||
|
||
export interface RunLogsResult {
|
||
case_id: string
|
||
rule_type: string
|
||
passed: boolean
|
||
score: number | null
|
||
reason: string | null
|
||
}
|
||
|
||
export interface CaseSnapshotExpectations {
|
||
intent?: string | null
|
||
keywords_include: string[]
|
||
keywords_exclude: string[]
|
||
response_time_max_ms?: number | null
|
||
coherence_min_score?: number | null
|
||
}
|
||
|
||
export interface CaseSnapshot {
|
||
id: string
|
||
type: string
|
||
messages: string[]
|
||
prompt?: string | null
|
||
turns?: number | null
|
||
expectations: CaseSnapshotExpectations
|
||
eval_rules: { type: string; params: Record<string, unknown> }[]
|
||
}
|
||
|
||
export interface RunLogsResponse {
|
||
turns: RunLogsTurn[]
|
||
results: RunLogsResult[]
|
||
case_verdicts: Record<string, { passed: boolean; connectivity: boolean }>
|
||
scenario_snapshot: Record<string, CaseSnapshot>
|
||
}
|
||
|
||
export const reportsApi = {
|
||
get: (runId: string) => api.get(`/reports/${runId}`),
|
||
download: async (runId: string, format: 'html' | 'json' | 'markdown') => {
|
||
const ext = format === 'markdown' ? 'md' : format
|
||
const res = await api.get(`/reports/${runId}/${format}`, { responseType: 'blob' })
|
||
const url = URL.createObjectURL(res.data as Blob)
|
||
const a = document.createElement('a')
|
||
a.href = url
|
||
a.download = `report-${runId.slice(0, 8)}.${ext}`
|
||
a.click()
|
||
URL.revokeObjectURL(url)
|
||
},
|
||
compare: (run1: string, run2: string) =>
|
||
api.get(`/reports/compare`, { params: { run1, run2 } }),
|
||
}
|
||
|
||
export const statsApi = {
|
||
dashboard: () => api.get<DashboardStats>('/stats/dashboard'),
|
||
trend: (days?: number) => api.get<TrendPoint[]>('/stats/trend', { params: { days } }),
|
||
}
|
||
|
||
// ── Campaigns(评估活动) ──────────────────────────────────────────
|
||
|
||
export interface CampaignPlanEntry {
|
||
scenario_id: string
|
||
offset_seconds: number
|
||
count: number
|
||
}
|
||
|
||
export interface ExplorationSeeds {
|
||
personas: string[]
|
||
goals: string[]
|
||
}
|
||
|
||
export interface ExplorationBudgetConfig {
|
||
max_sessions?: number | null
|
||
max_turns?: number | null
|
||
min_interval_seconds?: number | null
|
||
}
|
||
|
||
export interface ExplorationIssueCount {
|
||
issue: string
|
||
count: number
|
||
}
|
||
|
||
export interface ExplorationJudgeReview {
|
||
reviewed_sessions: number
|
||
findings: { dimension: string; rating: string; comment: string }[]
|
||
summaries: string[]
|
||
}
|
||
|
||
export interface ExplorationSummary {
|
||
session_count: number
|
||
sessions_with_experience: number
|
||
goal_achieved_count: number
|
||
goal_achievement_rate: number | null
|
||
issues: ExplorationIssueCount[]
|
||
misled: ExplorationIssueCount[]
|
||
judge_review: ExplorationJudgeReview | null
|
||
}
|
||
|
||
export interface ExplorationExperience {
|
||
goal_achieved: boolean
|
||
blockers: string[]
|
||
misled: string[]
|
||
emotion: string
|
||
notes: string
|
||
}
|
||
|
||
export interface ExplorationSession {
|
||
id: string
|
||
campaign_id: string
|
||
target_id: string
|
||
persona: Record<string, unknown>
|
||
goal: string
|
||
seed_ref: Record<string, unknown> | null
|
||
status: string
|
||
triggered_by: string
|
||
experience: ExplorationExperience | null
|
||
judge_review: Record<string, unknown> | null
|
||
turn_count: number
|
||
error: string | null
|
||
created_at: string | null
|
||
closed_at: string | null
|
||
}
|
||
|
||
export interface ExplorationMessage {
|
||
id: string
|
||
session_id: string
|
||
round_index: number
|
||
role: string
|
||
content: string
|
||
latency_ms: number | null
|
||
created_at: string | null
|
||
}
|
||
|
||
export const explorationApi = {
|
||
listSessions: (campaignId: string) =>
|
||
api.get<{ sessions: ExplorationSession[] }>(`/exploration/campaigns/${campaignId}/sessions`),
|
||
listMessages: (sessionId: string) =>
|
||
api.get<{ messages: ExplorationMessage[] }>(`/exploration/sessions/${sessionId}/messages`),
|
||
}
|
||
|
||
export interface CampaignProgress {
|
||
current_offset_seconds: number
|
||
spawned_runs: number
|
||
completed_runs: number
|
||
}
|
||
|
||
export interface CampaignListProgress {
|
||
completed_runs: number
|
||
planned_total: number
|
||
overall_pass_rate: number | null
|
||
}
|
||
|
||
export interface Campaign {
|
||
id: string
|
||
name: string
|
||
target_id: string
|
||
window_seconds: number
|
||
time_scale: number
|
||
plan: CampaignPlanEntry[]
|
||
status: string
|
||
started_at: string | null
|
||
completed_at: string | null
|
||
summary: Record<string, unknown> | null
|
||
analysis_model_config_id: string | null
|
||
exploration_seeds: ExplorationSeeds | null
|
||
exploration_budget: ExplorationBudgetConfig | null
|
||
progress?: CampaignProgress
|
||
}
|
||
|
||
export interface CampaignListItem extends Omit<Campaign, 'progress'> {
|
||
progress: CampaignListProgress
|
||
}
|
||
|
||
export interface CampaignTrendBucket {
|
||
bucket_index: number
|
||
start_seconds: number
|
||
end_seconds: number
|
||
run_count: number
|
||
pass_rate: number | null
|
||
availability: number | null
|
||
avg_latency_ms: number | null
|
||
}
|
||
|
||
export interface CampaignCapability {
|
||
scenario_id: string
|
||
scenario_name: string
|
||
run_count: number
|
||
pass_rate: number | null
|
||
availability: number | null
|
||
avg_latency_ms: number | null
|
||
}
|
||
|
||
export interface CampaignReport {
|
||
campaign_id: string
|
||
name: string
|
||
target_id: string
|
||
status: string
|
||
window_seconds: number
|
||
time_scale: number
|
||
started_at: string | null
|
||
completed_at: string | null
|
||
summary: {
|
||
total_runs: number
|
||
completed_runs: number
|
||
overall_pass_rate: number | null
|
||
overall_availability: number | null
|
||
avg_latency_ms: number | null
|
||
}
|
||
time_trend: CampaignTrendBucket[]
|
||
capability_summary: CampaignCapability[]
|
||
exploration?: ExplorationSummary | null
|
||
}
|
||
|
||
export interface CampaignTimelineEntry {
|
||
run_id: string
|
||
scenario_id: string
|
||
scenario_name: string
|
||
offset_seconds: number
|
||
status: string
|
||
pass_rate: number | null
|
||
avg_latency_ms: number | null
|
||
started_at: string | null
|
||
}
|
||
|
||
export type CampaignAnalysisStatus = 'none' | 'queued' | 'generating' | 'completed' | 'failed'
|
||
|
||
export interface CampaignAnalysisProblem {
|
||
severity: string
|
||
title: string
|
||
description: string
|
||
scenario_ids: string[]
|
||
evidence_run_ids: string[]
|
||
}
|
||
|
||
export interface CampaignAnalysisResult {
|
||
overall: string
|
||
problems: CampaignAnalysisProblem[]
|
||
scenario_narratives: { scenario_id: string; narrative: string }[]
|
||
suggestions: { priority: number; text: string }[]
|
||
}
|
||
|
||
export interface CampaignAnalysis {
|
||
status: CampaignAnalysisStatus
|
||
result?: CampaignAnalysisResult | null
|
||
error?: string | null
|
||
model_config_id?: string | null
|
||
triggered_by?: string
|
||
updated_at?: string | null
|
||
}
|
||
|
||
export interface CreateCampaignPayload {
|
||
name: string
|
||
target_id: string
|
||
window_seconds: number
|
||
time_scale: number
|
||
plan: CampaignPlanEntry[]
|
||
analysis_model_config_id?: string | null
|
||
exploration_seeds?: ExplorationSeeds | null
|
||
exploration_budget?: ExplorationBudgetConfig | null
|
||
}
|
||
|
||
// ── Period comparison (v0.8) ────────────────────────────────────
|
||
|
||
export type ComparisonTrend = 'improving' | 'stable' | 'regressing'
|
||
|
||
export interface MetricDeltaPair {
|
||
baseline: number | null
|
||
current: number | null
|
||
delta: number | null
|
||
}
|
||
|
||
export interface MetricDiffScenario {
|
||
scenario_id: string
|
||
scenario_name: string
|
||
pass_rate: MetricDeltaPair
|
||
availability: MetricDeltaPair
|
||
avg_latency_ms: MetricDeltaPair
|
||
}
|
||
|
||
export interface MetricDiff {
|
||
overall: {
|
||
pass_rate: MetricDeltaPair
|
||
availability: MetricDeltaPair
|
||
avg_latency_ms: MetricDeltaPair
|
||
}
|
||
scenarios: MetricDiffScenario[]
|
||
}
|
||
|
||
export interface ComparisonBaselineInfo {
|
||
id: string
|
||
name: string
|
||
completed_at: string | null
|
||
}
|
||
|
||
export interface ComparisonProblemEvolution {
|
||
status: string
|
||
title: string
|
||
detail: string
|
||
scenario_ids: string[]
|
||
}
|
||
|
||
export interface ComparisonSuggestionTracking {
|
||
text: string
|
||
status: string
|
||
note: string
|
||
}
|
||
|
||
export interface ComparisonResult {
|
||
trend: ComparisonTrend
|
||
summary: string
|
||
problem_evolution: ComparisonProblemEvolution[]
|
||
suggestion_tracking: ComparisonSuggestionTracking[]
|
||
}
|
||
|
||
export interface CampaignComparison {
|
||
status: CampaignAnalysisStatus
|
||
auto_baseline: ComparisonBaselineInfo | null
|
||
metric_diff: MetricDiff | null
|
||
comparison: {
|
||
baseline_campaign_id: string
|
||
baseline: ComparisonBaselineInfo | null
|
||
result: ComparisonResult | null
|
||
error: string | null
|
||
model_config_id: string | null
|
||
triggered_by: string
|
||
updated_at: string | null
|
||
} | null
|
||
}
|
||
|
||
export const campaignsApi = {
|
||
list: () => api.get<CampaignListItem[]>('/campaigns'),
|
||
get: (id: string) => api.get<Campaign>(`/campaigns/${id}`),
|
||
create: (data: CreateCampaignPayload) => api.post<Campaign>('/campaigns', data),
|
||
cancel: (id: string) => api.post<Campaign>(`/campaigns/${id}/cancel`),
|
||
report: (id: string) => api.get<CampaignReport>(`/campaigns/${id}/report`),
|
||
timeline: (id: string) =>
|
||
api.get<{ entries: CampaignTimelineEntry[] }>(`/campaigns/${id}/timeline`),
|
||
getAnalysis: (id: string) => api.get<CampaignAnalysis>(`/campaigns/${id}/analysis`),
|
||
generateAnalysis: (id: string) => api.post<{ status: string }>(`/campaigns/${id}/analysis`),
|
||
getComparison: (id: string) => api.get<CampaignComparison>(`/campaigns/${id}/comparison`),
|
||
generateComparison: (id: string, baselineCampaignId?: string) =>
|
||
api.post<{ status: string }>(
|
||
`/campaigns/${id}/comparison`,
|
||
baselineCampaignId ? { baseline_campaign_id: baselineCampaignId } : {},
|
||
),
|
||
downloadReport: async (id: string) => {
|
||
const res = await api.get(`/campaigns/${id}/report/markdown`, { responseType: 'blob' })
|
||
const url = URL.createObjectURL(res.data as Blob)
|
||
const a = document.createElement('a')
|
||
a.href = url
|
||
a.download = `campaign-report-${id.slice(0, 8)}.md`
|
||
a.click()
|
||
URL.revokeObjectURL(url)
|
||
},
|
||
}
|
||
|
||
// ── Intelligent Evaluation(智能评估 v1.0) ────────────────────────
|
||
|
||
export type IntelligentEvalStatus =
|
||
| 'draft' | 'planning' | 'pending_approval'
|
||
| 'executing' | 'completed' | 'cancelled' | 'failed'
|
||
|
||
export type IntelligentEvalSessionStatus = 'running' | 'completed' | 'failed' | 'expired'
|
||
|
||
export interface IntelligentEvalMessage {
|
||
id: string
|
||
session_id: string
|
||
role: string
|
||
content: string
|
||
latency_ms: number | null
|
||
created_at: string | null
|
||
}
|
||
|
||
export interface IntelligentEvalSession {
|
||
id: string
|
||
eval_id: string
|
||
target_id: string
|
||
persona: Record<string, unknown>
|
||
goal: string
|
||
dimension: string | null
|
||
status: IntelligentEvalSessionStatus
|
||
verdict: Record<string, unknown> | null
|
||
turn_count: number
|
||
created_at: string | null
|
||
closed_at: string | null
|
||
}
|
||
|
||
export interface IntelligentEvalVirtualUser {
|
||
persona: Record<string, unknown>
|
||
goal: string
|
||
}
|
||
|
||
export interface IntelligentEvalTimeSlot {
|
||
time_slot: string
|
||
sessions: number
|
||
scenario: string
|
||
}
|
||
|
||
export interface IntelligentEvalPlan {
|
||
dimensions: string[]
|
||
virtual_users: IntelligentEvalVirtualUser[]
|
||
time_distribution: IntelligentEvalTimeSlot[]
|
||
estimated_sessions: number
|
||
budget: { max_turns_per_session: number; total_max_turns: number }
|
||
completion_criteria: string
|
||
}
|
||
|
||
export interface ReportEvidence {
|
||
session_id?: string
|
||
turn_index?: number | null
|
||
user_said?: string
|
||
assistant_replied?: string
|
||
}
|
||
|
||
export interface ReportFinding {
|
||
issue: string
|
||
severity: string
|
||
dimension: string
|
||
evidence: ReportEvidence[]
|
||
suggestion?: string | null
|
||
related_sop?: string | null
|
||
}
|
||
|
||
export interface ReportHighlight {
|
||
description: string
|
||
dimension?: string | null
|
||
}
|
||
|
||
export interface IntelligentEvalReport {
|
||
summary: string
|
||
scores: Record<string, number> | null
|
||
findings: ReportFinding[]
|
||
highlights: ReportHighlight[]
|
||
priority_recommendations: string[]
|
||
}
|
||
|
||
export interface IntelligentEval {
|
||
id: string
|
||
name: string
|
||
target_id: string
|
||
status: IntelligentEvalStatus
|
||
goal: string
|
||
seeds: Record<string, unknown>
|
||
intent: string
|
||
role_description: string
|
||
plan: IntelligentEvalPlan | null
|
||
plan_feedback: string | null
|
||
time_window_hours: number
|
||
report: IntelligentEvalReport | null
|
||
created_at: string | null
|
||
updated_at: string | null
|
||
started_at: string | null
|
||
completed_at: string | null
|
||
session_count: number
|
||
completed_sessions: number
|
||
sessions?: IntelligentEvalSession[]
|
||
}
|
||
|
||
export interface CreateIntelligentEvalPayload {
|
||
name: string
|
||
target_id: string
|
||
goal: string
|
||
seeds: Record<string, unknown>
|
||
intent: string
|
||
role_description: string
|
||
time_window_hours: number
|
||
}
|
||
|
||
export interface ConfigSnapshot {
|
||
id: string
|
||
eval_id: string
|
||
snapshot_type: 'created' | 'plan_submitted' | 'config_updated'
|
||
goal: string
|
||
seeds: Record<string, unknown>
|
||
intent: string
|
||
role_description: string
|
||
time_window_hours: number
|
||
plan: IntelligentEvalPlan | null
|
||
created_at: string | null
|
||
created_by: string
|
||
}
|
||
|
||
export interface ConfigSnapshotComparison {
|
||
snapshot_1: {
|
||
id: string
|
||
snapshot_type: string
|
||
created_at: string | null
|
||
}
|
||
snapshot_2: {
|
||
id: string
|
||
snapshot_type: string
|
||
created_at: string | null
|
||
}
|
||
differences: Record<string, { old: unknown; new: unknown }>
|
||
}
|
||
|
||
export interface DecisionLog {
|
||
id: string
|
||
eval_id: string
|
||
decision_type: 'execute_session' | 'wait' | 'start_analysis'
|
||
reason: string
|
||
context: Record<string, unknown>
|
||
cron_id: string
|
||
created_at: string | null
|
||
}
|
||
|
||
export type TaskQueueStatus = 'pending' | 'assigned' | 'completed' | 'failed'
|
||
|
||
export interface TaskQueueStats {
|
||
pending: number
|
||
assigned: number
|
||
completed: number
|
||
failed: number
|
||
unresolved: number
|
||
}
|
||
|
||
export interface TaskQueueItem {
|
||
id: string
|
||
eval_id: string
|
||
eval_name: string | null
|
||
eval_status: string | null
|
||
status: TaskQueueStatus
|
||
priority: number
|
||
reason: string
|
||
assigned_cron_id: string | null
|
||
assigned_at: string | null
|
||
completed_at: string | null
|
||
created_at: string | null
|
||
updated_at: string | null
|
||
error: string | null
|
||
}
|
||
|
||
export interface TaskQueueList {
|
||
tasks: TaskQueueItem[]
|
||
stats: TaskQueueStats
|
||
}
|
||
|
||
export const intelligentEvalsApi = {
|
||
list: () => api.get<{ intelligent_evals: IntelligentEval[] }>('/intelligent-evals'),
|
||
listTasks: (params?: { status?: TaskQueueStatus; limit?: number }) =>
|
||
api.get<TaskQueueList>('/intelligent-evals/tasks', { params }),
|
||
get: (id: string) => api.get<IntelligentEval>(`/intelligent-evals/${id}`),
|
||
create: (data: CreateIntelligentEvalPayload) => api.post<IntelligentEval>('/intelligent-evals', data),
|
||
submitPlan: (id: string, plan: Record<string, unknown>) =>
|
||
api.put<IntelligentEval>(`/intelligent-evals/${id}/plan`, { plan }),
|
||
approve: (id: string) => api.post<IntelligentEval>(`/intelligent-evals/${id}/approve`),
|
||
reject: (id: string, feedback: string) =>
|
||
api.post<IntelligentEval>(`/intelligent-evals/${id}/reject`, { feedback }),
|
||
cancel: (id: string) => api.post<IntelligentEval>(`/intelligent-evals/${id}/cancel`),
|
||
listSessions: (id: string) =>
|
||
api.get<{ sessions: IntelligentEvalSession[] }>(`/intelligent-evals/${id}/sessions`),
|
||
listMessages: (id: string, sessionId: string) =>
|
||
api.get<{ messages: IntelligentEvalMessage[] }>(`/intelligent-evals/${id}/sessions/${sessionId}/messages`),
|
||
getReport: (id: string) => api.get<IntelligentEvalReport>(`/intelligent-evals/${id}/report`),
|
||
downloadReportMarkdown: async (id: string) => {
|
||
const res = await api.get(`/intelligent-evals/${id}/report/markdown`, { responseType: 'blob' })
|
||
const url = URL.createObjectURL(res.data as Blob)
|
||
const a = document.createElement('a')
|
||
a.href = url
|
||
a.download = `intelligent-eval-report-${id.slice(0, 8)}.md`
|
||
a.click()
|
||
URL.revokeObjectURL(url)
|
||
},
|
||
// Config Snapshots
|
||
listConfigSnapshots: (id: string) =>
|
||
api.get<{ snapshots: ConfigSnapshot[] }>(`/intelligent-evals/${id}/config-snapshots`),
|
||
getConfigSnapshot: (id: string, snapshotId: string) =>
|
||
api.get<ConfigSnapshot>(`/intelligent-evals/${id}/config-snapshots/${snapshotId}`),
|
||
compareConfigSnapshots: (id: string, snapshotId1: string, snapshotId2: string) =>
|
||
api.post<ConfigSnapshotComparison>(`/intelligent-evals/${id}/config-snapshots/compare`, {
|
||
snapshot_id_1: snapshotId1,
|
||
snapshot_id_2: snapshotId2,
|
||
}),
|
||
// Decision Logs
|
||
listDecisionLogs: (id: string) =>
|
||
api.get<{ logs: DecisionLog[] }>(`/intelligent-evals/${id}/decision-logs`),
|
||
}
|
||
|
||
// ── OpenClaw Cron Pool ──────────────────────────────────────────────
|
||
|
||
export interface CronPoolStatus {
|
||
total: number
|
||
idle: number
|
||
busy: number
|
||
stuck: number
|
||
min_size: number
|
||
max_size: number
|
||
}
|
||
|
||
export interface CronPoolMetrics {
|
||
pool_utilization: number
|
||
task_backlog: number
|
||
stuck_rate: number
|
||
avg_processing_time_seconds: number | null
|
||
eval_completion_rate: number
|
||
timestamp: string
|
||
}
|
||
|
||
export interface CronPoolAlert {
|
||
id: string
|
||
alert_type: string
|
||
severity: string
|
||
message: string
|
||
metric_value: number
|
||
threshold: number
|
||
created_at: string
|
||
resolved_at: string | null
|
||
webhook_sent: boolean
|
||
}
|
||
|
||
export const openclawCronPoolApi = {
|
||
getStatus: () => api.get<{ pool: CronPoolStatus }>('/openclaw/cron-pool'),
|
||
scale: (targetSize: number) =>
|
||
api.post<{ success: boolean; scaled_up?: number; scaled_down?: number; current_size: number }>(
|
||
'/openclaw/cron-pool/scale',
|
||
{ target_size: targetSize },
|
||
),
|
||
sync: () => api.post<{ success: boolean; synced: number }>('/openclaw/cron-pool/sync'),
|
||
autoScale: () =>
|
||
api.post<{ success: boolean; scaled_up: number; scaled_down: number }>('/openclaw/cron-pool/auto-scale'),
|
||
getMetrics: () => api.get<{ metrics: CronPoolMetrics }>('/openclaw/cron-pool/metrics'),
|
||
checkAlerts: () =>
|
||
api.post<{ success: boolean; alerts_triggered: number; alerts: CronPoolAlert[] }>('/openclaw/cron-pool/check-alerts'),
|
||
getAlerts: (limit = 100, unresolvedOnly = false) =>
|
||
api.get<{ alerts: CronPoolAlert[] }>('/openclaw/cron-pool/alerts', {
|
||
params: { limit, unresolved_only: unresolvedOnly },
|
||
}),
|
||
resolveAlert: (alertId: string) =>
|
||
api.post<{ success: boolean }>(`/openclaw/cron-pool/alerts/${alertId}/resolve`),
|
||
}
|
||
|
||
// ── File Management ──────────────────────────────────────────────
|
||
|
||
export interface FileCategory {
|
||
id: string
|
||
name: string
|
||
parent_id: string | null
|
||
children: FileCategory[]
|
||
}
|
||
|
||
export interface FileRecord {
|
||
id: string
|
||
original_name: string
|
||
file_size: number
|
||
mime_type: string
|
||
file_ext: string
|
||
category_id: string | null
|
||
storage_directory: string
|
||
created_at: string | null
|
||
}
|
||
|
||
export interface FileUploadConfig {
|
||
allowed_extensions: string[]
|
||
max_upload_size_mb: number
|
||
}
|
||
|
||
export const filesApi = {
|
||
// Categories
|
||
getConfig: () => api.get<FileUploadConfig>('/files/config'),
|
||
listCategories: () => api.get<FileCategory[]>('/files/categories'),
|
||
createCategory: (data: { name: string; parent_id?: string | null }) =>
|
||
api.post<FileCategory>('/files/categories', data),
|
||
updateCategory: (id: string, data: { name: string }) =>
|
||
api.put(`/files/categories/${id}`, data),
|
||
deleteCategory: (id: string) => api.delete(`/files/categories/${id}`),
|
||
|
||
// Files
|
||
list: (categoryId?: string | null) =>
|
||
api.get<FileRecord[]>('/files', { params: categoryId ? { category_id: categoryId } : {} }),
|
||
upload: (file: File, categoryId?: string | null) => {
|
||
const form = new FormData()
|
||
form.append('file', file)
|
||
if (categoryId) {
|
||
form.append('category_id', categoryId)
|
||
}
|
||
return api.post<FileRecord>('/files/upload', form, {
|
||
timeout: 120000,
|
||
})
|
||
},
|
||
download: (id: string) => api.get<Blob>(`/files/${id}/download`, { responseType: 'blob' }),
|
||
delete: (id: string) => api.delete(`/files/${id}`),
|
||
}
|