@c8y/ngx-components 1023.68.7 → 1023.70.0

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.
Files changed (48) hide show
  1. package/ai/agent-chat/index.d.ts +235 -75
  2. package/ai/agent-chat/index.d.ts.map +1 -1
  3. package/ai/ai-chat/index.d.ts +176 -26
  4. package/ai/ai-chat/index.d.ts.map +1 -1
  5. package/ai/index.d.ts +309 -75
  6. package/ai/index.d.ts.map +1 -1
  7. package/asset-properties/index.d.ts.map +1 -1
  8. package/echart/index.d.ts +4 -0
  9. package/echart/index.d.ts.map +1 -1
  10. package/echart/models/index.d.ts +2 -0
  11. package/echart/models/index.d.ts.map +1 -1
  12. package/fesm2022/c8y-ngx-components-ai-agent-chat.mjs +680 -242
  13. package/fesm2022/c8y-ngx-components-ai-agent-chat.mjs.map +1 -1
  14. package/fesm2022/c8y-ngx-components-ai-ai-chat.mjs +343 -44
  15. package/fesm2022/c8y-ngx-components-ai-ai-chat.mjs.map +1 -1
  16. package/fesm2022/c8y-ngx-components-ai.mjs +187 -75
  17. package/fesm2022/c8y-ngx-components-ai.mjs.map +1 -1
  18. package/fesm2022/c8y-ngx-components-asset-properties.mjs +11 -5
  19. package/fesm2022/c8y-ngx-components-asset-properties.mjs.map +1 -1
  20. 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
  21. 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
  22. 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
  23. package/fesm2022/c8y-ngx-components-computed-asset-properties-c8y-ngx-components-computed-asset-properties-9be_iMQg.mjs.map +1 -0
  24. 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
  25. package/fesm2022/c8y-ngx-components-computed-asset-properties-configuration-snapshot-config.component-B2em01_W.mjs.map +1 -0
  26. 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
  27. package/fesm2022/c8y-ngx-components-computed-asset-properties-event-count-config.component-CQuGa1RI.mjs.map +1 -0
  28. 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
  29. 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
  30. package/fesm2022/c8y-ngx-components-computed-asset-properties-last-measurement-config.component-CTK9zNUh.mjs +86 -0
  31. package/fesm2022/c8y-ngx-components-computed-asset-properties-last-measurement-config.component-CTK9zNUh.mjs.map +1 -0
  32. package/fesm2022/c8y-ngx-components-computed-asset-properties.mjs +1 -1
  33. package/fesm2022/c8y-ngx-components-echart-models.mjs.map +1 -1
  34. package/fesm2022/c8y-ngx-components-echart.mjs +39 -18
  35. package/fesm2022/c8y-ngx-components-echart.mjs.map +1 -1
  36. package/fesm2022/c8y-ngx-components-widgets-definitions-html-widget-ai-config.mjs +106 -4
  37. package/fesm2022/c8y-ngx-components-widgets-definitions-html-widget-ai-config.mjs.map +1 -1
  38. package/fesm2022/c8y-ngx-components-widgets-implementations-html-widget.mjs +14 -122
  39. package/fesm2022/c8y-ngx-components-widgets-implementations-html-widget.mjs.map +1 -1
  40. package/locales/locales.pot +83 -30
  41. package/package.json +1 -1
  42. package/widgets/implementations/html-widget/index.d.ts +11 -50
  43. package/widgets/implementations/html-widget/index.d.ts.map +1 -1
  44. package/fesm2022/c8y-ngx-components-computed-asset-properties-c8y-ngx-components-computed-asset-properties-BYHnA-5R.mjs.map +0 -1
  45. package/fesm2022/c8y-ngx-components-computed-asset-properties-configuration-snapshot-config.component-C4oL39m8.mjs.map +0 -1
  46. package/fesm2022/c8y-ngx-components-computed-asset-properties-event-count-config.component-DGwm6_C9.mjs.map +0 -1
  47. package/fesm2022/c8y-ngx-components-computed-asset-properties-last-measurement-config.component-C1cuxN3L.mjs +0 -92
  48. 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 Messages to send
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.parseMessages(messages);
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 Messages to send
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. The observable can be cancelled using the provided AbortController.
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: (message) => console.log('Received message part:', message),
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.parseMessages(messages);
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.messages = parsedMessages;
150
- agentCopy.agent.variables = variables;
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('No response body');
172
- throw 'No response body';
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
- parseMessages(messages) {
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(message => !message.options?.skipToLLM)
214
- .map(message => {
215
- if (message.options?.hiddenContent) {
216
- return {
217
- role: message.role,
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 || DataStreamType.STEP_START_V4:
250
- message.steps = message.steps || [];
251
- message.steps.push({
252
- type: 'text',
253
- toolCalls: [],
254
- toolResults: [],
255
- text: ''
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
- lastStep.toolCalls = [data];
272
- observer.next(message);
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
- lastStep.toolCalls = [data];
276
- observer.next(message);
368
+ observer.next({
369
+ message,
370
+ changedPart: this.addToolCallInfo('tool-executing', data, lastStep)
371
+ });
277
372
  return;
278
373
  case DataStreamType.TOOL_RESULT:
279
- lastStep.toolResults = [data];
280
- observer.next(message);
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
- const errorMessage = `<div
290
- class="alert alert-danger"
291
- role="alert"
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