import { EventSource } from "eventsource"; import { APIPath } from "../config"; import type { Link } from "../link/link"; import { ConversationStream, SteerResult, TurnStopMode, TurnStopped } from "./transport"; import { RequestResponseV3, RequestResponseV4 } from "../types/type_registry"; import { RequestResponseV5 } from "../types/response/v5/dialogue_response_v5"; import { ConversationStateResponse } from "../types/state/convo_state_response"; import { ConversationAddress } from "../types/conversation/address"; import { TurnProgressEntry } from "../types/response/v4/turn_registry_v4"; import { TurnProgressEntryV5 } from "../types/response/v5/turn_registry_v5"; export type DialogueRequestParams = { /** Unique identifier for the chat session */ chatId?: string; /** The message to be sent to the AI */ message: string; /** AI model to use for generating responses */ model?: string; /** Additional instructions for location-specific context */ instructions?: string; /** Platform where the chat is occurring */ platform?: string; /** * Where on the platform the conversation is — on Discord, the channel and the thread * inside it. Anything Alfred has to say on this conversation outside a turn is delivered * there. An address is whole: a new one replaces the old rather than merging into it. */ address?: ConversationAddress; /** Custom personality configuration for the AI */ personality?: string; }; export type DialogueRequestOptions = Omit, "chatId">; export interface RequestResponseByVersion { v3: RequestResponseV3; v4: RequestResponseV4; v5: RequestResponseV5; } interface TurnProgressEntryByVersion { v4: TurnProgressEntry; v5: TurnProgressEntryV5; } export type TurnProgressEntryForVersion = V extends keyof TurnProgressEntryByVersion ? TurnProgressEntryByVersion[V] : TurnProgressEntry; export type ConversationOptions = { /** The conversation ID to load the conversation from, server will error if this convo id doesn't exist */ convoId?: string; /** The API key to use */ apiKey: string; /** The server URL to use */ serverUrl?: string; /** * The path to append to the server URL specifying the API endpoint to use (specifically for conversations) * @deprecated use convoPath instead */ path?: string; /** The path to append to the server URL specifying the conversation API endpoint to use */ convoPath?: string; /** The path to append to the server URL specifying the history API endpoint to use */ historyPath?: string; /** The path to append to the server URL specifying the progress API endpoint to use */ progressPath?: string; /** The path to append to the server URL specifying the progress stream API endpoint to use */ progressStreamPath?: string; /** Whether to enable debug logs */ debug?: boolean; /** The API version to use */ chatApiV?: V; /** * How turns are carried. * * `"sse"` (the default) opens an HTTP stream per turn. Passing a connected `Link` * instead carries turns over that websocket, reusing a connection you already * have — everything else, including the payloads you receive, is identical. */ transport?: "sse" | Link; /** * Whether streamed text is put back together for you. On by default. * * The server streams each message a piece at a time. With this on, `payload.message` * is the whole message so far — what it has always been — and `payload.delta` is what * the event added, for anyone who would rather append than re-render. * * Turn it off to be handed the wire payloads untouched, where a piece arrives as * `delta` with no `message` beside it. Worth it only if you are appending anyway and * want nothing between you and the socket. */ accumulateStream?: boolean; }; export declare class Conversation { convoId?: string; apiKey: string; private debug; private chatApiV; private options?; private events; private transport; private accumulateStream; /** The link carrying this conversation, when it is not on SSE. */ readonly link?: Link; private endpoints; constructor(config: ConversationOptions); /** * Sets the endpoint where the request is sent to, appended to server URL * @deprecated Use setConversationEndpoint() instead */ setEndpoint(endpoint: string): this; /** Sets the conversation endpoint where the request is sent to, appended to server URL */ setConversationEndpoint(conversationEndpoint: string): this; /** Sets the history endpoint where the quest is sent to, appended to server URL */ setHistoryEndpoint(historyEndpoint: string): this; /** Sets the progress stream endpoint where the request is sent to, appended to server URL */ setProgressStreamEndpoint(progressStreamEndpoint: string): this; /** Sets the progress endpoint where the request is sent to, appended to server URL */ setProgressEndpoint(progressEndpoint: string): this; /** Sets the conversation ID, has to be an existing conversation ID, undefined otherwise */ setConvoId(convoId: string | undefined): this; /** Sets the AI model used for the next interaction, e.g Claude-Sonnet, GPT-4 */ setModel(model: string): this; /** Sets additional instructions for location-specific context */ setInstructions(instructions: string): this; /** Sets the platform where the chat is occurring, used internally for logging - ignore in most contexts */ setPlatform(platform: string): this; /** * Sets where on the platform the conversation is — on Discord, the channel and the thread * inside it. Anything Alfred has to say on this conversation outside a turn is delivered * there. The address is whole: this replaces whatever the conversation named before rather * than merging into it, so a conversation that left a thread stops naming one. */ setAddress(address: ConversationAddress): this; /** Sets a custom personality configuration for the AI */ setPersonality(personality: string): this; /** Gets the current conversation ID */ getConvoId(): string | undefined; /** * Gets the current endpoint * @deprecated Use getConversationEndpoint() instead */ getEndpoint(): string; /** Gets the current conversation endpoint */ getConversationEndpoint(): string; /** Gets the current history endpoint */ getHistoryEndpoint(): string; /** Gets the current progress stream endpoint */ getProgressStreamEndpoint(): string; /** Gets the current progress endpoint */ getProgressEndpoint(): string; /** Gets the current options object */ getModel(): string | undefined; /** Gets the current location-specific instructions */ getInstructions(): string | undefined; /** Gets the current platform */ getPlatform(): string | undefined; /** Gets where on the platform the conversation is */ getAddress(): import("../types/type_registry").DiscordAddress | undefined; /** Gets the current personality configuration */ getPersonality(): string | undefined; /** * Fires when the conversation ID is set * if convoId is already set when this is called, fires immediately * */ onConvoId(cb: (convoId: string) => any): string; /** Removes a convoId listener */ offConvoId(listenerId: string): void; /** * Fires once when the conversation ID is set, then removes the listener. * If convoId is already set, fires immediately */ onceConvoId(cb: (convoId: string) => any): string | undefined; /** Fetches the conversation state from the server, including message history and metadata */ fetchState(): Promise; /** * Follows the turn currently running in this conversation. * * Reopening a conversation mid-answer is watching a turn, not starting one, and a * turn belongs to the conversation rather than to whoever started it. When the * conversation is carried over a websocket Link this rides that same connection; * otherwise it opens the HTTP progress stream. Either way the payloads are the same. * * `afterEventId` resumes from what the caller already has, so a client that reloads * is sent what it missed rather than the turn from the beginning. */ fetchProgressStream(cb: (chunk: RequestResponseByVersion[V]) => any, options?: { afterEventId?: string; }): ConversationStream | EventSource; /** * Fetches the conversation progress from the server * Returns undefined if no active turn progress */ fetchProgress(options?: { lastEventId?: string; includeCompleted?: boolean; }): Promise[] | undefined>; /** * One stream's worth of delivery: rebuilds whole values, then hands them to the caller. * * The accumulator is made per stream rather than per conversation because it holds the * text of whatever is still being written, and two streams are two different answers. */ private receiver; /** Sends a message into the conversation */ send(message: string, cb: (chunk: RequestResponseByVersion[V]) => any, options?: DialogueRequestOptions): ConversationStream; /** * Stops the turn running in this conversation. * * `soft` (the default) lets the model finish the call it is in the middle of and takes no * further step: the answer so far is kept, in history and in what the model remembers * saying, and it works on every model. `hard` cuts the stream off mid-sentence and aborts * the tools with it — but only actually stops the bill where the provider supports * cancellation, so against one that does not the server applies a soft stop instead. The * returned `mode` says which you got; `undefined` means there was nothing left to stop. * * Either way the turn's stream ends the way it always does, carrying what was produced * before the stop. There is nothing else to clean up. */ stop(mode?: TurnStopMode): Promise; /** * Says something to the turn that is already running. * * The message reaches the model at its next step boundary, so nothing in flight is thrown * away and the model reads it as the user talking mid-task — which is usually what someone * typing while Alfred works actually means. * * Check the result. `too_late` means the message was NOT delivered, because the turn was * already stopping or over, and it should be sent with `send()` instead. Dropping it is * the one thing that is never right. */ steer(message: string): Promise; /** * Sends a message and resolves with the finished reply. * * For when you want the answer rather than the stream. Every event still arrives * through `onEvent` if you pass one. */ ask(message: string, options?: DialogueRequestOptions & { onEvent?: (chunk: RequestResponseByVersion[V]) => any; }): Promise<{ text: string; convoId?: string; events: RequestResponseByVersion[V][]; }>; } export {};