@sftech/ng-orchestrator 1.0.0 → 1.0.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/README.md +89 -3
- package/fesm2022/sftech-ng-orchestrator-agent-rag-upload.component-COPkzHNN.mjs +188 -0
- package/fesm2022/sftech-ng-orchestrator-agent-rag-upload.component-COPkzHNN.mjs.map +1 -0
- package/fesm2022/sftech-ng-orchestrator-agents.routes-DH_0cd5T.mjs +284 -0
- package/fesm2022/sftech-ng-orchestrator-agents.routes-DH_0cd5T.mjs.map +1 -0
- package/fesm2022/sftech-ng-orchestrator-chats.routes-CqFuG3l5.mjs +743 -0
- package/fesm2022/sftech-ng-orchestrator-chats.routes-CqFuG3l5.mjs.map +1 -0
- package/fesm2022/{sftech-ng-orchestrator-prompt-display.component--3QaYOkH.mjs → sftech-ng-orchestrator-prompt-display.component-CXGtE_Ui.mjs} +4 -4
- package/fesm2022/{sftech-ng-orchestrator-prompt-display.component--3QaYOkH.mjs.map → sftech-ng-orchestrator-prompt-display.component-CXGtE_Ui.mjs.map} +1 -1
- package/fesm2022/sftech-ng-orchestrator-prompts.routes-CvMtYkTK.mjs +51 -0
- package/fesm2022/sftech-ng-orchestrator-prompts.routes-CvMtYkTK.mjs.map +1 -0
- package/fesm2022/{sftech-ng-orchestrator-sftech-ng-orchestrator-BdFSlSN_.mjs → sftech-ng-orchestrator-sftech-ng-orchestrator-C51KniGY.mjs} +7 -7
- package/fesm2022/sftech-ng-orchestrator-sftech-ng-orchestrator-C51KniGY.mjs.map +1 -0
- package/fesm2022/sftech-ng-orchestrator.mjs +1 -1
- package/package.json +7 -4
- package/fesm2022/sftech-ng-orchestrator-agent-rag-upload.component-DGMkaMBy.mjs +0 -188
- package/fesm2022/sftech-ng-orchestrator-agent-rag-upload.component-DGMkaMBy.mjs.map +0 -1
- package/fesm2022/sftech-ng-orchestrator-agents.routes-DWOr4y4m.mjs +0 -284
- package/fesm2022/sftech-ng-orchestrator-agents.routes-DWOr4y4m.mjs.map +0 -1
- package/fesm2022/sftech-ng-orchestrator-chats.routes-BRjYTnIk.mjs +0 -743
- package/fesm2022/sftech-ng-orchestrator-chats.routes-BRjYTnIk.mjs.map +0 -1
- package/fesm2022/sftech-ng-orchestrator-prompts.routes-kVoXLKCC.mjs +0 -51
- package/fesm2022/sftech-ng-orchestrator-prompts.routes-kVoXLKCC.mjs.map +0 -1
- package/fesm2022/sftech-ng-orchestrator-sftech-ng-orchestrator-BdFSlSN_.mjs.map +0 -1
|
@@ -0,0 +1,743 @@
|
|
|
1
|
+
import { authenticationGuard } from '@sftech/ng-auth';
|
|
2
|
+
import { NgClass, DatePipe } from '@angular/common';
|
|
3
|
+
import * as i0 from '@angular/core';
|
|
4
|
+
import { Inject, Injectable, input, EventEmitter, inject, effect, SecurityContext, Output, Component, signal, computed, ViewChild } from '@angular/core';
|
|
5
|
+
import { ActivatedRoute, Router, RouterLink, RouterOutlet } from '@angular/router';
|
|
6
|
+
import { FaIconComponent } from '@fortawesome/angular-fontawesome';
|
|
7
|
+
import { BaseDbApiService, IconProvider, MappedApiError, FormErrorDisplayComponent, OData, OdataFilter, EFilterOperator, EFilterTypes, ESortDirection, BaseListComponent } from '@sftech/ng-shared';
|
|
8
|
+
import { MessageService } from 'primeng/api';
|
|
9
|
+
import { ButtonDirective } from 'primeng/button';
|
|
10
|
+
import { DialogService } from 'primeng/dynamicdialog';
|
|
11
|
+
import { Tooltip } from 'primeng/tooltip';
|
|
12
|
+
import { map, catchError } from 'rxjs';
|
|
13
|
+
import { A as AgentService, a as AgentRagUploadComponent } from './sftech-ng-orchestrator-agent-rag-upload.component-COPkzHNN.mjs';
|
|
14
|
+
import { A as AgentRun, O as ORCHESTRATOR_CONFIGURATION, C as Chat, E as EAgentRunStatus, a as OrchestratorService, b as ChatMessage } from './sftech-ng-orchestrator-sftech-ng-orchestrator-C51KniGY.mjs';
|
|
15
|
+
import * as i1 from '@angular/common/http';
|
|
16
|
+
import { faNoteSticky, faCog, faCheck, faXmark, faSpinner, faClipboardCheck, faCircleQuestion, faBrain, faUser, faNetworkWired } from '@fortawesome/free-solid-svg-icons';
|
|
17
|
+
import * as i1$1 from '@angular/forms';
|
|
18
|
+
import { FormGroup, FormControl, Validators, ReactiveFormsModule, FormsModule } from '@angular/forms';
|
|
19
|
+
import { DomSanitizer } from '@angular/platform-browser';
|
|
20
|
+
import { AgentRunIconTextDataprovider as AgentRunIconTextDataprovider$1 } from 'libs/ng-orchestrator/src/lib/ng-orchestrator/chats/core/dataprovider/agent-run-icon-text.dataprovider';
|
|
21
|
+
import { FloatLabel } from 'primeng/floatlabel';
|
|
22
|
+
import { InputText } from 'primeng/inputtext';
|
|
23
|
+
import { MarkdownComponent } from 'ngx-markdown';
|
|
24
|
+
|
|
25
|
+
class AgentRunService extends BaseDbApiService {
|
|
26
|
+
http;
|
|
27
|
+
config;
|
|
28
|
+
constructor(http, config) {
|
|
29
|
+
super(http, config);
|
|
30
|
+
this.http = http;
|
|
31
|
+
this.config = config;
|
|
32
|
+
this.url = `${this.config.orchestratorDbUrl}/agent-runs`;
|
|
33
|
+
}
|
|
34
|
+
// biome-ignore lint/correctness/noUnusedVariables: <explanation>
|
|
35
|
+
insert(dto) {
|
|
36
|
+
throw new Error('Method not implemented');
|
|
37
|
+
}
|
|
38
|
+
// biome-ignore lint/correctness/noUnusedVariables: <explanation>
|
|
39
|
+
update(id, dto) {
|
|
40
|
+
throw new Error('Method not implemented');
|
|
41
|
+
}
|
|
42
|
+
// biome-ignore lint/correctness/noUnusedVariables: <explanation>
|
|
43
|
+
delete(id) {
|
|
44
|
+
throw new Error('Method not implemented');
|
|
45
|
+
}
|
|
46
|
+
getNewModel() {
|
|
47
|
+
return new AgentRun();
|
|
48
|
+
}
|
|
49
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.9", ngImport: i0, type: AgentRunService, deps: [{ token: i1.HttpClient }, { token: ORCHESTRATOR_CONFIGURATION }], target: i0.ɵɵFactoryTarget.Injectable });
|
|
50
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.9", ngImport: i0, type: AgentRunService, providedIn: 'root' });
|
|
51
|
+
}
|
|
52
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.9", ngImport: i0, type: AgentRunService, decorators: [{
|
|
53
|
+
type: Injectable,
|
|
54
|
+
args: [{ providedIn: 'root' }]
|
|
55
|
+
}], ctorParameters: () => [{ type: i1.HttpClient }, { type: undefined, decorators: [{
|
|
56
|
+
type: Inject,
|
|
57
|
+
args: [ORCHESTRATOR_CONFIGURATION]
|
|
58
|
+
}] }] });
|
|
59
|
+
|
|
60
|
+
class ChatService extends BaseDbApiService {
|
|
61
|
+
http;
|
|
62
|
+
config;
|
|
63
|
+
constructor(http, config) {
|
|
64
|
+
super(http, config);
|
|
65
|
+
this.http = http;
|
|
66
|
+
this.config = config;
|
|
67
|
+
this.url = `${this.config.orchestratorDbUrl}/chats`;
|
|
68
|
+
}
|
|
69
|
+
odata(odataQuery) {
|
|
70
|
+
return this.http.get(`${this.url}/${odataQuery.toGetParameter()}`).pipe(map((response) => this.responseOdataMapping(response)), catchError((error) => {
|
|
71
|
+
return this.httpErrorToApiResponse(error);
|
|
72
|
+
}));
|
|
73
|
+
}
|
|
74
|
+
insert(dto) {
|
|
75
|
+
return this.http.post(this.url, dto).pipe(map((response) => this.responseMapping(response)), catchError((error) => {
|
|
76
|
+
return this.httpErrorToApiResponse(error);
|
|
77
|
+
}));
|
|
78
|
+
}
|
|
79
|
+
// biome-ignore lint/correctness/noUnusedVariables: <explanation>
|
|
80
|
+
update(id, dto) {
|
|
81
|
+
throw new Error('update of a chat is not allowed');
|
|
82
|
+
}
|
|
83
|
+
getNewModel() {
|
|
84
|
+
return new Chat();
|
|
85
|
+
}
|
|
86
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.9", ngImport: i0, type: ChatService, deps: [{ token: i1.HttpClient }, { token: ORCHESTRATOR_CONFIGURATION }], target: i0.ɵɵFactoryTarget.Injectable });
|
|
87
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.9", ngImport: i0, type: ChatService, providedIn: 'root' });
|
|
88
|
+
}
|
|
89
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.9", ngImport: i0, type: ChatService, decorators: [{
|
|
90
|
+
type: Injectable,
|
|
91
|
+
args: [{ providedIn: 'root' }]
|
|
92
|
+
}], ctorParameters: () => [{ type: i1.HttpClient }, { type: undefined, decorators: [{
|
|
93
|
+
type: Inject,
|
|
94
|
+
args: [ORCHESTRATOR_CONFIGURATION]
|
|
95
|
+
}] }] });
|
|
96
|
+
|
|
97
|
+
class OrchestratorIconProvider extends IconProvider {
|
|
98
|
+
static agentRunStatusInit = faNoteSticky;
|
|
99
|
+
static agentRunStatusRunning = faCog;
|
|
100
|
+
static agentRunStatusCompleted = faCheck;
|
|
101
|
+
static agentRunStatusFailed = faXmark;
|
|
102
|
+
static agentRunStatusPrerun = faSpinner;
|
|
103
|
+
static agentRunStatusPostrun = faClipboardCheck;
|
|
104
|
+
static agentRunStatusClarify = faCircleQuestion;
|
|
105
|
+
static messageTypeAI = faBrain;
|
|
106
|
+
static messageTypeHuman = faUser;
|
|
107
|
+
static messageTypeSystem = faNetworkWired;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// biome-ignore lint/complexity/noStaticOnlyClass: <explanation>
|
|
111
|
+
class AgentRunIconTextDataprovider {
|
|
112
|
+
static UNKNOWN_TEXT = 'Unbekannt';
|
|
113
|
+
static getTextForStatus(status) {
|
|
114
|
+
if (!status) {
|
|
115
|
+
return AgentRunIconTextDataprovider.UNKNOWN_TEXT;
|
|
116
|
+
}
|
|
117
|
+
switch (status) {
|
|
118
|
+
case EAgentRunStatus.INIT:
|
|
119
|
+
return 'Anlage';
|
|
120
|
+
case EAgentRunStatus.RUNNING:
|
|
121
|
+
return 'In Bearbeitung';
|
|
122
|
+
case EAgentRunStatus.COMPLETED:
|
|
123
|
+
return 'Abgeschlossen';
|
|
124
|
+
case EAgentRunStatus.FAILED:
|
|
125
|
+
return 'Fehlgeschlagen';
|
|
126
|
+
case EAgentRunStatus.CLARIFICATION_NEEDED:
|
|
127
|
+
return 'Rückfrage';
|
|
128
|
+
case EAgentRunStatus.PRERUNNING:
|
|
129
|
+
return 'Vorbereitung';
|
|
130
|
+
case EAgentRunStatus.POSTRUNNING:
|
|
131
|
+
return 'Nachbereitung';
|
|
132
|
+
default:
|
|
133
|
+
return AgentRunIconTextDataprovider.UNKNOWN_TEXT;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
static getIconForStatus(status) {
|
|
137
|
+
if (!status) {
|
|
138
|
+
return IconProvider.unknown;
|
|
139
|
+
}
|
|
140
|
+
switch (status) {
|
|
141
|
+
case EAgentRunStatus.INIT:
|
|
142
|
+
return OrchestratorIconProvider.agentRunStatusInit;
|
|
143
|
+
case EAgentRunStatus.RUNNING:
|
|
144
|
+
return OrchestratorIconProvider.agentRunStatusRunning;
|
|
145
|
+
case EAgentRunStatus.COMPLETED:
|
|
146
|
+
return OrchestratorIconProvider.agentRunStatusCompleted;
|
|
147
|
+
case EAgentRunStatus.FAILED:
|
|
148
|
+
return OrchestratorIconProvider.agentRunStatusFailed;
|
|
149
|
+
case EAgentRunStatus.CLARIFICATION_NEEDED:
|
|
150
|
+
return OrchestratorIconProvider.agentRunStatusClarify;
|
|
151
|
+
case EAgentRunStatus.PRERUNNING:
|
|
152
|
+
return OrchestratorIconProvider.agentRunStatusPrerun;
|
|
153
|
+
case EAgentRunStatus.POSTRUNNING:
|
|
154
|
+
return OrchestratorIconProvider.agentRunStatusPostrun;
|
|
155
|
+
default:
|
|
156
|
+
return IconProvider.unknown;
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
static getIconClassesForStatus(status) {
|
|
160
|
+
if (!status) {
|
|
161
|
+
return AgentRunIconTextDataprovider.UNKNOWN_TEXT;
|
|
162
|
+
}
|
|
163
|
+
switch (status) {
|
|
164
|
+
case EAgentRunStatus.INIT:
|
|
165
|
+
return '';
|
|
166
|
+
case EAgentRunStatus.RUNNING:
|
|
167
|
+
return 'text-primary-600';
|
|
168
|
+
case EAgentRunStatus.COMPLETED:
|
|
169
|
+
return 'text-green-600';
|
|
170
|
+
case EAgentRunStatus.FAILED:
|
|
171
|
+
return 'text-red-600';
|
|
172
|
+
case EAgentRunStatus.CLARIFICATION_NEEDED:
|
|
173
|
+
return '';
|
|
174
|
+
case EAgentRunStatus.PRERUNNING:
|
|
175
|
+
return '';
|
|
176
|
+
case EAgentRunStatus.POSTRUNNING:
|
|
177
|
+
return '';
|
|
178
|
+
default:
|
|
179
|
+
return '';
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
static getIconForMessageType(messageType) {
|
|
183
|
+
switch (messageType) {
|
|
184
|
+
case 'system':
|
|
185
|
+
return OrchestratorIconProvider.messageTypeSystem;
|
|
186
|
+
case 'human':
|
|
187
|
+
return OrchestratorIconProvider.messageTypeHuman;
|
|
188
|
+
case 'ai':
|
|
189
|
+
return OrchestratorIconProvider.messageTypeAI;
|
|
190
|
+
default:
|
|
191
|
+
return IconProvider.unknown;
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
class AgentChatMessageHumanInputInitialComponent {
|
|
197
|
+
agent = input(...(ngDevMode ? [undefined, { debugName: "agent" }] : []));
|
|
198
|
+
agentRunIdReceived = new EventEmitter();
|
|
199
|
+
messageService = inject(MessageService);
|
|
200
|
+
orchestratorService = inject(OrchestratorService);
|
|
201
|
+
sanitizer = inject(DomSanitizer);
|
|
202
|
+
form = new FormGroup({});
|
|
203
|
+
isSubmitting = false;
|
|
204
|
+
iconTextMapper = AgentRunIconTextDataprovider$1;
|
|
205
|
+
iconProvider = IconProvider;
|
|
206
|
+
_agentChanged$ = effect(() => {
|
|
207
|
+
this.agent()
|
|
208
|
+
.llmUserPrompt?.template.match(/{{(.*?)}}/g)
|
|
209
|
+
?.forEach((m) => {
|
|
210
|
+
const key = m.slice(2, -2);
|
|
211
|
+
this.form.addControl(key, new FormControl('', [Validators.required]));
|
|
212
|
+
});
|
|
213
|
+
}, ...(ngDevMode ? [{ debugName: "_agentChanged$" }] : []));
|
|
214
|
+
run() {
|
|
215
|
+
if (this.form.invalid || this.isSubmitting) {
|
|
216
|
+
// Mark all fields as touched to show validation errors
|
|
217
|
+
Object.keys(this.form.controls).forEach((key) => {
|
|
218
|
+
this.form.get(key)?.markAsTouched();
|
|
219
|
+
});
|
|
220
|
+
return;
|
|
221
|
+
}
|
|
222
|
+
this.isSubmitting = true;
|
|
223
|
+
this.orchestratorService
|
|
224
|
+
.runAgent({
|
|
225
|
+
agentIdentifier: this.agent().identifier,
|
|
226
|
+
userInput: this.form.value,
|
|
227
|
+
})
|
|
228
|
+
.subscribe({
|
|
229
|
+
next: (res) => {
|
|
230
|
+
if (res instanceof MappedApiError) {
|
|
231
|
+
this.messageService.add({
|
|
232
|
+
severity: 'error',
|
|
233
|
+
summary: 'Fehler',
|
|
234
|
+
detail: res.messages.join(' | '),
|
|
235
|
+
});
|
|
236
|
+
this.isSubmitting = false;
|
|
237
|
+
return;
|
|
238
|
+
}
|
|
239
|
+
this.agentRunIdReceived.emit(res);
|
|
240
|
+
this.isSubmitting = false;
|
|
241
|
+
},
|
|
242
|
+
error: () => {
|
|
243
|
+
this.isSubmitting = false;
|
|
244
|
+
},
|
|
245
|
+
});
|
|
246
|
+
}
|
|
247
|
+
getInputFormControlNames() {
|
|
248
|
+
return Object.keys(this.form.controls);
|
|
249
|
+
}
|
|
250
|
+
formatControlName(name) {
|
|
251
|
+
// Convert camelCase or snake_case to readable format
|
|
252
|
+
return name
|
|
253
|
+
.replace(/([A-Z])/g, ' $1')
|
|
254
|
+
.replace(/_/g, ' ')
|
|
255
|
+
.trim()
|
|
256
|
+
.split(' ')
|
|
257
|
+
.map((word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase())
|
|
258
|
+
.join(' ');
|
|
259
|
+
}
|
|
260
|
+
getFormattedTemplate() {
|
|
261
|
+
const template = this.agent().llmUserPrompt?.template || '';
|
|
262
|
+
// First sanitize the template to remove any malicious content
|
|
263
|
+
const sanitizedTemplate = this.sanitizer.sanitize(SecurityContext.HTML, template) || '';
|
|
264
|
+
// Replace {{variable}} with highlighted spans (variables are now safe after sanitization)
|
|
265
|
+
const formatted = sanitizedTemplate.replace(/\{\{(.*?)\}\}/g, '<span class="inline-flex items-center px-2 py-1 mx-1 bg-primary-100 text-primary-700 rounded-md font-semibold text-sm border border-primary-200">$1</span>');
|
|
266
|
+
return this.sanitizer.sanitize(SecurityContext.HTML, formatted);
|
|
267
|
+
}
|
|
268
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.9", ngImport: i0, type: AgentChatMessageHumanInputInitialComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
269
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.9", type: AgentChatMessageHumanInputInitialComponent, isStandalone: true, selector: "sftech-agent-chat-message-human-input-initial", inputs: { agent: { classPropertyName: "agent", publicName: "agent", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { agentRunIdReceived: "agentRunIdReceived" }, ngImport: i0, template: "<div class=\"my-4 max-w-4xl mx-auto\">\n <!-- Welcome Card -->\n <div class=\"bg-gradient-to-br from-primary-50 to-white rounded-2xl shadow-lg border-2 border-primary-200 p-6 mb-4\">\n <div class=\"flex items-start gap-4\">\n <div class=\"flex-shrink-0\">\n <div class=\"w-12 h-12 rounded-xl bg-gradient-to-br from-primary-500 to-primary-600 flex items-center justify-center text-white shadow-md\">\n <fa-icon [icon]=\"iconTextMapper.getIconForMessageType('human')\" size=\"lg\"></fa-icon>\n </div>\n </div>\n <div class=\"flex-grow\">\n <h3 class=\"text-xl font-bold text-gray-900 mb-2 flex items-center gap-2\">\n <fa-icon [icon]=\"iconProvider.sparkles\" class=\"text-primary-600\"></fa-icon>\n Chat starten mit {{ agent()!.name }}\n </h3>\n <p class=\"text-gray-600 text-sm mb-4\">\n F\u00FClle die Felder aus, um die Konversation zu beginnen. Der Agent wird basierend auf deinen Eingaben antworten.\n </p>\n\n <!-- Template Display -->\n @if (agent()!.llmUserPrompt?.template) {\n <div class=\"bg-white rounded-lg p-4 border border-primary-100 shadow-sm\">\n <div class=\"flex items-center gap-2 mb-2\">\n <fa-icon [icon]=\"iconProvider.message\" class=\"text-primary-600 text-sm\"></fa-icon>\n <span class=\"text-xs font-semibold text-primary-700 uppercase tracking-wide\">Prompt Template</span>\n </div>\n <div class=\"text-gray-700 leading-relaxed\" [innerHTML]=\"getFormattedTemplate()\"></div>\n </div>\n }\n </div>\n </div>\n </div>\n\n <!-- Input Form Card -->\n <div class=\"bg-white rounded-2xl shadow-lg border border-gray-200 p-6\">\n <form [formGroup]=\"form!\" (ngSubmit)=\"run()\">\n <!-- Input Fields -->\n @if (getInputFormControlNames().length > 0) {\n <div class=\"mb-6\">\n <div class=\"flex items-center gap-2 mb-4\">\n <fa-icon [icon]=\"iconProvider.documentAdd\" class=\"text-gray-600\"></fa-icon>\n <h4 class=\"font-semibold text-gray-900\">Erforderliche Informationen</h4>\n </div>\n <div class=\"space-y-4\">\n @for (controlName of getInputFormControlNames(); track controlName) {\n <div class=\"relative\">\n <p-floatlabel variant=\"in\" class=\"w-full\">\n <input\n type=\"text\"\n id=\"{{ controlName }}\"\n formControlName=\"{{ controlName }}\"\n pInputText\n [class.border-red-500]=\"form!.get(controlName)?.invalid && form!.get(controlName)?.touched\"\n class=\"w-full px-4 py-3 border-2 border-gray-300 rounded-xl focus:outline-none focus:ring-2 focus:ring-primary-600 focus:border-primary-600 transition-all bg-white\"\n [tabindex]=\"getInputFormControlNames().indexOf(controlName) + 1\"\n />\n <label for=\"{{ controlName }}\" class=\"text-gray-600\">\n {{ formatControlName(controlName) }}\n </label>\n </p-floatlabel>\n <sftech-form-error-display\n [control]=\"form!.get(controlName)!\"\n [label]=\"formatControlName(controlName)\"\n ></sftech-form-error-display>\n </div>\n }\n </div>\n </div>\n }\n\n <!-- Submit Button -->\n <div class=\"flex items-center justify-between pt-4 border-t border-gray-100\">\n <div class=\"text-sm text-gray-500\">\n <fa-icon [icon]=\"iconProvider.sparkles\" class=\"mr-1\"></fa-icon>\n @if (!isSubmitting) {\n <span>Bereit zum Senden</span>\n }\n @if (isSubmitting) {\n <span>Wird verarbeitet...</span>\n }\n </div>\n <button\n type=\"submit\"\n [disabled]=\"form!.invalid || isSubmitting\"\n class=\"px-6 py-3 bg-gradient-to-r from-primary-600 to-primary-700 text-white font-semibold rounded-xl shadow-md hover:shadow-lg hover:from-primary-700 hover:to-primary-800 disabled:opacity-50 disabled:cursor-not-allowed transition-all duration-200 flex items-center gap-2\"\n [tabindex]=\"getInputFormControlNames().length + 1\"\n >\n @if (isSubmitting) {\n <fa-icon [icon]=\"iconProvider.loading\" [animation]=\"'spin'\"></fa-icon>\n <span>Wird gesendet...</span>\n } @else {\n <fa-icon [icon]=\"iconProvider.sparkles\"></fa-icon>\n <span>Chat starten</span>\n }\n </button>\n </div>\n </form>\n </div>\n </div>", styles: [""], dependencies: [{ kind: "component", type: FaIconComponent, selector: "fa-icon", inputs: ["icon", "title", "animation", "mask", "flip", "size", "pull", "border", "inverse", "symbol", "rotate", "fixedWidth", "transform", "a11yRole"], outputs: ["iconChange", "titleChange", "animationChange", "maskChange", "flipChange", "sizeChange", "pullChange", "borderChange", "inverseChange", "symbolChange", "rotateChange", "fixedWidthChange", "transformChange", "a11yRoleChange"] }, { kind: "component", type: FloatLabel, selector: "p-floatlabel, p-floatLabel, p-float-label", inputs: ["variant"] }, { kind: "component", type: FormErrorDisplayComponent, selector: "sftech-form-error-display", inputs: ["control", "label"] }, { kind: "directive", type: InputText, selector: "[pInputText]", inputs: ["hostName", "ptInputText", "pSize", "variant", "fluid", "invalid"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1$1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { 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.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1$1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1$1.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }] });
|
|
270
|
+
}
|
|
271
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.9", ngImport: i0, type: AgentChatMessageHumanInputInitialComponent, decorators: [{
|
|
272
|
+
type: Component,
|
|
273
|
+
args: [{ selector: 'sftech-agent-chat-message-human-input-initial', imports: [FaIconComponent, FloatLabel, FormErrorDisplayComponent, InputText, ReactiveFormsModule], template: "<div class=\"my-4 max-w-4xl mx-auto\">\n <!-- Welcome Card -->\n <div class=\"bg-gradient-to-br from-primary-50 to-white rounded-2xl shadow-lg border-2 border-primary-200 p-6 mb-4\">\n <div class=\"flex items-start gap-4\">\n <div class=\"flex-shrink-0\">\n <div class=\"w-12 h-12 rounded-xl bg-gradient-to-br from-primary-500 to-primary-600 flex items-center justify-center text-white shadow-md\">\n <fa-icon [icon]=\"iconTextMapper.getIconForMessageType('human')\" size=\"lg\"></fa-icon>\n </div>\n </div>\n <div class=\"flex-grow\">\n <h3 class=\"text-xl font-bold text-gray-900 mb-2 flex items-center gap-2\">\n <fa-icon [icon]=\"iconProvider.sparkles\" class=\"text-primary-600\"></fa-icon>\n Chat starten mit {{ agent()!.name }}\n </h3>\n <p class=\"text-gray-600 text-sm mb-4\">\n F\u00FClle die Felder aus, um die Konversation zu beginnen. Der Agent wird basierend auf deinen Eingaben antworten.\n </p>\n\n <!-- Template Display -->\n @if (agent()!.llmUserPrompt?.template) {\n <div class=\"bg-white rounded-lg p-4 border border-primary-100 shadow-sm\">\n <div class=\"flex items-center gap-2 mb-2\">\n <fa-icon [icon]=\"iconProvider.message\" class=\"text-primary-600 text-sm\"></fa-icon>\n <span class=\"text-xs font-semibold text-primary-700 uppercase tracking-wide\">Prompt Template</span>\n </div>\n <div class=\"text-gray-700 leading-relaxed\" [innerHTML]=\"getFormattedTemplate()\"></div>\n </div>\n }\n </div>\n </div>\n </div>\n\n <!-- Input Form Card -->\n <div class=\"bg-white rounded-2xl shadow-lg border border-gray-200 p-6\">\n <form [formGroup]=\"form!\" (ngSubmit)=\"run()\">\n <!-- Input Fields -->\n @if (getInputFormControlNames().length > 0) {\n <div class=\"mb-6\">\n <div class=\"flex items-center gap-2 mb-4\">\n <fa-icon [icon]=\"iconProvider.documentAdd\" class=\"text-gray-600\"></fa-icon>\n <h4 class=\"font-semibold text-gray-900\">Erforderliche Informationen</h4>\n </div>\n <div class=\"space-y-4\">\n @for (controlName of getInputFormControlNames(); track controlName) {\n <div class=\"relative\">\n <p-floatlabel variant=\"in\" class=\"w-full\">\n <input\n type=\"text\"\n id=\"{{ controlName }}\"\n formControlName=\"{{ controlName }}\"\n pInputText\n [class.border-red-500]=\"form!.get(controlName)?.invalid && form!.get(controlName)?.touched\"\n class=\"w-full px-4 py-3 border-2 border-gray-300 rounded-xl focus:outline-none focus:ring-2 focus:ring-primary-600 focus:border-primary-600 transition-all bg-white\"\n [tabindex]=\"getInputFormControlNames().indexOf(controlName) + 1\"\n />\n <label for=\"{{ controlName }}\" class=\"text-gray-600\">\n {{ formatControlName(controlName) }}\n </label>\n </p-floatlabel>\n <sftech-form-error-display\n [control]=\"form!.get(controlName)!\"\n [label]=\"formatControlName(controlName)\"\n ></sftech-form-error-display>\n </div>\n }\n </div>\n </div>\n }\n\n <!-- Submit Button -->\n <div class=\"flex items-center justify-between pt-4 border-t border-gray-100\">\n <div class=\"text-sm text-gray-500\">\n <fa-icon [icon]=\"iconProvider.sparkles\" class=\"mr-1\"></fa-icon>\n @if (!isSubmitting) {\n <span>Bereit zum Senden</span>\n }\n @if (isSubmitting) {\n <span>Wird verarbeitet...</span>\n }\n </div>\n <button\n type=\"submit\"\n [disabled]=\"form!.invalid || isSubmitting\"\n class=\"px-6 py-3 bg-gradient-to-r from-primary-600 to-primary-700 text-white font-semibold rounded-xl shadow-md hover:shadow-lg hover:from-primary-700 hover:to-primary-800 disabled:opacity-50 disabled:cursor-not-allowed transition-all duration-200 flex items-center gap-2\"\n [tabindex]=\"getInputFormControlNames().length + 1\"\n >\n @if (isSubmitting) {\n <fa-icon [icon]=\"iconProvider.loading\" [animation]=\"'spin'\"></fa-icon>\n <span>Wird gesendet...</span>\n } @else {\n <fa-icon [icon]=\"iconProvider.sparkles\"></fa-icon>\n <span>Chat starten</span>\n }\n </button>\n </div>\n </form>\n </div>\n </div>" }]
|
|
274
|
+
}], propDecorators: { agent: [{ type: i0.Input, args: [{ isSignal: true, alias: "agent", required: false }] }], agentRunIdReceived: [{
|
|
275
|
+
type: Output
|
|
276
|
+
}] } });
|
|
277
|
+
|
|
278
|
+
class AgentChatMessageHumanInputComponent {
|
|
279
|
+
agent = input(...(ngDevMode ? [undefined, { debugName: "agent" }] : []));
|
|
280
|
+
agentRunId = input(...(ngDevMode ? [undefined, { debugName: "agentRunId" }] : []));
|
|
281
|
+
agentRunIdReceived = new EventEmitter();
|
|
282
|
+
messageService = inject(MessageService);
|
|
283
|
+
orchestratorService = inject(OrchestratorService);
|
|
284
|
+
form = new FormGroup({
|
|
285
|
+
message: new FormControl('', [Validators.required]),
|
|
286
|
+
});
|
|
287
|
+
isSubmitting = false;
|
|
288
|
+
iconTextMapper = AgentRunIconTextDataprovider$1;
|
|
289
|
+
iconProvider = IconProvider;
|
|
290
|
+
run() {
|
|
291
|
+
if (this.form.invalid || this.isSubmitting) {
|
|
292
|
+
this.form.get('message')?.markAsTouched();
|
|
293
|
+
return;
|
|
294
|
+
}
|
|
295
|
+
this.isSubmitting = true;
|
|
296
|
+
this.form.get('message')?.disable();
|
|
297
|
+
const message = this.form.value.message;
|
|
298
|
+
this.orchestratorService.chatAgent(this.agentRunId(), message).subscribe({
|
|
299
|
+
next: (res) => {
|
|
300
|
+
if (res instanceof MappedApiError) {
|
|
301
|
+
this.messageService.add({ severity: 'error', summary: 'Fehler', detail: res.messages.join('|') });
|
|
302
|
+
this.form.get('message')?.enable();
|
|
303
|
+
this.isSubmitting = false;
|
|
304
|
+
return;
|
|
305
|
+
}
|
|
306
|
+
this.form.reset();
|
|
307
|
+
this.form.get('message')?.enable();
|
|
308
|
+
this.isSubmitting = false;
|
|
309
|
+
this.agentRunIdReceived.emit(res);
|
|
310
|
+
},
|
|
311
|
+
error: () => {
|
|
312
|
+
this.form.get('message')?.enable();
|
|
313
|
+
this.isSubmitting = false;
|
|
314
|
+
},
|
|
315
|
+
});
|
|
316
|
+
}
|
|
317
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.9", ngImport: i0, type: AgentChatMessageHumanInputComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
318
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.9", type: AgentChatMessageHumanInputComponent, isStandalone: true, selector: "sftech-agent-chat-message-human-input", inputs: { agent: { classPropertyName: "agent", publicName: "agent", isSignal: true, isRequired: false, transformFunction: null }, agentRunId: { classPropertyName: "agentRunId", publicName: "agentRunId", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { agentRunIdReceived: "agentRunIdReceived" }, ngImport: i0, template: "<div class=\"my-4 max-w-4xl mx-auto\">\n <div class=\"bg-white rounded-2xl shadow-lg border border-gray-200 p-6\">\n <form [formGroup]=\"form\" (ngSubmit)=\"run()\">\n <div class=\"flex gap-4 items-start\">\n <div class=\"flex-shrink-0\">\n <div class=\"w-12 h-12 rounded-xl bg-gradient-to-br from-primary-500 to-primary-600 flex items-center justify-center shadow-md\">\n <fa-icon [icon]=\"iconTextMapper.getIconForMessageType('human')\" size=\"lg\" class=\"text-white\"></fa-icon>\n </div>\n </div>\n <div class=\"flex-grow space-y-4\">\n <div class=\"relative\">\n <p-floatlabel variant=\"in\" class=\"w-full\">\n <input\n type=\"text\"\n id=\"message\"\n formControlName=\"message\"\n pInputText\n [class.border-red-500]=\"form.get('message')?.invalid && form.get('message')?.touched\"\n class=\"w-full px-4 py-3 border-2 border-gray-300 rounded-xl focus:outline-none focus:ring-2 focus:ring-primary-600 focus:border-primary-600 transition-all bg-white\"\n />\n <label for=\"message\" class=\"text-gray-600\">Deine Nachricht</label>\n </p-floatlabel>\n <sftech-form-error-display\n [control]=\"form.get('message')!\"\n label=\"Nachricht\"\n ></sftech-form-error-display>\n </div>\n <div class=\"flex items-center justify-between\">\n <div class=\"text-sm text-gray-500\">\n <fa-icon [icon]=\"iconProvider.message\" class=\"mr-1\"></fa-icon>\n @if (!isSubmitting) {\n <span>Bereit zum Senden</span>\n } @else {\n <span>Wird verarbeitet...</span>\n }\n </div>\n <button\n type=\"submit\"\n [disabled]=\"form.invalid || isSubmitting\"\n class=\"px-6 py-3 bg-gradient-to-r from-primary-600 to-primary-700 text-white font-semibold rounded-xl shadow-md hover:shadow-lg hover:from-primary-700 hover:to-primary-800 disabled:opacity-50 disabled:cursor-not-allowed transition-all duration-200 flex items-center gap-2\"\n >\n @if (isSubmitting) {\n <fa-icon [icon]=\"iconProvider.loading\" [animation]=\"'spin'\"></fa-icon>\n <span>Wird gesendet...</span>\n } @else {\n <fa-icon [icon]=\"iconProvider.arrowRight\"></fa-icon>\n <span>Senden</span>\n }\n </button>\n </div>\n </div>\n </div>\n </form>\n </div>\n</div>\n", styles: [""], dependencies: [{ kind: "component", type: FaIconComponent, selector: "fa-icon", inputs: ["icon", "title", "animation", "mask", "flip", "size", "pull", "border", "inverse", "symbol", "rotate", "fixedWidth", "transform", "a11yRole"], outputs: ["iconChange", "titleChange", "animationChange", "maskChange", "flipChange", "sizeChange", "pullChange", "borderChange", "inverseChange", "symbolChange", "rotateChange", "fixedWidthChange", "transformChange", "a11yRoleChange"] }, { kind: "component", type: FloatLabel, selector: "p-floatlabel, p-floatLabel, p-float-label", inputs: ["variant"] }, { kind: "component", type: FormErrorDisplayComponent, selector: "sftech-form-error-display", inputs: ["control", "label"] }, { kind: "directive", type: InputText, selector: "[pInputText]", inputs: ["hostName", "ptInputText", "pSize", "variant", "fluid", "invalid"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1$1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { 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.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1$1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1$1.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }] });
|
|
319
|
+
}
|
|
320
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.9", ngImport: i0, type: AgentChatMessageHumanInputComponent, decorators: [{
|
|
321
|
+
type: Component,
|
|
322
|
+
args: [{ selector: 'sftech-agent-chat-message-human-input', imports: [FaIconComponent, FloatLabel, FormErrorDisplayComponent, InputText, ReactiveFormsModule], template: "<div class=\"my-4 max-w-4xl mx-auto\">\n <div class=\"bg-white rounded-2xl shadow-lg border border-gray-200 p-6\">\n <form [formGroup]=\"form\" (ngSubmit)=\"run()\">\n <div class=\"flex gap-4 items-start\">\n <div class=\"flex-shrink-0\">\n <div class=\"w-12 h-12 rounded-xl bg-gradient-to-br from-primary-500 to-primary-600 flex items-center justify-center shadow-md\">\n <fa-icon [icon]=\"iconTextMapper.getIconForMessageType('human')\" size=\"lg\" class=\"text-white\"></fa-icon>\n </div>\n </div>\n <div class=\"flex-grow space-y-4\">\n <div class=\"relative\">\n <p-floatlabel variant=\"in\" class=\"w-full\">\n <input\n type=\"text\"\n id=\"message\"\n formControlName=\"message\"\n pInputText\n [class.border-red-500]=\"form.get('message')?.invalid && form.get('message')?.touched\"\n class=\"w-full px-4 py-3 border-2 border-gray-300 rounded-xl focus:outline-none focus:ring-2 focus:ring-primary-600 focus:border-primary-600 transition-all bg-white\"\n />\n <label for=\"message\" class=\"text-gray-600\">Deine Nachricht</label>\n </p-floatlabel>\n <sftech-form-error-display\n [control]=\"form.get('message')!\"\n label=\"Nachricht\"\n ></sftech-form-error-display>\n </div>\n <div class=\"flex items-center justify-between\">\n <div class=\"text-sm text-gray-500\">\n <fa-icon [icon]=\"iconProvider.message\" class=\"mr-1\"></fa-icon>\n @if (!isSubmitting) {\n <span>Bereit zum Senden</span>\n } @else {\n <span>Wird verarbeitet...</span>\n }\n </div>\n <button\n type=\"submit\"\n [disabled]=\"form.invalid || isSubmitting\"\n class=\"px-6 py-3 bg-gradient-to-r from-primary-600 to-primary-700 text-white font-semibold rounded-xl shadow-md hover:shadow-lg hover:from-primary-700 hover:to-primary-800 disabled:opacity-50 disabled:cursor-not-allowed transition-all duration-200 flex items-center gap-2\"\n >\n @if (isSubmitting) {\n <fa-icon [icon]=\"iconProvider.loading\" [animation]=\"'spin'\"></fa-icon>\n <span>Wird gesendet...</span>\n } @else {\n <fa-icon [icon]=\"iconProvider.arrowRight\"></fa-icon>\n <span>Senden</span>\n }\n </button>\n </div>\n </div>\n </div>\n </form>\n </div>\n</div>\n" }]
|
|
323
|
+
}], propDecorators: { agent: [{ type: i0.Input, args: [{ isSignal: true, alias: "agent", required: false }] }], agentRunId: [{ type: i0.Input, args: [{ isSignal: true, alias: "agentRunId", required: false }] }], agentRunIdReceived: [{
|
|
324
|
+
type: Output
|
|
325
|
+
}] } });
|
|
326
|
+
|
|
327
|
+
class AgentChatMessageComponent {
|
|
328
|
+
message = input('message', ...(ngDevMode ? [{ debugName: "message" }] : []));
|
|
329
|
+
type = input('type', ...(ngDevMode ? [{ debugName: "type" }] : []));
|
|
330
|
+
iconTextMapper = AgentRunIconTextDataprovider;
|
|
331
|
+
extractJson(message) {
|
|
332
|
+
const jsonMatch = message.match(/```json\s*([\s\S]*?)\s*```/);
|
|
333
|
+
if (jsonMatch && jsonMatch[1]) {
|
|
334
|
+
try {
|
|
335
|
+
const data = JSON.parse(jsonMatch[1]);
|
|
336
|
+
const md = this._toMarkdownFromJson(data);
|
|
337
|
+
return md;
|
|
338
|
+
}
|
|
339
|
+
catch {
|
|
340
|
+
return 'Invalid JSON';
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
return 'No JSON found';
|
|
344
|
+
}
|
|
345
|
+
_toMarkdownFromJson(obj) {
|
|
346
|
+
let md = '';
|
|
347
|
+
for (const key of Object.keys(obj)) {
|
|
348
|
+
const value = obj[key];
|
|
349
|
+
// Überschrift für jede Kategorie
|
|
350
|
+
md += `\n### ${key}\n\n`;
|
|
351
|
+
if (Array.isArray(value) && value.length && typeof value[0] === 'object') {
|
|
352
|
+
// Tabellen-Header
|
|
353
|
+
const headers = Object.keys(value[0]);
|
|
354
|
+
md += `| ${headers.join(' | ')} |\n`;
|
|
355
|
+
md += `| ${headers.map(() => '---').join(' | ')} |\n`;
|
|
356
|
+
// Tabellen-Zeilen
|
|
357
|
+
for (const row of value) {
|
|
358
|
+
md += `| ${headers.map((h) => row[h]).join(' | ')} |\n`;
|
|
359
|
+
}
|
|
360
|
+
md += '\n';
|
|
361
|
+
}
|
|
362
|
+
else if (typeof value === 'object') {
|
|
363
|
+
// Rekursiver Aufruf bei verschachtelten Objekten
|
|
364
|
+
md += this._toMarkdownFromJson(value);
|
|
365
|
+
}
|
|
366
|
+
else {
|
|
367
|
+
// Einfaches Feld
|
|
368
|
+
md += `- **${key}:** ${value}\n`;
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
return md;
|
|
372
|
+
}
|
|
373
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.9", ngImport: i0, type: AgentChatMessageComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
374
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.9", type: AgentChatMessageComponent, isStandalone: true, selector: "sftech-agent-chat-message", inputs: { message: { classPropertyName: "message", publicName: "message", isSignal: true, isRequired: false, transformFunction: null }, type: { classPropertyName: "type", publicName: "type", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: "@if (type() === 'ai_loading') {\n <div class=\"my-2 sm:my-4 max-w-4xl mx-auto\">\n <div class=\"bg-white rounded-xl sm:rounded-2xl shadow-lg border border-gray-200 p-4 sm:p-6\">\n <div class=\"flex items-start gap-3 sm:gap-4\">\n <div class=\"flex-shrink-0\">\n <div class=\"w-10 h-10 sm:w-12 sm:h-12 rounded-lg sm:rounded-xl bg-gradient-to-br from-green-500 to-green-600 flex items-center justify-center shadow-md\">\n <fa-icon [icon]=\"iconTextMapper.getIconForMessageType('ai')\" class=\"text-white text-sm sm:text-lg\"></fa-icon>\n </div>\n </div>\n <div class=\"flex items-center space-x-3 text-gray-600 mt-1 sm:mt-2\">\n <span class=\"text-xs sm:text-sm font-medium\">Die Antwort wird ermittelt...</span>\n <div class=\"flex space-x-1\">\n <div class=\"w-2 h-2 sm:w-2.5 sm:h-2.5 bg-green-600 rounded-full animate-bounceDelay\"></div>\n <div class=\"w-2 h-2 sm:w-2.5 sm:h-2.5 bg-green-600 rounded-full animate-bounceDelay [animation-delay:-0.32s]\"></div>\n <div class=\"w-2 h-2 sm:w-2.5 sm:h-2.5 bg-green-600 rounded-full animate-bounceDelay [animation-delay:-0.16s]\"></div>\n </div>\n </div>\n </div>\n </div>\n </div>\n}\n\n@else if (type() === 'ai_failed') {\n <div class=\"my-2 sm:my-4 max-w-4xl mx-auto\">\n <div class=\"bg-white rounded-xl sm:rounded-2xl shadow-lg border-2 border-red-300 p-4 sm:p-6\">\n <div class=\"flex items-start gap-3 sm:gap-4\">\n <div class=\"flex-shrink-0\">\n <div class=\"w-10 h-10 sm:w-12 sm:h-12 rounded-lg sm:rounded-xl bg-gradient-to-br from-red-500 to-red-600 flex items-center justify-center shadow-md\">\n <fa-icon [icon]=\"iconTextMapper.getIconForMessageType('ai')\" class=\"text-white text-sm sm:text-lg\"></fa-icon>\n </div>\n </div>\n <div class=\"flex-grow\">\n <div class=\"text-xs sm:text-sm text-red-700 font-medium\">\n {{ message() }}\n </div>\n </div>\n </div>\n </div>\n </div>\n}\n\n@else {\n <div class=\"my-2 sm:my-4 max-w-4xl mx-auto\">\n <div\n class=\"rounded-xl sm:rounded-2xl shadow-lg p-3 sm:p-6 flex gap-3 sm:gap-4 border-2\"\n [ngClass]=\"{\n 'bg-white border-gray-200': type() === 'ai',\n 'bg-primary-50 border-primary-200 flex-row-reverse': type() === 'human',\n 'bg-yellow-50 border-yellow-300': type() === 'system'\n }\"\n >\n <div class=\"flex-shrink-0\">\n <div\n class=\"w-10 h-10 sm:w-12 sm:h-12 rounded-lg sm:rounded-xl flex items-center justify-center shadow-md bg-gradient-to-br\"\n [ngClass]=\"{\n 'from-green-500 to-green-600': type() === 'ai',\n 'from-primary-500 to-primary-600': type() === 'human',\n 'from-yellow-500 to-yellow-600': type() === 'system'\n }\"\n >\n <fa-icon\n [icon]=\"iconTextMapper.getIconForMessageType(type())\"\n class=\"text-white text-sm sm:text-lg\"\n ></fa-icon>\n </div>\n </div>\n\n <div\n class=\"flex-grow text-xs sm:text-sm leading-relaxed min-w-0\"\n [ngClass]=\"{\n 'text-left': type() === 'ai' || type() === 'system',\n 'text-right': type() === 'human'\n }\"\n >\n @if (message().includes('```json')) {\n <div class=\"p-2 sm:p-4 bg-gray-50 rounded-lg sm:rounded-xl border border-gray-200 overflow-x-auto\">\n <markdown class=\"markdown-body\">{{ extractJson(message()) }}</markdown>\n </div>\n } @else {\n <div class=\"text-gray-700 break-words\">\n <markdown class=\"markdown-body\">{{ message() }}</markdown>\n </div>\n }\n </div>\n </div>\n </div>\n}", styles: ["@layer utilities{@keyframes bounceDelay{0%,80%,to{transform:scale(0)}40%{transform:scale(1)}}.animate-bounceDelay{animation:bounceDelay 1.4s infinite ease-in-out both}}\n"], dependencies: [{ kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "component", type: FaIconComponent, selector: "fa-icon", inputs: ["icon", "title", "animation", "mask", "flip", "size", "pull", "border", "inverse", "symbol", "rotate", "fixedWidth", "transform", "a11yRole"], outputs: ["iconChange", "titleChange", "animationChange", "maskChange", "flipChange", "sizeChange", "pullChange", "borderChange", "inverseChange", "symbolChange", "rotateChange", "fixedWidthChange", "transformChange", "a11yRoleChange"] }, { kind: "component", type: MarkdownComponent, selector: "markdown, [markdown]", inputs: ["data", "src", "disableSanitizer", "inline", "clipboard", "clipboardButtonComponent", "clipboardButtonTemplate", "emoji", "katex", "katexOptions", "mermaid", "mermaidOptions", "lineHighlight", "line", "lineOffset", "lineNumbers", "start", "commandLine", "filterOutput", "host", "prompt", "output", "user"], outputs: ["error", "load", "ready"] }] });
|
|
375
|
+
}
|
|
376
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.9", ngImport: i0, type: AgentChatMessageComponent, decorators: [{
|
|
377
|
+
type: Component,
|
|
378
|
+
args: [{ selector: 'sftech-agent-chat-message', imports: [NgClass, FaIconComponent, MarkdownComponent], template: "@if (type() === 'ai_loading') {\n <div class=\"my-2 sm:my-4 max-w-4xl mx-auto\">\n <div class=\"bg-white rounded-xl sm:rounded-2xl shadow-lg border border-gray-200 p-4 sm:p-6\">\n <div class=\"flex items-start gap-3 sm:gap-4\">\n <div class=\"flex-shrink-0\">\n <div class=\"w-10 h-10 sm:w-12 sm:h-12 rounded-lg sm:rounded-xl bg-gradient-to-br from-green-500 to-green-600 flex items-center justify-center shadow-md\">\n <fa-icon [icon]=\"iconTextMapper.getIconForMessageType('ai')\" class=\"text-white text-sm sm:text-lg\"></fa-icon>\n </div>\n </div>\n <div class=\"flex items-center space-x-3 text-gray-600 mt-1 sm:mt-2\">\n <span class=\"text-xs sm:text-sm font-medium\">Die Antwort wird ermittelt...</span>\n <div class=\"flex space-x-1\">\n <div class=\"w-2 h-2 sm:w-2.5 sm:h-2.5 bg-green-600 rounded-full animate-bounceDelay\"></div>\n <div class=\"w-2 h-2 sm:w-2.5 sm:h-2.5 bg-green-600 rounded-full animate-bounceDelay [animation-delay:-0.32s]\"></div>\n <div class=\"w-2 h-2 sm:w-2.5 sm:h-2.5 bg-green-600 rounded-full animate-bounceDelay [animation-delay:-0.16s]\"></div>\n </div>\n </div>\n </div>\n </div>\n </div>\n}\n\n@else if (type() === 'ai_failed') {\n <div class=\"my-2 sm:my-4 max-w-4xl mx-auto\">\n <div class=\"bg-white rounded-xl sm:rounded-2xl shadow-lg border-2 border-red-300 p-4 sm:p-6\">\n <div class=\"flex items-start gap-3 sm:gap-4\">\n <div class=\"flex-shrink-0\">\n <div class=\"w-10 h-10 sm:w-12 sm:h-12 rounded-lg sm:rounded-xl bg-gradient-to-br from-red-500 to-red-600 flex items-center justify-center shadow-md\">\n <fa-icon [icon]=\"iconTextMapper.getIconForMessageType('ai')\" class=\"text-white text-sm sm:text-lg\"></fa-icon>\n </div>\n </div>\n <div class=\"flex-grow\">\n <div class=\"text-xs sm:text-sm text-red-700 font-medium\">\n {{ message() }}\n </div>\n </div>\n </div>\n </div>\n </div>\n}\n\n@else {\n <div class=\"my-2 sm:my-4 max-w-4xl mx-auto\">\n <div\n class=\"rounded-xl sm:rounded-2xl shadow-lg p-3 sm:p-6 flex gap-3 sm:gap-4 border-2\"\n [ngClass]=\"{\n 'bg-white border-gray-200': type() === 'ai',\n 'bg-primary-50 border-primary-200 flex-row-reverse': type() === 'human',\n 'bg-yellow-50 border-yellow-300': type() === 'system'\n }\"\n >\n <div class=\"flex-shrink-0\">\n <div\n class=\"w-10 h-10 sm:w-12 sm:h-12 rounded-lg sm:rounded-xl flex items-center justify-center shadow-md bg-gradient-to-br\"\n [ngClass]=\"{\n 'from-green-500 to-green-600': type() === 'ai',\n 'from-primary-500 to-primary-600': type() === 'human',\n 'from-yellow-500 to-yellow-600': type() === 'system'\n }\"\n >\n <fa-icon\n [icon]=\"iconTextMapper.getIconForMessageType(type())\"\n class=\"text-white text-sm sm:text-lg\"\n ></fa-icon>\n </div>\n </div>\n\n <div\n class=\"flex-grow text-xs sm:text-sm leading-relaxed min-w-0\"\n [ngClass]=\"{\n 'text-left': type() === 'ai' || type() === 'system',\n 'text-right': type() === 'human'\n }\"\n >\n @if (message().includes('```json')) {\n <div class=\"p-2 sm:p-4 bg-gray-50 rounded-lg sm:rounded-xl border border-gray-200 overflow-x-auto\">\n <markdown class=\"markdown-body\">{{ extractJson(message()) }}</markdown>\n </div>\n } @else {\n <div class=\"text-gray-700 break-words\">\n <markdown class=\"markdown-body\">{{ message() }}</markdown>\n </div>\n }\n </div>\n </div>\n </div>\n}", styles: ["@layer utilities{@keyframes bounceDelay{0%,80%,to{transform:scale(0)}40%{transform:scale(1)}}.animate-bounceDelay{animation:bounceDelay 1.4s infinite ease-in-out both}}\n"] }]
|
|
379
|
+
}], propDecorators: { message: [{ type: i0.Input, args: [{ isSignal: true, alias: "message", required: false }] }], type: [{ type: i0.Input, args: [{ isSignal: true, alias: "type", required: false }] }] } });
|
|
380
|
+
|
|
381
|
+
class AgentChatComponent {
|
|
382
|
+
activeRoute = inject(ActivatedRoute);
|
|
383
|
+
router = inject(Router);
|
|
384
|
+
agentService = inject(AgentService);
|
|
385
|
+
agentRunService = inject(AgentRunService);
|
|
386
|
+
chatService = inject(ChatService);
|
|
387
|
+
orchestratorService = inject(OrchestratorService);
|
|
388
|
+
messageService = inject(MessageService);
|
|
389
|
+
dialogService = inject(DialogService);
|
|
390
|
+
agent;
|
|
391
|
+
agentRuns = signal(undefined, ...(ngDevMode ? [{ debugName: "agentRuns" }] : []));
|
|
392
|
+
selectedAgentRun = signal(undefined, ...(ngDevMode ? [{ debugName: "selectedAgentRun" }] : []));
|
|
393
|
+
selectedAgent = signal(undefined, ...(ngDevMode ? [{ debugName: "selectedAgent" }] : []));
|
|
394
|
+
chatContainer;
|
|
395
|
+
iconProvider = IconProvider;
|
|
396
|
+
iconTextMapper = AgentRunIconTextDataprovider;
|
|
397
|
+
agentRunStatus = EAgentRunStatus;
|
|
398
|
+
// Mobile sidebar visibility
|
|
399
|
+
sidebarVisible = signal(false, ...(ngDevMode ? [{ debugName: "sidebarVisible" }] : []));
|
|
400
|
+
subscriptions = [];
|
|
401
|
+
toggleSidebar() {
|
|
402
|
+
this.sidebarVisible.update((v) => !v);
|
|
403
|
+
}
|
|
404
|
+
closeSidebar() {
|
|
405
|
+
this.sidebarVisible.set(false);
|
|
406
|
+
}
|
|
407
|
+
selectedAgentRunChanged$ = effect(() => {
|
|
408
|
+
if (!this.selectedAgentRun()) {
|
|
409
|
+
return;
|
|
410
|
+
}
|
|
411
|
+
this._loadChat();
|
|
412
|
+
}, ...(ngDevMode ? [{ debugName: "selectedAgentRunChanged$" }] : []));
|
|
413
|
+
chatMessagesChanged$ = effect(() => {
|
|
414
|
+
const messages = this.selectedAgentRun()?.chat?.messages;
|
|
415
|
+
if (messages && messages.length > 0) {
|
|
416
|
+
this.startScrollingStrategy();
|
|
417
|
+
}
|
|
418
|
+
}, ...(ngDevMode ? [{ debugName: "chatMessagesChanged$" }] : []));
|
|
419
|
+
loading = computed(() => {
|
|
420
|
+
if (!this.selectedAgentRun()?.status) {
|
|
421
|
+
return false;
|
|
422
|
+
}
|
|
423
|
+
return ![EAgentRunStatus.COMPLETED, EAgentRunStatus.CLARIFICATION_NEEDED, EAgentRunStatus.FAILED, EAgentRunStatus.INIT].includes(this.selectedAgentRun().status);
|
|
424
|
+
}, ...(ngDevMode ? [{ debugName: "loading" }] : []));
|
|
425
|
+
ngOnInit() {
|
|
426
|
+
const agentId = Number(this.activeRoute.snapshot.paramMap.get('agentId'));
|
|
427
|
+
const agentRunId = Number(this.activeRoute.snapshot.paramMap.get('agentRunId'));
|
|
428
|
+
if (agentId) {
|
|
429
|
+
const odata = new OData();
|
|
430
|
+
odata.filter.addAnd(new OdataFilter('agentId', agentId, EFilterOperator.EQUALS, EFilterTypes.NUMERIC));
|
|
431
|
+
odata.order.column = 'updatedAt';
|
|
432
|
+
odata.order.direction = ESortDirection.DESC;
|
|
433
|
+
this.agentRunService
|
|
434
|
+
.odata(odata)
|
|
435
|
+
.pipe(map((res) => {
|
|
436
|
+
if (res instanceof MappedApiError) {
|
|
437
|
+
this.messageService.add({
|
|
438
|
+
severity: 'error',
|
|
439
|
+
summary: 'Error',
|
|
440
|
+
detail: 'Fehler beim Laden der Chat-Daten',
|
|
441
|
+
});
|
|
442
|
+
throw new Error(res.messages.join('|'));
|
|
443
|
+
}
|
|
444
|
+
this.selectedAgentRun.set(res.data.items.find((ar) => agentRunId == ar.id));
|
|
445
|
+
this.agentRuns.set(res.data.items);
|
|
446
|
+
}))
|
|
447
|
+
.subscribe();
|
|
448
|
+
this.agentService.get(agentId, true).subscribe((res) => {
|
|
449
|
+
if (res instanceof MappedApiError) {
|
|
450
|
+
this.messageService.add({
|
|
451
|
+
severity: 'error',
|
|
452
|
+
summary: 'Error',
|
|
453
|
+
detail: 'Fehler beim Laden des Agenten-Daten',
|
|
454
|
+
});
|
|
455
|
+
throw new Error(res.messages.join('|'));
|
|
456
|
+
}
|
|
457
|
+
this.selectedAgent.set(res.data);
|
|
458
|
+
});
|
|
459
|
+
}
|
|
460
|
+
else {
|
|
461
|
+
this.messageService.add({ severity: 'error', summary: 'Error', detail: 'Fehler beim Laden des Agenten' });
|
|
462
|
+
// this.router.navigate(['../chats']);
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
ngOnDestroy() {
|
|
466
|
+
this.subscriptions.forEach((subscription) => subscription.unsubscribe());
|
|
467
|
+
}
|
|
468
|
+
startNewRun() {
|
|
469
|
+
if (!this.selectedAgent()) {
|
|
470
|
+
return;
|
|
471
|
+
}
|
|
472
|
+
const agentRun = new AgentRun();
|
|
473
|
+
agentRun.agentId = this.selectedAgent().id;
|
|
474
|
+
agentRun.agent = this.selectedAgent();
|
|
475
|
+
agentRun.status = EAgentRunStatus.INIT;
|
|
476
|
+
agentRun.createdAt = new Date();
|
|
477
|
+
agentRun.chat = new Chat();
|
|
478
|
+
const systemContextMessage = new ChatMessage();
|
|
479
|
+
systemContextMessage.messageType = 'system';
|
|
480
|
+
systemContextMessage.message = `${this.selectedAgent().llmSystemPrompt?.template}Antworte nach folgender Spezifikation:<br>${this.selectedAgent()?.answerSpecification}`;
|
|
481
|
+
agentRun.chat.messages = [systemContextMessage];
|
|
482
|
+
this.selectedAgentRun.set(agentRun);
|
|
483
|
+
}
|
|
484
|
+
selectAgentRun(agentRun) {
|
|
485
|
+
this.selectedAgentRun.set(agentRun);
|
|
486
|
+
if (this.selectedAgentRun().id) {
|
|
487
|
+
this._setRelativeUrl();
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
startObservingAgentRun(agentRunId) {
|
|
491
|
+
this.subscriptions.push(this.orchestratorService.observeAgentRun(agentRunId).subscribe({
|
|
492
|
+
next: (update) => {
|
|
493
|
+
if (update instanceof MappedApiError) {
|
|
494
|
+
this.messageService.add({ severity: 'error', summary: 'Error', detail: 'Fehler beim Beobachten des Agentenlaufs' });
|
|
495
|
+
return;
|
|
496
|
+
}
|
|
497
|
+
this.selectedAgentRun.update((agentRun) => {
|
|
498
|
+
return this._mapUpdateOnAgentRun(agentRun, update);
|
|
499
|
+
});
|
|
500
|
+
if (update.id && this.agentRuns()?.filter((ars) => ars.id === update.id).length === 0) {
|
|
501
|
+
this.agentRuns.update((ars) => {
|
|
502
|
+
ars?.unshift(update);
|
|
503
|
+
return ars;
|
|
504
|
+
});
|
|
505
|
+
}
|
|
506
|
+
},
|
|
507
|
+
error: () => {
|
|
508
|
+
this.messageService.add({ severity: 'error', summary: 'Error', detail: 'Fehler beim Beobachten des Agentenlaufs' });
|
|
509
|
+
},
|
|
510
|
+
complete: () => { },
|
|
511
|
+
}));
|
|
512
|
+
}
|
|
513
|
+
goBack() {
|
|
514
|
+
const index = this.router.url.indexOf('/agent/');
|
|
515
|
+
const result = this.router.url.substring(0, index);
|
|
516
|
+
this.router.navigateByUrl(result);
|
|
517
|
+
}
|
|
518
|
+
_loadChat() {
|
|
519
|
+
if (!this.selectedAgentRun() || !this.selectedAgentRun().chatId) {
|
|
520
|
+
return;
|
|
521
|
+
}
|
|
522
|
+
this.chatService.get(this.selectedAgentRun().chatId, true).subscribe((res) => {
|
|
523
|
+
if (res instanceof MappedApiError) {
|
|
524
|
+
this.messageService.add({
|
|
525
|
+
severity: 'error',
|
|
526
|
+
summary: 'Error',
|
|
527
|
+
detail: 'Fehler beim Laden der Chat-Daten',
|
|
528
|
+
});
|
|
529
|
+
return;
|
|
530
|
+
}
|
|
531
|
+
this.selectedAgentRun.update((ar) => {
|
|
532
|
+
ar.chat = res.data;
|
|
533
|
+
return ar;
|
|
534
|
+
});
|
|
535
|
+
this.startScrollingStrategy();
|
|
536
|
+
});
|
|
537
|
+
}
|
|
538
|
+
_setRelativeUrl() {
|
|
539
|
+
let relativeUrl;
|
|
540
|
+
if (this.router.url.includes('/r/')) {
|
|
541
|
+
const index = this.router.url.indexOf('/r/') + 3; // 3 = Länge von "/r/"
|
|
542
|
+
const result = this.router.url.substring(0, index);
|
|
543
|
+
relativeUrl = `${result}${this.selectedAgentRun().id}`;
|
|
544
|
+
}
|
|
545
|
+
else {
|
|
546
|
+
relativeUrl = `r/${this.selectedAgentRun().id}`;
|
|
547
|
+
}
|
|
548
|
+
this.router.navigate([relativeUrl], {
|
|
549
|
+
relativeTo: this.activeRoute,
|
|
550
|
+
replaceUrl: true, // überschreibt den Verlauf
|
|
551
|
+
skipLocationChange: false, // false = URL sichtbar, true = nur intern
|
|
552
|
+
});
|
|
553
|
+
}
|
|
554
|
+
_mapUpdateOnAgentRun(agentRun, update) {
|
|
555
|
+
if (!agentRun.chat?.messages && !update.chat?.messages) {
|
|
556
|
+
return update;
|
|
557
|
+
}
|
|
558
|
+
if (!update.chat && agentRun.chat) {
|
|
559
|
+
update.chat = agentRun.chat;
|
|
560
|
+
}
|
|
561
|
+
if (agentRun.chat?.messages && update.chat.messages.length <= agentRun.chat.messages.length) {
|
|
562
|
+
update.chat.messages = agentRun.chat.messages;
|
|
563
|
+
}
|
|
564
|
+
return update;
|
|
565
|
+
}
|
|
566
|
+
openRagUploadModal() {
|
|
567
|
+
const agent = this.selectedAgent();
|
|
568
|
+
const chatId = this.selectedAgentRun()?.chatId;
|
|
569
|
+
if (!agent) {
|
|
570
|
+
this.messageService.add({
|
|
571
|
+
severity: 'error',
|
|
572
|
+
summary: 'Fehler',
|
|
573
|
+
detail: 'Agent nicht gefunden',
|
|
574
|
+
});
|
|
575
|
+
return;
|
|
576
|
+
}
|
|
577
|
+
const header = chatId ? 'RAG-Dokumente für Chat hochladen' : 'RAG-Dokumente für Agent hochladen';
|
|
578
|
+
this.dialogService.open(AgentRagUploadComponent, {
|
|
579
|
+
inputValues: { agentId: agent.id, chatId },
|
|
580
|
+
focusOnShow: false,
|
|
581
|
+
modal: true,
|
|
582
|
+
dismissableMask: true,
|
|
583
|
+
header: header,
|
|
584
|
+
width: '50%',
|
|
585
|
+
contentStyle: { overflow: 'auto' },
|
|
586
|
+
});
|
|
587
|
+
}
|
|
588
|
+
startScrollingStrategy() {
|
|
589
|
+
this.scrollToBottom();
|
|
590
|
+
// Fallback-Scrolls für langsames Markdown-Rendering
|
|
591
|
+
[100, 500, 1000, 2000].forEach((delay) => {
|
|
592
|
+
setTimeout(() => this.scrollToBottom(), delay);
|
|
593
|
+
});
|
|
594
|
+
}
|
|
595
|
+
scrollToBottom() {
|
|
596
|
+
if (!this.chatContainer?.nativeElement) {
|
|
597
|
+
return;
|
|
598
|
+
}
|
|
599
|
+
const container = this.chatContainer.nativeElement;
|
|
600
|
+
container.scrollTop = container.scrollHeight - container.clientHeight;
|
|
601
|
+
}
|
|
602
|
+
getFirstUserMessage(agentRun) {
|
|
603
|
+
if (!agentRun.chat?.messages || agentRun.chat.messages.length === 0) {
|
|
604
|
+
return 'Neuer Chat';
|
|
605
|
+
}
|
|
606
|
+
const firstUserMessage = agentRun.chat.messages.find((msg) => msg.messageType === 'human' || msg.messageType === 'user');
|
|
607
|
+
if (!firstUserMessage?.message) {
|
|
608
|
+
return 'Chat gestartet';
|
|
609
|
+
}
|
|
610
|
+
// Remove HTML tags and limit length
|
|
611
|
+
const cleanText = firstUserMessage.message.replace(/<[^>]*>/g, '').trim();
|
|
612
|
+
// Bei 220px Breite mit text-xs passen ca. 40-45 Zeichen gut
|
|
613
|
+
return cleanText.length > 42 ? `${cleanText.substring(0, 42)}...` : cleanText;
|
|
614
|
+
}
|
|
615
|
+
getMessageCount(agentRun) {
|
|
616
|
+
if (!agentRun.chat?.messages) {
|
|
617
|
+
return 0;
|
|
618
|
+
}
|
|
619
|
+
// Count only human and AI messages, exclude system messages
|
|
620
|
+
return agentRun.chat.messages.filter((msg) => msg.messageType === 'human' || msg.messageType === 'user' || msg.messageType === 'ai').length;
|
|
621
|
+
}
|
|
622
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.9", ngImport: i0, type: AgentChatComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
623
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.9", type: AgentChatComponent, isStandalone: true, selector: "sftech-agent-chat", providers: [DialogService], viewQueries: [{ propertyName: "chatContainer", first: true, predicate: ["chatContainer"], descendants: true }], ngImport: i0, template: "<div class=\"flex flex-col h-full shadow-lg\">\n <!-- Header \u00FCber gesamte Breite -->\n @if (selectedAgent()) {\n <div class=\"border-b border-gray-300 px-3 sm:px-4 py-3 flex items-center bg-white gap-2\">\n <!-- Mobile Sidebar Toggle -->\n <div class=\"relative lg:hidden flex-shrink-0\">\n <button pButton\n type=\"button\"\n class=\"p-button-text p-button-rounded\"\n (click)=\"toggleSidebar()\"\n [pTooltip]=\"'Chat-Verlauf (' + (agentRuns()?.length ?? 0) + ')'\"\n tooltipPosition=\"bottom\">\n <fa-icon [icon]=\"iconProvider.chat\" size=\"lg\"></fa-icon>\n </button>\n @if (agentRuns()?.length) {\n <span class=\"absolute -top-1 -right-1 bg-primary-500 text-white text-xs font-bold rounded-full min-w-5 h-5 flex items-center justify-center px-1 pointer-events-none\">\n {{ agentRuns()!.length }}\n </span>\n }\n </div>\n\n <div class=\"flex items-center gap-2 sm:gap-3 flex-1 min-w-0\">\n <button pButton\n type=\"button\"\n class=\"p-button-text p-button-rounded flex-shrink-0\"\n (click)=\"goBack()\"\n [pTooltip]=\"'Zur\u00FCck'\"\n tooltipPosition=\"bottom\"\n tabindex=\"2\">\n <fa-icon [icon]=\"iconProvider.back\" size=\"lg\"></fa-icon>\n </button>\n @if (selectedAgentRun()) {\n <h2 class=\"text-base sm:text-xl font-semibold truncate\">\n <span class=\"hidden sm:inline\">Agent: </span>{{ selectedAgent()?.name }}\n <span class=\"hidden md:inline text-gray-500 font-normal\">\n vom {{ (selectedAgentRun()?.createdAt ?? selectedAgentRun()?.startedAt | date:'dd.MM.yyyy - HH:mm') }}\n </span>\n </h2>\n } @else {\n <h2 class=\"text-base sm:text-xl font-semibold truncate\">\n <span class=\"hidden sm:inline\">Agent: </span>{{ selectedAgent()?.name }}\n </h2>\n }\n </div>\n <div class=\"flex items-center gap-2 sm:gap-3 flex-shrink-0\">\n <button pButton\n type=\"button\"\n class=\"p-button-sm\"\n (click)=\"startNewRun()\"\n tabindex=\"3\">\n <fa-icon [icon]=\"iconProvider.add\" class=\"sm:mr-2\"></fa-icon>\n <span class=\"hidden sm:inline\">Neuer Chat</span>\n </button>\n\n @if (selectedAgentRun()) {\n @if (selectedAgent()?.ragProvider && selectedAgent()?.ragModel) {\n <button pButton\n type=\"button\"\n class=\"p-button-text p-button-rounded\"\n (click)=\"openRagUploadModal()\"\n [pTooltip]=\"'RAG-Dokumente hochladen'\"\n tooltipPosition=\"bottom\">\n <fa-icon [icon]=\"iconProvider.documentAdd\" size=\"lg\"></fa-icon>\n </button>\n }\n <div [pTooltip]=\"'Status: ' + iconTextMapper.getTextForStatus(selectedAgentRun()?.status)\"\n tooltipPosition=\"bottom\"\n class=\"cursor-help\">\n <fa-icon [icon]=\"iconTextMapper.getIconForStatus(selectedAgentRun()?.status)\"\n [classList]=\"iconTextMapper.getIconClassesForStatus(selectedAgentRun()?.status)\"\n [animation]=\"selectedAgentRun()?.status === 'running' ? 'spin' : undefined\"\n size=\"lg\"></fa-icon>\n </div>\n }\n </div>\n </div>\n }\n\n <!-- Sidebar + Chat nebeneinander -->\n <div class=\"flex flex-1 min-h-0 overflow-hidden relative\">\n <!-- Mobile Overlay -->\n @if (sidebarVisible()) {\n <div class=\"fixed inset-0 bg-black/50 z-40 lg:hidden\"\n (click)=\"closeSidebar()\"></div>\n }\n\n <!-- Sidebar -->\n <div class=\"chats-panel border-r border-gray-200 bg-gradient-to-b from-gray-50 to-white p-3 h-full flex-shrink-0 overflow-y-auto\n fixed lg:relative inset-y-0 left-0 z-50 lg:z-auto\n w-[280px] sm:w-[300px] lg:w-[220px]\n transform transition-transform duration-300 ease-in-out\n lg:transform-none\"\n [class.-translate-x-full]=\"!sidebarVisible()\"\n [class.translate-x-0]=\"sidebarVisible()\"\n [class.lg:translate-x-0]=\"true\">\n\n <!-- Mobile Close Button -->\n <div class=\"flex items-center justify-between mb-3 lg:hidden\">\n <h3 class=\"text-sm font-semibold text-gray-700\">Chat-Verlauf</h3>\n <button class=\"p-2 hover:bg-gray-100 rounded-lg transition-colors\"\n (click)=\"closeSidebar()\">\n <fa-icon [icon]=\"iconProvider.close\" class=\"text-gray-500\"></fa-icon>\n </button>\n </div>\n\n <div class=\"mb-3 hidden lg:block\">\n <h3 class=\"text-xs font-semibold text-gray-500 uppercase tracking-wider px-2\">Chat Verlauf</h3>\n </div>\n @if (agentRuns() === undefined) {\n <div class=\"flex items-center justify-center py-8\">\n <div class=\"text-center\">\n <fa-icon [icon]=\"iconProvider.loading\" [animation]=\"'spin'\" size=\"2x\" class=\"text-gray-400 mb-2\"></fa-icon>\n <p class=\"text-sm text-gray-500 m-0\">Chats werden geladen...</p>\n </div>\n </div>\n } @else if (!agentRuns()?.length) {\n <div class=\"flex items-center justify-center py-8\">\n <div class=\"text-center\">\n <fa-icon [icon]=\"iconProvider.chat\" size=\"2x\" class=\"text-gray-300 mb-2\"></fa-icon>\n <p class=\"text-sm text-gray-500 m-0\">Noch keine Chats</p>\n </div>\n </div>\n } @else {\n <div class=\"flex flex-col gap-2\">\n @for (agentRun of agentRuns(); track agentRun) {\n <div class=\"chat-item group relative rounded-xl p-3 cursor-pointer transition-all duration-200 border active:scale-[0.98]\"\n (click)=\"selectAgentRun(agentRun); closeSidebar()\"\n [class.active]=\"selectedAgentRun()?.id === agentRun.id\"\n [class.bg-white]=\"selectedAgentRun()?.id !== agentRun.id\"\n [class.shadow-sm]=\"selectedAgentRun()?.id !== agentRun.id\"\n [class.hover:shadow-md]=\"selectedAgentRun()?.id !== agentRun.id\"\n [class.border-gray-200]=\"selectedAgentRun()?.id !== agentRun.id\"\n [class.bg-primary-50]=\"selectedAgentRun()?.id === agentRun.id\"\n [class.border-primary-300]=\"selectedAgentRun()?.id === agentRun.id\"\n [class.shadow-md]=\"selectedAgentRun()?.id === agentRun.id\">\n <div class=\"flex items-start justify-between mb-2\">\n <div class=\"flex items-center gap-2 flex-1 min-w-0\">\n <fa-icon [icon]=\"iconTextMapper.getIconForStatus(agentRun.status)\"\n [classList]=\"iconTextMapper.getIconClassesForStatus(agentRun.status)\"\n [animation]=\"agentRun.status === 'running' ? 'spin' : undefined\"\n class=\"flex-shrink-0\"\n size=\"sm\"></fa-icon>\n <span class=\"text-xs font-medium truncate\"\n [class.text-primary-700]=\"selectedAgentRun()?.id === agentRun.id\"\n [class.text-gray-700]=\"selectedAgentRun()?.id !== agentRun.id\">\n {{ agentRun.createdAt | date:'dd.MM.YY' }}\n </span>\n </div>\n <span class=\"text-xs font-medium flex-shrink-0 ml-2\"\n [class.text-primary-600]=\"selectedAgentRun()?.id === agentRun.id\"\n [class.text-gray-500]=\"selectedAgentRun()?.id !== agentRun.id\">\n {{ agentRun.createdAt | date:'HH:mm' }}\n </span>\n </div>\n @if (agentRun.chat && agentRun.chat.messages && agentRun.chat.messages.length > 0) {\n <div class=\"text-xs truncate leading-relaxed\"\n [class.text-primary-600]=\"selectedAgentRun()?.id === agentRun.id\"\n [class.text-gray-600]=\"selectedAgentRun()?.id !== agentRun.id\">\n {{ getFirstUserMessage(agentRun) }}\n </div>\n } @else {\n <div class=\"text-xs italic truncate\"\n [class.text-primary-500]=\"selectedAgentRun()?.id === agentRun.id\"\n [class.text-gray-400]=\"selectedAgentRun()?.id !== agentRun.id\">\n Neuer Chat\n </div>\n }\n @if (agentRun.chat && agentRun.chat.messages) {\n <div class=\"flex items-center gap-2 mt-2 pt-2 border-t\"\n [class.border-primary-200]=\"selectedAgentRun()?.id === agentRun.id\"\n [class.border-gray-100]=\"selectedAgentRun()?.id !== agentRun.id\">\n <fa-icon [icon]=\"iconProvider.message\" size=\"xs\"\n [class.text-primary-500]=\"selectedAgentRun()?.id === agentRun.id\"\n [class.text-gray-400]=\"selectedAgentRun()?.id !== agentRun.id\"></fa-icon>\n <span class=\"text-xs\"\n [class.text-primary-600]=\"selectedAgentRun()?.id === agentRun.id\"\n [class.text-gray-500]=\"selectedAgentRun()?.id !== agentRun.id\">\n {{ getMessageCount(agentRun) }} Nachrichten\n </span>\n </div>\n }\n </div>\n }\n </div>\n }\n </div>\n\n <!-- Chat Area -->\n <div class=\"chat bg-gray-50 flex-1 relative min-w-0\">\n @if (selectedAgentRun() === undefined) {\n <div class=\"flex items-center justify-center h-full p-4\">\n <p class=\"m-0 text-gray-500 text-center\">\n <span class=\"hidden lg:inline\">Bitte einen Chat aus der linken Liste ausw\u00E4hlen</span>\n <span class=\"lg:hidden\">Tippe auf das Men\u00FC-Icon um einen Chat auszuw\u00E4hlen</span>\n </p>\n </div>\n } @else if (selectedAgent() === undefined) {\n <div class=\"flex items-center justify-center h-full\">\n <p class=\"m-0 text-gray-500\">\n Die Chat-Daten werden geladen...\n </p>\n </div>\n } @else {\n <div class=\"absolute inset-0 overflow-y-auto p-2 sm:p-3\" #chatContainer>\n @if (selectedAgentRun()!.chat) {\n @for (message of selectedAgentRun()!.chat!.messages; track message.id) {\n <div class=\"mb-2\">\n <sftech-agent-chat-message [message]=\"message.message\"\n [type]=\"message.messageType\"></sftech-agent-chat-message>\n </div>\n }\n }\n @if (selectedAgentRun()!.status === agentRunStatus.INIT) {\n <sftech-agent-chat-message-human-input-initial [agent]=\"selectedAgent()\"\n (agentRunIdReceived)=\"startObservingAgentRun($event)\"></sftech-agent-chat-message-human-input-initial>\n }\n @if (loading()) {\n <div class=\"mb-2\">\n <sftech-agent-chat-message [message]=\"'Am \u00DCberlegen'\"\n [type]=\"'ai_loading'\"></sftech-agent-chat-message>\n </div>\n }\n @if (selectedAgentRun()?.status === agentRunStatus.FAILED) {\n <div class=\"mb-2\">\n <sftech-agent-chat-message\n [message]=\"'Es ist ein Fehler aufgetreten! Bitte versuche es zu einem sp\u00E4teren Zeitpunkt erneut.'\"\n [type]=\"'ai_failed'\"></sftech-agent-chat-message>\n </div>\n }\n @if (selectedAgentRun()!.status === agentRunStatus.COMPLETED) {\n <sftech-agent-chat-message-human-input [agent]=\"selectedAgent()\"\n [agentRunId]=\"selectedAgentRun()!.id\"\n (agentRunIdReceived)=\"startObservingAgentRun(selectedAgentRun()!.id!)\"></sftech-agent-chat-message-human-input>\n }\n </div>\n }\n </div>\n </div>\n</div>\n", styles: [":host{display:block;height:100%;overflow:hidden}.chat-item{transform:translate(0);transition:all .2s ease-in-out}.chat-item:hover{transform:translate(2px)}.chat-item.active{border-left-width:3px}\n"], dependencies: [{ kind: "directive", type: ButtonDirective, selector: "[pButton]", inputs: ["ptButtonDirective", "hostName", "text", "plain", "raised", "size", "outlined", "rounded", "iconPos", "loadingIcon", "fluid", "label", "icon", "loading", "buttonProps", "severity"] }, { kind: "component", type: AgentChatMessageComponent, selector: "sftech-agent-chat-message", inputs: ["message", "type"] }, { kind: "component", type: FaIconComponent, selector: "fa-icon", inputs: ["icon", "title", "animation", "mask", "flip", "size", "pull", "border", "inverse", "symbol", "rotate", "fixedWidth", "transform", "a11yRole"], outputs: ["iconChange", "titleChange", "animationChange", "maskChange", "flipChange", "sizeChange", "pullChange", "borderChange", "inverseChange", "symbolChange", "rotateChange", "fixedWidthChange", "transformChange", "a11yRoleChange"] }, { kind: "component", type: AgentChatMessageHumanInputInitialComponent, selector: "sftech-agent-chat-message-human-input-initial", inputs: ["agent"], outputs: ["agentRunIdReceived"] }, { kind: "component", type: AgentChatMessageHumanInputComponent, selector: "sftech-agent-chat-message-human-input", inputs: ["agent", "agentRunId"], outputs: ["agentRunIdReceived"] }, { kind: "directive", type: Tooltip, selector: "[pTooltip]", inputs: ["tooltipPosition", "tooltipEvent", "positionStyle", "tooltipStyleClass", "tooltipZIndex", "escape", "showDelay", "hideDelay", "life", "positionTop", "positionLeft", "autoHide", "fitContent", "hideOnEscape", "pTooltip", "tooltipDisabled", "tooltipOptions", "appendTo", "ptTooltip"] }, { kind: "pipe", type: DatePipe, name: "date" }] });
|
|
624
|
+
}
|
|
625
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.9", ngImport: i0, type: AgentChatComponent, decorators: [{
|
|
626
|
+
type: Component,
|
|
627
|
+
args: [{ selector: 'sftech-agent-chat', imports: [ButtonDirective, AgentChatMessageComponent, DatePipe, FaIconComponent, AgentChatMessageHumanInputInitialComponent, AgentChatMessageHumanInputComponent, Tooltip], providers: [DialogService], template: "<div class=\"flex flex-col h-full shadow-lg\">\n <!-- Header \u00FCber gesamte Breite -->\n @if (selectedAgent()) {\n <div class=\"border-b border-gray-300 px-3 sm:px-4 py-3 flex items-center bg-white gap-2\">\n <!-- Mobile Sidebar Toggle -->\n <div class=\"relative lg:hidden flex-shrink-0\">\n <button pButton\n type=\"button\"\n class=\"p-button-text p-button-rounded\"\n (click)=\"toggleSidebar()\"\n [pTooltip]=\"'Chat-Verlauf (' + (agentRuns()?.length ?? 0) + ')'\"\n tooltipPosition=\"bottom\">\n <fa-icon [icon]=\"iconProvider.chat\" size=\"lg\"></fa-icon>\n </button>\n @if (agentRuns()?.length) {\n <span class=\"absolute -top-1 -right-1 bg-primary-500 text-white text-xs font-bold rounded-full min-w-5 h-5 flex items-center justify-center px-1 pointer-events-none\">\n {{ agentRuns()!.length }}\n </span>\n }\n </div>\n\n <div class=\"flex items-center gap-2 sm:gap-3 flex-1 min-w-0\">\n <button pButton\n type=\"button\"\n class=\"p-button-text p-button-rounded flex-shrink-0\"\n (click)=\"goBack()\"\n [pTooltip]=\"'Zur\u00FCck'\"\n tooltipPosition=\"bottom\"\n tabindex=\"2\">\n <fa-icon [icon]=\"iconProvider.back\" size=\"lg\"></fa-icon>\n </button>\n @if (selectedAgentRun()) {\n <h2 class=\"text-base sm:text-xl font-semibold truncate\">\n <span class=\"hidden sm:inline\">Agent: </span>{{ selectedAgent()?.name }}\n <span class=\"hidden md:inline text-gray-500 font-normal\">\n vom {{ (selectedAgentRun()?.createdAt ?? selectedAgentRun()?.startedAt | date:'dd.MM.yyyy - HH:mm') }}\n </span>\n </h2>\n } @else {\n <h2 class=\"text-base sm:text-xl font-semibold truncate\">\n <span class=\"hidden sm:inline\">Agent: </span>{{ selectedAgent()?.name }}\n </h2>\n }\n </div>\n <div class=\"flex items-center gap-2 sm:gap-3 flex-shrink-0\">\n <button pButton\n type=\"button\"\n class=\"p-button-sm\"\n (click)=\"startNewRun()\"\n tabindex=\"3\">\n <fa-icon [icon]=\"iconProvider.add\" class=\"sm:mr-2\"></fa-icon>\n <span class=\"hidden sm:inline\">Neuer Chat</span>\n </button>\n\n @if (selectedAgentRun()) {\n @if (selectedAgent()?.ragProvider && selectedAgent()?.ragModel) {\n <button pButton\n type=\"button\"\n class=\"p-button-text p-button-rounded\"\n (click)=\"openRagUploadModal()\"\n [pTooltip]=\"'RAG-Dokumente hochladen'\"\n tooltipPosition=\"bottom\">\n <fa-icon [icon]=\"iconProvider.documentAdd\" size=\"lg\"></fa-icon>\n </button>\n }\n <div [pTooltip]=\"'Status: ' + iconTextMapper.getTextForStatus(selectedAgentRun()?.status)\"\n tooltipPosition=\"bottom\"\n class=\"cursor-help\">\n <fa-icon [icon]=\"iconTextMapper.getIconForStatus(selectedAgentRun()?.status)\"\n [classList]=\"iconTextMapper.getIconClassesForStatus(selectedAgentRun()?.status)\"\n [animation]=\"selectedAgentRun()?.status === 'running' ? 'spin' : undefined\"\n size=\"lg\"></fa-icon>\n </div>\n }\n </div>\n </div>\n }\n\n <!-- Sidebar + Chat nebeneinander -->\n <div class=\"flex flex-1 min-h-0 overflow-hidden relative\">\n <!-- Mobile Overlay -->\n @if (sidebarVisible()) {\n <div class=\"fixed inset-0 bg-black/50 z-40 lg:hidden\"\n (click)=\"closeSidebar()\"></div>\n }\n\n <!-- Sidebar -->\n <div class=\"chats-panel border-r border-gray-200 bg-gradient-to-b from-gray-50 to-white p-3 h-full flex-shrink-0 overflow-y-auto\n fixed lg:relative inset-y-0 left-0 z-50 lg:z-auto\n w-[280px] sm:w-[300px] lg:w-[220px]\n transform transition-transform duration-300 ease-in-out\n lg:transform-none\"\n [class.-translate-x-full]=\"!sidebarVisible()\"\n [class.translate-x-0]=\"sidebarVisible()\"\n [class.lg:translate-x-0]=\"true\">\n\n <!-- Mobile Close Button -->\n <div class=\"flex items-center justify-between mb-3 lg:hidden\">\n <h3 class=\"text-sm font-semibold text-gray-700\">Chat-Verlauf</h3>\n <button class=\"p-2 hover:bg-gray-100 rounded-lg transition-colors\"\n (click)=\"closeSidebar()\">\n <fa-icon [icon]=\"iconProvider.close\" class=\"text-gray-500\"></fa-icon>\n </button>\n </div>\n\n <div class=\"mb-3 hidden lg:block\">\n <h3 class=\"text-xs font-semibold text-gray-500 uppercase tracking-wider px-2\">Chat Verlauf</h3>\n </div>\n @if (agentRuns() === undefined) {\n <div class=\"flex items-center justify-center py-8\">\n <div class=\"text-center\">\n <fa-icon [icon]=\"iconProvider.loading\" [animation]=\"'spin'\" size=\"2x\" class=\"text-gray-400 mb-2\"></fa-icon>\n <p class=\"text-sm text-gray-500 m-0\">Chats werden geladen...</p>\n </div>\n </div>\n } @else if (!agentRuns()?.length) {\n <div class=\"flex items-center justify-center py-8\">\n <div class=\"text-center\">\n <fa-icon [icon]=\"iconProvider.chat\" size=\"2x\" class=\"text-gray-300 mb-2\"></fa-icon>\n <p class=\"text-sm text-gray-500 m-0\">Noch keine Chats</p>\n </div>\n </div>\n } @else {\n <div class=\"flex flex-col gap-2\">\n @for (agentRun of agentRuns(); track agentRun) {\n <div class=\"chat-item group relative rounded-xl p-3 cursor-pointer transition-all duration-200 border active:scale-[0.98]\"\n (click)=\"selectAgentRun(agentRun); closeSidebar()\"\n [class.active]=\"selectedAgentRun()?.id === agentRun.id\"\n [class.bg-white]=\"selectedAgentRun()?.id !== agentRun.id\"\n [class.shadow-sm]=\"selectedAgentRun()?.id !== agentRun.id\"\n [class.hover:shadow-md]=\"selectedAgentRun()?.id !== agentRun.id\"\n [class.border-gray-200]=\"selectedAgentRun()?.id !== agentRun.id\"\n [class.bg-primary-50]=\"selectedAgentRun()?.id === agentRun.id\"\n [class.border-primary-300]=\"selectedAgentRun()?.id === agentRun.id\"\n [class.shadow-md]=\"selectedAgentRun()?.id === agentRun.id\">\n <div class=\"flex items-start justify-between mb-2\">\n <div class=\"flex items-center gap-2 flex-1 min-w-0\">\n <fa-icon [icon]=\"iconTextMapper.getIconForStatus(agentRun.status)\"\n [classList]=\"iconTextMapper.getIconClassesForStatus(agentRun.status)\"\n [animation]=\"agentRun.status === 'running' ? 'spin' : undefined\"\n class=\"flex-shrink-0\"\n size=\"sm\"></fa-icon>\n <span class=\"text-xs font-medium truncate\"\n [class.text-primary-700]=\"selectedAgentRun()?.id === agentRun.id\"\n [class.text-gray-700]=\"selectedAgentRun()?.id !== agentRun.id\">\n {{ agentRun.createdAt | date:'dd.MM.YY' }}\n </span>\n </div>\n <span class=\"text-xs font-medium flex-shrink-0 ml-2\"\n [class.text-primary-600]=\"selectedAgentRun()?.id === agentRun.id\"\n [class.text-gray-500]=\"selectedAgentRun()?.id !== agentRun.id\">\n {{ agentRun.createdAt | date:'HH:mm' }}\n </span>\n </div>\n @if (agentRun.chat && agentRun.chat.messages && agentRun.chat.messages.length > 0) {\n <div class=\"text-xs truncate leading-relaxed\"\n [class.text-primary-600]=\"selectedAgentRun()?.id === agentRun.id\"\n [class.text-gray-600]=\"selectedAgentRun()?.id !== agentRun.id\">\n {{ getFirstUserMessage(agentRun) }}\n </div>\n } @else {\n <div class=\"text-xs italic truncate\"\n [class.text-primary-500]=\"selectedAgentRun()?.id === agentRun.id\"\n [class.text-gray-400]=\"selectedAgentRun()?.id !== agentRun.id\">\n Neuer Chat\n </div>\n }\n @if (agentRun.chat && agentRun.chat.messages) {\n <div class=\"flex items-center gap-2 mt-2 pt-2 border-t\"\n [class.border-primary-200]=\"selectedAgentRun()?.id === agentRun.id\"\n [class.border-gray-100]=\"selectedAgentRun()?.id !== agentRun.id\">\n <fa-icon [icon]=\"iconProvider.message\" size=\"xs\"\n [class.text-primary-500]=\"selectedAgentRun()?.id === agentRun.id\"\n [class.text-gray-400]=\"selectedAgentRun()?.id !== agentRun.id\"></fa-icon>\n <span class=\"text-xs\"\n [class.text-primary-600]=\"selectedAgentRun()?.id === agentRun.id\"\n [class.text-gray-500]=\"selectedAgentRun()?.id !== agentRun.id\">\n {{ getMessageCount(agentRun) }} Nachrichten\n </span>\n </div>\n }\n </div>\n }\n </div>\n }\n </div>\n\n <!-- Chat Area -->\n <div class=\"chat bg-gray-50 flex-1 relative min-w-0\">\n @if (selectedAgentRun() === undefined) {\n <div class=\"flex items-center justify-center h-full p-4\">\n <p class=\"m-0 text-gray-500 text-center\">\n <span class=\"hidden lg:inline\">Bitte einen Chat aus der linken Liste ausw\u00E4hlen</span>\n <span class=\"lg:hidden\">Tippe auf das Men\u00FC-Icon um einen Chat auszuw\u00E4hlen</span>\n </p>\n </div>\n } @else if (selectedAgent() === undefined) {\n <div class=\"flex items-center justify-center h-full\">\n <p class=\"m-0 text-gray-500\">\n Die Chat-Daten werden geladen...\n </p>\n </div>\n } @else {\n <div class=\"absolute inset-0 overflow-y-auto p-2 sm:p-3\" #chatContainer>\n @if (selectedAgentRun()!.chat) {\n @for (message of selectedAgentRun()!.chat!.messages; track message.id) {\n <div class=\"mb-2\">\n <sftech-agent-chat-message [message]=\"message.message\"\n [type]=\"message.messageType\"></sftech-agent-chat-message>\n </div>\n }\n }\n @if (selectedAgentRun()!.status === agentRunStatus.INIT) {\n <sftech-agent-chat-message-human-input-initial [agent]=\"selectedAgent()\"\n (agentRunIdReceived)=\"startObservingAgentRun($event)\"></sftech-agent-chat-message-human-input-initial>\n }\n @if (loading()) {\n <div class=\"mb-2\">\n <sftech-agent-chat-message [message]=\"'Am \u00DCberlegen'\"\n [type]=\"'ai_loading'\"></sftech-agent-chat-message>\n </div>\n }\n @if (selectedAgentRun()?.status === agentRunStatus.FAILED) {\n <div class=\"mb-2\">\n <sftech-agent-chat-message\n [message]=\"'Es ist ein Fehler aufgetreten! Bitte versuche es zu einem sp\u00E4teren Zeitpunkt erneut.'\"\n [type]=\"'ai_failed'\"></sftech-agent-chat-message>\n </div>\n }\n @if (selectedAgentRun()!.status === agentRunStatus.COMPLETED) {\n <sftech-agent-chat-message-human-input [agent]=\"selectedAgent()\"\n [agentRunId]=\"selectedAgentRun()!.id\"\n (agentRunIdReceived)=\"startObservingAgentRun(selectedAgentRun()!.id!)\"></sftech-agent-chat-message-human-input>\n }\n </div>\n }\n </div>\n </div>\n</div>\n", styles: [":host{display:block;height:100%;overflow:hidden}.chat-item{transform:translate(0);transition:all .2s ease-in-out}.chat-item:hover{transform:translate(2px)}.chat-item.active{border-left-width:3px}\n"] }]
|
|
628
|
+
}], propDecorators: { chatContainer: [{
|
|
629
|
+
type: ViewChild,
|
|
630
|
+
args: ['chatContainer']
|
|
631
|
+
}] } });
|
|
632
|
+
|
|
633
|
+
class AgentSelectionComponent extends BaseListComponent {
|
|
634
|
+
_repo = inject(AgentService);
|
|
635
|
+
_route = 'chats/agents';
|
|
636
|
+
orchestratorService = inject(OrchestratorService);
|
|
637
|
+
messageService = inject(MessageService);
|
|
638
|
+
iconProvider = IconProvider;
|
|
639
|
+
tools = signal(undefined, ...(ngDevMode ? [{ debugName: "tools" }] : []));
|
|
640
|
+
searchTerm = '';
|
|
641
|
+
filteredAgents = signal([], ...(ngDevMode ? [{ debugName: "filteredAgents" }] : []));
|
|
642
|
+
constructor() {
|
|
643
|
+
super();
|
|
644
|
+
// Watch for data changes and update filtered agents
|
|
645
|
+
effect(() => {
|
|
646
|
+
const agents = this.data();
|
|
647
|
+
if (agents !== undefined) {
|
|
648
|
+
this.filterAgents();
|
|
649
|
+
}
|
|
650
|
+
});
|
|
651
|
+
}
|
|
652
|
+
ngOnInit() {
|
|
653
|
+
super.ngOnInit();
|
|
654
|
+
this.orchestratorService.getTools().subscribe((res) => {
|
|
655
|
+
if (res instanceof MappedApiError) {
|
|
656
|
+
this.messageService.add({ severity: 'error', summary: 'Fehler', detail: res.messages.join('|') });
|
|
657
|
+
return;
|
|
658
|
+
}
|
|
659
|
+
this.tools.set(res.data);
|
|
660
|
+
});
|
|
661
|
+
}
|
|
662
|
+
filterAgents() {
|
|
663
|
+
const agents = this.data() || [];
|
|
664
|
+
if (!this.searchTerm.trim()) {
|
|
665
|
+
this.filteredAgents.set(agents);
|
|
666
|
+
return;
|
|
667
|
+
}
|
|
668
|
+
const term = this.searchTerm.toLowerCase();
|
|
669
|
+
const filtered = agents.filter((agent) => {
|
|
670
|
+
// Search in name
|
|
671
|
+
if (agent.name.toLowerCase().includes(term)) {
|
|
672
|
+
return true;
|
|
673
|
+
}
|
|
674
|
+
// Search in description
|
|
675
|
+
if (agent.description?.toLowerCase().includes(term)) {
|
|
676
|
+
return true;
|
|
677
|
+
}
|
|
678
|
+
// Search in tools
|
|
679
|
+
const toolNames = this.getToolNames(agent).toLowerCase();
|
|
680
|
+
if (toolNames.includes(term)) {
|
|
681
|
+
return true;
|
|
682
|
+
}
|
|
683
|
+
return false;
|
|
684
|
+
});
|
|
685
|
+
this.filteredAgents.set(filtered);
|
|
686
|
+
}
|
|
687
|
+
clearSearch() {
|
|
688
|
+
this.searchTerm = '';
|
|
689
|
+
this.filterAgents();
|
|
690
|
+
}
|
|
691
|
+
getToolName(toolIdentifier) {
|
|
692
|
+
return this.tools()?.find((t) => t.identifier === toolIdentifier)?.name || toolIdentifier;
|
|
693
|
+
}
|
|
694
|
+
getToolNames(agent) {
|
|
695
|
+
if (!agent.tools || agent.tools.length === 0) {
|
|
696
|
+
return 'Keine Tools';
|
|
697
|
+
}
|
|
698
|
+
return agent.tools
|
|
699
|
+
.map((tool) => this.tools()?.find((t) => t.identifier === tool)?.name)
|
|
700
|
+
.filter((name) => !!name)
|
|
701
|
+
.join(' | ');
|
|
702
|
+
}
|
|
703
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.9", ngImport: i0, type: AgentSelectionComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
704
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.9", type: AgentSelectionComponent, isStandalone: true, selector: "sftech-agent-selection", providers: [DialogService], usesInheritance: true, ngImport: i0, template: "<div class=\"h-full overflow-y-auto px-4 sm:px-6 py-6 bg-gradient-to-b from-gray-50 to-white\">\n <!-- Header Section -->\n <div class=\"max-w-7xl mx-auto mb-6 sm:mb-8\">\n <div class=\"flex flex-col gap-4\">\n <div class=\"flex flex-col sm:flex-row sm:items-center sm:justify-between gap-3\">\n <div>\n <h1 class=\"text-2xl sm:text-3xl font-bold text-gray-900 flex items-center gap-3\">\n <fa-icon [icon]=\"iconProvider.robot\" class=\"text-primary-600\"></fa-icon>\n Agenten Auswahl\n </h1>\n <p class=\"text-gray-600 mt-1 text-sm sm:text-base\">W\u00E4hle einen KI-Agenten f\u00FCr deine Aufgabe</p>\n </div>\n @if (data() && data()!.length > 0) {\n <div class=\"text-sm text-gray-500 bg-white px-3 sm:px-4 py-2 rounded-lg border border-gray-200 self-start sm:self-auto\">\n <span class=\"font-semibold text-primary-600\">{{ filteredAgents().length }}</span>\n {{ filteredAgents().length === 1 ? 'Agent' : 'Agenten' }} verf\u00FCgbar\n </div>\n }\n </div>\n\n <!-- Search Bar -->\n @if (data() && data()!.length > 0) {\n <div class=\"relative\">\n <fa-icon [icon]=\"iconProvider.search\"\n class=\"absolute left-4 top-1/2 transform -translate-y-1/2 text-gray-400\"></fa-icon>\n <input\n type=\"text\"\n [(ngModel)]=\"searchTerm\"\n (input)=\"filterAgents()\"\n placeholder=\"Suche nach Agenten, Beschreibung oder Tools...\"\n class=\"w-full pl-12 pr-4 py-3 bg-white border border-gray-300 rounded-xl focus:outline-none focus:ring-2 focus:ring-primary-600 focus:border-primary-600 transition-all\"\n />\n @if (searchTerm) {\n <button\n (click)=\"clearSearch()\"\n class=\"absolute right-4 top-1/2 transform -translate-y-1/2 text-gray-400 hover:text-gray-600 transition-colors\">\n <fa-icon [icon]=\"iconProvider.close\"></fa-icon>\n </button>\n }\n </div>\n }\n </div>\n </div>\n\n <!-- Loading State -->\n @if (data() === undefined) {\n <div class=\"flex items-center justify-center py-20\">\n <div class=\"text-center\">\n <fa-icon [icon]=\"iconProvider.loading\" [animation]=\"'spin'\" size=\"3x\" class=\"text-primary-500 mb-4\"></fa-icon>\n <p class=\"text-gray-600 text-lg\">Agenten werden geladen...</p>\n </div>\n </div>\n }\n\n <!-- Empty State -->\n @else if (!data()?.length) {\n <div class=\"flex items-center justify-center py-20\">\n <div class=\"text-center max-w-md\">\n <fa-icon [icon]=\"iconProvider.robot\" size=\"4x\" class=\"text-gray-300 mb-4\"></fa-icon>\n <h3 class=\"text-xl font-semibold text-gray-700 mb-2\">Keine Agenten vorhanden</h3>\n <p class=\"text-gray-500\">Es wurden noch keine KI-Agenten konfiguriert.</p>\n </div>\n </div>\n }\n\n <!-- No Results State -->\n @else if (filteredAgents().length === 0) {\n <div class=\"flex items-center justify-center py-20\">\n <div class=\"text-center max-w-md\">\n <fa-icon [icon]=\"iconProvider.search\" size=\"4x\" class=\"text-gray-300 mb-4\"></fa-icon>\n <h3 class=\"text-xl font-semibold text-gray-700 mb-2\">Keine Ergebnisse gefunden</h3>\n <p class=\"text-gray-500\">Versuche einen anderen Suchbegriff.</p>\n <button\n (click)=\"clearSearch()\"\n class=\"mt-4 px-4 py-2 bg-primary-600 text-white rounded-lg hover:bg-primary-700 transition-colors\">\n Suche zur\u00FCcksetzen\n </button>\n </div>\n </div>\n }\n\n <!-- Agents Grid -->\n @else {\n <div class=\"max-w-7xl mx-auto grid gap-4 sm:gap-6 grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4\">\n @for (agent of filteredAgents(); track agent.id) {\n <div\n class=\"group relative flex flex-col p-4 sm:p-6 rounded-xl sm:rounded-2xl bg-white border-2 border-gray-300 shadow-sm hover:shadow-xl hover:border-primary-400 hover:-translate-y-1 transition-all duration-300 cursor-pointer overflow-hidden active:scale-[0.98]\"\n [routerLink]=\"['agent/' + agent.id]\"\n >\n <!-- Gradient Overlay on Hover -->\n <div class=\"absolute inset-0 bg-gradient-to-br from-primary-50/0 to-primary-100/0 group-hover:from-primary-50/30 group-hover:to-primary-100/20 transition-all duration-300 rounded-2xl\"></div>\n\n <!-- Content -->\n <div class=\"relative z-10 flex flex-col h-full\">\n <!-- Header with Avatar and RAG Badge -->\n <div class=\"flex items-start justify-between mb-4\">\n <div class=\"flex items-center gap-3\">\n <div class=\"w-12 h-12 rounded-xl bg-gradient-to-br from-primary-500 to-primary-600 text-white flex items-center justify-center font-bold text-xl shadow-md group-hover:scale-110 transition-transform\">\n {{ agent.name[0].toUpperCase() }}\n </div>\n <div>\n <h3 class=\"text-lg font-bold text-gray-900 group-hover:text-primary-700 transition-colors line-clamp-1\">\n {{ agent.name }}\n </h3>\n </div>\n </div>\n @if (agent.ragProvider && agent.ragModel) {\n <div class=\"flex-shrink-0 px-2 py-1 bg-green-100 text-green-700 text-xs font-semibold rounded-lg border border-green-200\">\n <fa-icon [icon]=\"iconProvider.database\" class=\"mr-1\"></fa-icon>\n RAG\n </div>\n }\n </div>\n\n <!-- Description -->\n <p class=\"text-gray-600 text-sm leading-relaxed mb-4 line-clamp-3 flex-grow\">\n {{ agent.description }}\n </p>\n\n <!-- Tools Section -->\n <div class=\"pt-4 border-t border-gray-100\">\n <div class=\"flex items-center gap-2 mb-2\">\n <fa-icon [icon]=\"iconProvider.tools\" class=\"text-gray-400 text-xs\"></fa-icon>\n <span class=\"text-xs font-semibold text-gray-500 uppercase tracking-wide\">Tools</span>\n </div>\n @if (!agent.tools || agent.tools.length === 0) {\n <span class=\"text-xs text-gray-400 italic\">Keine Tools konfiguriert</span>\n } @else {\n <div class=\"flex flex-wrap gap-1.5\">\n @for (toolIdentifier of agent.tools.slice(0, 3); track toolIdentifier) {\n <span class=\"px-2 py-1 bg-primary-50 text-primary-700 text-xs font-medium rounded-md border border-primary-100\">\n {{ getToolName(toolIdentifier) }}\n </span>\n }\n @if (agent.tools.length > 3) {\n <span class=\"px-2 py-1 bg-gray-100 text-gray-600 text-xs font-medium rounded-md\">\n +{{ agent.tools.length - 3 }}\n </span>\n }\n </div>\n }\n </div>\n\n <!-- Hover Arrow -->\n <div class=\"absolute bottom-4 right-4 opacity-0 group-hover:opacity-100 transition-opacity\">\n <fa-icon [icon]=\"iconProvider.arrowRight\" class=\"text-primary-600\"></fa-icon>\n </div>\n </div>\n </div>\n }\n </div>\n }\n</div>", styles: [""], dependencies: [{ kind: "directive", type: RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "component", type: FaIconComponent, selector: "fa-icon", inputs: ["icon", "title", "animation", "mask", "flip", "size", "pull", "border", "inverse", "symbol", "rotate", "fixedWidth", "transform", "a11yRole"], outputs: ["iconChange", "titleChange", "animationChange", "maskChange", "flipChange", "sizeChange", "pullChange", "borderChange", "inverseChange", "symbolChange", "rotateChange", "fixedWidthChange", "transformChange", "a11yRoleChange"] }, { 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"] }] });
|
|
705
|
+
}
|
|
706
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.9", ngImport: i0, type: AgentSelectionComponent, decorators: [{
|
|
707
|
+
type: Component,
|
|
708
|
+
args: [{ selector: 'sftech-agent-selection', imports: [RouterLink, FaIconComponent, FormsModule], providers: [DialogService], template: "<div class=\"h-full overflow-y-auto px-4 sm:px-6 py-6 bg-gradient-to-b from-gray-50 to-white\">\n <!-- Header Section -->\n <div class=\"max-w-7xl mx-auto mb-6 sm:mb-8\">\n <div class=\"flex flex-col gap-4\">\n <div class=\"flex flex-col sm:flex-row sm:items-center sm:justify-between gap-3\">\n <div>\n <h1 class=\"text-2xl sm:text-3xl font-bold text-gray-900 flex items-center gap-3\">\n <fa-icon [icon]=\"iconProvider.robot\" class=\"text-primary-600\"></fa-icon>\n Agenten Auswahl\n </h1>\n <p class=\"text-gray-600 mt-1 text-sm sm:text-base\">W\u00E4hle einen KI-Agenten f\u00FCr deine Aufgabe</p>\n </div>\n @if (data() && data()!.length > 0) {\n <div class=\"text-sm text-gray-500 bg-white px-3 sm:px-4 py-2 rounded-lg border border-gray-200 self-start sm:self-auto\">\n <span class=\"font-semibold text-primary-600\">{{ filteredAgents().length }}</span>\n {{ filteredAgents().length === 1 ? 'Agent' : 'Agenten' }} verf\u00FCgbar\n </div>\n }\n </div>\n\n <!-- Search Bar -->\n @if (data() && data()!.length > 0) {\n <div class=\"relative\">\n <fa-icon [icon]=\"iconProvider.search\"\n class=\"absolute left-4 top-1/2 transform -translate-y-1/2 text-gray-400\"></fa-icon>\n <input\n type=\"text\"\n [(ngModel)]=\"searchTerm\"\n (input)=\"filterAgents()\"\n placeholder=\"Suche nach Agenten, Beschreibung oder Tools...\"\n class=\"w-full pl-12 pr-4 py-3 bg-white border border-gray-300 rounded-xl focus:outline-none focus:ring-2 focus:ring-primary-600 focus:border-primary-600 transition-all\"\n />\n @if (searchTerm) {\n <button\n (click)=\"clearSearch()\"\n class=\"absolute right-4 top-1/2 transform -translate-y-1/2 text-gray-400 hover:text-gray-600 transition-colors\">\n <fa-icon [icon]=\"iconProvider.close\"></fa-icon>\n </button>\n }\n </div>\n }\n </div>\n </div>\n\n <!-- Loading State -->\n @if (data() === undefined) {\n <div class=\"flex items-center justify-center py-20\">\n <div class=\"text-center\">\n <fa-icon [icon]=\"iconProvider.loading\" [animation]=\"'spin'\" size=\"3x\" class=\"text-primary-500 mb-4\"></fa-icon>\n <p class=\"text-gray-600 text-lg\">Agenten werden geladen...</p>\n </div>\n </div>\n }\n\n <!-- Empty State -->\n @else if (!data()?.length) {\n <div class=\"flex items-center justify-center py-20\">\n <div class=\"text-center max-w-md\">\n <fa-icon [icon]=\"iconProvider.robot\" size=\"4x\" class=\"text-gray-300 mb-4\"></fa-icon>\n <h3 class=\"text-xl font-semibold text-gray-700 mb-2\">Keine Agenten vorhanden</h3>\n <p class=\"text-gray-500\">Es wurden noch keine KI-Agenten konfiguriert.</p>\n </div>\n </div>\n }\n\n <!-- No Results State -->\n @else if (filteredAgents().length === 0) {\n <div class=\"flex items-center justify-center py-20\">\n <div class=\"text-center max-w-md\">\n <fa-icon [icon]=\"iconProvider.search\" size=\"4x\" class=\"text-gray-300 mb-4\"></fa-icon>\n <h3 class=\"text-xl font-semibold text-gray-700 mb-2\">Keine Ergebnisse gefunden</h3>\n <p class=\"text-gray-500\">Versuche einen anderen Suchbegriff.</p>\n <button\n (click)=\"clearSearch()\"\n class=\"mt-4 px-4 py-2 bg-primary-600 text-white rounded-lg hover:bg-primary-700 transition-colors\">\n Suche zur\u00FCcksetzen\n </button>\n </div>\n </div>\n }\n\n <!-- Agents Grid -->\n @else {\n <div class=\"max-w-7xl mx-auto grid gap-4 sm:gap-6 grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4\">\n @for (agent of filteredAgents(); track agent.id) {\n <div\n class=\"group relative flex flex-col p-4 sm:p-6 rounded-xl sm:rounded-2xl bg-white border-2 border-gray-300 shadow-sm hover:shadow-xl hover:border-primary-400 hover:-translate-y-1 transition-all duration-300 cursor-pointer overflow-hidden active:scale-[0.98]\"\n [routerLink]=\"['agent/' + agent.id]\"\n >\n <!-- Gradient Overlay on Hover -->\n <div class=\"absolute inset-0 bg-gradient-to-br from-primary-50/0 to-primary-100/0 group-hover:from-primary-50/30 group-hover:to-primary-100/20 transition-all duration-300 rounded-2xl\"></div>\n\n <!-- Content -->\n <div class=\"relative z-10 flex flex-col h-full\">\n <!-- Header with Avatar and RAG Badge -->\n <div class=\"flex items-start justify-between mb-4\">\n <div class=\"flex items-center gap-3\">\n <div class=\"w-12 h-12 rounded-xl bg-gradient-to-br from-primary-500 to-primary-600 text-white flex items-center justify-center font-bold text-xl shadow-md group-hover:scale-110 transition-transform\">\n {{ agent.name[0].toUpperCase() }}\n </div>\n <div>\n <h3 class=\"text-lg font-bold text-gray-900 group-hover:text-primary-700 transition-colors line-clamp-1\">\n {{ agent.name }}\n </h3>\n </div>\n </div>\n @if (agent.ragProvider && agent.ragModel) {\n <div class=\"flex-shrink-0 px-2 py-1 bg-green-100 text-green-700 text-xs font-semibold rounded-lg border border-green-200\">\n <fa-icon [icon]=\"iconProvider.database\" class=\"mr-1\"></fa-icon>\n RAG\n </div>\n }\n </div>\n\n <!-- Description -->\n <p class=\"text-gray-600 text-sm leading-relaxed mb-4 line-clamp-3 flex-grow\">\n {{ agent.description }}\n </p>\n\n <!-- Tools Section -->\n <div class=\"pt-4 border-t border-gray-100\">\n <div class=\"flex items-center gap-2 mb-2\">\n <fa-icon [icon]=\"iconProvider.tools\" class=\"text-gray-400 text-xs\"></fa-icon>\n <span class=\"text-xs font-semibold text-gray-500 uppercase tracking-wide\">Tools</span>\n </div>\n @if (!agent.tools || agent.tools.length === 0) {\n <span class=\"text-xs text-gray-400 italic\">Keine Tools konfiguriert</span>\n } @else {\n <div class=\"flex flex-wrap gap-1.5\">\n @for (toolIdentifier of agent.tools.slice(0, 3); track toolIdentifier) {\n <span class=\"px-2 py-1 bg-primary-50 text-primary-700 text-xs font-medium rounded-md border border-primary-100\">\n {{ getToolName(toolIdentifier) }}\n </span>\n }\n @if (agent.tools.length > 3) {\n <span class=\"px-2 py-1 bg-gray-100 text-gray-600 text-xs font-medium rounded-md\">\n +{{ agent.tools.length - 3 }}\n </span>\n }\n </div>\n }\n </div>\n\n <!-- Hover Arrow -->\n <div class=\"absolute bottom-4 right-4 opacity-0 group-hover:opacity-100 transition-opacity\">\n <fa-icon [icon]=\"iconProvider.arrowRight\" class=\"text-primary-600\"></fa-icon>\n </div>\n </div>\n </div>\n }\n </div>\n }\n</div>" }]
|
|
709
|
+
}], ctorParameters: () => [] });
|
|
710
|
+
|
|
711
|
+
class ChatsComponent {
|
|
712
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.9", ngImport: i0, type: ChatsComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
713
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.9", type: ChatsComponent, isStandalone: true, selector: "sftech-chats", ngImport: i0, template: '<router-outlet></router-outlet>', isInline: true, styles: [":host{display:block;height:100%;overflow:hidden}\n"], dependencies: [{ kind: "directive", type: RouterOutlet, selector: "router-outlet", inputs: ["name", "routerOutletData"], outputs: ["activate", "deactivate", "attach", "detach"], exportAs: ["outlet"] }] });
|
|
714
|
+
}
|
|
715
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.9", ngImport: i0, type: ChatsComponent, decorators: [{
|
|
716
|
+
type: Component,
|
|
717
|
+
args: [{ selector: 'sftech-chats', imports: [RouterOutlet], template: '<router-outlet></router-outlet>', styles: [":host{display:block;height:100%;overflow:hidden}\n"] }]
|
|
718
|
+
}] });
|
|
719
|
+
|
|
720
|
+
const chatsRoutes = [
|
|
721
|
+
{
|
|
722
|
+
path: '',
|
|
723
|
+
component: ChatsComponent,
|
|
724
|
+
children: [
|
|
725
|
+
{
|
|
726
|
+
path: '',
|
|
727
|
+
component: AgentSelectionComponent,
|
|
728
|
+
},
|
|
729
|
+
{
|
|
730
|
+
path: 'agent/:agentId/r/:agentRunId',
|
|
731
|
+
component: AgentChatComponent,
|
|
732
|
+
},
|
|
733
|
+
{
|
|
734
|
+
path: 'agent/:agentId',
|
|
735
|
+
component: AgentChatComponent,
|
|
736
|
+
},
|
|
737
|
+
],
|
|
738
|
+
canActivate: [authenticationGuard],
|
|
739
|
+
},
|
|
740
|
+
];
|
|
741
|
+
|
|
742
|
+
export { chatsRoutes };
|
|
743
|
+
//# sourceMappingURL=sftech-ng-orchestrator-chats.routes-CqFuG3l5.mjs.map
|