@c8y/ngx-components 1023.68.7 → 1023.71.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/ai/agent-chat/index.d.ts +235 -75
- package/ai/agent-chat/index.d.ts.map +1 -1
- package/ai/ai-chat/index.d.ts +176 -26
- package/ai/ai-chat/index.d.ts.map +1 -1
- package/ai/index.d.ts +309 -75
- package/ai/index.d.ts.map +1 -1
- package/asset-properties/index.d.ts.map +1 -1
- package/echart/index.d.ts +4 -0
- package/echart/index.d.ts.map +1 -1
- package/echart/models/index.d.ts +2 -0
- package/echart/models/index.d.ts.map +1 -1
- package/events/events-timeline/index.d.ts +3 -2
- package/events/events-timeline/index.d.ts.map +1 -1
- package/fesm2022/c8y-ngx-components-ai-agent-chat.mjs +680 -242
- package/fesm2022/c8y-ngx-components-ai-agent-chat.mjs.map +1 -1
- package/fesm2022/c8y-ngx-components-ai-ai-chat.mjs +343 -44
- package/fesm2022/c8y-ngx-components-ai-ai-chat.mjs.map +1 -1
- package/fesm2022/c8y-ngx-components-ai.mjs +187 -75
- package/fesm2022/c8y-ngx-components-ai.mjs.map +1 -1
- package/fesm2022/c8y-ngx-components-asset-properties.mjs +11 -5
- package/fesm2022/c8y-ngx-components-asset-properties.mjs.map +1 -1
- package/fesm2022/{c8y-ngx-components-computed-asset-properties-alarm-count-config.component-B2cy8gI7.mjs → c8y-ngx-components-computed-asset-properties-alarm-count-config.component-CPLDClTp.mjs} +3 -3
- package/fesm2022/{c8y-ngx-components-computed-asset-properties-alarm-count-config.component-B2cy8gI7.mjs.map → c8y-ngx-components-computed-asset-properties-alarm-count-config.component-CPLDClTp.mjs.map} +1 -1
- package/fesm2022/{c8y-ngx-components-computed-asset-properties-c8y-ngx-components-computed-asset-properties-BYHnA-5R.mjs → c8y-ngx-components-computed-asset-properties-c8y-ngx-components-computed-asset-properties-9be_iMQg.mjs} +30 -13
- package/fesm2022/c8y-ngx-components-computed-asset-properties-c8y-ngx-components-computed-asset-properties-9be_iMQg.mjs.map +1 -0
- package/fesm2022/{c8y-ngx-components-computed-asset-properties-configuration-snapshot-config.component-C4oL39m8.mjs → c8y-ngx-components-computed-asset-properties-configuration-snapshot-config.component-B2em01_W.mjs} +3 -3
- package/fesm2022/c8y-ngx-components-computed-asset-properties-configuration-snapshot-config.component-B2em01_W.mjs.map +1 -0
- package/fesm2022/{c8y-ngx-components-computed-asset-properties-event-count-config.component-DGwm6_C9.mjs → c8y-ngx-components-computed-asset-properties-event-count-config.component-CQuGa1RI.mjs} +3 -3
- package/fesm2022/c8y-ngx-components-computed-asset-properties-event-count-config.component-CQuGa1RI.mjs.map +1 -0
- package/fesm2022/{c8y-ngx-components-computed-asset-properties-fieldbus-item-status-config.component-BxfCjbYY.mjs → c8y-ngx-components-computed-asset-properties-fieldbus-item-status-config.component-CkmurxJv.mjs} +5 -5
- package/fesm2022/{c8y-ngx-components-computed-asset-properties-fieldbus-item-status-config.component-BxfCjbYY.mjs.map → c8y-ngx-components-computed-asset-properties-fieldbus-item-status-config.component-CkmurxJv.mjs.map} +1 -1
- package/fesm2022/c8y-ngx-components-computed-asset-properties-last-measurement-config.component-CTK9zNUh.mjs +86 -0
- package/fesm2022/c8y-ngx-components-computed-asset-properties-last-measurement-config.component-CTK9zNUh.mjs.map +1 -0
- package/fesm2022/c8y-ngx-components-computed-asset-properties.mjs +1 -1
- package/fesm2022/c8y-ngx-components-device-shell.mjs +1 -1
- package/fesm2022/c8y-ngx-components-device-shell.mjs.map +1 -1
- package/fesm2022/c8y-ngx-components-echart-models.mjs.map +1 -1
- package/fesm2022/c8y-ngx-components-echart.mjs +39 -18
- package/fesm2022/c8y-ngx-components-echart.mjs.map +1 -1
- package/fesm2022/c8y-ngx-components-events-events-timeline.mjs +5 -2
- package/fesm2022/c8y-ngx-components-events-events-timeline.mjs.map +1 -1
- package/fesm2022/c8y-ngx-components-operations-operations-timeline.mjs +5 -2
- package/fesm2022/c8y-ngx-components-operations-operations-timeline.mjs.map +1 -1
- package/fesm2022/c8y-ngx-components-widgets-definitions-html-widget-ai-config.mjs +106 -4
- package/fesm2022/c8y-ngx-components-widgets-definitions-html-widget-ai-config.mjs.map +1 -1
- package/fesm2022/c8y-ngx-components-widgets-implementations-html-widget.mjs +14 -122
- package/fesm2022/c8y-ngx-components-widgets-implementations-html-widget.mjs.map +1 -1
- package/fesm2022/c8y-ngx-components.mjs +9 -5
- package/fesm2022/c8y-ngx-components.mjs.map +1 -1
- package/index.d.ts +2 -1
- package/index.d.ts.map +1 -1
- package/locales/de.po +88 -35
- package/locales/es.po +87 -34
- package/locales/fr.po +87 -34
- package/locales/ja_JP.po +87 -35
- package/locales/ko.po +88 -35
- package/locales/locales.pot +83 -30
- package/locales/nl.po +88 -35
- package/locales/pl.po +88 -35
- package/locales/pt_BR.po +87 -34
- package/locales/zh_CN.po +88 -35
- package/locales/zh_TW.po +88 -35
- package/operations/operations-timeline/index.d.ts +3 -2
- package/operations/operations-timeline/index.d.ts.map +1 -1
- package/package.json +1 -1
- package/widgets/implementations/html-widget/index.d.ts +11 -50
- package/widgets/implementations/html-widget/index.d.ts.map +1 -1
- package/fesm2022/c8y-ngx-components-computed-asset-properties-c8y-ngx-components-computed-asset-properties-BYHnA-5R.mjs.map +0 -1
- package/fesm2022/c8y-ngx-components-computed-asset-properties-configuration-snapshot-config.component-C4oL39m8.mjs.map +0 -1
- package/fesm2022/c8y-ngx-components-computed-asset-properties-event-count-config.component-DGwm6_C9.mjs.map +0 -1
- package/fesm2022/c8y-ngx-components-computed-asset-properties-last-measurement-config.component-C1cuxN3L.mjs +0 -92
- package/fesm2022/c8y-ngx-components-computed-asset-properties-last-measurement-config.component-C1cuxN3L.mjs.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"c8y-ngx-components-ai-ai-chat.mjs","sources":["../../ai/ai-chat/ai-chat-message-action.component.ts","../../ai/ai-chat/ai-chat-message-action.component.html","../../ai/ai-chat/ai-chat-message.component.ts","../../ai/ai-chat/ai-chat-message.component.html","../../ai/ai-chat/ai-chat-suggestion.component.ts","../../ai/ai-chat/ai-chat-suggestion.component.html","../../ai/ai-chat/ai-chat.component.ts","../../ai/ai-chat/ai-chat.component.html","../../ai/ai-chat/c8y-ngx-components-ai-ai-chat.ts"],"sourcesContent":["import { Component, EventEmitter, Input, Output } from '@angular/core';\nimport { C8yTranslatePipe, IconDirective } from '@c8y/ngx-components';\nimport { SupportedIconsSuggestions } from '@c8y/ngx-components/icon-selector/icons';\nimport { TooltipModule } from 'ngx-bootstrap/tooltip';\n\n/**\n * An action button that can be added to chat messages.\n * Typically used for actions like copying, regenerating, or providing feedback on messages.\n */\n@Component({\n selector: 'c8y-ai-chat-message-action',\n templateUrl: './ai-chat-message-action.component.html',\n standalone: true,\n imports: [TooltipModule, C8yTranslatePipe, IconDirective]\n})\nexport class AiChatMessageActionComponent {\n /**\n * Disables the action button when true.\n */\n @Input()\n disabled = false;\n\n /**\n * Tooltip text displayed when hovering over the action button.\n */\n @Input()\n tooltip = '';\n\n /**\n * Icon to display in the action button.\n */\n @Input()\n icon: SupportedIconsSuggestions = 'cog';\n\n /**\n * Emitted when the action button is clicked.\n */\n @Output()\n click = new EventEmitter<void>();\n}\n","<button\n class=\"btn btn-dot text-muted\"\n [attr.aria-label]=\"tooltip | translate\"\n [tooltip]=\"tooltip | translate\"\n [adaptivePosition]=\"true\"\n [delay]=\"500\"\n (click)=\"click.emit()\"\n [disabled]=\"disabled\"\n>\n <i\n class=\"text-12\"\n [c8yIcon]=\"icon\"\n ></i>\n</button>\n","import { Component, computed, input, inject } from '@angular/core';\nimport { gettext } from '@c8y/ngx-components/gettext';\nimport { AIMessage } from '@c8y/ngx-components/ai';\nimport { C8yTranslatePipe, DatePipe, MarkdownToHtmlPipe } from '@c8y/ngx-components';\nimport { AsyncPipe, NgClass } from '@angular/common';\nimport { TooltipModule } from 'ngx-bootstrap/tooltip';\nimport { TranslateService } from '@ngx-translate/core';\n\n@Component({\n selector: 'c8y-ai-chat-message',\n templateUrl: './ai-chat-message.component.html',\n standalone: true,\n imports: [MarkdownToHtmlPipe, AsyncPipe, NgClass, TooltipModule, C8yTranslatePipe, DatePipe]\n})\nexport class AiChatMessageComponent {\n readonly role = input<AIMessage['role']>();\n readonly message = input<AIMessage>();\n private readonly translateService = inject(TranslateService);\n\n private readonly roleResolved = computed(() => {\n return this.message()?.role || this.role();\n });\n\n /**\n * Generates an accessible label for the message container.\n * Includes the role (user or assistant) and timestamp if available.\n * @returns The aria-label string for screen readers\n */\n readonly ariaLabel = computed(() => {\n const tpl =\n this.roleResolved() === 'user'\n ? gettext('You said: \"{{ message }}\"')\n : gettext('Assistant said: \"{{ message }}\"');\n const msg = this.message();\n return this.translateService.instant(tpl, { message: msg?.content || gettext('No response.') });\n });\n\n /**\n * Generates an accessible label for the message content.\n * Prefixes the content with contextual information about who sent it.\n * @returns The aria-label string with prefixed role information\n */\n readonly messageContentAriaLabel = computed(() => {\n const msg = this.message();\n const time = msg?.timestamp ? new Date(msg.timestamp).toLocaleTimeString() : '';\n let tpl: string;\n if (time) {\n tpl =\n this.roleResolved() === 'user'\n ? gettext('User message at {{ time }}')\n : gettext('Assistant message at {{ time }}');\n } else {\n tpl = this.roleResolved() === 'user' ? gettext('User message') : gettext('Assistant message');\n }\n return this.translateService.instant(tpl, { time });\n });\n}\n","<div\n class=\"d-col p-b-16\"\n [attr.aria-label]=\"ariaLabel()\"\n role=\"article\"\n>\n <div\n class=\"chat-message text-break-word\"\n [ngClass]=\"{\n 'user-message': message()?.role === 'user' || role() === 'user',\n 'agent-message': message()?.role === 'assistant' || role() === 'assistant'\n }\"\n >\n <div\n class=\"message-content\"\n [attr.aria-label]=\"messageContentAriaLabel()\"\n [innerHTML]=\"message()?.content | markdownToHtml | async\"\n ></div>\n <ng-content select=\":not(c8y-ai-chat-message-action)\"></ng-content>\n @if (message()?.timestamp) {\n <div class=\"message-timestamp\">\n <span [tooltip]=\"message()?.timestamp | c8yDate\">\n {{ message()?.timestamp | c8yDate: 'adaptiveDate' }}\n </span>\n </div>\n }\n </div>\n <div\n class=\"message-action\"\n [attr.aria-label]=\"'Message actions' | translate\"\n role=\"toolbar\"\n [ngClass]=\"{\n 'user-action showOnHover': message()?.role === 'user' || role() === 'user',\n 'agent-action p-l-16': message()?.role === 'assistant' || role() === 'assistant'\n }\"\n >\n <ng-content select=\"c8y-ai-chat-message-action\"></ng-content>\n </div>\n</div>\n","import { Component, EventEmitter, Input, Output } from '@angular/core';\nimport { C8yTranslatePipe, IconDirective } from '@c8y/ngx-components';\nimport { AIMessage } from '@c8y/ngx-components/ai';\nimport { SupportedIconsSuggestions } from '@c8y/ngx-components/icon-selector/icons';\n\n/**\n * A suggestion chip that can be displayed in the chat interface.\n * When clicked, it automatically populates and sends a predefined prompt.\n */\n@Component({\n selector: 'c8y-ai-chat-suggestion',\n templateUrl: './ai-chat-suggestion.component.html',\n standalone: true,\n imports: [IconDirective, C8yTranslatePipe]\n})\nexport class AiChatSuggestionComponent {\n /**\n * The visible label text displayed on the suggestion chip.\n */\n @Input()\n label: string;\n\n /**\n * The prompt text that will be sent when the suggestion is clicked.\n */\n @Input()\n prompt: string;\n\n /**\n * Icon to display alongside the suggestion label.\n */\n @Input()\n icon: SupportedIconsSuggestions = 'c8y-bulb';\n\n /**\n * When true, uses AI-styled buttons instead of default styling.\n */\n @Input()\n useAiButtons = false;\n\n /**\n * Disables the suggestion chip when true.\n */\n @Input()\n disabled = false;\n\n /**\n * Emitted when the suggestion is clicked, providing the prompt as an AIMessage.\n */\n @Output()\n suggestionClicked = new EventEmitter<AIMessage>();\n\n /**\n * Handles suggestion click and emits the prompt as a user message.\n */\n suggest() {\n this.suggestionClicked.emit({\n content: this.prompt,\n role: 'user',\n timestamp: new Date().toISOString()\n });\n }\n}\n","@if (!useAiButtons) {\n <button\n class=\"btn btn-default btn-sm\"\n [title]=\"prompt | translate\"\n (click)=\"suggest()\"\n [disabled]=\"disabled\"\n >\n <i [c8yIcon]=\"icon\"></i>\n {{ label | translate }}\n </button>\n} @else {\n <button\n class=\"btn btn-sm btn-ai\"\n [title]=\"prompt | translate\"\n (click)=\"suggest()\"\n [disabled]=\"disabled\"\n >\n <span>{{ label | translate }}</span>\n </button>\n}\n","import { NgClass } from '@angular/common';\nimport { Component, ContentChildren, EventEmitter, Input, Output, QueryList } from '@angular/core';\nimport { FormsModule } from '@angular/forms';\nimport { C8yTranslatePipe, IconDirective, TextareaAutoresizeDirective } from '@c8y/ngx-components';\nimport { gettext } from '@c8y/ngx-components/gettext';\nimport { AIMessage, ChatConfig } from '@c8y/ngx-components/ai';\nimport { AiChatMessageComponent } from './ai-chat-message.component';\n\n/**\n * An interactive chat interface component for AI-powered conversations.\n * Displays messages in a conversation format with support for loading states,\n * custom configuration, and markdown formatting.\n */\n@Component({\n selector: 'c8y-ai-chat',\n templateUrl: './ai-chat.component.html',\n imports: [C8yTranslatePipe, FormsModule, TextareaAutoresizeDirective, IconDirective, NgClass],\n standalone: true,\n host: { class: 'd-contents' }\n})\nexport class AiChatComponent {\n /**\n * Indicates whether the chat is currently processing a request.\n * When true, displays a cancel button instead of send button and disables the input.\n */\n @Input()\n isLoading = false;\n\n /**\n * Disables the chat input and send button when true.\n */\n @Input()\n disabled = false;\n\n /**\n * The current text in the chat input field. Supports two-way binding.\n */\n @Input()\n prompt = '';\n\n /**\n * Emitted when the user sends a message.\n * Provides an AIMessage object with role, content, and timestamp.\n */\n @Output()\n onMessage = new EventEmitter<AIMessage>();\n\n /**\n * Emitted when the user cancels an ongoing operation during loading state.\n */\n @Output()\n onCancel = new EventEmitter<void>();\n\n /**\n * Child message components displayed in the chat.\n */\n @ContentChildren(AiChatMessageComponent) messages: QueryList<AiChatMessageComponent>;\n\n readonly componentId = `chat-${crypto.randomUUID()}`;\n\n private _config: ChatConfig = {\n headline: gettext('Welcome!'),\n welcomeText: '',\n title: gettext('What can I help you with?'),\n placeholder: gettext('Type your message here…'),\n placeholderAfterFirstMessage: gettext('Type your next message here…'),\n sendButtonText: gettext('Send'),\n cancelButtonText: gettext('Cancel'),\n disclaimerText: gettext(\n 'AI-generated responses can contain errors. Verify the details before use.'\n ),\n userInterfaceIcons: {\n send: 'arrow-circle-up',\n cancel: 'stop-circle'\n }\n };\n\n /**\n * Configuration object for customizing labels, placeholders, and icons.\n * Accepts partial configuration to override defaults.\n */\n @Input()\n set config(value: Partial<ChatConfig>) {\n this._config = { ...this._config, ...value };\n }\n get config() {\n return this._config;\n }\n\n /**\n * Handles message submission when the user sends a message.\n * Emits the onMessage event and clears the input.\n * @param $event - The event object from the form submission\n */\n sendMessage($event: Event): void {\n $event.preventDefault();\n if (!this.prompt) return;\n this.onMessage.emit({\n role: 'user',\n content: this.prompt,\n timestamp: new Date().toISOString()\n });\n this.prompt = '';\n }\n\n /**\n * Handles cancellation of ongoing operations during loading state.\n * Emits the onCancel event.\n */\n cancel(): void {\n this.onCancel.emit();\n }\n}\n","<div\n class=\"d-col fit-h fit-w flex-grow\"\n [attr.aria-label]=\"config.headline | translate\"\n role=\"region\"\n>\n @if (messages.length > 0) {\n <div\n class=\"inner-scroll flex-grow d-col-reverse min-height-0 bg-level-0\"\n [attr.aria-label]=\"'Chat conversation' | translate\"\n aria-live=\"polite\"\n aria-atomic=\"false\"\n role=\"log\"\n >\n <div class=\"d-col p-l-16 p-r-16\">\n <ng-content select=\"c8y-ai-chat-message\"></ng-content>\n <ng-content></ng-content>\n </div>\n </div>\n }\n <div [ngClass]=\"{ 'd-col fit-h': messages.length === 0 }\">\n @if (messages.length === 0) {\n <div\n class=\"p-24\"\n aria-live=\"polite\"\n role=\"status\"\n >\n <h4 class=\"m-b-16 text-medium\">{{ config.headline | translate }}</h4>\n <p class=\"p-b-8 text-balance\">{{ config.title | translate }}</p>\n <p class=\"text-muted text-balance\">{{ config.welcomeText | translate }}</p>\n </div>\n }\n <div class=\"chat-input bg-level-1\">\n <div class=\"d-flex inner-scroll a-i-center gap-8 p-l-16 p-r-16 p-b-8\">\n <ng-content select=\"c8y-ai-chat-suggestion\"></ng-content>\n </div>\n <div class=\"chat-input-group\">\n <label\n class=\"sr-only\"\n for=\"chat-input-{{ componentId }}\"\n >\n {{ config.placeholder | translate }}\n </label>\n <textarea\n class=\"form-control no-resize\"\n style=\"max-height: 200px !important\"\n [attr.aria-label]=\"config.placeholder | translate\"\n id=\"chat-input-{{ componentId }}\"\n [attr.aria-describedby]=\"config.disclaimerText ? 'chat-disclaimer-' + componentId : null\"\n [attr.aria-busy]=\"isLoading\"\n [placeholder]=\"\n (messages.length === 0 ? config.placeholder : config.placeholderAfterFirstMessage)\n | translate\n \"\n [(ngModel)]=\"prompt\"\n (keydown.enter)=\"sendMessage($event)\"\n [disabled]=\"disabled\"\n c8y-textarea-autoresize\n ></textarea>\n <div class=\"chat-input-group-btn\">\n @if (!isLoading) {\n <button\n class=\"btn btn-dot\"\n [attr.title]=\"config.sendButtonText | translate\"\n [attr.aria-label]=\"config.sendButtonText | translate\"\n type=\"button\"\n (click)=\"sendMessage($event)\"\n [disabled]=\"disabled || prompt.trim().length === 0\"\n >\n <i [c8yIcon]=\"config.userInterfaceIcons?.send || 'arrow-circle-right'\"></i>\n </button>\n } @else {\n <button\n class=\"btn btn-dot btn-dot--danger\"\n [attr.title]=\"config.cancelButtonText | translate\"\n [attr.aria-label]=\"config.cancelButtonText | translate\"\n type=\"button\"\n (click)=\"cancel()\"\n >\n <i [c8yIcon]=\"config.userInterfaceIcons?.cancel || 'stop'\"></i>\n </button>\n }\n </div>\n </div>\n @if (config.disclaimerText) {\n <div\n class=\"text-muted m-b-8 text-10 p-l-16\"\n id=\"chat-disclaimer-{{ componentId }}\"\n role=\"note\"\n >\n {{ config.disclaimerText | translate }}\n </div>\n }\n </div>\n </div>\n</div>\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":["i1"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"c8y-ngx-components-ai-ai-chat.mjs","sources":["../../ai/ai-chat/ai-chat-tool-call.component.ts","../../ai/ai-chat/ai-chat-tool-call.component.html","../../ai/ai-chat/ai-chat-assistant-part.component.ts","../../ai/ai-chat/ai-chat-assistant-part.component.html","../../ai/ai-chat/ai-chat-assistant-message.component.ts","../../ai/ai-chat/ai-chat-assistant-message.component.html","../../ai/ai-chat/ai-chat-message-action.component.ts","../../ai/ai-chat/ai-chat-message-action.component.html","../../ai/ai-chat/ai-chat-message.component.ts","../../ai/ai-chat/ai-chat-message.component.html","../../ai/ai-chat/ai-chat-suggestion.component.ts","../../ai/ai-chat/ai-chat-suggestion.component.html","../../ai/ai-chat/ai-chat.component.ts","../../ai/ai-chat/ai-chat.component.html","../../ai/ai-chat/c8y-ngx-components-ai-ai-chat.ts"],"sourcesContent":["import { ChangeDetectionStrategy, Component, inject, input, signal, Type } from '@angular/core';\nimport { gettext } from '@c8y/ngx-components/gettext';\nimport { ToolCallConfig, ToolCallPart } from '@c8y/ngx-components/ai';\nimport { C8yTranslateModule, C8yTranslatePipe, IconDirective } from '@c8y/ngx-components';\nimport { JsonPipe, NgComponentOutlet } from '@angular/common';\nimport { TranslateService } from '@ngx-translate/core';\nimport { CollapseModule } from 'ngx-bootstrap/collapse';\n\n@Component({\n selector: 'c8y-ai-chat-tool-call',\n templateUrl: './ai-chat-tool-call.component.html',\n changeDetection: ChangeDetectionStrategy.OnPush,\n imports: [\n NgComponentOutlet,\n JsonPipe,\n C8yTranslatePipe,\n IconDirective,\n CollapseModule,\n C8yTranslateModule\n ]\n})\nexport class AiChatToolCallComponent {\n /**\n * The tool call part to render. This includes all information about the tool call, including the name of the tool,\n * the input provided and (once available) the output from the tool.\n */\n readonly tool = input.required<ToolCallPart>();\n\n /**\n * Whether the tool call is still in progress.\n */\n readonly isExecuting = input.required<boolean>();\n\n /**\n * A custom component to render the details of the tool call. If not provided, the default details section will be shown.\n */\n readonly toolDetailsComponent = input<Type<unknown> | undefined>(undefined);\n\n /**\n * Shows the default details section for a tool call.\n */\n readonly showDefaultToolDetails = input<boolean>(false);\n\n /**\n * The label to show while the tool is executing. If not provided, a default label will be generated based on the tool name.\n */\n readonly executingLabel = input<string | undefined>(undefined);\n /**\n * The label to show once the tool has completed. If not provided, a default label will be generated based on the tool name and whether the tool call was successful or resulted in an error.\n */\n readonly completedLabel = input<string | undefined>(undefined);\n\n /**\n * A function that can generate a label for this tool call based on the tool call data.\n */\n readonly labelProvider = input<ToolCallConfig['labelProvider']>();\n\n protected readonly expanded = signal(false);\n protected readonly everExpanded = signal(false);\n\n private readonly translateService = inject(TranslateService);\n\n getToolLabel(tool: ToolCallPart): string {\n const isExecuting = tool.type !== 'tool-result';\n if (this.labelProvider()) {\n try {\n const dynamicLabel = this.labelProvider()(tool, this.translateService);\n if (dynamicLabel) return dynamicLabel;\n } catch (err) {\n // Should never happen, report it then fall back to default logic\n console.error(`Error in labelProvider for tool ${tool.toolName}:`, err);\n }\n }\n\n if (isExecuting && this.executingLabel()) {\n return this.executingLabel();\n }\n if (!isExecuting && this.completedLabel()) {\n return this.completedLabel();\n }\n\n return this.translateService.instant(\n isExecuting ? gettext('Calling tool {{toolName}}') : gettext('Used tool {{toolName}}'),\n { toolName: tool.toolName }\n );\n }\n\n protected toggleExpanded(): void {\n const willExpand = !this.expanded();\n this.expanded.set(willExpand);\n if (willExpand) {\n this.everExpanded.set(true);\n }\n }\n}\n","@let _tool = tool();\n@let showDetails = showDefaultToolDetails();\n@let isToolExpanded = expanded();\n\n<fieldset\n class=\"c8y-fieldset p-b-4 ai-tool-call__fieldset\"\n [attr.aria-label]=\"'Tool call: ' + _tool.toolName\"\n>\n <button\n class=\"btn-clean ai-tool-call__btn\"\n [attr.aria-expanded]=\"showDetails ? isToolExpanded : null\"\n [attr.aria-controls]=\"showDetails ? 'tool-call-' + _tool.toolCallId : null\"\n type=\"button\"\n [attr.data-cy]=\"'tool-call-' + _tool.toolName\"\n [disabled]=\"!showDetails || null\"\n (click)=\"toggleExpanded()\"\n >\n @if (isExecuting()) {\n <i\n class=\"icon-spin icon-14 text-primary m-r-4\"\n c8yIcon=\"spinner\"\n aria-hidden=\"true\"\n ></i>\n } @else {\n <!-- Treat it as an error if a tool still thinks its executing when the message containing it is not -->\n @if (_tool.error || isExecuting()) {\n <i\n class=\"icon-14 text-danger m-r-4\"\n [c8yIcon]=\"'exclamation-circle'\"\n aria-hidden=\"true\"\n ></i>\n } @else {\n <i\n class=\"icon-14 text-success m-r-4\"\n [c8yIcon]=\"'check'\"\n aria-hidden=\"true\"\n ></i>\n }\n }\n <span class=\"small\">{{ getToolLabel(_tool) | translate }}</span>\n\n @if (showDetails) {\n <i\n class=\"m-l-4 icon-12\"\n [c8yIcon]=\"isToolExpanded ? 'collapse-arrow' : 'expand-arrow'\"\n aria-hidden=\"true\"\n ></i>\n }\n </button>\n\n <!-- If this is an artifact tool, render it - but do so lazily only on first open,\n since these components could be heavyweight (e.g. Monaco)\n -->\n @if (showDetails && (isToolExpanded || everExpanded())) {\n <div\n class=\"collapse tool-details m-t-8 p-8 b-r-4\"\n [attr.aria-label]=\"'Tool details for message latest tool: ' + _tool.toolName\"\n role=\"region\"\n [collapse]=\"!isToolExpanded\"\n [id]=\"'tool-call-' + _tool.toolCallId\"\n [isAnimated]=\"true\"\n data-cy=\"ai-tool-component\"\n >\n @if (toolDetailsComponent()) {\n <ng-container\n [ngComponentOutlet]=\"toolDetailsComponent()\"\n [ngComponentOutletInputs]=\"{ tool: _tool }\"\n ></ng-container>\n } @else {\n @let noneLabel = '(no data)' | translate;\n\n <p class=\"text-label-small\">{{ 'Tool input' | translate }}</p>\n <pre class=\"fit-w small\">{{ _tool.input || noneLabel | json }}</pre>\n\n @if (_tool.type === 'tool-result') {\n <p class=\"text-label-small\">{{ 'Tool output' | translate }}</p>\n @if (\n typeof _tool.output === 'string' || _tool.output === undefined || _tool.output === null\n ) {\n <pre class=\"fit-w small\">{{ _tool.output || noneLabel }}</pre>\n } @else {\n <pre class=\"fit-w small\">{{ _tool.output | json }}</pre>\n }\n }\n }\n </div>\n }\n</fieldset>\n","import { ChangeDetectionStrategy, Component, input, signal } from '@angular/core';\nimport { AIMessagePart } from '@c8y/ngx-components/ai';\nimport { C8yTranslatePipe, IconDirective, MarkdownToHtmlPipe } from '@c8y/ngx-components';\nimport { AsyncPipe, NgComponentOutlet } from '@angular/common';\nimport { CollapseModule } from 'ngx-bootstrap/collapse';\nimport { AssistantMessageContext } from './ai-chat-assistant-message.component';\nimport { AiChatToolCallComponent } from './ai-chat-tool-call.component';\n\n/** This renders a part of an assistant message. Currently only ToolCallPart is supported, but later we can expand this to deal with all part types. */\n@Component({\n selector: 'c8y-ai-chat-assistant-part',\n templateUrl: './ai-chat-assistant-part.component.html',\n standalone: true,\n imports: [\n NgComponentOutlet,\n C8yTranslatePipe,\n IconDirective,\n CollapseModule,\n MarkdownToHtmlPipe,\n AsyncPipe,\n AiChatToolCallComponent\n ],\n changeDetection: ChangeDetectionStrategy.OnPush\n})\nexport class AiChatAssistantPartComponent {\n readonly part = input.required<AIMessagePart>();\n\n /** Whether this part is rendered as part of the main answer, or is an intermediate step with more muted rendering. */\n readonly displayAsPartOfMainAnswer = input(true);\n\n /**\n * The context needed to render a message.\n * This is a single input so we can extend in future without breaking people who have a custom rendering implementation.\n */\n readonly assistantMessageContext = input.required<AssistantMessageContext>();\n\n /** Tracks whether this is currently expanded (used for reasoning section). */\n protected readonly expanded = signal(false);\n\n protected toggleExpanded(): void {\n this.expanded.update(v => !v);\n }\n}\n","@let ctx = assistantMessageContext();\n@let _config = ctx.config;\n@let toolCallConfig = _config.toolCallConfig || {};\n@let _part = part();\n\n@if (_part.type === 'text') {\n <p\n [class.text-muted]=\"!displayAsPartOfMainAnswer()\"\n [innerHTML]=\"_part.text | markdownToHtml | async\"\n ></p>\n} @else if (_part.type === 'reasoning') {\n @let reasoningExpanded = expanded();\n\n <fieldset\n class=\"c8y-fieldset p-b-4 ai-tool-call__fieldset\"\n data-cy=\"ai-reasoning\"\n >\n <button\n class=\"btn-clean ai-tool-call__btn\"\n aria-label=\"Toggle display of the reasoning section\"\n [attr.aria-expanded]=\"reasoningExpanded\"\n [attr.aria-controls]=\"'reasoning-content-message' + ctx.messageDisplayIndex\"\n type=\"button\"\n (click)=\"toggleExpanded()\"\n >\n <i\n class=\"m-r-4\"\n [c8yIcon]=\"'ai-sparkles'\"\n ></i>\n @let hideReasoning = 'Hide reasoning' | translate;\n @let showReasoning = 'Show reasoning' | translate;\n <span class=\"small\">{{ reasoningExpanded ? hideReasoning : showReasoning }}</span>\n <i\n class=\"m-l-4 icon-12 text-muted\"\n [c8yIcon]=\"reasoningExpanded ? 'collapse-arrow' : 'expand-arrow'\"\n ></i>\n </button>\n\n <div\n class=\"collapse reasoning-content\"\n [attr.aria-label]=\"'Reasoning for message latest' + ctx.messageDisplayIndex\"\n role=\"region\"\n [collapse]=\"!reasoningExpanded\"\n [id]=\"'reasoning-content-message' + ctx.messageDisplayIndex\"\n [isAnimated]=\"true\"\n data-cy=\"ai-reasoning-content\"\n >\n <div\n class=\"m-t-8 p-b-4 text-muted\"\n [innerHTML]=\"_part.text | markdownToHtml | async\"\n ></div>\n </div>\n </fieldset>\n} @else if (_part.type === 'step-start') {\n <!-- Visually hidden label included when users copy-paste from the chat, e.g. to report issues -->\n <span\n class=\"ai-step-start hidden-copy-label\"\n aria-hidden=\"true\"\n data-cy=\"ai-step-start-separator\"\n >--- (step separator) ---</span\n >\n} @else if (_part.type.startsWith('tool')) {\n @let tool = $any(_part);\n @let toolDisplay = toolCallConfig[tool.toolName];\n\n <!-- Visually hidden label included when users copy-paste from the chat, e.g. to report issues -->\n <span\n class=\"hidden-copy-label\"\n aria-hidden=\"true\"\n >--- Tool call: {{ tool.toolName }} ---</span\n >\n\n @if (toolDisplay?.component) {\n <div data-cy=\"ai-tool-with-custom-component\">\n <ng-container\n [ngComponentOutlet]=\"toolDisplay.component\"\n [ngComponentOutletInputs]=\"{\n tool: tool,\n ctx: ctx\n }\"\n ></ng-container>\n </div>\n } @else if (!toolDisplay?.isHidden) {\n <!-- Don't allow expanding while tool call is in progress - it's unlikely to have anything worth rendering,\n and complicates implementation for the client to have to check for the isExecuting case.\n -->\n <c8y-ai-chat-tool-call\n [tool]=\"tool\"\n [isExecuting]=\"tool.type !== 'tool-result' && ctx.isMessageLoading\"\n [toolDetailsComponent]=\"_config.toolDetailsComponent?.(tool)\"\n [showDefaultToolDetails]=\"_config.showDefaultToolDetails === 'all'\"\n [executingLabel]=\"toolDisplay?.executingLabel\"\n [completedLabel]=\"toolDisplay?.completedLabel\"\n [labelProvider]=\"toolDisplay?.labelProvider\"\n ></c8y-ai-chat-tool-call>\n }\n}\n","import { Component, effect, inject, input, signal } from '@angular/core';\nimport { C8yTranslateModule, C8yTranslatePipe, IconDirective } from '@c8y/ngx-components';\nimport {\n AgentStep,\n AIMessage,\n AssistantMessageDisplayConfig,\n ToolCallPart\n} from '@c8y/ngx-components/ai';\nimport { gettext } from '@c8y/ngx-components/gettext';\nimport { TranslateService } from '@ngx-translate/core';\nimport { CollapseModule } from 'ngx-bootstrap/collapse';\nimport { AiChatAssistantPartComponent } from './ai-chat-assistant-part.component';\n\n/**\n * Context object which includes the inputs needed for assistant message rendering.\n * If creating a custom component for assistant message rendering, you will receive this as an input.\n */\nexport interface AssistantMessageContext {\n message: AIMessage;\n config: AssistantMessageDisplayConfig;\n\n /** The message response is currently streaming in (not yet complete). */\n isMessageLoading: boolean;\n\n /** A number identifying this message in the chat history numbered from the latest - 0 is the latest, -1 is the previous, etc. */\n messageDisplayIndex: number;\n}\n\n/**\n * This is the default component used to render the contents of a message from the AI assistant, including\n * the main answer text, text from earlier steps, reasoning text and tool calls.\n */\n@Component({\n selector: 'c8y-ai-chat-assistant-message',\n templateUrl: './ai-chat-assistant-message.component.html',\n standalone: true,\n imports: [\n C8yTranslatePipe,\n IconDirective,\n CollapseModule,\n C8yTranslateModule,\n AiChatAssistantPartComponent\n ]\n})\nexport class AiChatAssistantMessageComponent {\n /**\n * The context needed to render a message.\n * This is a single input so we can extend in future without breaking people who have a custom rendering implementation.\n */\n readonly assistantMessageContext = input.required<AssistantMessageContext>();\n\n /**\n * By default this component will render a \"Working...\" indicator while streaming results. This input can turn that off if required.\n */\n readonly showWorkingIndicator = input(true);\n\n /**\n * Whether the thinking section is expanded. Initialized to false unless the message is still loading.\n * Call setThinkingExpanded() via viewChild to control this programmatically. Does nothing if appearance does not distinguish thinking steps.\n */\n protected readonly thinkingExpanded = signal(false);\n\n /**\n * Tracks which reasoning sections are expanded, keyed by step index.\n */\n protected readonly expandedReasoningStepIndices = signal(new Set<number>());\n\n private readonly translateService = inject(TranslateService);\n\n /**\n * For now be conservative with the old behaviour, but hope to change this once we've got working really smoothly.\n */\n protected readonly defaultNonFinalStepTextDisplay: AssistantMessageDisplayConfig['experimental_nonFinalStepTextDisplay'] =\n 'main-answer';\n\n constructor() {\n // Expand when we initially set the message loading flag, without overwriting the value\n // any other time\n effect(() => {\n if (this.assistantMessageContext().isMessageLoading) {\n this.thinkingExpanded.set(true);\n }\n });\n }\n\n /**\n * This is public and exists for agent-chat to collapse older messages.\n * @param expanded Whether the thinking section should be expanded or not.\n */\n setThinkingExpanded(expanded: boolean): void {\n this.thinkingExpanded.set(expanded);\n }\n\n /**\n * Get a translated/translateable label for the specified tool.\n * @param tool The tool call part to get the label for.\n * @return The label to show for this tool call.\n */\n getToolLabel(tool: ToolCallPart): string {\n const isExecuting = tool.type !== 'tool-result';\n const config = this.assistantMessageContext().config;\n const toolCallConfig = config?.toolCallConfig?.[tool.toolName];\n if (toolCallConfig?.labelProvider) {\n try {\n const dynamicLabel = toolCallConfig.labelProvider(tool, this.translateService);\n // Run it through translate in case it contains gettext values\n if (dynamicLabel) return dynamicLabel;\n } catch (err) {\n // Should never happen, so report this - then fallback to default logic\n console.warn(`Error in labelProvider for tool ${tool.toolName}:`, err);\n }\n }\n\n if (isExecuting && toolCallConfig?.executingLabel) {\n return toolCallConfig.executingLabel;\n }\n if (!isExecuting && toolCallConfig?.completedLabel) {\n return toolCallConfig.completedLabel;\n }\n\n // If we have no specific label configured, use the default\n return this.translateService.instant(\n isExecuting ? gettext('Calling tool {{toolName}}') : gettext('Used tool {{toolName}}'),\n // Have to use the tool id since that's all we have\n { toolName: tool.toolName }\n );\n }\n\n protected getToolParts(step: AgentStep): ToolCallPart[] {\n // This method only exists because of the current structure of AIMessage; can remove this when we refactor\n return [...(step.toolCalls || []), ...(step.toolResults || [])];\n }\n\n protected toggleReasoningExpanded(stepIndex: number): void {\n this.expandedReasoningStepIndices.update(s => {\n const next = new Set(s);\n s.has(stepIndex) ? next.delete(stepIndex) : next.add(stepIndex);\n return next;\n });\n }\n\n /**\n * Note: ctx is passed as a parameter rather than reading the signal in the template to ensure\n * it's totally aligned with what the template is rendering.\n * @param ctx The context for the assistant message.\n * @return The label to display for the thinking section.\n */\n protected getThinkingLabel(ctx: AssistantMessageContext): string {\n return ctx.isMessageLoading\n ? gettext('Thinking')\n : this.thinkingExpanded()\n ? gettext('Hide thinking')\n : gettext('Show thinking');\n }\n\n protected getMainAnswerText(ctx: AssistantMessageContext): string {\n // If it's a simple plain message, do nothing\n if (!ctx.message.steps) return ctx.message.content;\n\n // Get the latest (non-empty) step text (the final one can occasionally be empty it seems)\n const lastStepWithText = ctx.message.steps.filter(step => step?.text && step.text !== '').pop();\n const lastStepText = lastStepWithText?.text || '';\n\n // The final step text goes in the main answer area if there are no pending tool calls (since otherwise the tools would show below it), and\n // message has finished loading, or we're expecting more steps than this.\n // Note that we check all steps for toolCalls not just the current one, for the sake of \"fake\" tool calls created by preprocessAgentMessage\n // where additional step boundaries may be added to simplify parsing.\n if (\n lastStepText &&\n !ctx.message.steps.some(step => step.toolCalls) &&\n !lastStepWithText?.toolResults &&\n (!ctx.isMessageLoading ||\n ctx.message.steps.length >= (ctx.config?.experimental_expectedStepCount || 2))\n ) {\n return lastStepText;\n }\n return '';\n }\n\n protected hasThinkingContent(ctx: AssistantMessageContext, mainAnswer: string): boolean {\n // This logic matches the rendering in the template to ensure we don't show an empty thinking section\n if (!ctx.message?.steps) return false;\n\n const result = ctx.message.steps.some(\n step =>\n (step.text && step.text !== mainAnswer) ||\n step.reasoning ||\n [...(step.toolResults || []), ...(step.toolCalls || [])].some(\n tool =>\n !ctx.config?.toolCallConfig ||\n (!ctx.config?.toolCallConfig[tool.toolName]?.isHidden &&\n !ctx.config?.toolCallConfig[tool.toolName]?.isShownWithMainAnswer)\n )\n );\n return result;\n }\n}\n","@let ctx = assistantMessageContext();\n@let steps = ctx.message.steps ?? [{ type: 'text', text: ctx.message.content }];\n@let _config = ctx.config;\n\n<!-- NB: be sure to update hasThinkingContent() if changing the anything that affects what content \nis rendered in the thinking area.\n-->\n\n<!-- \nWe show the assistant message in (up to) 3 slots:\n- collapsible thinking block (for intermediate step text, standard tool calls, etc)\n- special \"artifact\" tools that update user artifacts and need prominent display (e.g. for code edits made by the AI)\n- the main answer (typically from the final step)\n\nThis is a standard approach for AI chats - step text and tools from an LLM are treated as a work log, i.e. useful to \ndisplay progress while streaming the response, but only the text from the last step after all tool calls gives the main answer of the AI to the user. \n\nDepending on configuration, we have an experimental option (if we need it) to style the thinking block the same as the main answer. \n\nOnce the message finishes loading we know the final step IS the main content, but if it's still steaming \nwe don't know for sure which step has the final/main answer until the LLM has actually finished so have to take a heuistic guess \n(and minimize jumping in the UI). \n\nFor applications where we expect tool calls in most responses, assume the 2nd step is probably the final text; \nfor applications where tool calls are less ubiquitous, assume the first step is main answer until proved otherwise. \nThis minimizes jumping (except when there are >2 steps, which is rare).\n\n-->\n\n@let mainAnswer = getMainAnswerText(ctx);\n\n<!-- Thinking steps section - holds reasoning, text from non-final steps, and standard/non-artifact tool calls -->\n\n@if (hasThinkingContent(ctx, mainAnswer)) {\n @let nonFinalStepTextDisplay =\n _config?.experimental_nonFinalStepTextDisplay || defaultNonFinalStepTextDisplay;\n <div\n class=\"m-b-8\"\n [class.thinking-block]=\"nonFinalStepTextDisplay === 'collapsible-thinking-block'\"\n [class.text-muted]=\"nonFinalStepTextDisplay !== 'main-answer'\"\n [class.small]=\"nonFinalStepTextDisplay === 'muted-main-answer'\"\n [class.thinking-steps-appearance]=\"nonFinalStepTextDisplay\"\n data-cy=\"thinking-steps\"\n >\n @if (nonFinalStepTextDisplay === 'collapsible-thinking-block') {\n <button\n class=\"btn btn-clean btn-xs text-muted p-l-0\"\n aria-label=\"Toggle collapse of the thinking section\"\n [attr.aria-expanded]=\"thinkingExpanded()\"\n [attr.aria-controls]=\"'thinking-content-' + ctx.messageDisplayIndex\"\n type=\"button\"\n (click)=\"thinkingExpanded.set(!thinkingExpanded())\"\n >\n <span class=\"small\">{{ getThinkingLabel(ctx) }}</span>\n <i\n class=\"m-l-4 icon-12\"\n [c8yIcon]=\"thinkingExpanded() ? 'collapse-arrow' : 'expand-arrow'\"\n ></i>\n </button>\n }\n\n <div\n class=\"collapse\"\n [attr.aria-label]=\"'Thinking details for message latest' + ctx.messageDisplayIndex\"\n role=\"region\"\n [collapse]=\"nonFinalStepTextDisplay === 'collapsible-thinking-block' && !thinkingExpanded()\"\n [id]=\"'thinking-content-' + ctx.messageDisplayIndex\"\n [isAnimated]=\"true\"\n >\n <div\n [class]=\"\n nonFinalStepTextDisplay === 'collapsible-thinking-block'\n ? 'thinking-content bg-level-1 m-t-8 p-8 b-r-4 border-left-accent small m-b-0 text-muted'\n : ''\n \"\n >\n @for (step of steps; track $index) {\n <c8y-ai-chat-assistant-part\n [part]=\"{ type: 'step-start' }\"\n [assistantMessageContext]=\"ctx\"\n ></c8y-ai-chat-assistant-part>\n\n <!-- Non-final step text: avoid duplicating the final message content since we show that below -->\n @if (step.text && step.text !== mainAnswer) {\n <c8y-ai-chat-assistant-part\n [part]=\"{ type: 'text', text: step.text }\"\n [assistantMessageContext]=\"ctx\"\n [displayAsPartOfMainAnswer]=\"nonFinalStepTextDisplay !== 'muted-main-answer'\"\n ></c8y-ai-chat-assistant-part>\n }\n\n <!-- Expandable section for reasoning (only some models provide this). Collapse by default. -->\n @if (step.reasoning) {\n <c8y-ai-chat-assistant-part\n [part]=\"{ type: 'reasoning', text: step.reasoning }\"\n [assistantMessageContext]=\"ctx\"\n [displayAsPartOfMainAnswer]=\"nonFinalStepTextDisplay !== 'muted-main-answer'\"\n ></c8y-ai-chat-assistant-part>\n }\n\n @for (tool of getToolParts(step); track tool.toolCallId) {\n @if (!ctx.config.toolCallConfig?.[tool.toolName]?.isShownWithMainAnswer) {\n <c8y-ai-chat-assistant-part\n [part]=\"tool\"\n [assistantMessageContext]=\"ctx\"\n ></c8y-ai-chat-assistant-part>\n }\n }\n }\n </div>\n </div>\n </div>\n}\n\n<c8y-ai-chat-assistant-part\n [part]=\"{ type: 'step-start' }\"\n [assistantMessageContext]=\"ctx\"\n></c8y-ai-chat-assistant-part>\n\n<!-- Show tools which generate artifacts/outputs the user cares about in a dedicated area (outside the thinking section, never muted) -->\n\n<span data-cy=\"ai-tools-with-main-answer\">\n @for (step of steps; track $index) {\n @for (tool of getToolParts(step); track tool.toolCallId) {\n @if (ctx.config.toolCallConfig?.[tool.toolName]?.isShownWithMainAnswer) {\n <c8y-ai-chat-assistant-part\n [part]=\"tool\"\n [assistantMessageContext]=\"ctx\"\n ></c8y-ai-chat-assistant-part>\n }\n }\n }\n</span>\n\n<!-- Main/final text from the final step goes here -->\n@if (mainAnswer) {\n <div\n class=\"message-content m-t-8 text-default ai-main-answer-content\"\n data-cy=\"ai-main-message-content\"\n >\n <c8y-ai-chat-assistant-part\n [part]=\"{ type: 'text', text: mainAnswer }\"\n [assistantMessageContext]=\"ctx\"\n [displayAsPartOfMainAnswer]=\"true\"\n ></c8y-ai-chat-assistant-part>\n </div>\n}\n\n@if (ctx.isMessageLoading && showWorkingIndicator()) {\n <!-- Once the message starts to stream, show a small/low-key thinking indicator -->\n <div\n class=\"text-muted text-12 fade-in-out d-flex j-c-end\"\n aria-live=\"polite\"\n role=\"status\"\n data-cy=\"working-indicator\"\n >\n {{ 'Working…' | translate }}\n </div>\n}\n","import { Component, EventEmitter, Input, Output } from '@angular/core';\nimport { C8yTranslatePipe, IconDirective } from '@c8y/ngx-components';\nimport { SupportedIconsSuggestions } from '@c8y/ngx-components/icon-selector/icons';\nimport { TooltipModule } from 'ngx-bootstrap/tooltip';\n\n/**\n * An action button that can be added to chat messages.\n * Typically used for actions like copying, regenerating, or providing feedback on messages.\n */\n@Component({\n selector: 'c8y-ai-chat-message-action',\n templateUrl: './ai-chat-message-action.component.html',\n standalone: true,\n imports: [TooltipModule, C8yTranslatePipe, IconDirective]\n})\nexport class AiChatMessageActionComponent {\n /**\n * Set to true to use content projection for custom action button content.\n */\n @Input()\n custom = false;\n\n /**\n * Disables the action button when true.\n */\n @Input()\n disabled = false;\n\n /**\n * Tooltip text displayed when hovering over the action button.\n */\n @Input()\n tooltip = '';\n\n /**\n * Icon to display in the action button.\n */\n @Input()\n icon: SupportedIconsSuggestions = 'cog';\n\n /**\n * Emitted when the action button is clicked.\n */\n @Output()\n actionClicked = new EventEmitter<void>();\n}\n","@if (!custom) {\n <button\n class=\"btn btn-dot text-muted\"\n [attr.aria-label]=\"tooltip | translate\"\n [tooltip]=\"tooltip | translate\"\n [adaptivePosition]=\"true\"\n [delay]=\"500\"\n (click)=\"actionClicked.emit()\"\n [disabled]=\"disabled\"\n >\n <i\n class=\"text-12\"\n [c8yIcon]=\"icon\"\n ></i>\n </button>\n} @else {\n <ng-content></ng-content>\n}\n","import { Component, computed, input, inject } from '@angular/core';\n\nimport { gettext } from '@c8y/ngx-components/gettext';\nimport { AIMessage } from '@c8y/ngx-components/ai';\nimport { C8yTranslatePipe, DatePipe } from '@c8y/ngx-components';\nimport { NgClass } from '@angular/common';\nimport { TooltipModule } from 'ngx-bootstrap/tooltip';\nimport { TranslateService } from '@ngx-translate/core';\n\n/**\n * A container for content and actions that should be rendered for each chat message.\n *\n * Project content into this component to display the message, for example add an `<ai-chat-assistant-message>`\n * for assistant messages, and a simple `markdownToHtml | async` rendering of the content for user messages.\n */\n@Component({\n selector: 'c8y-ai-chat-message',\n templateUrl: './ai-chat-message.component.html',\n styleUrl: './ai-chat-message.component.scss',\n standalone: true,\n imports: [NgClass, TooltipModule, C8yTranslatePipe, DatePipe]\n})\nexport class AiChatMessageComponent {\n readonly role = input<AIMessage['role']>();\n readonly message = input<AIMessage>();\n\n private readonly translateService = inject(TranslateService);\n\n private readonly roleResolved = computed(() => {\n return this.message()?.role || this.role();\n });\n\n /**\n * Generates an accessible label for the message container.\n * Includes the role (user or assistant) and timestamp if available.\n * @returns The aria-label string for screen readers\n */\n readonly ariaLabel = computed(() => {\n const tpl =\n this.roleResolved() === 'user'\n ? gettext('You said: \"{{ message }}\"')\n : gettext('Assistant said: \"{{ message }}\"');\n const msg = this.message();\n return this.translateService.instant(tpl, { message: msg?.content || gettext('No response.') });\n });\n\n /**\n * Generates an accessible label for the message content.\n * Prefixes the content with contextual information about who sent it.\n * @returns The aria-label string with prefixed role information\n */\n readonly messageContentAriaLabel = computed(() => {\n const msg = this.message();\n const time = msg?.timestamp ? new Date(msg.timestamp).toLocaleTimeString() : '';\n let tpl: string;\n if (time) {\n tpl =\n this.roleResolved() === 'user'\n ? gettext('User message at {{ time }}')\n : gettext('Assistant message at {{ time }}');\n } else {\n tpl = this.roleResolved() === 'user' ? gettext('User message') : gettext('Assistant message');\n }\n return this.translateService.instant(tpl, { time });\n });\n}\n","<div\n class=\"d-col p-t-16\"\n [attr.aria-label]=\"ariaLabel()\"\n role=\"article\"\n>\n <div\n class=\"chat-message text-break-word\"\n [ngClass]=\"{\n 'user-message': message()?.role === 'user' || role() === 'user',\n 'agent-message': message()?.role === 'assistant' || role() === 'assistant'\n }\"\n >\n <!-- Visually hidden label included when users copy-paste from the chat, e.g. to report issues -->\n <span\n class=\"hidden-copy-label\"\n aria-hidden=\"true\"\n >\n {{ `====== ${messageContentAriaLabel()} ======` }}</span\n >\n\n <!-- Apply the class and aria-label to both the main content and whatever is projected, so we get the same styling by default -->\n <div\n class=\"message-content\"\n [attr.aria-label]=\"messageContentAriaLabel()\"\n >\n <div style=\"display: contents\">\n <ng-content select=\":not(c8y-ai-chat-message-action)\"></ng-content>\n </div>\n </div>\n @if (message()?.timestamp) {\n <div class=\"message-timestamp\">\n <span [tooltip]=\"message()?.timestamp | c8yDate\">\n {{ message()?.timestamp | c8yDate: 'adaptiveDate' }}\n </span>\n </div>\n }\n </div>\n <div\n class=\"message-action\"\n [attr.aria-label]=\"'Message actions' | translate\"\n role=\"toolbar\"\n [ngClass]=\"{\n 'user-action showOnHover': message()?.role === 'user' || role() === 'user',\n 'agent-action p-l-16': message()?.role === 'assistant' || role() === 'assistant'\n }\"\n >\n <ng-content select=\"c8y-ai-chat-message-action\"></ng-content>\n </div>\n</div>\n","import { Component, input, output } from '@angular/core';\nimport { C8yTranslatePipe, IconDirective } from '@c8y/ngx-components';\nimport { AIMessage } from '@c8y/ngx-components/ai';\nimport { SupportedIconsSuggestions } from '@c8y/ngx-components/icon-selector/icons';\n\n/**\n * A suggestion chip that can be displayed in the chat interface.\n * When clicked, it automatically populates and sends a predefined prompt.\n */\n@Component({\n selector: 'c8y-ai-chat-suggestion',\n templateUrl: './ai-chat-suggestion.component.html',\n host: { class: 'd-contents' },\n standalone: true,\n imports: [IconDirective, C8yTranslatePipe]\n})\nexport class AiChatSuggestionComponent {\n /**\n * The visible label text displayed on the suggestion chip.\n */\n readonly label = input.required<string>();\n\n /**\n * The prompt text that will be sent when the suggestion is clicked.\n */\n readonly prompt = input.required<string>();\n\n /**\n * Icon to display alongside the suggestion label.\n */\n readonly icon = input<SupportedIconsSuggestions>('c8y-bulb');\n\n /**\n * When true, uses AI-styled buttons instead of default styling.\n */\n readonly useAiButtons = input(false);\n\n /**\n * Disables the suggestion chip when true.\n */\n readonly disabled = input(false);\n\n /**\n * Emitted when the suggestion is clicked, providing the prompt as an AIMessage.\n */\n readonly suggestionClicked = output<AIMessage>();\n\n /**\n * Handles suggestion click and emits the prompt as a user message.\n */\n suggest() {\n this.suggestionClicked.emit({\n content: this.prompt(),\n role: 'user',\n timestamp: new Date().toISOString()\n });\n }\n}\n","@if (!useAiButtons()) {\n <button\n class=\"btn btn-default btn-sm\"\n [title]=\"prompt() | translate\"\n (click)=\"suggest()\"\n [disabled]=\"disabled()\"\n >\n <i [c8yIcon]=\"icon()\"></i>\n {{ label() | translate }}\n </button>\n} @else {\n <button\n class=\"btn btn-sm btn-ai\"\n [title]=\"prompt() | translate\"\n (click)=\"suggest()\"\n [disabled]=\"disabled()\"\n >\n <span>{{ label() | translate }}</span>\n </button>\n}\n","import { AsyncPipe, NgClass, NgTemplateOutlet } from '@angular/common';\nimport {\n Component,\n contentChildren,\n effect,\n ElementRef,\n EventEmitter,\n Input,\n Output,\n TemplateRef,\n viewChild\n} from '@angular/core';\nimport { FormsModule } from '@angular/forms';\nimport {\n C8yTranslatePipe,\n IconDirective,\n MarkdownToHtmlPipe,\n TextareaAutoresizeDirective\n} from '@c8y/ngx-components';\nimport { AIMessage, ChatConfig } from '@c8y/ngx-components/ai';\nimport { gettext } from '@c8y/ngx-components/gettext';\nimport { AiChatMessageComponent } from './ai-chat-message.component';\n\n/**\n * An interactive chat interface component for AI-powered conversations.\n * Displays messages in a conversation format with support for loading states,\n * custom configuration, and markdown formatting.\n */\n@Component({\n selector: 'c8y-ai-chat',\n templateUrl: './ai-chat.component.html',\n imports: [\n C8yTranslatePipe,\n FormsModule,\n TextareaAutoresizeDirective,\n IconDirective,\n NgClass,\n NgTemplateOutlet,\n MarkdownToHtmlPipe,\n AsyncPipe\n ],\n standalone: true,\n host: { class: 'd-contents' }\n})\nexport class AiChatComponent {\n /**\n * Indicates whether the chat is currently processing a request.\n * When true, displays a cancel button instead of send button and disables the input.\n */\n @Input()\n isLoading = false;\n\n /**\n * Disables the chat input and send button when true.\n */\n @Input()\n disabled = false;\n\n /**\n * The current text in the chat input field. Supports two-way binding.\n */\n @Input()\n prompt = '';\n\n /**\n * Template for rendering custom suggestions below the input field. Can be used to provide predefined prompts or actions\n * from a dynamic source such as the AI. To use this, wrap your suggestion components in an `<ng-template #suggestionsRef>`\n * (where `suggestionsRef` matches the suggestionsTemplate reference);\n */\n @Input()\n suggestionsTemplate?: TemplateRef<any>;\n\n /** Template for customizing the welcome view using `<ng-template>`. */\n @Input() welcomeTemplate?: TemplateRef<any>;\n\n /**\n * Emitted when the user sends a message.\n * Provides an AIMessage object with role, content, and timestamp.\n */\n @Output()\n onMessage = new EventEmitter<AIMessage>();\n\n /**\n * Emitted when the user cancels an ongoing operation during loading state.\n */\n @Output()\n onCancel = new EventEmitter<void>();\n\n /**\n * Child message components displayed in the chat.\n */\n readonly messages = contentChildren(AiChatMessageComponent);\n\n /**\n * Reference to the scroll container for the chat messages.\n */\n private readonly scrollContainer = viewChild<ElementRef<HTMLDivElement>>('chatScrollContainer');\n\n readonly componentId = `chat-${crypto.randomUUID()}`;\n\n private _config: ChatConfig = {\n headline: gettext('Welcome!'),\n welcomeText: '',\n welcomePosition: 'top',\n title: gettext('What can I help you with?'),\n placeholder: gettext('Type your message here...'),\n scrollbarOnlyOnHover: false,\n sendButtonText: gettext('Send'),\n cancelButtonText: gettext('Cancel'),\n disclaimerText: gettext(\n 'AI-generated responses can contain errors. Verify the details before use.'\n ),\n userInterfaceIcons: {\n send: 'arrow-circle-up',\n cancel: 'stop-circle'\n }\n };\n\n constructor() {\n // Auto-scroll to bottom when messages change\n effect(() => {\n this.messages(); // track changes\n const container = this.scrollContainer()?.nativeElement;\n if (!container) return;\n // In column-reverse, scrollTop=0 IS the visual bottom.\n // Only snap back if user is already near the bottom (wasn't scrolled up).\n if (container.scrollTop < 50) {\n container.scrollTop = 0;\n }\n });\n }\n\n /**\n * Configuration object for customizing labels, placeholders, and icons.\n * Accepts partial configuration to override defaults.\n * @param value Partial configuration to merge with defaults.\n * @returns The complete configuration object.\n */\n @Input()\n set config(value: Partial<ChatConfig>) {\n this._config = { ...this._config, ...value };\n }\n get config(): ChatConfig {\n return this._config;\n }\n\n /**\n * Handles message submission when the user sends a message.\n * Emits the onMessage event and clears the input.\n * @param $event The event object from the form submission\n */\n sendMessage($event: Event): void {\n $event.preventDefault();\n if (!this.prompt) return;\n this.onMessage.emit({\n role: 'user',\n content: this.prompt,\n timestamp: new Date().toISOString()\n });\n this.prompt = '';\n }\n\n /**\n * Handles cancellation of ongoing operations during loading state.\n * Emits the onCancel event.\n */\n cancel(): void {\n this.onCancel.emit();\n }\n}\n","<div\n class=\"d-col fit-h fit-w flex-grow\"\n [attr.aria-label]=\"config.headline | translate\"\n role=\"region\"\n>\n @if (messages().length > 0) {\n <div\n [attr.aria-label]=\"'Chat conversation' | translate\"\n aria-live=\"polite\"\n aria-atomic=\"false\"\n role=\"log\"\n #chatScrollContainer\n [ngClass]=\"{\n 'inner-scroll': true,\n 'd-col-reverse': true,\n 'scrollbar-only-on-hover': config.scrollbarOnlyOnHover,\n 'flex-grow': true,\n 'min-height-0': true,\n 'bg-level-0': true\n }\"\n >\n <div class=\"d-col p-l-16 p-r-16\">\n <ng-content select=\"[slot='before-messages']\"></ng-content>\n <ng-content select=\"c8y-ai-chat-message\"></ng-content>\n <ng-content select=\"[slot='after-messages']\"></ng-content>\n </div>\n </div>\n }\n <div\n [ngClass]=\"{\n 'd-col fit-h': messages().length === 0\n }\"\n >\n @if (messages().length === 0) {\n <div\n class=\"p-24 d-col fit-h inner-scroll\"\n aria-live=\"polite\"\n role=\"status\"\n [ngClass]=\"{\n 'j-c-start': config.welcomePosition === 'top',\n 'j-c-center': config.welcomePosition === 'center',\n 'j-c-end': config.welcomePosition === 'bottom'\n }\"\n >\n <h4 class=\"m-b-16 text-medium\">{{ config.headline | translate }}</h4>\n @if (config.title.length > 0) {\n <p class=\"p-b-8 text-balance\">{{ config.title | translate }}</p>\n }\n <div class=\"text-balance chat-message min-height-0\">\n <div\n class=\"text-muted\"\n [innerHTML]=\"config.welcomeText | translate | markdownToHtml | async\"\n ></div>\n @if (welcomeTemplate) {\n <ng-container *ngTemplateOutlet=\"welcomeTemplate\"></ng-container>\n }\n </div>\n </div>\n }\n <div\n class=\"chat-input\"\n [class.bg-level-1]=\"config.appearance !== 'flat'\"\n >\n <!-- For simple cases allow ng-content projection; however this doesn't seem to work with dynamic \n suggestion lists from a signal (e.g. returned from the AI) so also support `suggestionsTemplate` for that. \n -->\n @if (!isLoading) {\n <div\n [class]=\"\n 'd-flex inner-scroll gap-8 p-l-16 p-r-16 p-b-8 ' +\n (config.suggestionsLayout === 'vertical' ? 'flex-wrap' : 'a-i-center')\n \"\n >\n <ng-content select=\"c8y-ai-chat-suggestion\"></ng-content>\n\n @if (suggestionsTemplate) {\n <ng-container *ngTemplateOutlet=\"suggestionsTemplate\"></ng-container>\n }\n </div>\n }\n <div class=\"chat-input-group\">\n <label\n class=\"sr-only\"\n for=\"chat-input-{{ componentId }}\"\n >\n {{ config.placeholder | translate }}\n </label>\n <textarea\n class=\"form-control no-resize\"\n [class.text-muted]=\"isLoading\"\n style=\"max-height: 200px !important\"\n [attr.aria-label]=\"config.placeholder | translate\"\n id=\"chat-input-{{ componentId }}\"\n placeholder=\"{{ config.placeholder | translate }}\"\n [attr.aria-describedby]=\"config.disclaimerText ? 'chat-disclaimer-' + componentId : null\"\n [attr.aria-busy]=\"isLoading\"\n [(ngModel)]=\"prompt\"\n (keydown.enter)=\"!isLoading && sendMessage($event)\"\n [disabled]=\"disabled\"\n c8y-textarea-autoresize\n ></textarea>\n <div class=\"chat-input-group-btn\">\n @if (!isLoading) {\n <button\n class=\"btn btn-dot\"\n [attr.title]=\"config.sendButtonText || '' | translate\"\n [attr.aria-label]=\"config.sendButtonText || '' | translate\"\n type=\"button\"\n (click)=\"sendMessage($event)\"\n [disabled]=\"disabled || prompt.trim().length === 0\"\n >\n <i [c8yIcon]=\"config.userInterfaceIcons.send || 'arrow-circle-right'\"></i>\n </button>\n } @else {\n <button\n class=\"btn btn-dot btn-dot--danger\"\n [attr.title]=\"config.cancelButtonText || '' | translate\"\n [attr.aria-label]=\"config.cancelButtonText || '' | translate\"\n type=\"button\"\n (click)=\"cancel()\"\n >\n <i [c8yIcon]=\"config.userInterfaceIcons.cancel || 'stop'\"></i>\n </button>\n }\n </div>\n </div>\n @if (config.disclaimerText) {\n <div\n class=\"text-muted m-b-8 text-10 p-l-16\"\n id=\"chat-disclaimer-{{ componentId }}\"\n role=\"note\"\n >\n {{ config.disclaimerText | translate }}\n </div>\n }\n </div>\n </div>\n</div>\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":["i1"],"mappings":"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"}
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import * as i0 from '@angular/core';
|
|
2
2
|
import { inject, Injectable } from '@angular/core';
|
|
3
|
+
import * as i1 from '@c8y/client';
|
|
3
4
|
import { FetchClient } from '@c8y/client';
|
|
4
5
|
import { Observable } from 'rxjs';
|
|
5
6
|
|
|
7
|
+
// Internal implementation detail; maybe we don't even need this enum since
|
|
6
8
|
var DataStreamType;
|
|
7
9
|
(function (DataStreamType) {
|
|
8
10
|
DataStreamType["TEXT_DELTA"] = "text-delta";
|
|
@@ -26,14 +28,15 @@ var DataStreamType;
|
|
|
26
28
|
DataStreamType["FILE"] = "file";
|
|
27
29
|
DataStreamType["STEP_START"] = "start-step";
|
|
28
30
|
DataStreamType["STEP_FINISH"] = "finish-step";
|
|
29
|
-
DataStreamType["STEP_START_V4"] = "step-start";
|
|
30
|
-
DataStreamType["STEP_FINISH_V4"] = "step-finish";
|
|
31
31
|
})(DataStreamType || (DataStreamType = {}));
|
|
32
|
-
|
|
32
|
+
/** This service manages communication with the AI Agent Manager microservice, supporting
|
|
33
|
+
* management of agents, and sending requests to the agents.
|
|
34
|
+
*/
|
|
33
35
|
class AIService {
|
|
34
|
-
constructor
|
|
36
|
+
// nb: allowing the client to be passed in the constructor allows efficient unit testing without setting up the Angular TestBed
|
|
37
|
+
constructor(client) {
|
|
35
38
|
this.baseUrl = '/service/ai';
|
|
36
|
-
this.client = inject(FetchClient);
|
|
39
|
+
this.client = client ?? inject(FetchClient);
|
|
37
40
|
}
|
|
38
41
|
/**
|
|
39
42
|
* Creates or updates an agent.
|
|
@@ -57,7 +60,7 @@ class AIService {
|
|
|
57
60
|
}
|
|
58
61
|
}
|
|
59
62
|
/**
|
|
60
|
-
* Check if an agent exists.
|
|
63
|
+
* Check if an agent exists and is ready for use.
|
|
61
64
|
* @param name Agent name (optional)
|
|
62
65
|
* @param fromApp The app context path to check for an agent. This can be used, if the agent should get distributed by the plugin (optional).
|
|
63
66
|
* @returns Agent health check response.
|
|
@@ -69,24 +72,37 @@ class AIService {
|
|
|
69
72
|
});
|
|
70
73
|
if (!response.ok) {
|
|
71
74
|
return {
|
|
75
|
+
status: response.status === 403 ? 'missing-permissions' : 'missing-microservice',
|
|
72
76
|
exists: false,
|
|
73
77
|
canCreate: false,
|
|
74
|
-
isProviderConfigured: false
|
|
78
|
+
isProviderConfigured: false,
|
|
79
|
+
messages: response.status === 403 ? [] : [response.statusText]
|
|
75
80
|
};
|
|
76
81
|
}
|
|
77
82
|
const json = await response.json();
|
|
83
|
+
if (!json.isProviderConfigured) {
|
|
84
|
+
json.status = 'missing-provider';
|
|
85
|
+
}
|
|
86
|
+
else if (!json.exists) {
|
|
87
|
+
json.status = 'missing-agent';
|
|
88
|
+
}
|
|
89
|
+
else {
|
|
90
|
+
json.status = 'ready';
|
|
91
|
+
}
|
|
78
92
|
return json;
|
|
79
93
|
}
|
|
80
94
|
/**
|
|
81
95
|
* Send a text message to the agent.
|
|
82
96
|
* @param name Agent name
|
|
83
|
-
* @param messages
|
|
97
|
+
* @param messages The message to send, including any previous message history.
|
|
98
|
+
* Typically you should use `defaultPruneMessagesForAgent` or a custom function to prepare the message history
|
|
99
|
+
* by removing unnecessary content such as large/old tool inputs/outputs.
|
|
84
100
|
* @param variables Variables to include
|
|
85
101
|
* @param fromApp The app context path to check for an agent. This can be used, if the agent should get distributed by the plugin (optional).
|
|
86
102
|
* @returns Text response from the agent.
|
|
87
103
|
*/
|
|
88
104
|
async text(name, messages, variables, fromApp) {
|
|
89
|
-
const parsedMessages = this.
|
|
105
|
+
const parsedMessages = this.convertToAgentMessageFormat(messages);
|
|
90
106
|
const data = this.client.fetch(`${this.baseUrl}/agent/text/${name}${fromApp ? `?fromApp=${fromApp}` : ''}`, {
|
|
91
107
|
body: JSON.stringify({ messages: parsedMessages, variables }),
|
|
92
108
|
method: 'POST',
|
|
@@ -102,11 +118,15 @@ class AIService {
|
|
|
102
118
|
/**
|
|
103
119
|
* Stream a text message to the agent.
|
|
104
120
|
* @param agent Agent name or an agent definition.
|
|
105
|
-
* @param messages
|
|
121
|
+
* @param messages The message to send, including any previous message history.
|
|
122
|
+
* Typically you should use `defaultPruneMessagesForAgent` or a custom function to prepare the message history
|
|
123
|
+
* by removing unnecessary content such as large/old tool inputs/outputs.
|
|
106
124
|
* @param variables Variables to include
|
|
107
125
|
* @param abortController An AbortController to cancel the request.
|
|
108
126
|
* @param fromApp The app context path to check for an agent. This can be used, if the agent should get distributed by the plugin (optional).
|
|
109
|
-
* @returns An observable that emits partial AIMessage objects as they are received.
|
|
127
|
+
* @returns An observable that emits AIStreamResponse including partial `AIMessage` objects as they are received.
|
|
128
|
+
* When additional useful metadata is available, this will be streamed with type indicating it is metadata.
|
|
129
|
+
* The observable can be cancelled using the provided AbortController.
|
|
110
130
|
* The observable will emit an error if the request fails or is aborted.
|
|
111
131
|
* The observable will complete when the stream is finished.
|
|
112
132
|
*
|
|
@@ -124,7 +144,7 @@ class AIService {
|
|
|
124
144
|
* ];
|
|
125
145
|
* const observable = aiService.stream$('my-agent', messages, {}, abortController);
|
|
126
146
|
* const subscription = observable.subscribe({
|
|
127
|
-
* next: (
|
|
147
|
+
* next: (response) => { console.log('Received message update:', response.message); },
|
|
128
148
|
* error: (err) => console.error('Error:', err),
|
|
129
149
|
* complete: () => console.log('Stream complete')
|
|
130
150
|
* });
|
|
@@ -135,7 +155,7 @@ class AIService {
|
|
|
135
155
|
* ```
|
|
136
156
|
*/
|
|
137
157
|
async stream$(agent, messages, variables, abortController, fromApp) {
|
|
138
|
-
const parsedMessages = this.
|
|
158
|
+
const parsedMessages = this.convertToAgentMessageFormat(messages);
|
|
139
159
|
const agentName = typeof agent === 'string' ? agent : agent.definition.name;
|
|
140
160
|
const useTest = typeof agent !== 'string' && agent.snapshot;
|
|
141
161
|
const baseUrl = useTest
|
|
@@ -146,8 +166,8 @@ class AIService {
|
|
|
146
166
|
const agentCopy = {
|
|
147
167
|
...agent.definition
|
|
148
168
|
};
|
|
149
|
-
agentCopy.agent
|
|
150
|
-
agentCopy.agent
|
|
169
|
+
agentCopy.agent['messages'] = parsedMessages;
|
|
170
|
+
agentCopy.agent['variables'] = variables;
|
|
151
171
|
body = JSON.stringify(agentCopy);
|
|
152
172
|
}
|
|
153
173
|
const response = await this.client.fetch(`${baseUrl}?fullResponse=true${fromApp ? `&fromApp=${fromApp}` : ''}`, {
|
|
@@ -160,16 +180,23 @@ class AIService {
|
|
|
160
180
|
},
|
|
161
181
|
signal: abortController.signal
|
|
162
182
|
});
|
|
163
|
-
if (response.status > 300) {
|
|
164
|
-
const data = await response.json();
|
|
165
|
-
throw new Error(JSON.stringify(data, null, 2));
|
|
166
|
-
}
|
|
167
183
|
const stream = response.body;
|
|
168
184
|
const decoder = new TextDecoder();
|
|
169
185
|
return new Observable(observer => {
|
|
186
|
+
if (response.status > 300) {
|
|
187
|
+
response
|
|
188
|
+
.json()
|
|
189
|
+
.then(data => {
|
|
190
|
+
observer.error(`Error communicating with server: ${JSON.stringify(data, null, 2)}`);
|
|
191
|
+
})
|
|
192
|
+
.catch(err => {
|
|
193
|
+
observer.error(`Error communicating with server: HTTP ${response.status}: ${response.statusText}; ${err}`);
|
|
194
|
+
});
|
|
195
|
+
return;
|
|
196
|
+
}
|
|
170
197
|
if (!stream) {
|
|
171
|
-
observer.error('
|
|
172
|
-
|
|
198
|
+
observer.error('Server error - no response body');
|
|
199
|
+
return;
|
|
173
200
|
}
|
|
174
201
|
const reader = stream.getReader();
|
|
175
202
|
const message = {
|
|
@@ -208,32 +235,78 @@ class AIService {
|
|
|
208
235
|
};
|
|
209
236
|
});
|
|
210
237
|
}
|
|
211
|
-
|
|
238
|
+
convertToAgentMessageFormat(messages) {
|
|
239
|
+
// We will need to rewrite this to include tool outputs soon (effectively this means targetting LanguageModelV*ToolResultOutput).
|
|
240
|
+
// Once we have more fields than just role and content, we will need to convert between our format and
|
|
241
|
+
// the Vercel format expected by the agent manager (e.g. steps->content parts)
|
|
212
242
|
return messages
|
|
213
|
-
.filter(
|
|
214
|
-
.map(
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
content: message.options.hiddenContent
|
|
219
|
-
};
|
|
220
|
-
}
|
|
221
|
-
return {
|
|
222
|
-
role: message.role,
|
|
223
|
-
content: message.content
|
|
224
|
-
};
|
|
225
|
-
});
|
|
243
|
+
.filter(m => m)
|
|
244
|
+
.map(m => ({
|
|
245
|
+
role: m.role,
|
|
246
|
+
content: m.content
|
|
247
|
+
}));
|
|
226
248
|
}
|
|
249
|
+
/**
|
|
250
|
+
* Convert a tool output from the wire format (which is different/more compact than LanguageModelV3ToolResultOutput)
|
|
251
|
+
* into our own standard representation.
|
|
252
|
+
*/
|
|
253
|
+
convertStreamingToolOutputToToolCallPart(output, target) {
|
|
254
|
+
// So far we just pass the data through unchanged from the streaming API but that's pretty hard to work with since it's neither
|
|
255
|
+
// in the format returned by the tool nor in the LanguageModelV3ToolResultOutput format that we have to use
|
|
256
|
+
// when passing it back for the next request
|
|
257
|
+
target.output = output;
|
|
258
|
+
if (output?.type?.startsWith('error-') || output?.isError)
|
|
259
|
+
target.error = true;
|
|
260
|
+
}
|
|
261
|
+
/** Add the specified tool call info to the current step. If this toolCallId is already known,
|
|
262
|
+
* updates it rather than adding a new one, since for UI rendering and compactness of data it's
|
|
263
|
+
* easiest to just have one item per tool call.
|
|
264
|
+
*/
|
|
265
|
+
addToolCallInfo(type, rawInfo, addTo) {
|
|
266
|
+
// Vercel v4 seems to set this to "id" not "toolCallId" in some places despite doc to the contrary, so normalize it here
|
|
267
|
+
if (!rawInfo.toolCallId)
|
|
268
|
+
rawInfo.toolCallId = rawInfo.id;
|
|
269
|
+
// We can simplify this logic once we have AIMessage as a list of parts instead of separate lists for these
|
|
270
|
+
let existing = addTo.toolCalls?.find(tc => tc.toolCallId === rawInfo.toolCallId);
|
|
271
|
+
if (!existing)
|
|
272
|
+
existing = addTo.toolResults?.find(tc => tc.toolCallId === rawInfo.toolCallId);
|
|
273
|
+
// Move from toolCalls to toolResults when transitioning to tool-result
|
|
274
|
+
if (existing && type === 'tool-result' && existing.type !== 'tool-result') {
|
|
275
|
+
addTo.toolCalls = addTo.toolCalls?.filter(tc => tc.toolCallId !== existing?.toolCallId);
|
|
276
|
+
existing.type = type;
|
|
277
|
+
addTo.toolResults = [...(addTo.toolResults || []), existing];
|
|
278
|
+
}
|
|
279
|
+
else if (existing && existing.type !== type) {
|
|
280
|
+
// Just update the type in place for other transitions
|
|
281
|
+
existing.type = type;
|
|
282
|
+
}
|
|
283
|
+
if (!existing) {
|
|
284
|
+
existing = { type: type, toolName: rawInfo.toolName, toolCallId: rawInfo.toolCallId };
|
|
285
|
+
if (type !== 'tool-result')
|
|
286
|
+
addTo.toolCalls = [...(addTo.toolCalls || []), existing];
|
|
287
|
+
else
|
|
288
|
+
addTo.toolResults = [...(addTo.toolResults || []), existing];
|
|
289
|
+
}
|
|
290
|
+
if (rawInfo?.input)
|
|
291
|
+
existing.input = rawInfo.input;
|
|
292
|
+
if (rawInfo?.output)
|
|
293
|
+
this.convertStreamingToolOutputToToolCallPart(rawInfo.output, existing);
|
|
294
|
+
return existing;
|
|
295
|
+
}
|
|
296
|
+
/** Unpacks data from the Vercel Data Stream Protocol (sent by the Agent Manager over SSE) to update the message
|
|
297
|
+
*
|
|
298
|
+
* See streamText().fullStream() from https://ai-sdk.dev/docs/ai-sdk-ui/stream-protocol#data-stream-protocol
|
|
299
|
+
*
|
|
300
|
+
* This is similar to what the Vercel `toUIMessageStreamResponse` API does (though it'd need the agent manager to publish the
|
|
301
|
+
* UIMessageStream which has a bit more - useful - data than what is included here).
|
|
302
|
+
*
|
|
303
|
+
* We always use the same steps (or in future, parts) object, to make it easier for consumers (eg. agent-chat)
|
|
304
|
+
* to add items to the list and have them still there on the next update from this service.
|
|
305
|
+
*/
|
|
227
306
|
processLine(line, observer, message) {
|
|
228
307
|
if (!line.trim()) {
|
|
229
308
|
return;
|
|
230
309
|
}
|
|
231
|
-
const lastStep = message.steps?.[message.steps.length - 1] || {
|
|
232
|
-
type: 'text',
|
|
233
|
-
toolCalls: [],
|
|
234
|
-
toolResults: [],
|
|
235
|
-
text: ''
|
|
236
|
-
};
|
|
237
310
|
try {
|
|
238
311
|
let data = {};
|
|
239
312
|
let type = '';
|
|
@@ -242,70 +315,86 @@ class AIService {
|
|
|
242
315
|
type = data.type;
|
|
243
316
|
}
|
|
244
317
|
catch (e) {
|
|
245
|
-
console.error('Error parsing line', line, e);
|
|
318
|
+
console.error('Error parsing line from AI response: ', line, e);
|
|
246
319
|
return;
|
|
247
320
|
}
|
|
321
|
+
// Ensure there's a step into which we can store everything
|
|
322
|
+
if (!message.steps || message.steps.length === 0) {
|
|
323
|
+
message.steps = [{ type: 'text', text: '' }];
|
|
324
|
+
}
|
|
325
|
+
const lastStep = message.steps[message.steps.length - 1];
|
|
248
326
|
switch (type) {
|
|
249
|
-
case DataStreamType.STEP_START
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
327
|
+
case DataStreamType.STEP_START:
|
|
328
|
+
if (data.request && data.request.body) {
|
|
329
|
+
observer.next({
|
|
330
|
+
message: message,
|
|
331
|
+
changedPart: {
|
|
332
|
+
type: 'response-metadata',
|
|
333
|
+
model: data.request.body.model,
|
|
334
|
+
systemPrompt: data.request.body.systemPrompt
|
|
335
|
+
}
|
|
336
|
+
});
|
|
337
|
+
}
|
|
338
|
+
// Create a step, unless we already have an empty one from the initialization section above
|
|
339
|
+
if (lastStep.text || lastStep.reasoning || lastStep.toolCalls || lastStep.toolResults) {
|
|
340
|
+
message.steps.push({
|
|
341
|
+
type: 'text',
|
|
342
|
+
text: ''
|
|
343
|
+
});
|
|
344
|
+
}
|
|
257
345
|
return;
|
|
258
346
|
case DataStreamType.REASONING:
|
|
259
347
|
if (lastStep.reasoning === undefined) {
|
|
260
348
|
lastStep.reasoning = '';
|
|
261
349
|
}
|
|
262
350
|
lastStep.reasoning += data.text || data.textDelta;
|
|
263
|
-
observer.next(message);
|
|
351
|
+
observer.next({ message, changedPart: undefined });
|
|
264
352
|
return;
|
|
265
353
|
case DataStreamType.TEXT_DELTA:
|
|
266
354
|
lastStep.text += data.text || data.textDelta;
|
|
355
|
+
// FUTURE: this setting the same value to message.content is probably not correct;
|
|
356
|
+
// duplicating the same text wastes space so not necessary given it's already in steps, and
|
|
357
|
+
// the Vercel SDK sets message.content to the text of the LAST step only (the summarizing step)
|
|
267
358
|
message.content += data.text || data.textDelta;
|
|
268
|
-
observer.next(message);
|
|
359
|
+
observer.next({ message, changedPart: undefined });
|
|
269
360
|
return;
|
|
270
361
|
case DataStreamType.TOOL_INPUT_START:
|
|
271
|
-
|
|
272
|
-
|
|
362
|
+
observer.next({
|
|
363
|
+
message,
|
|
364
|
+
changedPart: this.addToolCallInfo('tool-input-streaming', data, lastStep)
|
|
365
|
+
});
|
|
273
366
|
return;
|
|
274
367
|
case DataStreamType.TOOL_CALL:
|
|
275
|
-
|
|
276
|
-
|
|
368
|
+
observer.next({
|
|
369
|
+
message,
|
|
370
|
+
changedPart: this.addToolCallInfo('tool-executing', data, lastStep)
|
|
371
|
+
});
|
|
277
372
|
return;
|
|
278
373
|
case DataStreamType.TOOL_RESULT:
|
|
279
|
-
|
|
280
|
-
|
|
374
|
+
observer.next({
|
|
375
|
+
message,
|
|
376
|
+
changedPart: this.addToolCallInfo('tool-result', data, lastStep)
|
|
377
|
+
});
|
|
281
378
|
return;
|
|
282
379
|
case DataStreamType.FINISH:
|
|
283
|
-
message.finishReason = data.finishReason;
|
|
284
|
-
observer.next(message);
|
|
380
|
+
message.finishReason = data.finishReason; // FUTURE could be anything, may not match the options in AIMessage
|
|
381
|
+
observer.next({ message, changedPart: undefined });
|
|
285
382
|
observer.complete();
|
|
286
383
|
return;
|
|
287
384
|
case DataStreamType.ERROR:
|
|
288
385
|
message.finishReason = 'error';
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
>
|
|
293
|
-
<strong>Error</strong>
|
|
294
|
-
${data.message || 'An unknown error occurred'}
|
|
295
|
-
</div>`;
|
|
296
|
-
message.content += errorMessage;
|
|
297
|
-
lastStep.text += errorMessage;
|
|
298
|
-
observer.next(message);
|
|
299
|
-
observer.error(data);
|
|
300
|
-
observer.complete();
|
|
386
|
+
observer.next({ message, changedPart: undefined });
|
|
387
|
+
observer.error(data?.message || data?.errorText || data);
|
|
388
|
+
// Error is instead of completing the stream
|
|
301
389
|
return;
|
|
390
|
+
// Should we also handle other types such as "abort"?
|
|
302
391
|
}
|
|
303
392
|
}
|
|
304
393
|
catch (e) {
|
|
305
394
|
observer.error(e);
|
|
306
395
|
}
|
|
307
396
|
}
|
|
308
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: AIService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
397
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: AIService, deps: [{ token: i1.FetchClient }], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
309
398
|
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: AIService, providedIn: 'root' }); }
|
|
310
399
|
}
|
|
311
400
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.18", ngImport: i0, type: AIService, decorators: [{
|
|
@@ -313,11 +402,34 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.18", ngImpo
|
|
|
313
402
|
args: [{
|
|
314
403
|
providedIn: 'root'
|
|
315
404
|
}]
|
|
316
|
-
}] });
|
|
405
|
+
}], ctorParameters: () => [{ type: i1.FetchClient }] });
|
|
406
|
+
/**
|
|
407
|
+
* The default method for reducing the size of the message history so it is ready to be sent to the LLM agent.
|
|
408
|
+
*
|
|
409
|
+
* This is important since old tool inputs and outputs can be very large and fill up the agent's context window.
|
|
410
|
+
*
|
|
411
|
+
* When sending an AI request you can provide your own function instead of using this one,
|
|
412
|
+
* or you can write your own function that calls this and adds extra logic,
|
|
413
|
+
* for example to suppress specific tools from the AIMessage, or
|
|
414
|
+
* retain only the final step text in multi-step responses, etc.
|
|
415
|
+
*
|
|
416
|
+
* The current default implementation preserves text content, but does not include any tool calls.
|
|
417
|
+
* This may change in future releases.
|
|
418
|
+
*/
|
|
419
|
+
function defaultPruneMessagesForAgent(messages) {
|
|
420
|
+
// When we change this to include tool calls, we should remove large inputs and truncate large and/or old outputs
|
|
421
|
+
// to avoid too many tokens and context window consumption.
|
|
422
|
+
// Some applications will want to entirely remove specific tool calls that have only transient use,
|
|
423
|
+
// which can be done by providing their own pruneMessagesForAgent function.
|
|
424
|
+
return messages.map(m => ({
|
|
425
|
+
role: m.role,
|
|
426
|
+
content: m.content
|
|
427
|
+
}));
|
|
428
|
+
}
|
|
317
429
|
|
|
318
430
|
/**
|
|
319
431
|
* Generated bundle index. Do not edit.
|
|
320
432
|
*/
|
|
321
433
|
|
|
322
|
-
export { AIService, DataStreamType };
|
|
434
|
+
export { AIService, DataStreamType, defaultPruneMessagesForAgent };
|
|
323
435
|
//# sourceMappingURL=c8y-ngx-components-ai.mjs.map
|