UNPKG

@c8y/ngx-components

Version:

Angular modules for Cumulocity IoT applications

342 lines (338 loc) 24.2 kB
import * as _angular_core from '@angular/core'; import { OnInit, OnDestroy, OnChanges, TemplateRef, Type, SimpleChanges, Injector } from '@angular/core'; import { TranslateService } from '@ngx-translate/core'; import { ClientAgentDefinition, Suggestion, AgentChatConfig, AssistantMessageDisplayConfig, AIAssistantMessage, AIStreamResponse, pruneMessagesFunction, AIMessage, ToolCallPart, ChatHistory, AIUserMessage, WidgetAiChatSectionComponentConfig } from '@c8y/ngx-components/ai'; import { Observable } from 'rxjs'; declare class AgentChatComponent implements OnInit, OnDestroy, OnChanges { readonly translateService: TranslateService; private readonly datePipe; private readonly modalService; /** Debug logging function for AI messages. Can be enabled at runtime with localStorage.setItem('c8y-debug-log.agent-chat', 'true'); */ readonly debugLog: (message: string, ...args: any[]) => void; /** * Identifies which AI agent from the AI Agent Manager service will be used by this component. * * Where possible, provide a `ClientAgentDefinition` rather than just a name. * * It is recommended to define a `toolCallConfig` in the same place you define `ClientAgentDefinition`, * and to use the keys from `assistantMessageDisplayConfig.toolCallConfig` * in the `ClientAgentDefinition` tools list. */ readonly agent: _angular_core.InputSignal<string | ClientAgentDefinition>; get agentName(): string; get agentDefinition(): ClientAgentDefinition | undefined; /** * Clickable suggestions buttons to display just above the chat, for likely next user actions. * These may be hardcoded or be set by the AI agent (e.g. in a tool call). * This input should be `undefined` to indicate that there are no new suggestions yet, and that any restored from the chat history should be used instead. * You can use the `isWelcoming` output to show different suggestions while the initial welcome message is displayed. */ readonly suggestions: _angular_core.InputSignal<Suggestion[]>; /** ChatConfig to customize the ai-chat component's configuration if desired. */ readonly chatConfig: _angular_core.InputSignal<Partial<AgentChatConfig>>; /** Template for customizing the welcome view using `ng-template`. */ readonly welcomeTemplate: _angular_core.InputSignal<TemplateRef<any>>; /** Indicates whether the component has zero messages and is showing the welcome page. */ readonly isWelcoming: _angular_core.Signal<boolean>; /** Configures whether this component should automatically create the provided `agent: ClientAgentDefinition` * if an agent of that name doesn't already exist. This is an alternative to the usual mechanism of configuring * agents using a `.agent.c8y.ts` file. */ readonly autoCreateAgents: _angular_core.InputSignal<boolean>; /** Variables to pass to the AI agent for placeholders specified in the agent's system prompt. */ readonly variables: _angular_core.InputSignal<Record<string, any>>; /** * Allows overriding the component used to render messages from the AI assistant, if the default * AiChatAssistantMessageComponent does not do what you need. * * The component must accept a single input `assistantMessageContext: AssistantMessageContext`. * This object contains all data needed to render the assistant message and is forward-compatible with future additions. * * Example usage: * <c8y-agent-chat [assistantMessageComponent]="MyCustomAssistantMessageComponent"></c8y-agent-chat> */ readonly assistantMessageComponent: _angular_core.InputSignal<Type<any>>; /** * Configuration passed to `AiChatAssistantMessageComponent` for controlling how messages from the assistant * are rendered, including tool calls. * * If you are performing any tool calls it is recommended to configure the `toolCallConfig` * to provide localized display names for the tools used in your `AgentDefinitionConfig`. */ readonly assistantMessageDisplayConfig: _angular_core.InputSignal<AssistantMessageDisplayConfig>; /** * Optional function to preprocess messages from the assistant before they are displayed, for example if you need to remove special * tagged sections from the content to move into simulated tool calls. * * This function may be called many times for each message, as more steps, tool calls and content are streamed from the agent. * * If you modify any part of the message other than the `changedPart`, you should replace it with a new shallow copy of the object so * that change detection works correctly. This is not necessary for changedPart. * * @param message The message from the AI assistant. * @param changedPart The part of the message that has changed, which can be modified in-place if desired. */ readonly preprocessAgentMessage: _angular_core.InputSignal<(message: AIAssistantMessage, changedPart?: AIStreamResponse["changedPart"]) => AIAssistantMessage>; /** Optional function to override how the message history is prepared and compacted ready for sending with * agent requests. This overrides the default behaviour from `defaultPruneMessagesForAgent`. * * For example this can be used to remove unnecessary information to minimize tokens, reduce the number of messages, * or to make what is sent to the agent different from what is rendered in the UI. */ readonly pruneMessagesForAgent: _angular_core.InputSignal<pruneMessagesFunction>; /** When true, skips the agent health check on initialization. * Use this when the component is used with the test endpoint (snapshot mode) where the agent * does not need to exist on the backend. Default: false. */ readonly skipHealthCheck: _angular_core.InputSignal<boolean>; /** Input that provides a previously-saved chat history snapshot to restore. */ readonly initialChatHistory: _angular_core.InputSignal<string | number | boolean | (string | number | boolean | (string | number | boolean | (string | number | boolean | (string | number | boolean | (string | number | boolean | (string | number | boolean | (string | number | boolean | (string | number | boolean | (string | number | boolean | (string | number | boolean | (string | number | boolean | /*elided*/ any | { [key: string]: string | number | boolean | /*elided*/ any | /*elided*/ any; })[] | { [key: string]: string | number | boolean | (string | number | boolean | /*elided*/ any | /*elided*/ any)[] | /*elided*/ any; })[] | { [key: string]: string | number | boolean | (string | number | boolean | (string | number | boolean | /*elided*/ any | /*elided*/ any)[] | /*elided*/ any)[] | /*elided*/ any; })[] | { [key: string]: string | number | boolean | (string | number | boolean | (string | number | boolean | (string | number | boolean | /*elided*/ any | /*elided*/ any)[] | /*elided*/ any)[] | /*elided*/ any)[] | /*elided*/ any; })[] | { [key: string]: string | number | boolean | (string | number | boolean | (string | number | boolean | (string | number | boolean | (string | number | boolean | /*elided*/ any | /*elided*/ any)[] | /*elided*/ any)[] | /*elided*/ any)[] | /*elided*/ any)[] | /*elided*/ any; })[] | { [key: string]: string | number | boolean | (string | number | boolean | (string | number | boolean | (string | number | boolean | (string | number | boolean | (string | number | boolean | /*elided*/ any | /*elided*/ any)[] | /*elided*/ any)[] | /*elided*/ any)[] | /*elided*/ any)[] | /*elided*/ any)[] | /*elided*/ any; })[] | { [key: string]: string | number | boolean | (string | number | boolean | (string | number | boolean | (string | number | boolean | (string | number | boolean | (string | number | boolean | (string | number | boolean | /*elided*/ any | /*elided*/ any)[] | /*elided*/ any)[] | /*elided*/ any)[] | /*elided*/ any)[] | /*elided*/ any)[] | /*elided*/ any)[] | /*elided*/ any; })[] | { [key: string]: string | number | boolean | (string | number | boolean | (string | number | boolean | (string | number | boolean | (string | number | boolean | (string | number | boolean | (string | number | boolean | (string | number | boolean | /*elided*/ any | /*elided*/ any)[] | /*elided*/ any)[] | /*elided*/ any)[] | /*elided*/ any)[] | /*elided*/ any)[] | /*elided*/ any)[] | /*elided*/ any)[] | /*elided*/ any; })[] | { [key: string]: string | number | boolean | (string | number | boolean | (string | number | boolean | (string | number | boolean | (string | number | boolean | (string | number | boolean | (string | number | boolean | (string | number | boolean | (string | number | boolean | /*elided*/ any | /*elided*/ any)[] | /*elided*/ any)[] | /*elided*/ any)[] | /*elided*/ any)[] | /*elided*/ any)[] | /*elided*/ any)[] | /*elided*/ any)[] | /*elided*/ any)[] | /*elided*/ any; })[] | { [key: string]: string | number | boolean | (string | number | boolean | (string | number | boolean | (string | number | boolean | (string | number | boolean | (string | number | boolean | (string | number | boolean | (string | number | boolean | (string | number | boolean | (string | number | boolean | /*elided*/ any | /*elided*/ any)[] | /*elided*/ any)[] | /*elided*/ any)[] | /*elided*/ any)[] | /*elided*/ any)[] | /*elided*/ any)[] | /*elided*/ any)[] | /*elided*/ any)[] | /*elided*/ any)[] | /*elided*/ any; })[] | { [key: string]: string | number | boolean | (string | number | boolean | (string | number | boolean | (string | number | boolean | (string | number | boolean | (string | number | boolean | (string | number | boolean | (string | number | boolean | (string | number | boolean | (string | number | boolean | (string | number | boolean | /*elided*/ any | /*elided*/ any)[] | /*elided*/ any)[] | /*elided*/ any)[] | /*elided*/ any)[] | /*elided*/ any)[] | /*elided*/ any)[] | /*elided*/ any)[] | /*elided*/ any)[] | /*elided*/ any)[] | /*elided*/ any)[] | /*elided*/ any; })[] | { [key: string]: string | number | boolean | (string | number | boolean | (string | number | boolean | (string | number | boolean | (string | number | boolean | (string | number | boolean | (string | number | boolean | (string | number | boolean | (string | number | boolean | (string | number | boolean | (string | number | boolean | (string | number | boolean | /*elided*/ any | /*elided*/ any)[] | /*elided*/ any)[] | /*elided*/ any)[] | /*elided*/ any)[] | /*elided*/ any)[] | /*elided*/ any)[] | /*elided*/ any)[] | /*elided*/ any)[] | /*elided*/ any)[] | /*elided*/ any)[] | /*elided*/ any)[] | /*elided*/ any; }>; /** Suggestions that were restored from the initial chat history; used if `suggestions` input is not yet set. */ protected _restoredSuggestions: Suggestion[]; /** * An optional function that returns a message sent in AI agent requests just before each new user message to provide context or "grounding" to * anchor the AI's response - such as the current state of the document the AI is helping with. * * Typically this string would begin with a special prefix (e.g. "[STATE_SNAPSHOT]") that is described by the system prompt. * * This message does not form part of the chat history, to avoid confusing the AI agent with older grounding state. * It is injected into the message history just before the latest user message as per standard recommendations, * since AI gives more attention to the recent content. */ readonly groundingContextProvider: _angular_core.InputSignal<() => string>; /** * Provides application-defined fields to enrich the user feedback tracking data for this component. * * Do not include Intellectual Property or Personal Identifiable Information in this object, as it may * be included in feedback data sent to third-party services. */ readonly userAnalyticsContext: _angular_core.InputSignal<Record<string, any>>; readonly onMessageFinish: _angular_core.OutputEmitterRef<AIMessage>; /** * Notified when a tool result is received from the agent. * * This is called before it is added to the message displayed in the component. * * Tools with a client/UI implementation may provide `output` for the tool * (e.g. data to render in the UI) by calling `addToolOutput`. */ readonly onToolResult: _angular_core.OutputEmitterRef<ToolCallPart<Record<string, unknown>, unknown>>; /** Notified when the user presses the positive or negative feedback buttons for an assistant message.*/ readonly onFeedback: _angular_core.OutputEmitterRef<{ userMessage: AIMessage; assistantMessage: AIMessage; feedbackPositive: boolean; }>; /** The model name from the most recently received metadata while streaming a response, which is used by the analytics. */ protected model?: string; /** The system prompt from the most recently received metadata while streaming a response, which is used by the analytics. */ protected systemPrompt?: Array<{ text: string; type: string; }>; /** * Emitted after any meaningful mutation of the messages list (send, finish, reprompt, reload, delete, reset). * Allows the parent to react to conversation changes, e.g. to persist the conversation. */ readonly onMessagesChange: _angular_core.OutputEmitterRef<AIMessage[]>; protected readonly _isLoading: _angular_core.WritableSignal<boolean>; /** Is an AI agent request currently in progress. This can be used to disable UI elements while a reply (which might change the state) is being loaded. */ get isLoadingAiResponse(): _angular_core.Signal<boolean>; private readonly aiAgentManagerApplicationName; /** Stores any error with the agent or backend microservice. This error message is already translated. */ protected readonly _agentHealthError: _angular_core.WritableSignal<string>; /** Stores the most recent agent health check response, or `undefined` while the initial check is in progress. */ private readonly _agentHealth; /** Returns the most recent agent health check status such as `ready`, or an error. */ get agentHealthStatus(): "loading" | "missing-microservice" | "missing-permissions" | "missing-provider" | "missing-agent" | "ready"; /** Stores any detailed error messages from the backend. */ protected readonly agentHealthDetailedMessages: _angular_core.WritableSignal<string>; /** * Stores any error from LLM call. * * If possible this string is translated already or registered with `gettext`. * In practice, this may not be translated if it comes from the backend. */ readonly agentRequestError: _angular_core.WritableSignal<string>; readonly messages: _angular_core.WritableSignal<AIMessage[]>; /** Computed cumulative token usage across all messages in the conversation. */ readonly cumulativeUsage: _angular_core.Signal<{ inputTokens: number; outputTokens: number; totalTokens: number; }>; /** * Computed signal holding the last assistant message, which is the one which may be rapidly changing as we * stream it back. This allows us to update the UI reactively without affecting the rest of the message history. */ protected readonly lastAssistantMessageContext: _angular_core.Signal<{ message: AIAssistantMessage; config: AssistantMessageDisplayConfig; isMessageLoading: boolean; messageDisplayIndex: number; }>; /** If the create agent button should be shown to the user. */ canCreate: boolean; prompt: string; private readonly aiService; private readonly userAnalyticsService; private readonly alertService; private readonly chatHistoryService; private abortController; private assistantSubscription; private pluginContextPath; private appState; private readonly assistantMessageComponents; private get currentContextPath(); /** * Stores client-supplied tool outputs to override tool results for a given toolCallId in the current assistant message. * Cleared at the start of each new message. */ private _clientToolOutputs; ngOnInit(): Promise<void>; ngOnChanges(changes: SimpleChanges): Promise<void>; /** * Save the current chat history and suggestions to an opaque, versioned, JSON-serializable snapshot of the chat history for persistence. * * To save space, only recent tool results and reasoning are included. * Additional options for compressing the history can be provided using the config parameters. * * @param pruner Optional function to prune/transform messages before serialization. * If not specified, `ChatHistoryService.createDefaultSerializationMessagePruner()` is used. */ saveChatHistory(pruner?: pruneMessagesFunction): ChatHistory; /** Sends a message to the AI */ sendMessage(message: AIMessage): Promise<void>; ngOnDestroy(): void; reprompt(userMessage: AIUserMessage): Promise<void>; /** Rates an AI message as positive/negative, sending the feedback to GainSight */ rate(assistantMessage: AIAssistantMessage, positive: boolean): Promise<void>; reload(assistantMessage: AIMessage): void; cancel(): void; /** Clears all messages and cancels any in-flight stream. */ resetMessages(): void; /** Removes the last user message and all assistant messages that follow it from the conversation. */ deleteLastExchange(): void; /** * Overrides the output of a specific tool result (in the current assistant message), including optionally indicating that an error occurred. * * This can be used to provide output for tool calls that are implemented on the client, * and is typically called from `onToolResult`. * * @param output: A string or JSON-serializable object. */ addToolOutput(toolCallId: string, output: any, error?: boolean): void; createAgent(): Promise<void>; private initializeChat; /** Restore chat history from a previously-saved snapshot. Does nothing if the component already has some messages. */ protected _restoreChatHistory(history: ChatHistory | undefined): void; private checkAgentHealth; private getAnalyticsMetadataContext; private handleAgentError; /** * Called as responses are incrementally streamed from the agent. * Reads the current assistant message from the end of the messages array, processes the update, * and replaces it with a new immutable message object to trigger change detection. * @param updatedAssistantMsg contains the latest data received from the agent * @param changedPart if provided, indicates which part of the message was changed in this update */ private processAgentMessage; private handleMessageFinish; /** Handles sending a message to a text-type agent via SSE streaming. */ private _sendStreamMessage; /** Handles sending a message to an object-type agent via a single non-streaming request. * The response JSON is rendered as a fenced code block in the assistant message. */ private _sendObjectMessage; static ɵfac: _angular_core.ɵɵFactoryDeclaration<AgentChatComponent, never>; static ɵcmp: _angular_core.ɵɵComponentDeclaration<AgentChatComponent, "c8y-agent-chat", never, { "agent": { "alias": "agent"; "required": true; "isSignal": true; }; "suggestions": { "alias": "suggestions"; "required": false; "isSignal": true; }; "chatConfig": { "alias": "chatConfig"; "required": false; "isSignal": true; }; "welcomeTemplate": { "alias": "welcomeTemplate"; "required": false; "isSignal": true; }; "autoCreateAgents": { "alias": "autoCreateAgents"; "required": false; "isSignal": true; }; "variables": { "alias": "variables"; "required": false; "isSignal": true; }; "assistantMessageComponent": { "alias": "assistantMessageComponent"; "required": false; "isSignal": true; }; "assistantMessageDisplayConfig": { "alias": "assistantMessageDisplayConfig"; "required": false; "isSignal": true; }; "preprocessAgentMessage": { "alias": "preprocessAgentMessage"; "required": false; "isSignal": true; }; "pruneMessagesForAgent": { "alias": "pruneMessagesForAgent"; "required": false; "isSignal": true; }; "skipHealthCheck": { "alias": "skipHealthCheck"; "required": false; "isSignal": true; }; "initialChatHistory": { "alias": "initialChatHistory"; "required": false; "isSignal": true; }; "groundingContextProvider": { "alias": "groundingContextProvider"; "required": false; "isSignal": true; }; "userAnalyticsContext": { "alias": "userAnalyticsContext"; "required": false; "isSignal": true; }; }, { "onMessageFinish": "onMessageFinish"; "onToolResult": "onToolResult"; "onFeedback": "onFeedback"; "onMessagesChange": "onMessagesChange"; }, never, never, true, never>; } declare class WidgetAiChatSectionComponent implements OnInit { /** * Asynchronously return the parts of the config that may require dynamically importing other modules. * @param componentInjector The function is provided a injector that can inject other services/components if needed. */ loadComponentConfig?: (componentInjector: Injector) => Promise<WidgetAiChatSectionComponentConfig>; protected readonly _componentConfig: _angular_core.WritableSignal<WidgetAiChatSectionComponentConfig>; suggestions: Array<{ label: string; prompt: string; }>; agent: string | ClientAgentDefinition; chatConfig?: Partial<AgentChatConfig>; /** Provide the specified `contextVariableName` as a variable that can be used in the prompt. */ useContextAsVariable: boolean; contextVariableName: string; private variables$; /** * If set, the variables will be loaded async on each round-trip. * This is useful if the variables depend on some async data source or if they need to be updated on each round-trip. * * @example * ```ts * variables = combineLatest([ * this.someService.getData(), * this.anotherService.getOtherData() * ]).pipe( * map(([data, otherData]) => ({ * myVarOne: data, * myVarTwo: otherData * })) * ); * ``` */ set variables(val: Observable<{ [key: string]: any; }> | { [key: string]: any; }); get variables(): Observable<{ [key: string]: any; }>; private readonly injector; private readonly widgetConfigService; private readonly alertService; private readonly contextRouteService; /** * A callback that is invoked when a tool result is returned. The function can return either: * - `undefined` or `void`: No action is taken. * - an object which is stored to the widget configuration. */ onToolResult: (step: ToolCallPart) => undefined | any; /** * @ignore */ ngOnInit(): Promise<void>; /** * Handles the tool result returned from the AI agent. * @param tool The tool result returned from the AI agent. */ toolResultHandler(tool: ToolCallPart): void; static ɵfac: _angular_core.ɵɵFactoryDeclaration<WidgetAiChatSectionComponent, never>; static ɵcmp: _angular_core.ɵɵComponentDeclaration<WidgetAiChatSectionComponent, "c8y-widget-ai-chat-section", never, { "loadComponentConfig": { "alias": "loadComponentConfig"; "required": false; }; "suggestions": { "alias": "suggestions"; "required": false; }; "agent": { "alias": "agent"; "required": false; }; "chatConfig": { "alias": "chatConfig"; "required": false; }; "useContextAsVariable": { "alias": "useContextAsVariable"; "required": false; }; "contextVariableName": { "alias": "contextVariableName"; "required": false; }; "variables": { "alias": "variables"; "required": false; }; "onToolResult": { "alias": "onToolResult"; "required": false; }; }, {}, never, never, true, never>; } export { AgentChatComponent, WidgetAiChatSectionComponent }; //# sourceMappingURL=index.d.ts.map