2026-04-16 08:38:18 +08:00
|
|
|
import { request, getBaseUrlValue, getApiKey } from '../client'
|
2026-04-11 15:59:14 +08:00
|
|
|
|
|
|
|
|
export interface ChatMessage {
|
|
|
|
|
role: 'user' | 'assistant' | 'system'
|
|
|
|
|
content: string
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export interface StartRunRequest {
|
|
|
|
|
input: string | ChatMessage[]
|
|
|
|
|
instructions?: string
|
|
|
|
|
conversation_history?: ChatMessage[]
|
|
|
|
|
session_id?: string
|
2026-04-12 23:23:50 +08:00
|
|
|
model?: string
|
2026-04-11 15:59:14 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export interface StartRunResponse {
|
|
|
|
|
run_id: string
|
|
|
|
|
status: string
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// SSE event types from /v1/runs/{id}/events
|
|
|
|
|
export interface RunEvent {
|
|
|
|
|
event: string
|
|
|
|
|
run_id?: string
|
|
|
|
|
delta?: string
|
2026-04-25 08:46:50 +08:00
|
|
|
/** Payload text for `reasoning.delta` / `thinking.delta` / `reasoning.available` events. */
|
|
|
|
|
text?: string
|
2026-04-11 15:59:14 +08:00
|
|
|
tool?: string
|
|
|
|
|
name?: string
|
|
|
|
|
preview?: string
|
|
|
|
|
timestamp?: number
|
|
|
|
|
error?: string
|
2026-04-25 16:21:07 +08:00
|
|
|
/** Final response text on `run.completed`. May be empty/null if the agent
|
|
|
|
|
* silently swallowed an upstream error — see chat store for fallback. */
|
|
|
|
|
output?: string | null
|
2026-04-22 16:14:50 +08:00
|
|
|
usage?: {
|
|
|
|
|
input_tokens: number
|
|
|
|
|
output_tokens: number
|
|
|
|
|
total_tokens: number
|
|
|
|
|
}
|
2026-04-11 15:59:14 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export async function startRun(body: StartRunRequest): Promise<StartRunResponse> {
|
2026-04-26 17:47:39 +08:00
|
|
|
const headers: Record<string, string> = {}
|
|
|
|
|
if (body.session_id) {
|
|
|
|
|
headers['X-Hermes-Session-Id'] = body.session_id
|
|
|
|
|
}
|
2026-04-16 08:38:18 +08:00
|
|
|
return request<StartRunResponse>('/api/hermes/v1/runs', {
|
2026-04-11 15:59:14 +08:00
|
|
|
method: 'POST',
|
|
|
|
|
body: JSON.stringify(body),
|
2026-04-26 17:47:39 +08:00
|
|
|
headers,
|
2026-04-11 15:59:14 +08:00
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function streamRunEvents(
|
|
|
|
|
runId: string,
|
|
|
|
|
onEvent: (event: RunEvent) => void,
|
|
|
|
|
onDone: () => void,
|
|
|
|
|
onError: (err: Error) => void,
|
|
|
|
|
) {
|
|
|
|
|
const baseUrl = getBaseUrlValue()
|
2026-04-15 09:13:27 +08:00
|
|
|
const token = getApiKey()
|
2026-04-19 20:59:25 +08:00
|
|
|
const profile = localStorage.getItem('hermes_active_profile_name')
|
|
|
|
|
const params = new URLSearchParams()
|
|
|
|
|
if (token) params.set('token', token)
|
|
|
|
|
if (profile && profile !== 'default') params.set('profile', profile)
|
|
|
|
|
const qs = params.toString()
|
|
|
|
|
const url = `${baseUrl}/api/hermes/v1/runs/${runId}/events${qs ? `?${qs}` : ''}`
|
2026-04-11 15:59:14 +08:00
|
|
|
|
|
|
|
|
let closed = false
|
|
|
|
|
const source = new EventSource(url)
|
|
|
|
|
|
|
|
|
|
source.onmessage = (e) => {
|
|
|
|
|
if (closed) return
|
|
|
|
|
try {
|
|
|
|
|
const parsed = JSON.parse(e.data)
|
|
|
|
|
onEvent(parsed)
|
|
|
|
|
|
|
|
|
|
if (parsed.event === 'run.completed' || parsed.event === 'run.failed') {
|
|
|
|
|
closed = true
|
|
|
|
|
source.close()
|
|
|
|
|
onDone()
|
|
|
|
|
}
|
|
|
|
|
} catch {
|
2026-04-24 22:18:32 +08:00
|
|
|
onEvent({ event: 'message', delta: e.data })
|
2026-04-11 15:59:14 +08:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
source.onerror = () => {
|
|
|
|
|
if (closed) return
|
|
|
|
|
closed = true
|
|
|
|
|
source.close()
|
|
|
|
|
onError(new Error('SSE connection error'))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Return AbortController-compatible object
|
|
|
|
|
return {
|
|
|
|
|
abort: () => {
|
|
|
|
|
if (!closed) {
|
|
|
|
|
closed = true
|
|
|
|
|
source.close()
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
} as unknown as AbortController
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export async function fetchModels(): Promise<{ data: Array<{ id: string }> }> {
|
2026-04-16 08:38:18 +08:00
|
|
|
return request('/api/hermes/v1/models')
|
2026-04-11 15:59:14 +08:00
|
|
|
}
|