@c8y/ngx-components 1023.0.0 → 1023.4.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 +114 -0
- package/ai/agent-chat/index.d.ts.map +1 -0
- package/ai/ai-chat/index.d.ts +145 -0
- package/ai/ai-chat/index.d.ts.map +1 -0
- package/ai/index.d.ts +203 -0
- package/ai/index.d.ts.map +1 -0
- package/context-dashboard/device/add/index.d.ts +15 -1
- package/context-dashboard/device/add/index.d.ts.map +1 -1
- package/context-dashboard/device/view/index.d.ts +14 -2
- package/context-dashboard/device/view/index.d.ts.map +1 -1
- package/context-dashboard/devicemanagement/index.d.ts +10 -0
- package/context-dashboard/devicemanagement/index.d.ts.map +1 -0
- package/context-dashboard/index.d.ts +6 -30
- package/context-dashboard/index.d.ts.map +1 -1
- package/fesm2022/c8y-ngx-components-ai-agent-chat.mjs +387 -0
- package/fesm2022/c8y-ngx-components-ai-agent-chat.mjs.map +1 -0
- package/fesm2022/c8y-ngx-components-ai-ai-chat.mjs +258 -0
- package/fesm2022/c8y-ngx-components-ai-ai-chat.mjs.map +1 -0
- package/fesm2022/c8y-ngx-components-ai.mjs +291 -0
- package/fesm2022/c8y-ngx-components-ai.mjs.map +1 -0
- package/fesm2022/c8y-ngx-components-context-dashboard-cockpit-home-dashboard.mjs +1 -1
- package/fesm2022/c8y-ngx-components-context-dashboard-cockpit-home-dashboard.mjs.map +1 -1
- package/fesm2022/c8y-ngx-components-context-dashboard-device-add.mjs +1 -1
- package/fesm2022/c8y-ngx-components-context-dashboard-device-view.mjs +2 -2
- package/fesm2022/c8y-ngx-components-context-dashboard-devicemanagement.mjs +68 -0
- package/fesm2022/c8y-ngx-components-context-dashboard-devicemanagement.mjs.map +1 -0
- package/fesm2022/c8y-ngx-components-context-dashboard.mjs +27 -45
- package/fesm2022/c8y-ngx-components-context-dashboard.mjs.map +1 -1
- package/fesm2022/c8y-ngx-components-echart.mjs +15 -17
- package/fesm2022/c8y-ngx-components-echart.mjs.map +1 -1
- package/fesm2022/c8y-ngx-components-global-context.mjs +10 -10
- package/fesm2022/c8y-ngx-components-global-context.mjs.map +1 -1
- package/fesm2022/c8y-ngx-components-register-device.mjs +4 -6
- package/fesm2022/c8y-ngx-components-register-device.mjs.map +1 -1
- package/fesm2022/c8y-ngx-components-sensor-phone-sensor-phone-modal.mjs +2 -2
- package/fesm2022/c8y-ngx-components-sensor-phone-sensor-phone-modal.mjs.map +1 -1
- package/fesm2022/c8y-ngx-components-widgets-definitions-html-widget-ai-config.mjs +574 -0
- package/fesm2022/c8y-ngx-components-widgets-definitions-html-widget-ai-config.mjs.map +1 -0
- package/fesm2022/c8y-ngx-components-widgets-exports.mjs +8 -1
- package/fesm2022/c8y-ngx-components-widgets-exports.mjs.map +1 -1
- package/fesm2022/c8y-ngx-components-widgets-implementations-html-widget.mjs +176 -41
- package/fesm2022/c8y-ngx-components-widgets-implementations-html-widget.mjs.map +1 -1
- package/fesm2022/c8y-ngx-components.mjs +96 -44
- package/fesm2022/c8y-ngx-components.mjs.map +1 -1
- package/index.d.ts +25 -4
- package/index.d.ts.map +1 -1
- package/locales/de.po +49 -52
- package/locales/es.po +42 -42
- package/locales/fr.po +35 -39
- package/locales/ja_JP.po +32 -32
- package/locales/ko.po +39 -40
- package/locales/locales.pot +144 -9
- package/locales/nl.po +37 -37
- package/locales/pl.po +51 -54
- package/locales/pt_BR.po +38 -39
- package/locales/zh_CN.po +36 -36
- package/locales/zh_TW.po +45 -45
- package/package.json +1 -1
- package/register-device/index.d.ts.map +1 -1
- package/widgets/cockpit-exports/index.d.ts +6 -0
- package/widgets/cockpit-exports/index.d.ts.map +1 -1
- package/widgets/definitions/html-widget-ai-config/index.d.ts +6 -0
- package/widgets/definitions/html-widget-ai-config/index.d.ts.map +1 -0
- package/widgets/device-management-exports/index.d.ts +6 -0
- package/widgets/device-management-exports/index.d.ts.map +1 -1
- package/widgets/exports/index.d.ts +8 -1
- package/widgets/exports/index.d.ts.map +1 -1
- package/widgets/implementations/html-widget/index.d.ts +72 -16
- package/widgets/implementations/html-widget/index.d.ts.map +1 -1
|
@@ -0,0 +1,258 @@
|
|
|
1
|
+
import * as i0 from '@angular/core';
|
|
2
|
+
import { EventEmitter, Output, Input, Component, input, inject, computed, ContentChildren } from '@angular/core';
|
|
3
|
+
import { IconDirective, C8yTranslatePipe, MarkdownToHtmlPipe, DatePipe, TextareaAutoresizeDirective } from '@c8y/ngx-components';
|
|
4
|
+
import * as i1 from 'ngx-bootstrap/tooltip';
|
|
5
|
+
import { TooltipModule } from 'ngx-bootstrap/tooltip';
|
|
6
|
+
import { gettext } from '@c8y/ngx-components/gettext';
|
|
7
|
+
import { NgClass, AsyncPipe } from '@angular/common';
|
|
8
|
+
import { TranslateService } from '@ngx-translate/core';
|
|
9
|
+
import * as i1$1 from '@angular/forms';
|
|
10
|
+
import { FormsModule } from '@angular/forms';
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* An action button that can be added to chat messages.
|
|
14
|
+
* Typically used for actions like copying, regenerating, or providing feedback on messages.
|
|
15
|
+
*/
|
|
16
|
+
class AiChatMessageActionComponent {
|
|
17
|
+
constructor() {
|
|
18
|
+
/**
|
|
19
|
+
* Disables the action button when true.
|
|
20
|
+
*/
|
|
21
|
+
this.disabled = false;
|
|
22
|
+
/**
|
|
23
|
+
* Tooltip text displayed when hovering over the action button.
|
|
24
|
+
*/
|
|
25
|
+
this.tooltip = '';
|
|
26
|
+
/**
|
|
27
|
+
* Icon to display in the action button.
|
|
28
|
+
*/
|
|
29
|
+
this.icon = 'cog';
|
|
30
|
+
/**
|
|
31
|
+
* Emitted when the action button is clicked.
|
|
32
|
+
*/
|
|
33
|
+
this.click = new EventEmitter();
|
|
34
|
+
}
|
|
35
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.3", ngImport: i0, type: AiChatMessageActionComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
36
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.3", type: AiChatMessageActionComponent, isStandalone: true, selector: "c8y-ai-chat-message-action", inputs: { disabled: "disabled", tooltip: "tooltip", icon: "icon" }, outputs: { click: "click" }, ngImport: i0, template: "<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", dependencies: [{ kind: "ngmodule", type: TooltipModule }, { kind: "directive", type: i1.TooltipDirective, selector: "[tooltip], [tooltipHtml]", inputs: ["adaptivePosition", "tooltip", "placement", "triggers", "container", "containerClass", "boundariesElement", "isOpen", "isDisabled", "delay", "tooltipHtml", "tooltipPlacement", "tooltipIsOpen", "tooltipEnable", "tooltipAppendToBody", "tooltipAnimation", "tooltipClass", "tooltipContext", "tooltipPopupDelay", "tooltipFadeDuration", "tooltipTrigger"], outputs: ["tooltipChange", "onShown", "onHidden", "tooltipStateChanged"], exportAs: ["bs-tooltip"] }, { kind: "directive", type: IconDirective, selector: "[c8yIcon]", inputs: ["c8yIcon"] }, { kind: "pipe", type: C8yTranslatePipe, name: "translate" }] }); }
|
|
37
|
+
}
|
|
38
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.3", ngImport: i0, type: AiChatMessageActionComponent, decorators: [{
|
|
39
|
+
type: Component,
|
|
40
|
+
args: [{ selector: 'c8y-ai-chat-message-action', standalone: true, imports: [TooltipModule, C8yTranslatePipe, IconDirective], template: "<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" }]
|
|
41
|
+
}], propDecorators: { disabled: [{
|
|
42
|
+
type: Input
|
|
43
|
+
}], tooltip: [{
|
|
44
|
+
type: Input
|
|
45
|
+
}], icon: [{
|
|
46
|
+
type: Input
|
|
47
|
+
}], click: [{
|
|
48
|
+
type: Output
|
|
49
|
+
}] } });
|
|
50
|
+
|
|
51
|
+
class AiChatMessageComponent {
|
|
52
|
+
constructor() {
|
|
53
|
+
this.role = input(...(ngDevMode ? [undefined, { debugName: "role" }] : []));
|
|
54
|
+
this.message = input(...(ngDevMode ? [undefined, { debugName: "message" }] : []));
|
|
55
|
+
this.translateService = inject(TranslateService);
|
|
56
|
+
this.roleResolved = computed(() => {
|
|
57
|
+
return this.message()?.role || this.role();
|
|
58
|
+
}, ...(ngDevMode ? [{ debugName: "roleResolved" }] : []));
|
|
59
|
+
/**
|
|
60
|
+
* Generates an accessible label for the message container.
|
|
61
|
+
* Includes the role (user or assistant) and timestamp if available.
|
|
62
|
+
* @returns The aria-label string for screen readers
|
|
63
|
+
*/
|
|
64
|
+
this.ariaLabel = computed(() => {
|
|
65
|
+
const tpl = this.roleResolved() === 'user'
|
|
66
|
+
? gettext('You said: "{{ message }}"')
|
|
67
|
+
: gettext('Assistant said: "{{ message }}"');
|
|
68
|
+
const msg = this.message();
|
|
69
|
+
return this.translateService.instant(tpl, { message: msg?.content || gettext('No response.') });
|
|
70
|
+
}, ...(ngDevMode ? [{ debugName: "ariaLabel" }] : []));
|
|
71
|
+
/**
|
|
72
|
+
* Generates an accessible label for the message content.
|
|
73
|
+
* Prefixes the content with contextual information about who sent it.
|
|
74
|
+
* @returns The aria-label string with prefixed role information
|
|
75
|
+
*/
|
|
76
|
+
this.messageContentAriaLabel = computed(() => {
|
|
77
|
+
const msg = this.message();
|
|
78
|
+
const time = msg?.timestamp ? new Date(msg.timestamp).toLocaleTimeString() : '';
|
|
79
|
+
let tpl;
|
|
80
|
+
if (time) {
|
|
81
|
+
tpl =
|
|
82
|
+
this.roleResolved() === 'user'
|
|
83
|
+
? gettext('User message at {{ time }}')
|
|
84
|
+
: gettext('Assistant message at {{ time }}');
|
|
85
|
+
}
|
|
86
|
+
else {
|
|
87
|
+
tpl = this.roleResolved() === 'user' ? gettext('User message') : gettext('Assistant message');
|
|
88
|
+
}
|
|
89
|
+
return this.translateService.instant(tpl, { time });
|
|
90
|
+
}, ...(ngDevMode ? [{ debugName: "messageContentAriaLabel" }] : []));
|
|
91
|
+
}
|
|
92
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.3", ngImport: i0, type: AiChatMessageComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
93
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.3", type: AiChatMessageComponent, isStandalone: true, selector: "c8y-ai-chat-message", inputs: { role: { classPropertyName: "role", publicName: "role", isSignal: true, isRequired: false, transformFunction: null }, message: { classPropertyName: "message", publicName: "message", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: "<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 <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", dependencies: [{ kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "ngmodule", type: TooltipModule }, { kind: "directive", type: i1.TooltipDirective, selector: "[tooltip], [tooltipHtml]", inputs: ["adaptivePosition", "tooltip", "placement", "triggers", "container", "containerClass", "boundariesElement", "isOpen", "isDisabled", "delay", "tooltipHtml", "tooltipPlacement", "tooltipIsOpen", "tooltipEnable", "tooltipAppendToBody", "tooltipAnimation", "tooltipClass", "tooltipContext", "tooltipPopupDelay", "tooltipFadeDuration", "tooltipTrigger"], outputs: ["tooltipChange", "onShown", "onHidden", "tooltipStateChanged"], exportAs: ["bs-tooltip"] }, { kind: "pipe", type: MarkdownToHtmlPipe, name: "markdownToHtml" }, { kind: "pipe", type: AsyncPipe, name: "async" }, { kind: "pipe", type: C8yTranslatePipe, name: "translate" }, { kind: "pipe", type: DatePipe, name: "c8yDate" }] }); }
|
|
94
|
+
}
|
|
95
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.3", ngImport: i0, type: AiChatMessageComponent, decorators: [{
|
|
96
|
+
type: Component,
|
|
97
|
+
args: [{ selector: 'c8y-ai-chat-message', standalone: true, imports: [MarkdownToHtmlPipe, AsyncPipe, NgClass, TooltipModule, C8yTranslatePipe, DatePipe], template: "<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 <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" }]
|
|
98
|
+
}] });
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* A suggestion chip that can be displayed in the chat interface.
|
|
102
|
+
* When clicked, it automatically populates and sends a predefined prompt.
|
|
103
|
+
*/
|
|
104
|
+
class AiChatSuggestionComponent {
|
|
105
|
+
constructor() {
|
|
106
|
+
/**
|
|
107
|
+
* Icon to display alongside the suggestion label.
|
|
108
|
+
*/
|
|
109
|
+
this.icon = 'c8y-bulb';
|
|
110
|
+
/**
|
|
111
|
+
* When true, uses AI-styled buttons instead of default styling.
|
|
112
|
+
*/
|
|
113
|
+
this.useAiButtons = false;
|
|
114
|
+
/**
|
|
115
|
+
* Disables the suggestion chip when true.
|
|
116
|
+
*/
|
|
117
|
+
this.disabled = false;
|
|
118
|
+
/**
|
|
119
|
+
* Emitted when the suggestion is clicked, providing the prompt as an AIMessage.
|
|
120
|
+
*/
|
|
121
|
+
this.suggestionClicked = new EventEmitter();
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Handles suggestion click and emits the prompt as a user message.
|
|
125
|
+
*/
|
|
126
|
+
suggest() {
|
|
127
|
+
this.suggestionClicked.emit({
|
|
128
|
+
content: this.prompt,
|
|
129
|
+
role: 'user',
|
|
130
|
+
timestamp: new Date().toISOString()
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.3", ngImport: i0, type: AiChatSuggestionComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
134
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.3", type: AiChatSuggestionComponent, isStandalone: true, selector: "c8y-ai-chat-suggestion", inputs: { label: "label", prompt: "prompt", icon: "icon", useAiButtons: "useAiButtons", disabled: "disabled" }, outputs: { suggestionClicked: "suggestionClicked" }, ngImport: i0, template: "@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", dependencies: [{ kind: "directive", type: IconDirective, selector: "[c8yIcon]", inputs: ["c8yIcon"] }, { kind: "pipe", type: C8yTranslatePipe, name: "translate" }] }); }
|
|
135
|
+
}
|
|
136
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.3", ngImport: i0, type: AiChatSuggestionComponent, decorators: [{
|
|
137
|
+
type: Component,
|
|
138
|
+
args: [{ selector: 'c8y-ai-chat-suggestion', standalone: true, imports: [IconDirective, C8yTranslatePipe], template: "@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" }]
|
|
139
|
+
}], propDecorators: { label: [{
|
|
140
|
+
type: Input
|
|
141
|
+
}], prompt: [{
|
|
142
|
+
type: Input
|
|
143
|
+
}], icon: [{
|
|
144
|
+
type: Input
|
|
145
|
+
}], useAiButtons: [{
|
|
146
|
+
type: Input
|
|
147
|
+
}], disabled: [{
|
|
148
|
+
type: Input
|
|
149
|
+
}], suggestionClicked: [{
|
|
150
|
+
type: Output
|
|
151
|
+
}] } });
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* An interactive chat interface component for AI-powered conversations.
|
|
155
|
+
* Displays messages in a conversation format with support for loading states,
|
|
156
|
+
* custom configuration, and markdown formatting.
|
|
157
|
+
*/
|
|
158
|
+
class AiChatComponent {
|
|
159
|
+
constructor() {
|
|
160
|
+
/**
|
|
161
|
+
* Indicates whether the chat is currently processing a request.
|
|
162
|
+
* When true, displays a cancel button instead of send button and disables the input.
|
|
163
|
+
*/
|
|
164
|
+
this.isLoading = false;
|
|
165
|
+
/**
|
|
166
|
+
* Disables the chat input and send button when true.
|
|
167
|
+
*/
|
|
168
|
+
this.disabled = false;
|
|
169
|
+
/**
|
|
170
|
+
* The current text in the chat input field. Supports two-way binding.
|
|
171
|
+
*/
|
|
172
|
+
this.prompt = '';
|
|
173
|
+
/**
|
|
174
|
+
* Emitted when the user sends a message.
|
|
175
|
+
* Provides an AIMessage object with role, content, and timestamp.
|
|
176
|
+
*/
|
|
177
|
+
this.onMessage = new EventEmitter();
|
|
178
|
+
/**
|
|
179
|
+
* Emitted when the user cancels an ongoing operation during loading state.
|
|
180
|
+
*/
|
|
181
|
+
this.onCancel = new EventEmitter();
|
|
182
|
+
this.componentId = `chat-${crypto.randomUUID()}`;
|
|
183
|
+
this._config = {
|
|
184
|
+
headline: gettext('Welcome!'),
|
|
185
|
+
welcomeText: '',
|
|
186
|
+
title: gettext('What can I help you with?'),
|
|
187
|
+
placeholder: gettext('Type your message here...'),
|
|
188
|
+
sendButtonText: gettext('Send'),
|
|
189
|
+
cancelButtonText: gettext('Cancel'),
|
|
190
|
+
disclaimerText: gettext('AI-generated responses can contain errors. Verify the details before use.'),
|
|
191
|
+
userInterfaceIcons: {
|
|
192
|
+
send: 'arrow-circle-up',
|
|
193
|
+
cancel: 'stop-circle'
|
|
194
|
+
}
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
/**
|
|
198
|
+
* Configuration object for customizing labels, placeholders, and icons.
|
|
199
|
+
* Accepts partial configuration to override defaults.
|
|
200
|
+
*/
|
|
201
|
+
set config(value) {
|
|
202
|
+
this._config = { ...this._config, ...value };
|
|
203
|
+
}
|
|
204
|
+
get config() {
|
|
205
|
+
return this._config;
|
|
206
|
+
}
|
|
207
|
+
/**
|
|
208
|
+
* Handles message submission when the user sends a message.
|
|
209
|
+
* Emits the onMessage event and clears the input.
|
|
210
|
+
* @param $event - The event object from the form submission
|
|
211
|
+
*/
|
|
212
|
+
sendMessage($event) {
|
|
213
|
+
$event.preventDefault();
|
|
214
|
+
if (!this.prompt)
|
|
215
|
+
return;
|
|
216
|
+
this.onMessage.emit({
|
|
217
|
+
role: 'user',
|
|
218
|
+
content: this.prompt,
|
|
219
|
+
timestamp: new Date().toISOString()
|
|
220
|
+
});
|
|
221
|
+
this.prompt = '';
|
|
222
|
+
}
|
|
223
|
+
/**
|
|
224
|
+
* Handles cancellation of ongoing operations during loading state.
|
|
225
|
+
* Emits the onCancel event.
|
|
226
|
+
*/
|
|
227
|
+
cancel() {
|
|
228
|
+
this.onCancel.emit();
|
|
229
|
+
}
|
|
230
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.3", ngImport: i0, type: AiChatComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
231
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.3", type: AiChatComponent, isStandalone: true, selector: "c8y-ai-chat", inputs: { isLoading: "isLoading", disabled: "disabled", prompt: "prompt", config: "config" }, outputs: { onMessage: "onMessage", onCancel: "onCancel" }, host: { classAttribute: "d-contents" }, queries: [{ propertyName: "messages", predicate: AiChatMessageComponent }], ngImport: i0, template: "<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 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)=\"sendMessage($event)\"\n [disabled]=\"isLoading || 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", dependencies: [{ kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "directive", type: TextareaAutoresizeDirective, selector: "[c8y-textarea-autoresize]" }, { kind: "directive", type: IconDirective, selector: "[c8yIcon]", inputs: ["c8yIcon"] }, { kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "pipe", type: C8yTranslatePipe, name: "translate" }] }); }
|
|
232
|
+
}
|
|
233
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.3", ngImport: i0, type: AiChatComponent, decorators: [{
|
|
234
|
+
type: Component,
|
|
235
|
+
args: [{ selector: 'c8y-ai-chat', imports: [C8yTranslatePipe, FormsModule, TextareaAutoresizeDirective, IconDirective, NgClass], standalone: true, host: { class: 'd-contents' }, template: "<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 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)=\"sendMessage($event)\"\n [disabled]=\"isLoading || 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" }]
|
|
236
|
+
}], propDecorators: { isLoading: [{
|
|
237
|
+
type: Input
|
|
238
|
+
}], disabled: [{
|
|
239
|
+
type: Input
|
|
240
|
+
}], prompt: [{
|
|
241
|
+
type: Input
|
|
242
|
+
}], onMessage: [{
|
|
243
|
+
type: Output
|
|
244
|
+
}], onCancel: [{
|
|
245
|
+
type: Output
|
|
246
|
+
}], messages: [{
|
|
247
|
+
type: ContentChildren,
|
|
248
|
+
args: [AiChatMessageComponent]
|
|
249
|
+
}], config: [{
|
|
250
|
+
type: Input
|
|
251
|
+
}] } });
|
|
252
|
+
|
|
253
|
+
/**
|
|
254
|
+
* Generated bundle index. Do not edit.
|
|
255
|
+
*/
|
|
256
|
+
|
|
257
|
+
export { AiChatComponent, AiChatMessageActionComponent, AiChatMessageComponent, AiChatSuggestionComponent };
|
|
258
|
+
//# sourceMappingURL=c8y-ngx-components-ai-ai-chat.mjs.map
|
|
@@ -0,0 +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-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 <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 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 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)=\"sendMessage($event)\"\n [disabled]=\"isLoading || 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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"}
|
|
@@ -0,0 +1,291 @@
|
|
|
1
|
+
import * as i0 from '@angular/core';
|
|
2
|
+
import { Injectable } from '@angular/core';
|
|
3
|
+
import * as i1 from '@c8y/client';
|
|
4
|
+
import { Observable } from 'rxjs';
|
|
5
|
+
|
|
6
|
+
var DataStreamType;
|
|
7
|
+
(function (DataStreamType) {
|
|
8
|
+
DataStreamType["TEXT_DELTA"] = "text-delta";
|
|
9
|
+
DataStreamType["TOOL_CALL"] = "tool-call";
|
|
10
|
+
DataStreamType["TOOL_CALL_STREAMING"] = "tool-call-streaming";
|
|
11
|
+
DataStreamType["TOOL_CALL_DELTA"] = "tool-call-delta";
|
|
12
|
+
DataStreamType["TOOL_RESULT"] = "tool-result";
|
|
13
|
+
DataStreamType["REASONING"] = "reasoning";
|
|
14
|
+
DataStreamType["REASONING_DELTA"] = "reasoning-delta";
|
|
15
|
+
DataStreamType["REDACTED_REASONING"] = "redacted-reasoning";
|
|
16
|
+
DataStreamType["REASONING_SIGNATURE"] = "reasoning-signature";
|
|
17
|
+
DataStreamType["FINISH_REASONING"] = "finish-reasoning";
|
|
18
|
+
DataStreamType["FINISH"] = "finish";
|
|
19
|
+
DataStreamType["FINISH_STEP"] = "finish-step";
|
|
20
|
+
DataStreamType["ERROR"] = "error";
|
|
21
|
+
DataStreamType["DATA"] = "data";
|
|
22
|
+
DataStreamType["MESSAGE_ANNOTATIONS"] = "message-annotations";
|
|
23
|
+
DataStreamType["SOURCE"] = "source";
|
|
24
|
+
DataStreamType["FILE"] = "file";
|
|
25
|
+
DataStreamType["STEP_START"] = "step-start";
|
|
26
|
+
DataStreamType["STEP_FINISH"] = "step-finish";
|
|
27
|
+
})(DataStreamType || (DataStreamType = {}));
|
|
28
|
+
|
|
29
|
+
class AIService {
|
|
30
|
+
constructor(client) {
|
|
31
|
+
this.client = client;
|
|
32
|
+
this.baseUrl = '/service/ai';
|
|
33
|
+
}
|
|
34
|
+
async createOrUpdateAgent(agentsDef) {
|
|
35
|
+
for (const def of agentsDef.definitions) {
|
|
36
|
+
const health = await this.getAgentHealth(def.name);
|
|
37
|
+
let resource = `${this.baseUrl}/agent/${def.type}`;
|
|
38
|
+
let method = 'POST';
|
|
39
|
+
if (health.exists) {
|
|
40
|
+
resource = `${this.baseUrl}/agent/${def.type}/${def.name}`;
|
|
41
|
+
method = 'PUT';
|
|
42
|
+
}
|
|
43
|
+
const response = await this.client.fetch(resource, {
|
|
44
|
+
body: JSON.stringify(def),
|
|
45
|
+
method,
|
|
46
|
+
headers: { 'Content-Type': 'application/json' }
|
|
47
|
+
});
|
|
48
|
+
if (!response.ok) {
|
|
49
|
+
throw new Error(`Failed to create agent: ${response.statusText}`);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Check if an agent exists.
|
|
55
|
+
* @param name Agent name
|
|
56
|
+
* @returns Agent health check response.
|
|
57
|
+
*/
|
|
58
|
+
async getAgentHealth(name) {
|
|
59
|
+
const response = await this.client.fetch(`${this.baseUrl}/agent/test/${name}`, {
|
|
60
|
+
method: 'GET',
|
|
61
|
+
headers: { 'Content-Type': 'application/json' }
|
|
62
|
+
});
|
|
63
|
+
if (!response.ok) {
|
|
64
|
+
return {
|
|
65
|
+
exists: false,
|
|
66
|
+
canCreate: false,
|
|
67
|
+
isProviderConfigured: false
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
const json = await response.json();
|
|
71
|
+
return json;
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Send a text message to the agent.
|
|
75
|
+
* @param name Agent name
|
|
76
|
+
* @param messages Messages to send
|
|
77
|
+
* @param variables Variables to include
|
|
78
|
+
* @returns Text response from the agent.
|
|
79
|
+
*/
|
|
80
|
+
async text(name, messages, variables) {
|
|
81
|
+
const parsedMessages = this.parseMessages(messages);
|
|
82
|
+
const data = this.client.fetch(`${this.baseUrl}/agent/text/${name}`, {
|
|
83
|
+
body: JSON.stringify({ messages: parsedMessages, variables }),
|
|
84
|
+
method: 'POST',
|
|
85
|
+
headers: { 'Content-Type': 'application/json' }
|
|
86
|
+
});
|
|
87
|
+
const response = await data;
|
|
88
|
+
if (!response.ok) {
|
|
89
|
+
throw new Error(`Failed to talk with agent: ${response.statusText}`);
|
|
90
|
+
}
|
|
91
|
+
const text = await response.text();
|
|
92
|
+
return text;
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Stream a text message to the agent.
|
|
96
|
+
* @param agentName Agent name
|
|
97
|
+
* @param messages Messages to send
|
|
98
|
+
* @param variables Variables to include
|
|
99
|
+
* @param abortController An AbortController to cancel the request.
|
|
100
|
+
* @returns An observable that emits partial AIMessage objects as they are received. The observable can be cancelled using the provided AbortController.
|
|
101
|
+
* The observable will emit an error if the request fails or is aborted.
|
|
102
|
+
* The observable will complete when the stream is finished.
|
|
103
|
+
*
|
|
104
|
+
* The messages sent to the agent can include special options:
|
|
105
|
+
* - `hiddenContent`: If set, this content will be sent to the agent instead of the `content` field.
|
|
106
|
+
* - `skipToLLM`: If set to true, this message will be skipped when sending to the agent.
|
|
107
|
+
*
|
|
108
|
+
* Example usage:
|
|
109
|
+
* ```typescript
|
|
110
|
+
* const abortController = new AbortController();
|
|
111
|
+
* const messages: AIMessage[] = [
|
|
112
|
+
* { role: 'user', content: 'Hello' },
|
|
113
|
+
* { role: 'assistant', content: 'Hi there!' },
|
|
114
|
+
* { role: 'user', content: 'Tell me a joke.', options: { hiddenContent: 'Tell me a joke about cats.' } }
|
|
115
|
+
* ];
|
|
116
|
+
* const observable = aiService.stream$('my-agent', messages, {}, abortController);
|
|
117
|
+
* const subscription = observable.subscribe({
|
|
118
|
+
* next: (message) => console.log('Received message part:', message),
|
|
119
|
+
* error: (err) => console.error('Error:', err),
|
|
120
|
+
* complete: () => console.log('Stream complete')
|
|
121
|
+
* });
|
|
122
|
+
*
|
|
123
|
+
* // To cancel the request:
|
|
124
|
+
* abortController.abort();
|
|
125
|
+
* subscription.unsubscribe();
|
|
126
|
+
* ```
|
|
127
|
+
*/
|
|
128
|
+
async stream$(agentName, messages, variables, abortController) {
|
|
129
|
+
const parsedMessages = this.parseMessages(messages);
|
|
130
|
+
const response = await this.client.fetch(`${this.baseUrl}/agent/text/${agentName}?fullResponse=true`, {
|
|
131
|
+
method: 'POST',
|
|
132
|
+
body: JSON.stringify({ messages: parsedMessages, variables }),
|
|
133
|
+
headers: {
|
|
134
|
+
...this.client.defaultHeaders,
|
|
135
|
+
'content-type': 'application/json',
|
|
136
|
+
accept: 'text/event-stream'
|
|
137
|
+
},
|
|
138
|
+
signal: abortController.signal
|
|
139
|
+
});
|
|
140
|
+
if (response.status > 300) {
|
|
141
|
+
const data = await response.json();
|
|
142
|
+
throw new Error(JSON.stringify(data, null, 2));
|
|
143
|
+
}
|
|
144
|
+
const stream = response.body;
|
|
145
|
+
const decoder = new TextDecoder();
|
|
146
|
+
return new Observable(observer => {
|
|
147
|
+
if (!stream) {
|
|
148
|
+
observer.error('No response body');
|
|
149
|
+
throw 'No response body';
|
|
150
|
+
}
|
|
151
|
+
const reader = stream.getReader();
|
|
152
|
+
const message = {
|
|
153
|
+
role: 'assistant',
|
|
154
|
+
content: '',
|
|
155
|
+
steps: []
|
|
156
|
+
};
|
|
157
|
+
const abortHandler = () => {
|
|
158
|
+
reader.cancel();
|
|
159
|
+
observer.error(new DOMException('Aborted', 'AbortError'));
|
|
160
|
+
};
|
|
161
|
+
abortController.signal.addEventListener('abort', abortHandler);
|
|
162
|
+
let buffer = '';
|
|
163
|
+
const read = () => {
|
|
164
|
+
reader.read().then(({ done, value }) => {
|
|
165
|
+
if (done) {
|
|
166
|
+
if (buffer.trim())
|
|
167
|
+
this.processLine(buffer, observer, message); // process any remaining data
|
|
168
|
+
observer.complete();
|
|
169
|
+
return;
|
|
170
|
+
}
|
|
171
|
+
buffer += decoder.decode(value, { stream: true });
|
|
172
|
+
const lines = buffer.split('\n\n');
|
|
173
|
+
buffer = lines.pop() || '';
|
|
174
|
+
for (const line of lines) {
|
|
175
|
+
if (line.trim())
|
|
176
|
+
this.processLine(line, observer, message);
|
|
177
|
+
}
|
|
178
|
+
read();
|
|
179
|
+
});
|
|
180
|
+
};
|
|
181
|
+
read();
|
|
182
|
+
return () => {
|
|
183
|
+
abortController.signal.removeEventListener('abort', abortHandler);
|
|
184
|
+
reader.cancel();
|
|
185
|
+
};
|
|
186
|
+
});
|
|
187
|
+
}
|
|
188
|
+
parseMessages(messages) {
|
|
189
|
+
return messages
|
|
190
|
+
.filter(message => !message.options?.skipToLLM)
|
|
191
|
+
.map(message => {
|
|
192
|
+
if (message.options?.hiddenContent) {
|
|
193
|
+
return {
|
|
194
|
+
role: message.role,
|
|
195
|
+
content: message.options.hiddenContent
|
|
196
|
+
};
|
|
197
|
+
}
|
|
198
|
+
return {
|
|
199
|
+
role: message.role,
|
|
200
|
+
content: message.content
|
|
201
|
+
};
|
|
202
|
+
});
|
|
203
|
+
}
|
|
204
|
+
processLine(line, observer, message) {
|
|
205
|
+
if (!line.trim()) {
|
|
206
|
+
return;
|
|
207
|
+
}
|
|
208
|
+
const lastStep = message.steps?.[message.steps.length - 1];
|
|
209
|
+
try {
|
|
210
|
+
let data = {};
|
|
211
|
+
let type = '';
|
|
212
|
+
try {
|
|
213
|
+
data = JSON.parse(line.replace('data: ', ''));
|
|
214
|
+
type = data.type;
|
|
215
|
+
}
|
|
216
|
+
catch (e) {
|
|
217
|
+
console.error('Error parsing line', line, e);
|
|
218
|
+
return;
|
|
219
|
+
}
|
|
220
|
+
switch (type) {
|
|
221
|
+
case DataStreamType.STEP_START:
|
|
222
|
+
message.steps = message.steps || [];
|
|
223
|
+
message.steps.push({
|
|
224
|
+
type: 'text',
|
|
225
|
+
toolCalls: [],
|
|
226
|
+
toolResults: [],
|
|
227
|
+
text: ''
|
|
228
|
+
});
|
|
229
|
+
return;
|
|
230
|
+
case DataStreamType.REASONING:
|
|
231
|
+
if (lastStep.reasoning === undefined) {
|
|
232
|
+
lastStep.reasoning = '';
|
|
233
|
+
}
|
|
234
|
+
lastStep.reasoning += data.textDelta;
|
|
235
|
+
observer.next(message);
|
|
236
|
+
return;
|
|
237
|
+
case DataStreamType.TEXT_DELTA:
|
|
238
|
+
lastStep.text += data.textDelta;
|
|
239
|
+
message.content += data.textDelta;
|
|
240
|
+
observer.next(message);
|
|
241
|
+
return;
|
|
242
|
+
case DataStreamType.TOOL_CALL:
|
|
243
|
+
lastStep.toolCalls = [data];
|
|
244
|
+
observer.next(message);
|
|
245
|
+
return;
|
|
246
|
+
case DataStreamType.TOOL_RESULT:
|
|
247
|
+
lastStep.toolResults = [data];
|
|
248
|
+
observer.next(message);
|
|
249
|
+
return;
|
|
250
|
+
case DataStreamType.FINISH:
|
|
251
|
+
message.finishReason = data.finishReason;
|
|
252
|
+
observer.next(message);
|
|
253
|
+
observer.complete();
|
|
254
|
+
return;
|
|
255
|
+
case DataStreamType.ERROR:
|
|
256
|
+
message.finishReason = 'error';
|
|
257
|
+
const errorMessage = `<div
|
|
258
|
+
class="alert alert-danger"
|
|
259
|
+
role="alert"
|
|
260
|
+
>
|
|
261
|
+
<strong>Error</strong>
|
|
262
|
+
${data.message || 'An unknown error occurred'}
|
|
263
|
+
</div>`;
|
|
264
|
+
message.content += errorMessage;
|
|
265
|
+
lastStep.text += errorMessage;
|
|
266
|
+
observer.next(message);
|
|
267
|
+
observer.error(data);
|
|
268
|
+
observer.complete();
|
|
269
|
+
return;
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
catch (e) {
|
|
273
|
+
observer.error(e);
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.3", ngImport: i0, type: AIService, deps: [{ token: i1.FetchClient }], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
277
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.3", ngImport: i0, type: AIService, providedIn: 'root' }); }
|
|
278
|
+
}
|
|
279
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.3", ngImport: i0, type: AIService, decorators: [{
|
|
280
|
+
type: Injectable,
|
|
281
|
+
args: [{
|
|
282
|
+
providedIn: 'root'
|
|
283
|
+
}]
|
|
284
|
+
}], ctorParameters: () => [{ type: i1.FetchClient }] });
|
|
285
|
+
|
|
286
|
+
/**
|
|
287
|
+
* Generated bundle index. Do not edit.
|
|
288
|
+
*/
|
|
289
|
+
|
|
290
|
+
export { AIService, DataStreamType };
|
|
291
|
+
//# sourceMappingURL=c8y-ngx-components-ai.mjs.map
|