Files
freya/apps/freya-backend/src/agent/query-agent.ts

108 lines
2.5 KiB
TypeScript
Raw Normal View History

2026-06-14 16:05:04 +01:00
export interface QueryAgentAsk {
message: string
2026-06-16 20:16:03 +01:00
conversationId?: string
userMessageEntry?: QueryAgentConversationEntryRef
2026-07-01 23:50:38 +01:00
signal?: AbortSignal
2026-06-14 16:05:04 +01:00
}
2026-06-16 20:16:03 +01:00
export type QueryAgentStreamEvent =
| { type: "conversation"; conversationId: string }
2026-06-14 16:05:04 +01:00
| { type: "text_delta"; text: string }
| { type: "tool_start"; toolName: string }
| { type: "tool_end"; toolName: string; ok: boolean }
| { type: "done" }
| { type: "error"; message: string }
2026-06-16 20:16:03 +01:00
export const QueryAgentEvent = {
Compaction: "compaction",
} as const
export type QueryAgentEvent = (typeof QueryAgentEvent)[keyof typeof QueryAgentEvent]
export interface QueryAgentConversationEntryRef {
id: string
sequence: number
}
export interface QueryAgentCompactedEntryRange {
startSequence: number
endSequence: number
}
export interface QueryAgentCompactionEvent {
type: typeof QueryAgentEvent.Compaction
conversationId: string
summary: string
firstKeptEntryId: string
compactedEntryRange: QueryAgentCompactedEntryRange | null
tokensBefore: number
details?: unknown
fromExtension: boolean
}
export interface QueryAgentEventMap {
[QueryAgentEvent.Compaction]: QueryAgentCompactionEvent
}
export type QueryAgentEventListener<T extends QueryAgentEvent> = (
event: QueryAgentEventMap[T],
) => void | Promise<void>
export type QueryAgentEventListeners = {
[T in QueryAgentEvent]: Set<QueryAgentEventListener<T>>
}
export function createQueryAgentEventListeners(): QueryAgentEventListeners {
return {
[QueryAgentEvent.Compaction]: new Set(),
}
}
2026-06-14 16:05:04 +01:00
export interface QueryAgent {
2026-06-16 20:16:03 +01:00
ask(input: QueryAgentAsk): AsyncIterable<QueryAgentStreamEvent>
addEventListener<T extends QueryAgentEvent>(
type: T,
listener: QueryAgentEventListener<T>,
): () => void
2026-06-14 16:05:04 +01:00
dispose(): void
}
export interface QueryAgentResponse {
message: string
2026-06-16 20:16:03 +01:00
conversationId?: string
2026-06-14 16:05:04 +01:00
}
export class QueryAgentError extends Error {
constructor(message: string) {
super(message)
this.name = "QueryAgentError"
}
}
export async function collectQueryAgentResponse(
agent: QueryAgent,
input: QueryAgentAsk,
): Promise<QueryAgentResponse> {
let message = ""
2026-06-16 20:16:03 +01:00
let conversationId: string | undefined
2026-06-14 16:05:04 +01:00
for await (const event of agent.ask(input)) {
switch (event.type) {
2026-06-16 20:16:03 +01:00
case "conversation":
conversationId = event.conversationId
break
2026-06-14 16:05:04 +01:00
case "text_delta":
message += event.text
break
case "error":
throw new QueryAgentError(event.message)
case "tool_start":
case "tool_end":
case "done":
break
}
}
2026-06-16 20:16:03 +01:00
return { message, conversationId }
2026-06-14 16:05:04 +01:00
}