@c8y/tutorial 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.
package/package.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@c8y/tutorial",
|
|
3
|
-
"version": "1023.
|
|
3
|
+
"version": "1023.70.0",
|
|
4
4
|
"description": "This package is used to scaffold a tutorial for Cumulocity IoT Web SDK which explains a lot of concepts.",
|
|
5
5
|
"dependencies": {
|
|
6
|
-
"@c8y/style": "1023.
|
|
7
|
-
"@c8y/ngx-components": "1023.
|
|
8
|
-
"@c8y/client": "1023.
|
|
9
|
-
"@c8y/bootstrap": "1023.
|
|
6
|
+
"@c8y/style": "1023.70.0",
|
|
7
|
+
"@c8y/ngx-components": "1023.70.0",
|
|
8
|
+
"@c8y/client": "1023.70.0",
|
|
9
|
+
"@c8y/bootstrap": "1023.70.0",
|
|
10
10
|
"@angular/cdk": "^20.2.14",
|
|
11
11
|
"monaco-editor": "~0.53.0",
|
|
12
12
|
"ngx-bootstrap": "20.0.2",
|
|
@@ -14,8 +14,8 @@
|
|
|
14
14
|
"rxjs": "7.8.2"
|
|
15
15
|
},
|
|
16
16
|
"devDependencies": {
|
|
17
|
-
"@c8y/options": "1023.
|
|
18
|
-
"@c8y/devkit": "1023.
|
|
17
|
+
"@c8y/options": "1023.70.0",
|
|
18
|
+
"@c8y/devkit": "1023.70.0"
|
|
19
19
|
},
|
|
20
20
|
"peerDependencies": {
|
|
21
21
|
"@angular/common": ">=20 <21"
|
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
<c8y-title>Chat component example</c8y-title>
|
|
2
|
-
<div class="row d-flex-
|
|
3
|
-
<div class="col-md-4 p-0">
|
|
2
|
+
<div class="row d-flex-md">
|
|
3
|
+
<div class="col-md-4 col-xs-12 p-0">
|
|
4
4
|
<div class="d-col p-16 form-group-sm bg-level-1">
|
|
5
|
-
<p class="text-medium p-b-16">
|
|
5
|
+
<p class="text-medium p-b-16">ChatConfig options</p>
|
|
6
6
|
|
|
7
|
-
<label>
|
|
7
|
+
<label>headline</label>
|
|
8
8
|
<div class="input-group input-group-sm m-b-8">
|
|
9
9
|
<input
|
|
10
10
|
class="form-control"
|
|
11
11
|
type="text"
|
|
12
|
-
[(ngModel)]="config.
|
|
12
|
+
[(ngModel)]="config.headline"
|
|
13
13
|
/>
|
|
14
14
|
<div class="input-group-btn">
|
|
15
15
|
<button
|
|
@@ -21,12 +21,12 @@
|
|
|
21
21
|
</div>
|
|
22
22
|
</div>
|
|
23
23
|
|
|
24
|
-
<label>
|
|
24
|
+
<label>title</label>
|
|
25
25
|
<div class="input-group input-group-sm m-b-8">
|
|
26
26
|
<input
|
|
27
27
|
class="form-control"
|
|
28
28
|
type="text"
|
|
29
|
-
[(ngModel)]="config.
|
|
29
|
+
[(ngModel)]="config.title"
|
|
30
30
|
/>
|
|
31
31
|
<div class="input-group-btn">
|
|
32
32
|
<button
|
|
@@ -55,6 +55,86 @@
|
|
|
55
55
|
</div>
|
|
56
56
|
</div>
|
|
57
57
|
|
|
58
|
+
<label>welcomePosition</label>
|
|
59
|
+
<div class="input-group input-group-sm form-group-sm m-b-8">
|
|
60
|
+
<div class="c8y-select-wrapper">
|
|
61
|
+
<select
|
|
62
|
+
class="form-control"
|
|
63
|
+
[(ngModel)]="config.welcomePosition"
|
|
64
|
+
>
|
|
65
|
+
<option value="top">Top</option>
|
|
66
|
+
<option value="center">Center</option>
|
|
67
|
+
<option value="bottom">Bottom</option>
|
|
68
|
+
</select>
|
|
69
|
+
</div>
|
|
70
|
+
<div class="input-group-btn">
|
|
71
|
+
<button
|
|
72
|
+
class="btn btn-default"
|
|
73
|
+
(click)="reloadComponent()"
|
|
74
|
+
>
|
|
75
|
+
{{ 'Apply' | translate }}
|
|
76
|
+
</button>
|
|
77
|
+
</div>
|
|
78
|
+
</div>
|
|
79
|
+
|
|
80
|
+
<label>suggestionsLayout</label>
|
|
81
|
+
<div class="input-group input-group-sm m-b-8">
|
|
82
|
+
<div class="c8y-select-wrapper">
|
|
83
|
+
<select
|
|
84
|
+
class="form-control"
|
|
85
|
+
[(ngModel)]="config.suggestionsLayout"
|
|
86
|
+
>
|
|
87
|
+
<option value="vertical">Vertical</option>
|
|
88
|
+
<option value="horizontal">Horizontal</option>
|
|
89
|
+
</select>
|
|
90
|
+
</div>
|
|
91
|
+
<div class="input-group-btn">
|
|
92
|
+
<button
|
|
93
|
+
class="btn btn-default"
|
|
94
|
+
(click)="reloadComponent()"
|
|
95
|
+
>
|
|
96
|
+
{{ 'Apply' | translate }}
|
|
97
|
+
</button>
|
|
98
|
+
</div>
|
|
99
|
+
</div>
|
|
100
|
+
|
|
101
|
+
<label>scrollbarOnlyOnHover</label>
|
|
102
|
+
<div class="m-b-8">
|
|
103
|
+
<label
|
|
104
|
+
class="c8y-switch"
|
|
105
|
+
title="scrollbarOnlyOnHover"
|
|
106
|
+
>
|
|
107
|
+
<input
|
|
108
|
+
type="checkbox"
|
|
109
|
+
[(ngModel)]="config.scrollbarOnlyOnHover"
|
|
110
|
+
(ngModelChange)="reloadComponent()"
|
|
111
|
+
/>
|
|
112
|
+
<span></span>
|
|
113
|
+
<span>scrollbarOnlyOnHover</span>
|
|
114
|
+
</label>
|
|
115
|
+
</div>
|
|
116
|
+
|
|
117
|
+
<label>appearance</label>
|
|
118
|
+
<div class="input-group input-group-sm m-b-8">
|
|
119
|
+
<div class="c8y-select-wrapper">
|
|
120
|
+
<select
|
|
121
|
+
class="form-control"
|
|
122
|
+
[(ngModel)]="config.appearance"
|
|
123
|
+
>
|
|
124
|
+
<option value="framed">framed</option>
|
|
125
|
+
<option value="flat">flat</option>
|
|
126
|
+
</select>
|
|
127
|
+
</div>
|
|
128
|
+
<div class="input-group-btn">
|
|
129
|
+
<button
|
|
130
|
+
class="btn btn-default"
|
|
131
|
+
(click)="reloadComponent()"
|
|
132
|
+
>
|
|
133
|
+
{{ 'Apply' | translate }}
|
|
134
|
+
</button>
|
|
135
|
+
</div>
|
|
136
|
+
</div>
|
|
137
|
+
|
|
58
138
|
<label>placeholder</label>
|
|
59
139
|
<div class="input-group input-group-sm m-b-8">
|
|
60
140
|
<input
|
|
@@ -72,12 +152,12 @@
|
|
|
72
152
|
</div>
|
|
73
153
|
</div>
|
|
74
154
|
|
|
75
|
-
<label>
|
|
155
|
+
<label>disclaimerText</label>
|
|
76
156
|
<div class="input-group input-group-sm m-b-8">
|
|
77
157
|
<input
|
|
78
158
|
class="form-control"
|
|
79
159
|
type="text"
|
|
80
|
-
[(ngModel)]="config.
|
|
160
|
+
[(ngModel)]="config.disclaimerText"
|
|
81
161
|
/>
|
|
82
162
|
<div class="input-group-btn">
|
|
83
163
|
<button
|
|
@@ -89,12 +169,12 @@
|
|
|
89
169
|
</div>
|
|
90
170
|
</div>
|
|
91
171
|
|
|
92
|
-
<label>
|
|
172
|
+
<label>sendButtonText (displayed as a tooltip)</label>
|
|
93
173
|
<div class="input-group input-group-sm m-b-8">
|
|
94
174
|
<input
|
|
95
175
|
class="form-control"
|
|
96
176
|
type="text"
|
|
97
|
-
[(ngModel)]="config.
|
|
177
|
+
[(ngModel)]="config.sendButtonText"
|
|
98
178
|
/>
|
|
99
179
|
<div class="input-group-btn">
|
|
100
180
|
<button
|
|
@@ -106,12 +186,12 @@
|
|
|
106
186
|
</div>
|
|
107
187
|
</div>
|
|
108
188
|
|
|
109
|
-
<label>
|
|
189
|
+
<label>cancelButtonText (displayed as a tooltip)</label>
|
|
110
190
|
<div class="input-group input-group-sm m-b-8">
|
|
111
191
|
<input
|
|
112
192
|
class="form-control"
|
|
113
193
|
type="text"
|
|
114
|
-
[(ngModel)]="config.
|
|
194
|
+
[(ngModel)]="config.cancelButtonText"
|
|
115
195
|
/>
|
|
116
196
|
<div class="input-group-btn">
|
|
117
197
|
<button
|
|
@@ -126,7 +206,7 @@
|
|
|
126
206
|
<pre class="m-t-16 inner-scroll small">{{ config | json }}</pre>
|
|
127
207
|
</div>
|
|
128
208
|
</div>
|
|
129
|
-
<div class="col-md-8">
|
|
209
|
+
<div class="col-md-8 col-xs-12">
|
|
130
210
|
@if (showComponent) {
|
|
131
211
|
<label
|
|
132
212
|
class="c8y-switch m-t-8"
|
|
@@ -143,12 +223,13 @@
|
|
|
143
223
|
</label>
|
|
144
224
|
<div
|
|
145
225
|
class="card m-t-16"
|
|
146
|
-
style="height:
|
|
226
|
+
style="height: 730px"
|
|
147
227
|
>
|
|
148
228
|
<c8y-ai-chat
|
|
149
229
|
[config]="config"
|
|
150
230
|
(onMessage)="sendMessage($event)"
|
|
151
231
|
[isLoading]="isLoading"
|
|
232
|
+
[suggestionsTemplate]="suggestionsRef"
|
|
152
233
|
>
|
|
153
234
|
@if (showInitialMessage) {
|
|
154
235
|
<c8y-ai-chat-message>
|
|
@@ -163,8 +244,24 @@
|
|
|
163
244
|
></c8y-ai-chat-message-action>
|
|
164
245
|
</c8y-ai-chat-message>
|
|
165
246
|
}
|
|
166
|
-
@for (message of displayMessages; track message) {
|
|
247
|
+
@for (message of displayMessages; track message; let i = $index) {
|
|
167
248
|
<c8y-ai-chat-message [message]="message">
|
|
249
|
+
@if (message.role === 'user') {
|
|
250
|
+
<div [innerHTML]="message.content | markdownToHtml | async"></div>
|
|
251
|
+
} @else {
|
|
252
|
+
<ng-container
|
|
253
|
+
[ngComponentOutlet]="AiChatAssistantMessageComponent"
|
|
254
|
+
[ngComponentOutletInputs]="{
|
|
255
|
+
assistantMessageContext: {
|
|
256
|
+
message: message,
|
|
257
|
+
config: assistantMessageDisplayConfig,
|
|
258
|
+
isMessageLoading: isLoading && i === messages.length - 1,
|
|
259
|
+
messageDisplayIndex: messages.length - 1 - i
|
|
260
|
+
}
|
|
261
|
+
}"
|
|
262
|
+
></ng-container>
|
|
263
|
+
}
|
|
264
|
+
|
|
168
265
|
@if (message.role === 'assistant') {
|
|
169
266
|
<c8y-ai-chat-message-action
|
|
170
267
|
icon="thumbs-up"
|
|
@@ -183,7 +280,7 @@
|
|
|
183
280
|
<c8y-ai-chat-message-action
|
|
184
281
|
icon="undo"
|
|
185
282
|
tooltip="Revert"
|
|
186
|
-
(
|
|
283
|
+
(actionClicked)="revertMessage(message)"
|
|
187
284
|
></c8y-ai-chat-message-action>
|
|
188
285
|
}
|
|
189
286
|
|
|
@@ -208,6 +305,21 @@
|
|
|
208
305
|
</c8y-ai-chat-message>
|
|
209
306
|
}
|
|
210
307
|
|
|
308
|
+
<div slot="welcome">
|
|
309
|
+
You can also project custom content into the welcome area of the chat component.
|
|
310
|
+
</div>
|
|
311
|
+
@if (showInitialMessage) {
|
|
312
|
+
<div slot="before-messages">
|
|
313
|
+
You can also project custom content before all messages.
|
|
314
|
+
</div>
|
|
315
|
+
}
|
|
316
|
+
@if (showInitialMessage) {
|
|
317
|
+
<div slot="after-messages">You can also project custom content after all messages.</div>
|
|
318
|
+
}
|
|
319
|
+
@if (showInitialMessage) {
|
|
320
|
+
<div>This is content that is projected by default and always shown.</div>
|
|
321
|
+
}
|
|
322
|
+
|
|
211
323
|
<c8y-ai-chat-suggestion
|
|
212
324
|
[label]="'Create device '"
|
|
213
325
|
[prompt]="'AHOI, create a new device'"
|
|
@@ -221,6 +333,17 @@
|
|
|
221
333
|
[prompt]="'Create a device with a measurement and an event'"
|
|
222
334
|
(suggestionClicked)="sendMessage($event)"
|
|
223
335
|
></c8y-ai-chat-suggestion>
|
|
336
|
+
|
|
337
|
+
<!-- For cases where dynamic behaviour is needed, use `<ng-template>` -->
|
|
338
|
+
<ng-template #suggestionsRef>
|
|
339
|
+
<c8y-ai-chat-suggestion
|
|
340
|
+
[icon]="'c8y-bulb'"
|
|
341
|
+
[useAiButtons]="true"
|
|
342
|
+
[label]="'Dynamic suggestion from AI'"
|
|
343
|
+
[prompt]="'Dynamic suggestion from AI'"
|
|
344
|
+
(suggestionClicked)="sendMessage($event)"
|
|
345
|
+
></c8y-ai-chat-suggestion>
|
|
346
|
+
</ng-template>
|
|
224
347
|
</c8y-ai-chat>
|
|
225
348
|
</div>
|
|
226
349
|
}
|
|
@@ -1,14 +1,16 @@
|
|
|
1
|
-
import { JsonPipe } from '@angular/common';
|
|
1
|
+
import { JsonPipe, NgComponentOutlet } from '@angular/common';
|
|
2
2
|
import { Component } from '@angular/core';
|
|
3
3
|
import { FormsModule } from '@angular/forms';
|
|
4
4
|
import { CoreModule, LoadingComponent } from '@c8y/ngx-components';
|
|
5
|
-
import { AIMessage } from '@c8y/ngx-components/ai';
|
|
5
|
+
import { AIMessage, AssistantMessageDisplayConfig, ChatConfig } from '@c8y/ngx-components/ai';
|
|
6
6
|
import {
|
|
7
|
+
AiChatAssistantMessageComponent,
|
|
7
8
|
AiChatComponent,
|
|
8
9
|
AiChatMessageActionComponent,
|
|
9
10
|
AiChatMessageComponent,
|
|
10
11
|
AiChatSuggestionComponent
|
|
11
12
|
} from '@c8y/ngx-components/ai/ai-chat';
|
|
13
|
+
import { gettext } from '@c8y/ngx-components/gettext';
|
|
12
14
|
|
|
13
15
|
@Component({
|
|
14
16
|
selector: 'c8y-ai-chat-example',
|
|
@@ -20,12 +22,15 @@ import {
|
|
|
20
22
|
AiChatMessageActionComponent,
|
|
21
23
|
AiChatSuggestionComponent,
|
|
22
24
|
LoadingComponent,
|
|
25
|
+
NgComponentOutlet,
|
|
23
26
|
CoreModule,
|
|
24
27
|
FormsModule,
|
|
25
28
|
JsonPipe
|
|
26
29
|
]
|
|
27
30
|
})
|
|
28
31
|
export class ChatExampleComponent {
|
|
32
|
+
AiChatAssistantMessageComponent = AiChatAssistantMessageComponent;
|
|
33
|
+
|
|
29
34
|
private _showTimestamps = false;
|
|
30
35
|
showInitialMessage = false;
|
|
31
36
|
|
|
@@ -38,16 +43,27 @@ export class ChatExampleComponent {
|
|
|
38
43
|
this.updateDisplayMessages();
|
|
39
44
|
}
|
|
40
45
|
|
|
41
|
-
config = {
|
|
46
|
+
config: ChatConfig = {
|
|
42
47
|
headline: 'Welcome!',
|
|
43
48
|
welcomeText: 'Type a message below to get started.',
|
|
49
|
+
welcomePosition: 'top',
|
|
44
50
|
title: 'What can I help you with?',
|
|
45
51
|
placeholder: 'Type your message here...',
|
|
46
52
|
sendButtonText: 'Send',
|
|
47
53
|
cancelButtonText: 'Cancel',
|
|
48
54
|
disclaimerText: 'AI-generated responses can contain errors. Verify the details before use.',
|
|
55
|
+
suggestionsLayout: 'horizontal',
|
|
56
|
+
appearance: 'framed',
|
|
49
57
|
userInterfaceIcons: { send: 'arrow-circle-up', cancel: 'stop-circle' }
|
|
50
58
|
};
|
|
59
|
+
assistantMessageDisplayConfig: AssistantMessageDisplayConfig = {
|
|
60
|
+
toolCallConfig: {
|
|
61
|
+
'c8y-read-documentation': {
|
|
62
|
+
executingLabel: gettext('Reading Cumulocity documentation'),
|
|
63
|
+
completedLabel: gettext('Read Cumulocity documentation')
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
};
|
|
51
67
|
|
|
52
68
|
messages: AIMessage[] = [];
|
|
53
69
|
displayMessages: AIMessage[] = [];
|
|
@@ -62,7 +78,43 @@ export class ChatExampleComponent {
|
|
|
62
78
|
setTimeout(() => {
|
|
63
79
|
// Simulate a delay for the response
|
|
64
80
|
this.messages.push({
|
|
65
|
-
content: '
|
|
81
|
+
content: "I've finished implementing your **amazing idea**.",
|
|
82
|
+
steps: [
|
|
83
|
+
{
|
|
84
|
+
type: 'text',
|
|
85
|
+
text: '**Great idea**, I can definitely help you with that!\n\nFirst let me use some tools to gather the information I need.',
|
|
86
|
+
reasoning:
|
|
87
|
+
"The user asked me a question but I need more information. I see, I have some tools available that can help! \n\nNow I'll initiate a call to help solve this problem.",
|
|
88
|
+
toolResults: [
|
|
89
|
+
{
|
|
90
|
+
toolName: 'c8y-read-documentation',
|
|
91
|
+
toolCallId: 'call1',
|
|
92
|
+
type: 'tool-result',
|
|
93
|
+
input: { query: 'Example query' },
|
|
94
|
+
output: '<html>\n<h1>\n C8Y doc sample\n</h1>\n</html>'
|
|
95
|
+
},
|
|
96
|
+
{
|
|
97
|
+
toolName: 'c8y-tool-with-json-output',
|
|
98
|
+
toolCallId: 'call3',
|
|
99
|
+
type: 'tool-result',
|
|
100
|
+
input: {},
|
|
101
|
+
output: { 'Key 1': 123, 'Key 2': 'Value 2' }
|
|
102
|
+
},
|
|
103
|
+
{
|
|
104
|
+
toolName: 'c8y-tool-with-error',
|
|
105
|
+
toolCallId: 'call4',
|
|
106
|
+
type: 'tool-result',
|
|
107
|
+
input: { url: 'something invalid' },
|
|
108
|
+
output: 'This is an error message\nSomething bad happened',
|
|
109
|
+
error: true
|
|
110
|
+
}
|
|
111
|
+
]
|
|
112
|
+
},
|
|
113
|
+
{
|
|
114
|
+
type: 'text',
|
|
115
|
+
text: 'Your **excellent idea** has now been implemented!\n\nWhat would you like me to help with next?'
|
|
116
|
+
}
|
|
117
|
+
],
|
|
66
118
|
role: 'assistant',
|
|
67
119
|
timestamp: new Date().toISOString()
|
|
68
120
|
});
|