Files
NotesAgentic/frontend/src/services/agentService.ts
T

108 lines
3.4 KiB
TypeScript

import apiClient from './apiClient'
import { SseClient } from './sseClient'
import type { AgentRun, AgentEvent, AgentTraceResponse, ApiAgentRun, OperationResponse, PageMeta, ToolDefinition } from '@/contracts'
function toAgentRun(run: ApiAgentRun): AgentRun {
// API 的 token_usage 是累计值,UI 模型预留了输入/输出拆分字段。
return {
run_id: run.run_id,
status: run.status,
current_step: run.current_step,
max_steps: run.max_steps,
token_usage: {
total_tokens: run.token_usage,
},
started_at: run.created_at,
completed_at: ['completed', 'failed', 'cancelled'].includes(run.status) ? run.updated_at : undefined,
error: run.error_message ?? undefined,
}
}
export async function listAgentRuns(params?: {
limit?: number
offset?: number
}): Promise<{ items: AgentRun[]; total: number }> {
const response = await apiClient.get<{ items: ApiAgentRun[]; page: PageMeta }>('/api/agent/runs', { params })
return { items: response.items.map(toAgentRun), total: response.page.total }
}
export async function getAgentRun(runId: string): Promise<AgentRun> {
return toAgentRun(await apiClient.get<ApiAgentRun>(`/api/agent/runs/${runId}`))
}
export interface CreateAgentRunRequest {
input: string
provider_id: string
model: string
skill_id?: string
allowed_tools?: string[]
max_steps?: number
tool_timeout_seconds?: number
run_timeout_seconds?: number
token_budget?: number
allow_network?: boolean
max_concurrent_tools?: number
}
export async function createAgentRun(request: CreateAgentRunRequest): Promise<AgentRun> {
return toAgentRun(await apiClient.post<ApiAgentRun>('/api/agent/runs', request))
}
export async function cancelAgentRun(runId: string): Promise<OperationResponse> {
return apiClient.post(`/api/agent/runs/${runId}/cancel`)
}
export async function getAgentTrace(
runId: string,
params?: { after_sequence?: number; limit?: number },
): Promise<AgentTraceResponse> {
return apiClient.get(`/api/agent/runs/${runId}/trace`, { params })
}
export async function listTools(): Promise<ToolDefinition[]> {
const response = await apiClient.get<{ items: ToolDefinition[] }>('/api/tools')
return response.items
}
export function streamAgentEvents(
runId: string,
handlers: {
onEvent?: (event: AgentEvent) => void
onError?: (error: Error) => void
onDone?: () => void
onOpen?: () => void
},
afterSequence = -1,
): SseClient {
// 将通用 SSE 包装成领域事件,Store 无需了解传输层 envelope。
const client = new SseClient({
url: `/api/agent/runs/${runId}/events?after_sequence=${afterSequence}`,
method: 'GET',
lastEventId: afterSequence >= 0 ? String(afterSequence) : undefined,
onEvent: (eventName, data) => {
handlers.onEvent?.({
event: eventName as AgentEvent['event'],
sequence: (data.sequence as number) || 0,
run_id: (data.run_id as string) || runId,
data: (data.data || data) as Record<string, unknown>,
timestamp: (data.timestamp as string) || new Date().toISOString(),
})
},
onError: handlers.onError,
onDone: handlers.onDone,
onOpen: handlers.onOpen,
})
client.connect().catch(() => {})
return client
}
export async function respondToPermission(
runId: string,
requestId: string,
decision: 'allow_once' | 'allow_session' | 'deny'
): Promise<OperationResponse> {
return apiClient.post(`/api/agent/runs/${runId}/permissions/${requestId}`, {
decision,
})
}