@dataclouder/ngx-agent-cards 0.0.86 → 0.0.88
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 +17 -7
- package/fesm2022/dataclouder-ngx-agent-cards.mjs +1162 -1477
- package/fesm2022/dataclouder-ngx-agent-cards.mjs.map +1 -1
- package/lib/components/chat-container/chat-container.component.d.ts +10 -24
- package/lib/components/chat-container/chat-footer/chat-footer.component.d.ts +16 -10
- package/lib/components/chat-container/chat-header/chat-header.component.d.ts +7 -8
- package/lib/components/chat-container/chat-messages-list/chat-message/chat-message.component.d.ts +16 -9
- package/lib/components/chat-container/chat-messages-list/chat-message/chat-message.utils.d.ts +3 -2
- package/lib/components/chat-container/chat-messages-list/chat-message/multi-message-content/multi-message-content.d.ts +17 -6
- package/lib/components/chat-container/chat-messages-list/chat-messages-list.component.d.ts +5 -3
- package/lib/components/chat-settings/dc-conversation-userchat-settings.component.d.ts +7 -8
- package/lib/components/dc-agent-card-details/dc-agent-card-details.component.d.ts +4 -6
- package/lib/components/dc-agent-card-lists/agent-card-default-ui/agent-card-default-ui.component.d.ts +5 -5
- package/lib/components/dc-agent-card-lists/dc-agent-card-lists.component.d.ts +9 -10
- package/lib/components/dc-agent-form/account-platform/account-platform-form.component.d.ts +3 -4
- package/lib/components/dc-agent-form/dc-agent-card-form.component.d.ts +16 -18
- package/lib/components/icons/icons.component.d.ts +7 -7
- package/lib/components/prompt-preview-dialog/prompt-preview-dialog.component.d.ts +3 -2
- package/lib/components/provider-selector/provider-selector.component.d.ts +3 -3
- package/lib/components/standalone-audio-text-sync/standalone-audio-text-sync.component.d.ts +62 -0
- package/lib/components/translate-dialog/translate-dialog.component.d.ts +3 -5
- package/lib/models/agent.models.d.ts +2 -0
- package/lib/models/conversation-ai.class.d.ts +2 -2
- package/lib/services/conversation.service.d.ts +2 -3
- package/lib/services/dc-conversation-builder.service.d.ts +2 -2
- package/lib/services/evaluation.service.d.ts +2 -2
- package/package.json +1 -1
- package/public-api.d.ts +3 -1
- package/lib/components/audio-text-sync/audio-text-sync.component.d.ts +0 -42
- package/lib/components/chat/dc-chat.component.d.ts +0 -58
- package/lib/components/chat-container/chat-messages-list/chat-message/message-content/message-content.component.d.ts +0 -24
- /package/lib/components/{chat → chat-container}/chat.models.d.ts +0 -0
|
@@ -1,66 +1,62 @@
|
|
|
1
1
|
import * as i0 from '@angular/core';
|
|
2
|
-
import { InjectionToken, Injectable,
|
|
3
|
-
import * as i1$
|
|
4
|
-
import { CommonModule, NgComponentOutlet } from '@angular/common';
|
|
5
|
-
import
|
|
6
|
-
import
|
|
7
|
-
import * as i6 from 'primeng/dialog';
|
|
2
|
+
import { InjectionToken, Injectable, inject, input, output, Input, Component, signal, DestroyRef, computed, ChangeDetectorRef, effect, ChangeDetectionStrategy, Pipe, ViewChild, ViewChildren } from '@angular/core';
|
|
3
|
+
import * as i1$1 from '@angular/common';
|
|
4
|
+
import { CommonModule, DatePipe, DecimalPipe, NgComponentOutlet } from '@angular/common';
|
|
5
|
+
import { DynamicDialogRef, DialogService, DynamicDialogConfig, DynamicDialogModule } from 'primeng/dynamicdialog';
|
|
6
|
+
import * as i2$4 from 'primeng/dialog';
|
|
8
7
|
import { DialogModule } from 'primeng/dialog';
|
|
9
8
|
import * as i2 from 'primeng/progressbar';
|
|
10
9
|
import { ProgressBarModule } from 'primeng/progressbar';
|
|
11
10
|
import * as i1 from '@angular/forms';
|
|
12
|
-
import { FormControl, ReactiveFormsModule, FormsModule } from '@angular/forms';
|
|
11
|
+
import { FormControl, ReactiveFormsModule, FormBuilder, FormsModule } from '@angular/forms';
|
|
13
12
|
import { DCMicComponent } from '@dataclouder/ngx-mic';
|
|
14
13
|
import * as i3 from 'primeng/textarea';
|
|
15
14
|
import { TextareaModule } from 'primeng/textarea';
|
|
16
|
-
import * as
|
|
15
|
+
import * as i2$1 from 'primeng/button';
|
|
17
16
|
import { ButtonModule } from 'primeng/button';
|
|
18
|
-
import * as i1$1 from '@angular/platform-browser';
|
|
19
17
|
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
|
|
20
18
|
import { BehaviorSubject, Subject, fromEvent, filter } from 'rxjs';
|
|
21
19
|
import { takeUntil, map } from 'rxjs/operators';
|
|
22
|
-
import
|
|
20
|
+
import { DomSanitizer } from '@angular/platform-browser';
|
|
21
|
+
import { AudioSpeed as AudioSpeed$1, TOAST_ALERTS_TOKEN, PaginationBase, DCFilterBarComponent, QuickTableComponent } from '@dataclouder/ngx-core';
|
|
22
|
+
import * as i1$2 from 'primeng/skeleton';
|
|
23
23
|
import { SkeletonModule } from 'primeng/skeleton';
|
|
24
|
-
import * as
|
|
24
|
+
import * as i2$3 from 'primeng/checkbox';
|
|
25
25
|
import { CheckboxModule } from 'primeng/checkbox';
|
|
26
26
|
import { SliderModule } from 'primeng/slider';
|
|
27
|
-
import * as
|
|
27
|
+
import * as i2$2 from 'primeng/radiobutton';
|
|
28
28
|
import { RadioButtonModule } from 'primeng/radiobutton';
|
|
29
|
-
import * as
|
|
29
|
+
import * as i6$1 from 'primeng/rating';
|
|
30
30
|
import { RatingModule } from 'primeng/rating';
|
|
31
|
-
import * as
|
|
31
|
+
import * as i4 from 'primeng/table';
|
|
32
32
|
import { TableModule } from 'primeng/table';
|
|
33
33
|
import { BadgeModule } from 'primeng/badge';
|
|
34
|
-
import * as
|
|
34
|
+
import * as i6 from 'primeng/tooltip';
|
|
35
35
|
import { TooltipModule } from 'primeng/tooltip';
|
|
36
|
-
import * as
|
|
37
|
-
import
|
|
38
|
-
import { TOAST_ALERTS_TOKEN, AudioSpeed as AudioSpeed$1, PaginationBase, DCFilterBarComponent, QuickTableComponent } from '@dataclouder/ngx-core';
|
|
36
|
+
import * as i3$1 from 'primeng/api';
|
|
37
|
+
import { ActivatedRoute, Router } from '@angular/router';
|
|
39
38
|
import { OverlayModule } from '@angular/cdk/overlay';
|
|
40
39
|
import { PortalModule } from '@angular/cdk/portal';
|
|
41
|
-
import * as
|
|
40
|
+
import * as i4$1 from 'primeng/inputtext';
|
|
42
41
|
import { InputTextModule } from 'primeng/inputtext';
|
|
43
|
-
import * as
|
|
42
|
+
import * as i6$2 from 'primeng/togglebutton';
|
|
44
43
|
import { ToggleButtonModule } from 'primeng/togglebutton';
|
|
45
|
-
import
|
|
46
|
-
import {
|
|
47
|
-
import * as
|
|
48
|
-
import { DIALOG_DATA } from '@angular/cdk/dialog';
|
|
49
|
-
import * as i13 from 'primeng/toggleswitch';
|
|
44
|
+
import { ResolutionType, AspectType, MultiImagesStorageService, CropperComponentModal } from '@dataclouder/ngx-cloud-storage';
|
|
45
|
+
import { DialogRef, DIALOG_DATA } from '@angular/cdk/dialog';
|
|
46
|
+
import * as i8 from 'primeng/toggleswitch';
|
|
50
47
|
import { ToggleSwitchModule } from 'primeng/toggleswitch';
|
|
51
|
-
import * as
|
|
48
|
+
import * as i9 from 'primeng/select';
|
|
52
49
|
import { SelectModule } from 'primeng/select';
|
|
53
|
-
import * as
|
|
50
|
+
import * as i10 from 'primeng/popover';
|
|
54
51
|
import { PopoverModule } from 'primeng/popover';
|
|
55
|
-
import * as
|
|
52
|
+
import * as i2$5 from 'primeng/card';
|
|
56
53
|
import { CardModule } from 'primeng/card';
|
|
57
|
-
import * as
|
|
54
|
+
import * as i3$2 from 'primeng/dropdown';
|
|
58
55
|
import { DropdownModule } from 'primeng/dropdown';
|
|
59
56
|
import { ChipModule } from 'primeng/chip';
|
|
60
|
-
import * as i1$
|
|
61
|
-
import * as i3$3 from 'primeng/paginator';
|
|
57
|
+
import * as i1$3 from 'primeng/paginator';
|
|
62
58
|
import { PaginatorModule } from 'primeng/paginator';
|
|
63
|
-
import * as i2$
|
|
59
|
+
import * as i2$6 from 'primeng/speeddial';
|
|
64
60
|
import { SpeedDialModule } from 'primeng/speeddial';
|
|
65
61
|
|
|
66
62
|
const characterCardStringDataDefinition = `
|
|
@@ -512,10 +508,10 @@ class AudioService {
|
|
|
512
508
|
// const srcURL = await this.filesCacheService.getURLSrcFile(path);
|
|
513
509
|
this.playWithSrc(path);
|
|
514
510
|
}
|
|
515
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.
|
|
516
|
-
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.
|
|
511
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: AudioService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
512
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: AudioService, providedIn: 'root' }); }
|
|
517
513
|
}
|
|
518
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.
|
|
514
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: AudioService, decorators: [{
|
|
519
515
|
type: Injectable,
|
|
520
516
|
args: [{
|
|
521
517
|
providedIn: 'root',
|
|
@@ -535,8 +531,8 @@ class UserDataExchangeAbstractService {
|
|
|
535
531
|
}
|
|
536
532
|
|
|
537
533
|
class DCConversationPromptBuilderService {
|
|
538
|
-
constructor(
|
|
539
|
-
this.userDataExchange =
|
|
534
|
+
constructor() {
|
|
535
|
+
this.userDataExchange = inject(USER_DATA_EXCHANGE);
|
|
540
536
|
}
|
|
541
537
|
// For chat conversation i need inital settings.
|
|
542
538
|
buildConversationSettings(agentCard, parseDict = null) {
|
|
@@ -678,27 +674,24 @@ class DCConversationPromptBuilderService {
|
|
|
678
674
|
};
|
|
679
675
|
return settings;
|
|
680
676
|
}
|
|
681
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.
|
|
682
|
-
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.
|
|
677
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: DCConversationPromptBuilderService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
678
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: DCConversationPromptBuilderService, providedIn: 'root' }); }
|
|
683
679
|
}
|
|
684
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.
|
|
680
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: DCConversationPromptBuilderService, decorators: [{
|
|
685
681
|
type: Injectable,
|
|
686
682
|
args: [{
|
|
687
683
|
providedIn: 'root',
|
|
688
684
|
}]
|
|
689
|
-
}], ctorParameters: () => [
|
|
690
|
-
type: Inject,
|
|
691
|
-
args: [USER_DATA_EXCHANGE]
|
|
692
|
-
}] }] });
|
|
685
|
+
}], ctorParameters: () => [] });
|
|
693
686
|
|
|
694
687
|
class ChatHeaderComponent {
|
|
695
|
-
constructor(
|
|
696
|
-
this.agentCardService =
|
|
697
|
-
this.isAdmin = false;
|
|
688
|
+
constructor() {
|
|
689
|
+
this.agentCardService = inject(CONVERSATION_AI_TOKEN);
|
|
690
|
+
this.isAdmin = input(false);
|
|
698
691
|
this.alternativeConversation = [];
|
|
699
|
-
this.restartConversationEvent =
|
|
700
|
-
this.showInfoEvent =
|
|
701
|
-
this.settingsClickEvent =
|
|
692
|
+
this.restartConversationEvent = output();
|
|
693
|
+
this.showInfoEvent = output();
|
|
694
|
+
this.settingsClickEvent = output();
|
|
702
695
|
}
|
|
703
696
|
restartConversation(conversation = null) {
|
|
704
697
|
this.restartConversationEvent.emit(conversation);
|
|
@@ -723,87 +716,16 @@ class ChatHeaderComponent {
|
|
|
723
716
|
this.alternativeConversation = conversationCards.rows;
|
|
724
717
|
}
|
|
725
718
|
}
|
|
726
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.
|
|
727
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.
|
|
719
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: ChatHeaderComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
720
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.4", type: ChatHeaderComponent, isStandalone: true, selector: "dc-chat-header", inputs: { isAdmin: { classPropertyName: "isAdmin", publicName: "isAdmin", isSignal: true, isRequired: false, transformFunction: null }, alternativeConversation: { classPropertyName: "alternativeConversation", publicName: "alternativeConversation", isSignal: false, isRequired: false, transformFunction: null }, agentCard: { classPropertyName: "agentCard", publicName: "agentCard", isSignal: false, isRequired: false, transformFunction: null } }, outputs: { restartConversationEvent: "restartConversationEvent", showInfoEvent: "showInfoEvent", settingsClickEvent: "settingsClickEvent" }, ngImport: i0, template: "<div class=\"chat-header\">\n <span class=\"pointer\" (click)=\"restartConversation()\">\n @if (agentCard?.title) {\n {{ agentCard.title }}\n } @else {\n Reiniciar conversaci\u00F3n\n }\n </span>\n\n @for (conversation of alternativeConversation; track conversation._id) {\n <span class=\"pointer\" (click)=\"restartConversation(conversation)\"> {{ conversation.title }} </span>\n }\n\n <div class=\"header-controls\">\n @if (isAdmin()){\n <div class=\"admin-controls\">\n <span class=\"pointer\" (click)=\"changeConversationCard()\"> \uD83D\uDD04 </span>\n <span class=\"pointer\" (click)=\"showInfo()\"> \u26A1\uFE0F </span>\n </div>\n }\n\n <div>\n <span class=\"pointer\" (click)=\"settingsClick()\"> \u2699\uFE0F </span>\n </div>\n </div>\n</div>\n", styles: [".chat-header{display:flex;justify-content:space-between;align-items:center;width:100%}.pointer{cursor:pointer}.header-controls{font-size:large;display:flex;justify-content:space-between;gap:10px}.admin-controls{background-color:bisque;padding:2px 5px;border-radius:4px}\n"] }); }
|
|
728
721
|
}
|
|
729
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.
|
|
722
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: ChatHeaderComponent, decorators: [{
|
|
730
723
|
type: Component,
|
|
731
|
-
args: [{ selector: 'dc-chat-header', standalone: true, imports: [
|
|
732
|
-
}], ctorParameters: () => [
|
|
733
|
-
type: Inject,
|
|
734
|
-
args: [CONVERSATION_AI_TOKEN]
|
|
735
|
-
}] }], propDecorators: { isAdmin: [{
|
|
736
|
-
type: Input
|
|
737
|
-
}], alternativeConversation: [{
|
|
724
|
+
args: [{ selector: 'dc-chat-header', standalone: true, imports: [], template: "<div class=\"chat-header\">\n <span class=\"pointer\" (click)=\"restartConversation()\">\n @if (agentCard?.title) {\n {{ agentCard.title }}\n } @else {\n Reiniciar conversaci\u00F3n\n }\n </span>\n\n @for (conversation of alternativeConversation; track conversation._id) {\n <span class=\"pointer\" (click)=\"restartConversation(conversation)\"> {{ conversation.title }} </span>\n }\n\n <div class=\"header-controls\">\n @if (isAdmin()){\n <div class=\"admin-controls\">\n <span class=\"pointer\" (click)=\"changeConversationCard()\"> \uD83D\uDD04 </span>\n <span class=\"pointer\" (click)=\"showInfo()\"> \u26A1\uFE0F </span>\n </div>\n }\n\n <div>\n <span class=\"pointer\" (click)=\"settingsClick()\"> \u2699\uFE0F </span>\n </div>\n </div>\n</div>\n", styles: [".chat-header{display:flex;justify-content:space-between;align-items:center;width:100%}.pointer{cursor:pointer}.header-controls{font-size:large;display:flex;justify-content:space-between;gap:10px}.admin-controls{background-color:bisque;padding:2px 5px;border-radius:4px}\n"] }]
|
|
725
|
+
}], ctorParameters: () => [], propDecorators: { alternativeConversation: [{
|
|
738
726
|
type: Input
|
|
739
727
|
}], agentCard: [{
|
|
740
728
|
type: Input
|
|
741
|
-
}], restartConversationEvent: [{
|
|
742
|
-
type: Output
|
|
743
|
-
}], showInfoEvent: [{
|
|
744
|
-
type: Output
|
|
745
|
-
}], settingsClickEvent: [{
|
|
746
|
-
type: Output
|
|
747
|
-
}] } });
|
|
748
|
-
|
|
749
|
-
class ChatFooterComponent {
|
|
750
|
-
constructor() {
|
|
751
|
-
this.isAIThinking = false;
|
|
752
|
-
this.score = 0;
|
|
753
|
-
this.micSettings = { useWhisper: true, lang: 'en' };
|
|
754
|
-
this.sendMessage = new EventEmitter();
|
|
755
|
-
this.textInputChanged = new EventEmitter();
|
|
756
|
-
this.micFinishedEvent = new EventEmitter();
|
|
757
|
-
this.chatInputControl = new FormControl();
|
|
758
|
-
}
|
|
759
|
-
/**
|
|
760
|
-
* Sets the input text in the textarea
|
|
761
|
-
* @param text The text to set
|
|
762
|
-
*/
|
|
763
|
-
setInputText(text) {
|
|
764
|
-
this.chatInputControl.setValue(text);
|
|
765
|
-
this.textInputChanged.emit(text);
|
|
766
|
-
}
|
|
767
|
-
/**
|
|
768
|
-
* Handles the mic finished event
|
|
769
|
-
* @param eventBlob The blob event from the mic component
|
|
770
|
-
*/
|
|
771
|
-
micFinished(eventBlob) {
|
|
772
|
-
this.micFinishedEvent.emit(eventBlob);
|
|
773
|
-
}
|
|
774
|
-
/**
|
|
775
|
-
* Sends the user message
|
|
776
|
-
*/
|
|
777
|
-
sendUserMessage() {
|
|
778
|
-
if (this.isAIThinking || !this.chatInputControl.value) {
|
|
779
|
-
return;
|
|
780
|
-
}
|
|
781
|
-
const text = this.chatInputControl.value;
|
|
782
|
-
const message = {
|
|
783
|
-
content: text,
|
|
784
|
-
role: ChatRole.User,
|
|
785
|
-
};
|
|
786
|
-
this.sendMessage.emit(message);
|
|
787
|
-
this.chatInputControl.setValue('');
|
|
788
|
-
}
|
|
789
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.1.1", ngImport: i0, type: ChatFooterComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
790
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.1.1", type: ChatFooterComponent, isStandalone: true, selector: "dc-chat-footer", inputs: { isAIThinking: "isAIThinking", score: "score", micSettings: "micSettings" }, outputs: { sendMessage: "sendMessage", textInputChanged: "textInputChanged", micFinishedEvent: "micFinishedEvent" }, ngImport: i0, template: "<div class=\"progress-input\">\n <div class=\"input-container\">\n <dc-mic\n style=\"display: flex; align-items: center\"\n (onInterpretedText)=\"setInputText($event)\"\n (onFinished)=\"micFinished($event)\"\n [micSettings]=\"micSettings\"></dc-mic>\n\n <textarea pTextarea [formControl]=\"chatInputControl\" (keyup.enter)=\"sendUserMessage()\" rows=\"1\"></textarea>\n\n <p-button (click)=\"sendUserMessage()\" [disabled]=\"isAIThinking || !chatInputControl.value\" label=\"Enviar\" [rounded]=\"true\" />\n </div>\n\n <div>\n <p-progressbar showValue=\"false\" [value]=\"score\" [style]=\"{ height: '6px' }\" />\n </div>\n</div>\n", styles: [".progress-input{padding:10px;background-color:#f5f5f545;border-top:1px solid #b1a8a8}.progress-input .input-container{display:flex;align-items:center;margin-bottom:5px}.progress-input .input-container textarea{flex:1;resize:none;margin:0 10px}.progress-input .input-container .send-button{background-color:#007bff;color:#fff;border:none;border-radius:4px;padding:8px 15px;cursor:pointer}.progress-input .input-container .send-button:disabled{background-color:#ccc;cursor:not-allowed}.progress-input .input-container .send-button:hover:not(:disabled){background-color:#0069d9}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.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.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "ngmodule", type: ProgressBarModule }, { kind: "component", type: i2.ProgressBar, selector: "p-progressBar, p-progressbar, p-progress-bar", inputs: ["value", "showValue", "styleClass", "valueStyleClass", "style", "unit", "mode", "color"] }, { kind: "component", type: DCMicComponent, selector: "dc-mic", inputs: ["isDone", "useWhisper", "targetOrBase", "micSettings"], outputs: ["onInterpretedText", "onFinishedRecognition", "onFinished"] }, { kind: "ngmodule", type: TextareaModule }, { kind: "directive", type: i3.Textarea, selector: "[pTextarea]", inputs: ["autoResize", "variant", "fluid", "pSize"], outputs: ["onResize"] }, { kind: "ngmodule", type: ButtonModule }, { kind: "component", type: i7.Button, selector: "p-button", inputs: ["type", "iconPos", "icon", "badge", "label", "disabled", "loading", "loadingIcon", "raised", "rounded", "text", "plain", "severity", "outlined", "link", "tabindex", "size", "variant", "style", "styleClass", "badgeClass", "badgeSeverity", "ariaLabel", "autofocus", "fluid", "buttonProps"], outputs: ["onClick", "onFocus", "onBlur"] }] }); }
|
|
791
|
-
}
|
|
792
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.1.1", ngImport: i0, type: ChatFooterComponent, decorators: [{
|
|
793
|
-
type: Component,
|
|
794
|
-
args: [{ selector: 'dc-chat-footer', standalone: true, imports: [CommonModule, ReactiveFormsModule, ProgressBarModule, DCMicComponent, TextareaModule, ButtonModule], template: "<div class=\"progress-input\">\n <div class=\"input-container\">\n <dc-mic\n style=\"display: flex; align-items: center\"\n (onInterpretedText)=\"setInputText($event)\"\n (onFinished)=\"micFinished($event)\"\n [micSettings]=\"micSettings\"></dc-mic>\n\n <textarea pTextarea [formControl]=\"chatInputControl\" (keyup.enter)=\"sendUserMessage()\" rows=\"1\"></textarea>\n\n <p-button (click)=\"sendUserMessage()\" [disabled]=\"isAIThinking || !chatInputControl.value\" label=\"Enviar\" [rounded]=\"true\" />\n </div>\n\n <div>\n <p-progressbar showValue=\"false\" [value]=\"score\" [style]=\"{ height: '6px' }\" />\n </div>\n</div>\n", styles: [".progress-input{padding:10px;background-color:#f5f5f545;border-top:1px solid #b1a8a8}.progress-input .input-container{display:flex;align-items:center;margin-bottom:5px}.progress-input .input-container textarea{flex:1;resize:none;margin:0 10px}.progress-input .input-container .send-button{background-color:#007bff;color:#fff;border:none;border-radius:4px;padding:8px 15px;cursor:pointer}.progress-input .input-container .send-button:disabled{background-color:#ccc;cursor:not-allowed}.progress-input .input-container .send-button:hover:not(:disabled){background-color:#0069d9}\n"] }]
|
|
795
|
-
}], propDecorators: { isAIThinking: [{
|
|
796
|
-
type: Input
|
|
797
|
-
}], score: [{
|
|
798
|
-
type: Input
|
|
799
|
-
}], micSettings: [{
|
|
800
|
-
type: Input
|
|
801
|
-
}], sendMessage: [{
|
|
802
|
-
type: Output
|
|
803
|
-
}], textInputChanged: [{
|
|
804
|
-
type: Output
|
|
805
|
-
}], micFinishedEvent: [{
|
|
806
|
-
type: Output
|
|
807
729
|
}] } });
|
|
808
730
|
|
|
809
731
|
function markdownToHtml(markdown) {
|
|
@@ -1007,119 +929,452 @@ function removeAllEmojis(text) {
|
|
|
1007
929
|
return text.replace(/[\p{Emoji_Presentation}\p{Extended_Pictographic}\u{1F3FB}-\u{1F3FF}\u{E0020}-\u{E007F}\u{FE00}-\u{FE0F}\u{1F900}-\u{1F9FF}\u{1F1E6}-\u{1F1FF}]/gu, '');
|
|
1008
930
|
}
|
|
1009
931
|
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
<line x1="2" y1="12" x2="6" y2="12"></line>
|
|
1042
|
-
<line x1="18" y1="12" x2="22" y2="12"></line>
|
|
1043
|
-
<line x1="4.93" y1="19.07" x2="7.76" y2="16.24"></line>
|
|
1044
|
-
<line x1="16.24" y1="7.76" x2="19.07" y2="4.93"></line>
|
|
1045
|
-
</svg>`,
|
|
1046
|
-
};
|
|
1047
|
-
|
|
1048
|
-
class IconsComponent {
|
|
1049
|
-
constructor(sanitizer) {
|
|
1050
|
-
this.sanitizer = sanitizer;
|
|
1051
|
-
this.size = 14;
|
|
1052
|
-
this.color = 'currentColor';
|
|
932
|
+
class MessageProcessingService {
|
|
933
|
+
constructor() { }
|
|
934
|
+
// Process message for display
|
|
935
|
+
processMessage(message, conversationSettings, mutate = false) {
|
|
936
|
+
const defaultVoice = this.getVoice(conversationSettings.voice);
|
|
937
|
+
let processedMessage;
|
|
938
|
+
if (mutate) {
|
|
939
|
+
// Modify the existing message object
|
|
940
|
+
message.voice = defaultVoice;
|
|
941
|
+
processedMessage = message;
|
|
942
|
+
}
|
|
943
|
+
else {
|
|
944
|
+
// Create a new message object
|
|
945
|
+
processedMessage = {
|
|
946
|
+
content: message.content,
|
|
947
|
+
role: message.role,
|
|
948
|
+
voice: defaultVoice
|
|
949
|
+
};
|
|
950
|
+
}
|
|
951
|
+
// Process based on text engine
|
|
952
|
+
if (conversationSettings.textEngine === TextEngines.MarkdownMultiMessages) {
|
|
953
|
+
this.processMultiMessages(processedMessage, conversationSettings);
|
|
954
|
+
}
|
|
955
|
+
else if (conversationSettings.textEngine === TextEngines.MarkdownSSML) {
|
|
956
|
+
if (!conversationSettings.secondaryVoice) {
|
|
957
|
+
throw new Error('Secondary voice is required for SSML');
|
|
958
|
+
}
|
|
959
|
+
const content = this.subsItalicsByTag(processedMessage.content, conversationSettings.secondaryVoice);
|
|
960
|
+
processedMessage.ssml = '<speak>' + content + '</speak>';
|
|
961
|
+
}
|
|
962
|
+
return processedMessage;
|
|
1053
963
|
}
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
964
|
+
// Process multi-messages from markdown
|
|
965
|
+
processMultiMessages(message, settings) {
|
|
966
|
+
// Convert markdown to HTML segments
|
|
967
|
+
const mdToHtml = convertToHTML(message.content);
|
|
968
|
+
// Map segments to multi-messages
|
|
969
|
+
message.multiMessages = mdToHtml.map((val) => {
|
|
970
|
+
// Get the narrator voice from conversation settings
|
|
971
|
+
const narratorVoice = settings.secondaryVoice || 'en-US-News-L';
|
|
972
|
+
// Determine if this is italicized text (narrator)
|
|
973
|
+
const isItalics = val.tag === 'em';
|
|
974
|
+
const voice = isItalics ? narratorVoice : message.voice;
|
|
975
|
+
return {
|
|
976
|
+
voice,
|
|
977
|
+
content: val.content,
|
|
978
|
+
audioUrl: null,
|
|
979
|
+
audioPromise: null,
|
|
980
|
+
text: val.text,
|
|
981
|
+
tag: val.tag
|
|
982
|
+
};
|
|
983
|
+
});
|
|
984
|
+
}
|
|
985
|
+
// Replace italics with voice tags for SSML
|
|
986
|
+
subsItalicsByTag(text, voiceId = 'it-IT-Neural2-A', tagName = 'voice') {
|
|
987
|
+
const regex = /\*(.*?)\*/g;
|
|
988
|
+
return text.replace(regex, (match, p1) => `<${tagName} name="${voiceId}">${p1}</${tagName}>`);
|
|
989
|
+
}
|
|
990
|
+
// Get appropriate voice based on settings
|
|
991
|
+
getVoice(voice_value, targetLang = 'en') {
|
|
992
|
+
if ([null, '', 'random'].includes(voice_value)) {
|
|
993
|
+
const voiceCodes = VoiceTTSOptions.map((val) => val.id);
|
|
994
|
+
return voiceCodes[Math.floor(Math.random() * voiceCodes.length)];
|
|
995
|
+
}
|
|
996
|
+
if (voice_value === 'randomMan') {
|
|
997
|
+
const voiceCodes = VoiceTTSOptions.filter((voice) => voice.gender === 'male' && voice.lang.includes(targetLang)).map((val) => val.id);
|
|
998
|
+
return voiceCodes[Math.floor(Math.random() * voiceCodes.length)];
|
|
999
|
+
}
|
|
1000
|
+
else if (voice_value === 'randomWoman') {
|
|
1001
|
+
const voiceCodes = VoiceTTSOptions.filter((voice) => voice.gender === 'female' && voice.lang.includes(targetLang)).map((val) => val.id);
|
|
1002
|
+
return voiceCodes[Math.floor(Math.random() * voiceCodes.length)];
|
|
1003
|
+
}
|
|
1004
|
+
else {
|
|
1005
|
+
const voice = VoiceTTSOptions.find((voice) => voice.id === voice_value);
|
|
1006
|
+
if (voice) {
|
|
1007
|
+
return voice.id;
|
|
1008
|
+
}
|
|
1009
|
+
else {
|
|
1010
|
+
console.error('Voice not found getting something random', voice_value);
|
|
1011
|
+
return VoiceTTSOptions.find((voice) => voice.lang.includes(targetLang))?.id || '';
|
|
1012
|
+
}
|
|
1058
1013
|
}
|
|
1059
1014
|
}
|
|
1060
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.
|
|
1061
|
-
static { this.ɵ
|
|
1062
|
-
<span
|
|
1063
|
-
[innerHTML]="sanitizedIcon"
|
|
1064
|
-
[style.display]="'inline-flex'"
|
|
1065
|
-
[style.alignItems]="'center'"
|
|
1066
|
-
[style.justifyContent]="'center'"
|
|
1067
|
-
[style.width.px]="size"
|
|
1068
|
-
[style.height.px]="size"
|
|
1069
|
-
[style.color]="color"></span>
|
|
1070
|
-
`, isInline: true, styles: [":host{display:inline-flex;align-items:center;line-height:0}span{line-height:0}:host ::ng-deep svg{width:100%;height:100%}\n"] }); }
|
|
1015
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: MessageProcessingService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
1016
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: MessageProcessingService, providedIn: 'root' }); }
|
|
1071
1017
|
}
|
|
1072
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.
|
|
1073
|
-
type:
|
|
1074
|
-
args: [{
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
[style.alignItems]="'center'"
|
|
1079
|
-
[style.justifyContent]="'center'"
|
|
1080
|
-
[style.width.px]="size"
|
|
1081
|
-
[style.height.px]="size"
|
|
1082
|
-
[style.color]="color"></span>
|
|
1083
|
-
`, standalone: true, styles: [":host{display:inline-flex;align-items:center;line-height:0}span{line-height:0}:host ::ng-deep svg{width:100%;height:100%}\n"] }]
|
|
1084
|
-
}], ctorParameters: () => [{ type: i1$1.DomSanitizer }], propDecorators: { name: [{
|
|
1085
|
-
type: Input
|
|
1086
|
-
}], size: [{
|
|
1087
|
-
type: Input
|
|
1088
|
-
}], color: [{
|
|
1089
|
-
type: Input
|
|
1090
|
-
}] } });
|
|
1018
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: MessageProcessingService, decorators: [{
|
|
1019
|
+
type: Injectable,
|
|
1020
|
+
args: [{
|
|
1021
|
+
providedIn: 'root',
|
|
1022
|
+
}]
|
|
1023
|
+
}], ctorParameters: () => [] });
|
|
1091
1024
|
|
|
1092
|
-
class
|
|
1025
|
+
class ConversationService {
|
|
1093
1026
|
constructor() {
|
|
1094
|
-
|
|
1095
|
-
this.
|
|
1096
|
-
this.
|
|
1097
|
-
|
|
1098
|
-
this.
|
|
1099
|
-
this.
|
|
1100
|
-
this.
|
|
1101
|
-
// Ensure cleanup when service is destroyed
|
|
1102
|
-
this.destroyRef.onDestroy(() => {
|
|
1103
|
-
this.stopAllSyncs();
|
|
1104
|
-
});
|
|
1027
|
+
this.agentCardService = inject(CONVERSATION_AI_TOKEN);
|
|
1028
|
+
this.messageProcessingService = inject(MessageProcessingService);
|
|
1029
|
+
this.messagesSignal = signal([]);
|
|
1030
|
+
this.isThinkingSignal = signal(false);
|
|
1031
|
+
this.conversationSettingsSignal = signal(null);
|
|
1032
|
+
this.agentCardSignal = signal(null);
|
|
1033
|
+
this.isDestroyedSignal = signal(false);
|
|
1105
1034
|
}
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1035
|
+
// Get messages as a signal
|
|
1036
|
+
getMessages() {
|
|
1037
|
+
return this.messagesSignal;
|
|
1038
|
+
}
|
|
1039
|
+
// Get thinking state as a signal
|
|
1040
|
+
isThinking() {
|
|
1041
|
+
return this.isThinkingSignal;
|
|
1042
|
+
}
|
|
1043
|
+
// Get conversation settings as a signal
|
|
1044
|
+
getConversationSettings() {
|
|
1045
|
+
return this.conversationSettingsSignal;
|
|
1046
|
+
}
|
|
1047
|
+
// Get agent card as a signal
|
|
1048
|
+
getAgentCard() {
|
|
1049
|
+
return this.agentCardSignal;
|
|
1050
|
+
}
|
|
1051
|
+
// Set destroyed state
|
|
1052
|
+
setDestroyed(value) {
|
|
1053
|
+
this.isDestroyedSignal.set(value);
|
|
1054
|
+
}
|
|
1055
|
+
// Initialize conversation
|
|
1056
|
+
async initConversation(agentCard, conversationBuilder, parseDict) {
|
|
1057
|
+
if (!agentCard?.conversationSettings) {
|
|
1058
|
+
throw new Error('Conversation settings are required');
|
|
1118
1059
|
}
|
|
1119
|
-
|
|
1120
|
-
|
|
1060
|
+
this.agentCardSignal.set(agentCard);
|
|
1061
|
+
// Build conversation settings
|
|
1062
|
+
const conversationSettings = conversationBuilder.buildConversationSettings(agentCard, parseDict);
|
|
1063
|
+
this.conversationSettingsSignal.set(conversationSettings);
|
|
1064
|
+
// Update agent card with conversation settings
|
|
1065
|
+
const updatedAgentCard = { ...agentCard, conversationSettings };
|
|
1066
|
+
this.agentCardSignal.set(updatedAgentCard);
|
|
1067
|
+
if (!conversationSettings.messages) {
|
|
1068
|
+
throw new Error('conversationSettings.messages is required in proper format to start conversation');
|
|
1121
1069
|
}
|
|
1122
|
-
//
|
|
1070
|
+
// Set initial messages
|
|
1071
|
+
this.messagesSignal.set(conversationSettings.messages);
|
|
1072
|
+
// Find first assistant message
|
|
1073
|
+
const firstAssistantMsg = conversationSettings.messages.find((message) => message.role === ChatRole.Assistant);
|
|
1074
|
+
if (firstAssistantMsg) {
|
|
1075
|
+
// Process the first assistant message
|
|
1076
|
+
this.processAssistantMessage(firstAssistantMsg, true);
|
|
1077
|
+
}
|
|
1078
|
+
else if (agentCard.conversationSettings.autoStart) {
|
|
1079
|
+
// Auto-start conversation if configured
|
|
1080
|
+
await this.sendCurrentConversation();
|
|
1081
|
+
}
|
|
1082
|
+
}
|
|
1083
|
+
// Send user message
|
|
1084
|
+
async sendUserMessage(message) {
|
|
1085
|
+
if (this.isThinkingSignal()) {
|
|
1086
|
+
return;
|
|
1087
|
+
}
|
|
1088
|
+
// Add message to conversation
|
|
1089
|
+
this.addMessage(message);
|
|
1090
|
+
// Set thinking state
|
|
1091
|
+
this.isThinkingSignal.set(true);
|
|
1092
|
+
try {
|
|
1093
|
+
if (message.audioUrl && this.getAudioPlaybackSetting()) {
|
|
1094
|
+
// Wait for audio to finish playing before sending response
|
|
1095
|
+
await new Promise((resolve) => {
|
|
1096
|
+
if (message.audioHtml) {
|
|
1097
|
+
message.audioHtml.addEventListener('ended', () => {
|
|
1098
|
+
resolve();
|
|
1099
|
+
});
|
|
1100
|
+
}
|
|
1101
|
+
else {
|
|
1102
|
+
resolve();
|
|
1103
|
+
}
|
|
1104
|
+
});
|
|
1105
|
+
}
|
|
1106
|
+
// Send to AI service
|
|
1107
|
+
await this.sendCurrentConversation();
|
|
1108
|
+
}
|
|
1109
|
+
finally {
|
|
1110
|
+
this.isThinkingSignal.set(false);
|
|
1111
|
+
}
|
|
1112
|
+
}
|
|
1113
|
+
// Add message to conversation
|
|
1114
|
+
addMessage(message) {
|
|
1115
|
+
this.messagesSignal.update((messages) => [...messages, message]);
|
|
1116
|
+
}
|
|
1117
|
+
// Process assistant message
|
|
1118
|
+
processAssistantMessage(message, mutate = false) {
|
|
1119
|
+
const conversationSettings = this.conversationSettingsSignal();
|
|
1120
|
+
if (!conversationSettings) {
|
|
1121
|
+
throw new Error('Conversation settings not initialized');
|
|
1122
|
+
}
|
|
1123
|
+
return this.messageProcessingService.processMessage(message, conversationSettings, mutate);
|
|
1124
|
+
}
|
|
1125
|
+
// Send current conversation to AI
|
|
1126
|
+
async sendCurrentConversation() {
|
|
1127
|
+
if (this.isDestroyedSignal()) {
|
|
1128
|
+
return;
|
|
1129
|
+
}
|
|
1130
|
+
const messages = this.messagesSignal();
|
|
1131
|
+
const conversationSettings = this.conversationSettingsSignal();
|
|
1132
|
+
const agentCard = this.agentCardSignal();
|
|
1133
|
+
if (!conversationSettings || !agentCard) {
|
|
1134
|
+
throw new Error('Conversation not properly initialized');
|
|
1135
|
+
}
|
|
1136
|
+
if (messages.length > 31) {
|
|
1137
|
+
// Safety limit to prevent infinite conversations
|
|
1138
|
+
return;
|
|
1139
|
+
}
|
|
1140
|
+
let conversationMessages = messages;
|
|
1141
|
+
// Add last prompt if available
|
|
1142
|
+
if (conversationSettings.last_prompt) {
|
|
1143
|
+
conversationMessages = [
|
|
1144
|
+
...messages,
|
|
1145
|
+
{ content: conversationSettings.last_prompt, role: ChatRole.System },
|
|
1146
|
+
];
|
|
1147
|
+
}
|
|
1148
|
+
// Prepare conversation DTO
|
|
1149
|
+
const conversation = {
|
|
1150
|
+
messages: conversationMessages,
|
|
1151
|
+
conversationType: conversationSettings.conversationType,
|
|
1152
|
+
textEngine: conversationSettings.textEngine,
|
|
1153
|
+
model: agentCard.model,
|
|
1154
|
+
};
|
|
1155
|
+
// Call AI service
|
|
1156
|
+
const response = await this.agentCardService.callChatCompletion(conversation);
|
|
1157
|
+
if (!response) {
|
|
1158
|
+
console.error('No message returned from AI, is your service working?');
|
|
1159
|
+
throw new Error('No message returned from AI');
|
|
1160
|
+
}
|
|
1161
|
+
// Process response
|
|
1162
|
+
const newMessage = this.processAssistantMessage(response);
|
|
1163
|
+
// Add to messages
|
|
1164
|
+
this.addMessage(newMessage);
|
|
1165
|
+
this.isThinkingSignal.set(false);
|
|
1166
|
+
}
|
|
1167
|
+
// Helper to get audio playback setting
|
|
1168
|
+
getAudioPlaybackSetting() {
|
|
1169
|
+
const agentCard = this.agentCardSignal();
|
|
1170
|
+
return agentCard?.conversationSettings?.repeatRecording || false;
|
|
1171
|
+
}
|
|
1172
|
+
// Reset conversation
|
|
1173
|
+
async resetConversation(agentCard) {
|
|
1174
|
+
if (agentCard) {
|
|
1175
|
+
this.agentCardSignal.set(agentCard);
|
|
1176
|
+
}
|
|
1177
|
+
// Clear messages
|
|
1178
|
+
this.messagesSignal.set([]);
|
|
1179
|
+
// Re-initialize with current agent card
|
|
1180
|
+
const currentAgentCard = agentCard || this.agentCardSignal();
|
|
1181
|
+
if (currentAgentCard) {
|
|
1182
|
+
// Note: This would need the conversationBuilder to be injected or passed
|
|
1183
|
+
// await this.initConversation(currentAgentCard, conversationBuilder);
|
|
1184
|
+
}
|
|
1185
|
+
}
|
|
1186
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: ConversationService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
1187
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: ConversationService, providedIn: 'root' }); }
|
|
1188
|
+
}
|
|
1189
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: ConversationService, decorators: [{
|
|
1190
|
+
type: Injectable,
|
|
1191
|
+
args: [{
|
|
1192
|
+
providedIn: 'root',
|
|
1193
|
+
}]
|
|
1194
|
+
}], ctorParameters: () => [] });
|
|
1195
|
+
|
|
1196
|
+
function extractJsonFromResponse(content) {
|
|
1197
|
+
const jsonMatch = content.match(/\{[\s\S]*?\}/); // Match everything between first { and }
|
|
1198
|
+
if (!jsonMatch)
|
|
1199
|
+
return null;
|
|
1200
|
+
try {
|
|
1201
|
+
return JSON.parse(jsonMatch[0]);
|
|
1202
|
+
}
|
|
1203
|
+
catch (error) {
|
|
1204
|
+
console.error('Error parsing JSON:', error);
|
|
1205
|
+
return null;
|
|
1206
|
+
}
|
|
1207
|
+
}
|
|
1208
|
+
|
|
1209
|
+
class EvaluationService {
|
|
1210
|
+
constructor() {
|
|
1211
|
+
this.agentCardService = inject(CONVERSATION_AI_TOKEN);
|
|
1212
|
+
this.scoreSignal = signal(10);
|
|
1213
|
+
this.evaluationResultSignal = signal(null);
|
|
1214
|
+
}
|
|
1215
|
+
// Get score as a signal
|
|
1216
|
+
getScore() {
|
|
1217
|
+
return this.scoreSignal;
|
|
1218
|
+
}
|
|
1219
|
+
// Get evaluation result as a signal
|
|
1220
|
+
getEvaluationResult() {
|
|
1221
|
+
return this.evaluationResultSignal;
|
|
1222
|
+
}
|
|
1223
|
+
// Evaluate conversation
|
|
1224
|
+
async evaluateConversation(messages, evaluator) {
|
|
1225
|
+
// Filter conversation to only include user and assistant messages
|
|
1226
|
+
const conversationMessages = messages.filter((message) => [ChatRole.User, ChatRole.Assistant].includes(message.role));
|
|
1227
|
+
// Check if there are any user messages to evaluate
|
|
1228
|
+
const userMessages = conversationMessages.filter((message) => message.role === ChatRole.User);
|
|
1229
|
+
if (userMessages.length <= 0) {
|
|
1230
|
+
console.log('No messages to evaluate', conversationMessages);
|
|
1231
|
+
return;
|
|
1232
|
+
}
|
|
1233
|
+
// Format conversation for evaluation
|
|
1234
|
+
const conversationMessagesString = conversationMessages
|
|
1235
|
+
.map((message) => `${message.role}: ${message.content}`)
|
|
1236
|
+
.join('\n');
|
|
1237
|
+
// Create evaluation prompt
|
|
1238
|
+
const instructions = `
|
|
1239
|
+
Please replay to this task:
|
|
1240
|
+
${evaluator.task}
|
|
1241
|
+
This is the conversation history:
|
|
1242
|
+
${conversationMessagesString}
|
|
1243
|
+
and give the response in next JSON format.
|
|
1244
|
+
${evaluator.expectedResponseType}
|
|
1245
|
+
`;
|
|
1246
|
+
// Send evaluation request
|
|
1247
|
+
const evaluationMessages = [{ content: instructions, role: ChatRole.User }];
|
|
1248
|
+
const response = await this.agentCardService.callChatCompletion({ messages: evaluationMessages });
|
|
1249
|
+
// Extract JSON from response
|
|
1250
|
+
const jsonData = extractJsonFromResponse(response.content);
|
|
1251
|
+
this.evaluationResultSignal.set(jsonData);
|
|
1252
|
+
// Update score if available
|
|
1253
|
+
if (jsonData.score) {
|
|
1254
|
+
if (jsonData.score <= 3) {
|
|
1255
|
+
this.updateScore(jsonData.score * 10);
|
|
1256
|
+
}
|
|
1257
|
+
}
|
|
1258
|
+
}
|
|
1259
|
+
// Update score with limits
|
|
1260
|
+
updateScore(additionalScore) {
|
|
1261
|
+
this.scoreSignal.update(currentScore => {
|
|
1262
|
+
const newScore = currentScore + additionalScore;
|
|
1263
|
+
return newScore > 100 ? 100 : newScore;
|
|
1264
|
+
});
|
|
1265
|
+
}
|
|
1266
|
+
// Reset score
|
|
1267
|
+
resetScore() {
|
|
1268
|
+
this.scoreSignal.set(10);
|
|
1269
|
+
}
|
|
1270
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: EvaluationService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
1271
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: EvaluationService, providedIn: 'root' }); }
|
|
1272
|
+
}
|
|
1273
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: EvaluationService, decorators: [{
|
|
1274
|
+
type: Injectable,
|
|
1275
|
+
args: [{
|
|
1276
|
+
providedIn: 'root',
|
|
1277
|
+
}]
|
|
1278
|
+
}], ctorParameters: () => [] });
|
|
1279
|
+
|
|
1280
|
+
class ChatFooterComponent {
|
|
1281
|
+
constructor() {
|
|
1282
|
+
this.conversationService = inject(ConversationService);
|
|
1283
|
+
this.evaluationService = inject(EvaluationService);
|
|
1284
|
+
this.isAIThinking = input(false);
|
|
1285
|
+
this.evaluatorAgentCard = input();
|
|
1286
|
+
this.micSettings = input({ useWhisper: true, lang: 'en' });
|
|
1287
|
+
this.sendMessage = output();
|
|
1288
|
+
this.textInputChanged = output();
|
|
1289
|
+
this.micFinishedEvent = output();
|
|
1290
|
+
this.chatInputControl = new FormControl();
|
|
1291
|
+
// Get score from evaluation service
|
|
1292
|
+
this.score = this.evaluationService.getScore();
|
|
1293
|
+
}
|
|
1294
|
+
/**
|
|
1295
|
+
* Sets the input text in the textarea
|
|
1296
|
+
* @param text The text to set
|
|
1297
|
+
*/
|
|
1298
|
+
setInputText(text) {
|
|
1299
|
+
this.chatInputControl.setValue(text);
|
|
1300
|
+
this.textInputChanged.emit(text);
|
|
1301
|
+
}
|
|
1302
|
+
/**
|
|
1303
|
+
* Handles the mic finished event
|
|
1304
|
+
* @param eventBlob The blob event from the mic component
|
|
1305
|
+
*/
|
|
1306
|
+
micFinished(eventBlob) {
|
|
1307
|
+
this.micFinishedEvent.emit(eventBlob);
|
|
1308
|
+
}
|
|
1309
|
+
/**
|
|
1310
|
+
* Sends the user message
|
|
1311
|
+
*/
|
|
1312
|
+
async sendUserMessage() {
|
|
1313
|
+
if (this.isAIThinking() || !this.chatInputControl.value) {
|
|
1314
|
+
return;
|
|
1315
|
+
}
|
|
1316
|
+
const text = this.chatInputControl.value;
|
|
1317
|
+
const message = {
|
|
1318
|
+
content: text,
|
|
1319
|
+
role: ChatRole.User,
|
|
1320
|
+
};
|
|
1321
|
+
// Emit the message for parent components that need it
|
|
1322
|
+
this.sendMessage.emit(message);
|
|
1323
|
+
// Clear the input field
|
|
1324
|
+
this.chatInputControl.setValue('');
|
|
1325
|
+
// Send the user message to the conversation service
|
|
1326
|
+
await this.conversationService.sendUserMessage(message);
|
|
1327
|
+
// Evaluate conversation after sending message
|
|
1328
|
+
this.evaluateConversation();
|
|
1329
|
+
}
|
|
1330
|
+
/**
|
|
1331
|
+
* Evaluate conversation using evaluator agent
|
|
1332
|
+
*/
|
|
1333
|
+
async evaluateConversation() {
|
|
1334
|
+
const messages = this.conversationService.getMessages()();
|
|
1335
|
+
if (this.evaluatorAgentCard()) {
|
|
1336
|
+
await this.evaluationService.evaluateConversation(messages, this.evaluatorAgentCard());
|
|
1337
|
+
}
|
|
1338
|
+
}
|
|
1339
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: ChatFooterComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
1340
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "19.2.4", type: ChatFooterComponent, isStandalone: true, selector: "dc-chat-footer", inputs: { isAIThinking: { classPropertyName: "isAIThinking", publicName: "isAIThinking", isSignal: true, isRequired: false, transformFunction: null }, evaluatorAgentCard: { classPropertyName: "evaluatorAgentCard", publicName: "evaluatorAgentCard", isSignal: true, isRequired: false, transformFunction: null }, micSettings: { classPropertyName: "micSettings", publicName: "micSettings", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { sendMessage: "sendMessage", textInputChanged: "textInputChanged", micFinishedEvent: "micFinishedEvent" }, ngImport: i0, template: "<div class=\"progress-input\">\n <div class=\"input-container\">\n <dc-mic\n style=\"display: flex; align-items: center\"\n (onInterpretedText)=\"setInputText($event)\"\n (onFinished)=\"micFinished($event)\"\n [micSettings]=\"micSettings()\"></dc-mic>\n\n <textarea pTextarea [formControl]=\"chatInputControl\" (keyup.enter)=\"sendUserMessage()\" rows=\"1\"></textarea>\n\n <p-button (click)=\"sendUserMessage()\" [disabled]=\"isAIThinking() || !chatInputControl.value\" label=\"Enviar\" [rounded]=\"true\" />\n </div>\n\n <div>\n <p-progressbar showValue=\"false\" [value]=\"score()\" [style]=\"{ height: '6px' }\" />\n </div>\n</div>\n", styles: [".progress-input{padding:10px;background-color:#f5f5f545;border-top:1px solid #b1a8a8}.progress-input .input-container{display:flex;align-items:center;margin-bottom:5px}.progress-input .input-container textarea{flex:1;resize:none;margin:0 10px}.progress-input .input-container .send-button{background-color:#007bff;color:#fff;border:none;border-radius:4px;padding:8px 15px;cursor:pointer}.progress-input .input-container .send-button:disabled{background-color:#ccc;cursor:not-allowed}.progress-input .input-container .send-button:hover:not(:disabled){background-color:#0069d9}\n"], dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.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.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "ngmodule", type: ProgressBarModule }, { kind: "component", type: i2.ProgressBar, selector: "p-progressBar, p-progressbar, p-progress-bar", inputs: ["value", "showValue", "styleClass", "valueStyleClass", "style", "unit", "mode", "color"] }, { kind: "component", type: DCMicComponent, selector: "dc-mic", inputs: ["isDone", "useWhisper", "targetOrBase", "micSettings"], outputs: ["onInterpretedText", "onFinishedRecognition", "onFinished"] }, { kind: "ngmodule", type: TextareaModule }, { kind: "directive", type: i3.Textarea, selector: "[pTextarea]", inputs: ["autoResize", "variant", "fluid", "pSize"], outputs: ["onResize"] }, { kind: "ngmodule", type: ButtonModule }, { kind: "component", type: i2$1.Button, selector: "p-button", inputs: ["type", "iconPos", "icon", "badge", "label", "disabled", "loading", "loadingIcon", "raised", "rounded", "text", "plain", "severity", "outlined", "link", "tabindex", "size", "variant", "style", "styleClass", "badgeClass", "badgeSeverity", "ariaLabel", "autofocus", "fluid", "buttonProps"], outputs: ["onClick", "onFocus", "onBlur"] }] }); }
|
|
1341
|
+
}
|
|
1342
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: ChatFooterComponent, decorators: [{
|
|
1343
|
+
type: Component,
|
|
1344
|
+
args: [{ selector: 'dc-chat-footer', standalone: true, imports: [ReactiveFormsModule, ProgressBarModule, DCMicComponent, TextareaModule, ButtonModule], template: "<div class=\"progress-input\">\n <div class=\"input-container\">\n <dc-mic\n style=\"display: flex; align-items: center\"\n (onInterpretedText)=\"setInputText($event)\"\n (onFinished)=\"micFinished($event)\"\n [micSettings]=\"micSettings()\"></dc-mic>\n\n <textarea pTextarea [formControl]=\"chatInputControl\" (keyup.enter)=\"sendUserMessage()\" rows=\"1\"></textarea>\n\n <p-button (click)=\"sendUserMessage()\" [disabled]=\"isAIThinking() || !chatInputControl.value\" label=\"Enviar\" [rounded]=\"true\" />\n </div>\n\n <div>\n <p-progressbar showValue=\"false\" [value]=\"score()\" [style]=\"{ height: '6px' }\" />\n </div>\n</div>\n", styles: [".progress-input{padding:10px;background-color:#f5f5f545;border-top:1px solid #b1a8a8}.progress-input .input-container{display:flex;align-items:center;margin-bottom:5px}.progress-input .input-container textarea{flex:1;resize:none;margin:0 10px}.progress-input .input-container .send-button{background-color:#007bff;color:#fff;border:none;border-radius:4px;padding:8px 15px;cursor:pointer}.progress-input .input-container .send-button:disabled{background-color:#ccc;cursor:not-allowed}.progress-input .input-container .send-button:hover:not(:disabled){background-color:#0069d9}\n"] }]
|
|
1345
|
+
}] });
|
|
1346
|
+
|
|
1347
|
+
class AudioTextSyncService {
|
|
1348
|
+
constructor() {
|
|
1349
|
+
// Maps to store message-specific signals and observables
|
|
1350
|
+
this.highlightedWordsSignalMap = new Map();
|
|
1351
|
+
this.highlightedWords$Map = new Map();
|
|
1352
|
+
// Maps for cleanup and active audio elements
|
|
1353
|
+
this.cleanup$Map = new Map();
|
|
1354
|
+
this.activeAudioMap = new Map();
|
|
1355
|
+
this.destroyRef = inject(DestroyRef);
|
|
1356
|
+
// Ensure cleanup when service is destroyed
|
|
1357
|
+
this.destroyRef.onDestroy(() => {
|
|
1358
|
+
this.stopAllSyncs();
|
|
1359
|
+
});
|
|
1360
|
+
}
|
|
1361
|
+
/**
|
|
1362
|
+
* Synchronizes audio playback with text transcription
|
|
1363
|
+
* @param audioElement The audio element to sync with
|
|
1364
|
+
* @param transcriptionTimestamps Array of word timestamps
|
|
1365
|
+
* @param messageId Unique identifier for the message
|
|
1366
|
+
*/
|
|
1367
|
+
syncAudioWithText(audioElement, transcriptionTimestamps, messageId) {
|
|
1368
|
+
// Stop any existing sync for this message
|
|
1369
|
+
this.stopSync(messageId);
|
|
1370
|
+
// Create new signal and subject for this message if they don't exist
|
|
1371
|
+
if (!this.highlightedWordsSignalMap.has(messageId)) {
|
|
1372
|
+
this.highlightedWordsSignalMap.set(messageId, signal([]));
|
|
1373
|
+
}
|
|
1374
|
+
if (!this.highlightedWords$Map.has(messageId)) {
|
|
1375
|
+
this.highlightedWords$Map.set(messageId, new BehaviorSubject([]));
|
|
1376
|
+
}
|
|
1377
|
+
// Create cleanup subject
|
|
1123
1378
|
const cleanup$ = new Subject();
|
|
1124
1379
|
this.cleanup$Map.set(messageId, cleanup$);
|
|
1125
1380
|
// Store the active audio element
|
|
@@ -1243,202 +1498,385 @@ class AudioTextSyncService {
|
|
|
1243
1498
|
isWordHighlighted$(messageId, index) {
|
|
1244
1499
|
return this.getHighlightedWords$(messageId).pipe(map((words) => words.some((word) => word.index === index && word.isHighlighted)));
|
|
1245
1500
|
}
|
|
1246
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.
|
|
1247
|
-
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.
|
|
1501
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: AudioTextSyncService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
1502
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: AudioTextSyncService, providedIn: 'root' }); }
|
|
1248
1503
|
}
|
|
1249
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.
|
|
1504
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: AudioTextSyncService, decorators: [{
|
|
1250
1505
|
type: Injectable,
|
|
1251
1506
|
args: [{
|
|
1252
1507
|
providedIn: 'root',
|
|
1253
1508
|
}]
|
|
1254
1509
|
}], ctorParameters: () => [] });
|
|
1255
1510
|
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1511
|
+
const ICONS = {
|
|
1512
|
+
chat: `<svg viewBox="0 0 24 24" fill="currentColor">
|
|
1513
|
+
<path d="M20 2H4c-1.1 0-2 .9-2 2v18l4-4h14c..." />
|
|
1514
|
+
</svg>`,
|
|
1515
|
+
play: `<svg
|
|
1516
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
1517
|
+
width="16"
|
|
1518
|
+
height="16"
|
|
1519
|
+
viewBox="0 0 24 24"
|
|
1520
|
+
fill="none"
|
|
1521
|
+
stroke="#263042"
|
|
1522
|
+
stroke-width="2"
|
|
1523
|
+
stroke-linecap="round"
|
|
1524
|
+
stroke-linejoin="round">
|
|
1525
|
+
<circle cx="12" cy="12" r="10"></circle>
|
|
1526
|
+
<polygon points="10 8 16 12 10 16 10 8"></polygon>
|
|
1527
|
+
</svg>`,
|
|
1528
|
+
loading: `<svg
|
|
1529
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
1530
|
+
width="16"
|
|
1531
|
+
height="16"
|
|
1532
|
+
viewBox="0 0 24 24"
|
|
1533
|
+
fill="none"
|
|
1534
|
+
stroke="#263042"
|
|
1535
|
+
stroke-width="2"
|
|
1536
|
+
stroke-linecap="round"
|
|
1537
|
+
stroke-linejoin="round">
|
|
1538
|
+
<line x1="12" y1="2" x2="12" y2="6"></line>
|
|
1539
|
+
<line x1="12" y1="18" x2="12" y2="22"></line>
|
|
1540
|
+
<line x1="4.93" y1="4.93" x2="7.76" y2="7.76"></line>
|
|
1541
|
+
<line x1="16.24" y1="16.24" x2="19.07" y2="19.07"></line>
|
|
1542
|
+
<line x1="2" y1="12" x2="6" y2="12"></line>
|
|
1543
|
+
<line x1="18" y1="12" x2="22" y2="12"></line>
|
|
1544
|
+
<line x1="4.93" y1="19.07" x2="7.76" y2="16.24"></line>
|
|
1545
|
+
<line x1="16.24" y1="7.76" x2="19.07" y2="4.93"></line>
|
|
1546
|
+
</svg>`,
|
|
1547
|
+
};
|
|
1548
|
+
|
|
1549
|
+
class IconsComponent {
|
|
1550
|
+
constructor() {
|
|
1551
|
+
this.sanitizer = inject(DomSanitizer);
|
|
1552
|
+
this.name = input.required();
|
|
1553
|
+
this.size = input(14);
|
|
1554
|
+
this.color = input('currentColor');
|
|
1555
|
+
}
|
|
1556
|
+
ngOnChanges(changes) {
|
|
1557
|
+
if (changes['name']) {
|
|
1558
|
+
const svg = ICONS[this.name()] || '';
|
|
1559
|
+
this.sanitizedIcon = this.sanitizer.bypassSecurityTrustHtml(svg);
|
|
1560
|
+
}
|
|
1561
|
+
}
|
|
1562
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: IconsComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
1563
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "19.2.4", type: IconsComponent, isStandalone: true, selector: "dc-icon", inputs: { name: { classPropertyName: "name", publicName: "name", isSignal: true, isRequired: true, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, color: { classPropertyName: "color", publicName: "color", isSignal: true, isRequired: false, transformFunction: null } }, usesOnChanges: true, ngImport: i0, template: `
|
|
1564
|
+
<span
|
|
1565
|
+
[innerHTML]="sanitizedIcon"
|
|
1566
|
+
[style.display]="'inline-flex'"
|
|
1567
|
+
[style.alignItems]="'center'"
|
|
1568
|
+
[style.justifyContent]="'center'"
|
|
1569
|
+
[style.width.px]="size()"
|
|
1570
|
+
[style.height.px]="size()"
|
|
1571
|
+
[style.color]="color()"></span>
|
|
1572
|
+
`, isInline: true, styles: [":host{display:inline-flex;align-items:center;line-height:0}span{line-height:0}:host ::ng-deep svg{width:100%;height:100%}\n"] }); }
|
|
1573
|
+
}
|
|
1574
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: IconsComponent, decorators: [{
|
|
1575
|
+
type: Component,
|
|
1576
|
+
args: [{ selector: 'dc-icon', template: `
|
|
1577
|
+
<span
|
|
1578
|
+
[innerHTML]="sanitizedIcon"
|
|
1579
|
+
[style.display]="'inline-flex'"
|
|
1580
|
+
[style.alignItems]="'center'"
|
|
1581
|
+
[style.justifyContent]="'center'"
|
|
1582
|
+
[style.width.px]="size()"
|
|
1583
|
+
[style.height.px]="size()"
|
|
1584
|
+
[style.color]="color()"></span>
|
|
1585
|
+
`, standalone: true, styles: [":host{display:inline-flex;align-items:center;line-height:0}span{line-height:0}:host ::ng-deep svg{width:100%;height:100%}\n"] }]
|
|
1586
|
+
}], ctorParameters: () => [] });
|
|
1587
|
+
|
|
1588
|
+
/**
|
|
1589
|
+
* Standalone component for audio-text synchronization
|
|
1590
|
+
* This component can work independently from the chat component
|
|
1591
|
+
*/
|
|
1592
|
+
class StandaloneAudioTextSyncComponent {
|
|
1593
|
+
constructor() {
|
|
1594
|
+
this.message = input(undefined); // Or input.required<MessageAudio>() if always expected
|
|
1595
|
+
this.highlightedWords = signal([]); // Signal for highlighted words
|
|
1596
|
+
this.isLoading = computed(() => this.message()?.isLoading);
|
|
1597
|
+
this.shouldPlayAudio = computed(() => {
|
|
1598
|
+
debugger;
|
|
1599
|
+
const shouldPlay = !!this.message() && this.message()?.shouldPlayAudio;
|
|
1600
|
+
return shouldPlay;
|
|
1601
|
+
});
|
|
1602
|
+
this.playAudio = output();
|
|
1603
|
+
this.audioCompleted = output();
|
|
1604
|
+
this.audioElement = null; // Audio element for playback
|
|
1605
|
+
this.destroy$ = new Subject(); // Cleanup subject
|
|
1606
|
+
// Inject services
|
|
1607
|
+
this.cdr = inject(ChangeDetectorRef); // Keep for now, might remove if template fully signal-driven
|
|
1608
|
+
this.destroyRef = inject(DestroyRef);
|
|
1609
|
+
// Computed signal for message text
|
|
1610
|
+
this.messageText = computed(() => {
|
|
1611
|
+
const msg = this.message(); // Read the input signal
|
|
1612
|
+
return msg?.content || msg?.text || '';
|
|
1613
|
+
});
|
|
1614
|
+
// Computed signal for transcription availability
|
|
1615
|
+
this.hasTranscription = computed(() => {
|
|
1616
|
+
const hasTranscription = !!this.message()?.transcriptionTimestamps && this.message()?.transcriptionTimestamps.length > 0;
|
|
1617
|
+
return hasTranscription;
|
|
1618
|
+
});
|
|
1619
|
+
// Effect to react to message changes and re-initialize
|
|
1620
|
+
effect(() => {
|
|
1621
|
+
debugger;
|
|
1622
|
+
const currentMsg = this.message(); // Read the input signal
|
|
1623
|
+
console.log('Input message signal changed:', currentMsg);
|
|
1624
|
+
if (currentMsg) {
|
|
1625
|
+
// Re-run initialization logic whenever the message signal changes
|
|
1626
|
+
this.initializeBasedOnMessage(currentMsg);
|
|
1627
|
+
// Check if shouldPlayAudio flag is set
|
|
1628
|
+
if (currentMsg.shouldPlayAudio) {
|
|
1629
|
+
this.startAudioPlayback();
|
|
1630
|
+
}
|
|
1631
|
+
}
|
|
1632
|
+
else {
|
|
1633
|
+
// Handle case where message becomes undefined (cleanup?)
|
|
1634
|
+
this.cleanupAudio();
|
|
1635
|
+
this.highlightedWords.set([]);
|
|
1636
|
+
}
|
|
1637
|
+
// No cdr.markForCheck() needed here usually if template bindings use signals/computed
|
|
1638
|
+
});
|
|
1639
|
+
// Keep the effect for highlightedWords for now, might be redundant if template binds directly
|
|
1268
1640
|
effect(() => {
|
|
1269
|
-
// Just accessing the signal in an effect will re-run the effect when the signal changes
|
|
1270
1641
|
this.highlightedWords();
|
|
1271
|
-
//
|
|
1272
|
-
this.cdr.markForCheck();
|
|
1642
|
+
this.cdr.markForCheck(); // Keep if template uses non-signal bindings for highlighted words
|
|
1273
1643
|
});
|
|
1274
1644
|
}
|
|
1275
1645
|
/**
|
|
1276
|
-
*
|
|
1646
|
+
* Track function for ngFor to improve performance
|
|
1277
1647
|
*/
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1648
|
+
trackByIndex(index, item) {
|
|
1649
|
+
return index;
|
|
1650
|
+
}
|
|
1651
|
+
ngOnDestroy() {
|
|
1652
|
+
this.destroy$.next();
|
|
1653
|
+
this.destroy$.complete();
|
|
1654
|
+
this.cleanupAudio();
|
|
1655
|
+
}
|
|
1656
|
+
// This method is called by the effect when the message signal changes
|
|
1657
|
+
initializeBasedOnMessage(msg) {
|
|
1658
|
+
// Initialize or cleanup audio based on URL presence and change
|
|
1659
|
+
if (msg.audioUrl && (!this.audioElement || this.audioElement.src !== msg.audioUrl)) {
|
|
1660
|
+
this.initializeAudio(msg.audioUrl); // Pass URL
|
|
1661
|
+
}
|
|
1662
|
+
else if (!msg.audioUrl && this.audioElement) {
|
|
1663
|
+
// Cleanup if URL removed and element exists
|
|
1664
|
+
this.cleanupAudio();
|
|
1665
|
+
}
|
|
1666
|
+
// Initialize words and sync based on transcription presence
|
|
1667
|
+
if (this.hasTranscription()) {
|
|
1668
|
+
// Use computed signal
|
|
1669
|
+
const timestamps = msg.transcriptionTimestamps || [];
|
|
1670
|
+
this.initializeHighlightedWords(timestamps); // Pass timestamps
|
|
1671
|
+
// Ensure audio sync setup happens if audio element exists
|
|
1672
|
+
if (this.audioElement) {
|
|
1673
|
+
this.setupAudioSync(timestamps); // Pass timestamps
|
|
1674
|
+
}
|
|
1675
|
+
}
|
|
1676
|
+
else {
|
|
1677
|
+
// No transcription, clear words and potentially stop sync listeners
|
|
1678
|
+
this.highlightedWords.set([]);
|
|
1679
|
+
this.destroy$.next(); // Signal existing listeners to stop
|
|
1680
|
+
}
|
|
1286
1681
|
}
|
|
1287
1682
|
/**
|
|
1288
|
-
*
|
|
1683
|
+
* Initialize the audio element and set up event listeners
|
|
1289
1684
|
*/
|
|
1290
|
-
|
|
1291
|
-
|
|
1685
|
+
initializeAudio(audioUrl) {
|
|
1686
|
+
// Clean up any existing audio element and listeners first
|
|
1687
|
+
this.cleanupAudio();
|
|
1688
|
+
this.audioElement = new Audio(audioUrl); // Use passed URL
|
|
1292
1689
|
}
|
|
1293
1690
|
/**
|
|
1294
|
-
*
|
|
1295
|
-
* @param index The current item's index
|
|
1296
|
-
* @param item The current item
|
|
1691
|
+
* Initialize highlighted words from transcription timestamps
|
|
1297
1692
|
*/
|
|
1298
|
-
|
|
1299
|
-
|
|
1693
|
+
initializeHighlightedWords(timestamps) {
|
|
1694
|
+
const initialWords = timestamps.map((word, index) => ({
|
|
1695
|
+
word: word.word,
|
|
1696
|
+
index,
|
|
1697
|
+
isHighlighted: false,
|
|
1698
|
+
}));
|
|
1699
|
+
this.highlightedWords.set(initialWords);
|
|
1300
1700
|
}
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
//
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1701
|
+
/**
|
|
1702
|
+
* Set up audio synchronization with text
|
|
1703
|
+
*/
|
|
1704
|
+
setupAudioSync(timestamps) {
|
|
1705
|
+
// Accept timestamps
|
|
1706
|
+
if (!this.audioElement) {
|
|
1707
|
+
// Guard against missing audio element
|
|
1708
|
+
return;
|
|
1709
|
+
}
|
|
1710
|
+
// Important: Clean up previous listeners before setting up new ones
|
|
1711
|
+
this.destroy$.next(); // Signal previous subscriptions to complete
|
|
1712
|
+
// Listen to timeupdate events
|
|
1713
|
+
fromEvent(this.audioElement, 'timeupdate')
|
|
1714
|
+
.pipe(takeUntilDestroyed(this.destroyRef), // Use DestroyRef for component lifecycle
|
|
1715
|
+
takeUntil(this.destroy$), // Use manual subject for re-initialization cleanup
|
|
1716
|
+
map(() => this.audioElement?.currentTime || 0))
|
|
1717
|
+
.subscribe((currentTime) => {
|
|
1718
|
+
// Use the passed timestamps array
|
|
1719
|
+
const updatedWords = timestamps.map((word, index) => {
|
|
1720
|
+
const isHighlighted = currentTime >= word.start - 0.15 && currentTime < word.end + 0.15;
|
|
1721
|
+
return { word: word.word, index, isHighlighted };
|
|
1318
1722
|
});
|
|
1723
|
+
this.highlightedWords.set(updatedWords);
|
|
1319
1724
|
});
|
|
1320
|
-
//
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1725
|
+
// Listen to ended event for cleanup
|
|
1726
|
+
fromEvent(this.audioElement, 'ended')
|
|
1727
|
+
.pipe(takeUntilDestroyed(this.destroyRef), takeUntil(this.destroy$))
|
|
1728
|
+
.subscribe(() => {
|
|
1729
|
+
// Reset highlighting when audio ends
|
|
1730
|
+
const resetWords = this.highlightedWords().map((word) => ({
|
|
1731
|
+
// Read current words signal
|
|
1732
|
+
...word,
|
|
1733
|
+
isHighlighted: false,
|
|
1328
1734
|
}));
|
|
1329
|
-
this.highlightedWords.set(
|
|
1330
|
-
//
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1735
|
+
this.highlightedWords.set(resetWords);
|
|
1736
|
+
// Emit audio completed event with the current message
|
|
1737
|
+
const currentMsg = this.message();
|
|
1738
|
+
if (currentMsg) {
|
|
1739
|
+
// Reset the shouldPlayAudio flag
|
|
1740
|
+
currentMsg.shouldPlayAudio = false;
|
|
1741
|
+
this.audioCompleted.emit(currentMsg);
|
|
1742
|
+
}
|
|
1743
|
+
});
|
|
1334
1744
|
}
|
|
1335
1745
|
/**
|
|
1336
|
-
*
|
|
1337
|
-
* Uses message content/text and a timestamp to ensure uniqueness
|
|
1746
|
+
* Clean up audio element and event listeners
|
|
1338
1747
|
*/
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1748
|
+
cleanupAudio() {
|
|
1749
|
+
if (this.audioElement) {
|
|
1750
|
+
this.audioElement.pause();
|
|
1751
|
+
this.audioElement.removeAttribute('src'); // Use removeAttribute for better cleanup
|
|
1752
|
+
this.audioElement = null;
|
|
1753
|
+
this.destroy$.next(); // Ensure listeners tied to this audio instance are cleaned up
|
|
1754
|
+
// Reset highlighting immediately on cleanup
|
|
1755
|
+
const resetWords = this.highlightedWords().map((word) => ({
|
|
1756
|
+
...word,
|
|
1757
|
+
isHighlighted: false,
|
|
1758
|
+
}));
|
|
1759
|
+
this.highlightedWords.set(resetWords);
|
|
1760
|
+
}
|
|
1343
1761
|
}
|
|
1344
1762
|
/**
|
|
1345
|
-
*
|
|
1763
|
+
* Play or pause the audio
|
|
1346
1764
|
*/
|
|
1347
1765
|
onPlayMessage() {
|
|
1348
|
-
this.
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
}
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
}
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
get hasTranscription() {
|
|
1370
|
-
return !!this.message.transcriptionTimestamps;
|
|
1371
|
-
}
|
|
1372
|
-
get messageText() {
|
|
1373
|
-
return this.message.content || this.message.text;
|
|
1374
|
-
}
|
|
1375
|
-
get messageTag() {
|
|
1376
|
-
return this.message.tag || null;
|
|
1377
|
-
}
|
|
1378
|
-
onPlayMessage() {
|
|
1379
|
-
this.playAudio.emit(this.message);
|
|
1766
|
+
const currentMsg = this.message(); // Read signal
|
|
1767
|
+
if (this.audioElement) {
|
|
1768
|
+
if (this.audioElement.paused) {
|
|
1769
|
+
this.startAudioPlayback();
|
|
1770
|
+
}
|
|
1771
|
+
else {
|
|
1772
|
+
this.audioElement.pause();
|
|
1773
|
+
}
|
|
1774
|
+
}
|
|
1775
|
+
else if (currentMsg?.audioUrl) {
|
|
1776
|
+
// Initialization now happens via effect. If element doesn't exist yet,
|
|
1777
|
+
// the effect should create it. Clicking play might need to ensure init runs.
|
|
1778
|
+
// For simplicity, let's re-call initializeAudio here if needed,
|
|
1779
|
+
// though ideally the effect handles it.
|
|
1780
|
+
this.initializeAudio(currentMsg.audioUrl); // Ensure it's created if somehow missed
|
|
1781
|
+
this.startAudioPlayback();
|
|
1782
|
+
}
|
|
1783
|
+
else if (currentMsg) {
|
|
1784
|
+
// If no audio URL is available, emit event to parent component
|
|
1785
|
+
this.playAudio.emit(currentMsg); // Emit the message value
|
|
1786
|
+
}
|
|
1380
1787
|
}
|
|
1381
1788
|
/**
|
|
1382
|
-
*
|
|
1383
|
-
* @param index The current item's index
|
|
1384
|
-
* @param item The current item
|
|
1789
|
+
* Start audio playback and handle any setup needed
|
|
1385
1790
|
*/
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1791
|
+
startAudioPlayback() {
|
|
1792
|
+
if (!this.audioElement)
|
|
1793
|
+
return;
|
|
1794
|
+
// Play the audio
|
|
1795
|
+
this.audioElement.play().catch((error) => {
|
|
1796
|
+
console.error('Error playing audio:', error);
|
|
1797
|
+
});
|
|
1391
1798
|
}
|
|
1392
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.
|
|
1393
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.
|
|
1799
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: StandaloneAudioTextSyncComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
1800
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.4", type: StandaloneAudioTextSyncComponent, isStandalone: true, selector: "dc-standalone-audio-text-sync", inputs: { message: { classPropertyName: "message", publicName: "message", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { playAudio: "playAudio", audioCompleted: "audioCompleted" }, ngImport: i0, template: "<div class=\"audio-text-sync-container\">\n <!-- Icon for play/loading status -->\n <div>\n <!-- <span> lo: {{ isLoading() }} tr: {{ hasTranscription() }} play: {{ shouldPlayAudio() }} </span> -->\n </div>\n <i (click)=\"onPlayMessage()\" class=\"play-button\">\n @if (isLoading()) {\n <!-- <dc-icon class=\"spin-animation\" name=\"loading\"></dc-icon> -->\n <i class=\"spin-animation pi pi-spinner-dotted\"></i>\n\n } @else {\n <dc-icon name=\"play\"></dc-icon>\n }\n </i>\n\n <!-- Display transcription with highlighting -->\n <div class=\"text-content\">\n @if (hasTranscription()) { @for (wordState of highlightedWords(); track trackByIndex($index, wordState)) {\n <span [class.highlight]=\"wordState.isHighlighted\">{{ wordState.word }} </span>\n } } @else {\n <!-- Display regular text content when no transcription is available -->\n <span [innerHtml]=\"messageText()\"></span>\n }\n </div>\n</div>\n", styles: [":host{display:block}.audio-text-sync-container{display:flex;align-items:flex-start;gap:8px}.play-button{cursor:pointer;display:flex;align-items:center;justify-content:center;min-width:24px}.play-button:hover{opacity:.8}.text-content{flex:1}.highlight{background-color:#3cd8ff;border-radius:4px}.spin-animation{animation:spin 1s linear infinite}@keyframes spin{0%{transform:rotate(0)}to{transform:rotate(360deg)}}\n"], dependencies: [{ kind: "component", type: IconsComponent, selector: "dc-icon", inputs: ["name", "size", "color"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
1394
1801
|
}
|
|
1395
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.
|
|
1802
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: StandaloneAudioTextSyncComponent, decorators: [{
|
|
1396
1803
|
type: Component,
|
|
1397
|
-
args: [{ selector: 'dc-
|
|
1398
|
-
}], ctorParameters: () => []
|
|
1399
|
-
type: Input
|
|
1400
|
-
}], isLoading: [{
|
|
1401
|
-
type: Input
|
|
1402
|
-
}], playAudio: [{
|
|
1403
|
-
type: Output
|
|
1404
|
-
}] } });
|
|
1804
|
+
args: [{ selector: 'dc-standalone-audio-text-sync', standalone: true, imports: [IconsComponent], changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"audio-text-sync-container\">\n <!-- Icon for play/loading status -->\n <div>\n <!-- <span> lo: {{ isLoading() }} tr: {{ hasTranscription() }} play: {{ shouldPlayAudio() }} </span> -->\n </div>\n <i (click)=\"onPlayMessage()\" class=\"play-button\">\n @if (isLoading()) {\n <!-- <dc-icon class=\"spin-animation\" name=\"loading\"></dc-icon> -->\n <i class=\"spin-animation pi pi-spinner-dotted\"></i>\n\n } @else {\n <dc-icon name=\"play\"></dc-icon>\n }\n </i>\n\n <!-- Display transcription with highlighting -->\n <div class=\"text-content\">\n @if (hasTranscription()) { @for (wordState of highlightedWords(); track trackByIndex($index, wordState)) {\n <span [class.highlight]=\"wordState.isHighlighted\">{{ wordState.word }} </span>\n } } @else {\n <!-- Display regular text content when no transcription is available -->\n <span [innerHtml]=\"messageText()\"></span>\n }\n </div>\n</div>\n", styles: [":host{display:block}.audio-text-sync-container{display:flex;align-items:flex-start;gap:8px}.play-button{cursor:pointer;display:flex;align-items:center;justify-content:center;min-width:24px}.play-button:hover{opacity:.8}.text-content{flex:1}.highlight{background-color:#3cd8ff;border-radius:4px}.spin-animation{animation:spin 1s linear infinite}@keyframes spin{0%{transform:rotate(0)}to{transform:rotate(360deg)}}\n"] }]
|
|
1805
|
+
}], ctorParameters: () => [] });
|
|
1405
1806
|
|
|
1406
1807
|
class MultiMessageContentComponent {
|
|
1407
1808
|
constructor() {
|
|
1408
|
-
this.
|
|
1409
|
-
this.
|
|
1809
|
+
this.messages = input.required();
|
|
1810
|
+
this.isLoading = input(false);
|
|
1811
|
+
this.playAudio = output();
|
|
1812
|
+
// Track current playing message index
|
|
1813
|
+
this.currentPlayingIndex = -1;
|
|
1410
1814
|
}
|
|
1411
1815
|
/**
|
|
1412
1816
|
* Checks if a message has transcription timestamps
|
|
1413
1817
|
* @param message The message to check
|
|
1414
1818
|
*/
|
|
1415
1819
|
hasTranscription(message) {
|
|
1416
|
-
//
|
|
1820
|
+
//
|
|
1417
1821
|
return !!message.transcriptionTimestamps;
|
|
1418
1822
|
}
|
|
1419
1823
|
/**
|
|
1420
|
-
* Emits the playAudio event with the selected message
|
|
1824
|
+
* Emits the playAudio event with the selected message and starts sequential playback
|
|
1421
1825
|
* @param message The message to play
|
|
1422
1826
|
*/
|
|
1423
1827
|
onPlayMessage(message) {
|
|
1828
|
+
// Find the index of the message to play
|
|
1829
|
+
const index = this.messages().findIndex((msg) => msg === message || msg.messageId === message.messageId);
|
|
1830
|
+
if (index >= 0) {
|
|
1831
|
+
this.startSequentialPlayback(index);
|
|
1832
|
+
}
|
|
1833
|
+
// Also emit to parent if needed
|
|
1424
1834
|
this.playAudio.emit(message);
|
|
1425
1835
|
}
|
|
1836
|
+
/**
|
|
1837
|
+
* Method to start playing the sequence
|
|
1838
|
+
* @param startIndex Index of the message to start playing from
|
|
1839
|
+
*/
|
|
1840
|
+
startSequentialPlayback(startIndex = 0) {
|
|
1841
|
+
this.currentPlayingIndex = startIndex;
|
|
1842
|
+
const messages = this.messages();
|
|
1843
|
+
if (messages && messages.length > startIndex) {
|
|
1844
|
+
// Reset all messages
|
|
1845
|
+
messages.forEach((msg) => (msg.shouldPlayAudio = false));
|
|
1846
|
+
// Set the current message to play
|
|
1847
|
+
messages[startIndex].shouldPlayAudio = true;
|
|
1848
|
+
}
|
|
1849
|
+
}
|
|
1850
|
+
/**
|
|
1851
|
+
* Handle audio completion from child component
|
|
1852
|
+
* @param message The message that completed playback
|
|
1853
|
+
*/
|
|
1854
|
+
onAudioCompleted(message) {
|
|
1855
|
+
// Find the index of the completed message
|
|
1856
|
+
const completedIndex = this.messages().findIndex((msg) => msg === message || msg.messageId === message.messageId);
|
|
1857
|
+
if (completedIndex >= 0 && completedIndex < this.messages().length - 1) {
|
|
1858
|
+
// Reset current message
|
|
1859
|
+
const messages = this.messages();
|
|
1860
|
+
messages[completedIndex].shouldPlayAudio = false;
|
|
1861
|
+
// Play the next message
|
|
1862
|
+
this.currentPlayingIndex = completedIndex + 1;
|
|
1863
|
+
messages[this.currentPlayingIndex].shouldPlayAudio = true;
|
|
1864
|
+
}
|
|
1865
|
+
else {
|
|
1866
|
+
// All messages played or message not found
|
|
1867
|
+
this.currentPlayingIndex = -1;
|
|
1868
|
+
}
|
|
1869
|
+
}
|
|
1426
1870
|
ngOnInit() {
|
|
1427
|
-
console.log(this.messages);
|
|
1871
|
+
console.log(this.messages());
|
|
1428
1872
|
}
|
|
1429
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.
|
|
1430
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.
|
|
1873
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: MultiMessageContentComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
1874
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.4", type: MultiMessageContentComponent, isStandalone: true, selector: "dc-multi-message-content", inputs: { messages: { classPropertyName: "messages", publicName: "messages", isSignal: true, isRequired: true, transformFunction: null }, isLoading: { classPropertyName: "isLoading", publicName: "isLoading", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { playAudio: "playAudio" }, ngImport: i0, template: "@for (message of messages(); track message) {\n <!-- Message with transcription -->\n @if (hasTranscription(message)) {\n <dc-standalone-audio-text-sync [message]=\"message\" (playAudio)=\"onPlayMessage(message)\" (audioCompleted)=\"onAudioCompleted(message)\"></dc-standalone-audio-text-sync>\n }@else {\n <!-- Message without transcription -->\n\n <div style=\"display: flex\">\n <ng-container *ngTemplateOutlet=\"iconTemplate; context: { isLoading: isLoading(), message: message }\"></ng-container>\n <span style=\"margin-left: 2px\" [ngClass]=\"message.tag\" [innerHtml]=\"message.text || message.content\"></span>\n </div>\n } }\n\n <!-- Icon template for play/loading status -->\n <ng-template #iconTemplate let-isLoading=\"isLoading\" let-message=\"message\">\n <i (click)=\"onPlayMessage(message)\">\n @if (isLoading) {\n <dc-icon class=\"spin-animation\" name=\"loading\"></dc-icon>\n }\n @if (!isLoading) {\n <dc-icon name=\"play\"></dc-icon>\n }\n </i>\n </ng-template>\n", styles: [".highlight{background-color:#ffff004d}i{cursor:pointer;display:inline-flex;align-items:center;margin-right:4px}.spin-animation{animation:spin 1s linear infinite}@keyframes spin{0%{transform:rotate(0)}to{transform:rotate(360deg)}}\n"], dependencies: [{ kind: "component", type: IconsComponent, selector: "dc-icon", inputs: ["name", "size", "color"] }, { kind: "component", type: StandaloneAudioTextSyncComponent, selector: "dc-standalone-audio-text-sync", inputs: ["message"], outputs: ["playAudio", "audioCompleted"] }, { kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1$1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
1431
1875
|
}
|
|
1432
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.
|
|
1433
|
-
type: Component,
|
|
1434
|
-
args: [{ selector: 'dc-multi-message-content', standalone: true, imports: [
|
|
1435
|
-
}], ctorParameters: () => []
|
|
1436
|
-
type: Input
|
|
1437
|
-
}], isLoading: [{
|
|
1438
|
-
type: Input
|
|
1439
|
-
}], playAudio: [{
|
|
1440
|
-
type: Output
|
|
1441
|
-
}] } });
|
|
1876
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: MultiMessageContentComponent, decorators: [{
|
|
1877
|
+
type: Component,
|
|
1878
|
+
args: [{ selector: 'dc-multi-message-content', standalone: true, imports: [IconsComponent, StandaloneAudioTextSyncComponent, CommonModule], changeDetection: ChangeDetectionStrategy.OnPush, template: "@for (message of messages(); track message) {\n <!-- Message with transcription -->\n @if (hasTranscription(message)) {\n <dc-standalone-audio-text-sync [message]=\"message\" (playAudio)=\"onPlayMessage(message)\" (audioCompleted)=\"onAudioCompleted(message)\"></dc-standalone-audio-text-sync>\n }@else {\n <!-- Message without transcription -->\n\n <div style=\"display: flex\">\n <ng-container *ngTemplateOutlet=\"iconTemplate; context: { isLoading: isLoading(), message: message }\"></ng-container>\n <span style=\"margin-left: 2px\" [ngClass]=\"message.tag\" [innerHtml]=\"message.text || message.content\"></span>\n </div>\n } }\n\n <!-- Icon template for play/loading status -->\n <ng-template #iconTemplate let-isLoading=\"isLoading\" let-message=\"message\">\n <i (click)=\"onPlayMessage(message)\">\n @if (isLoading) {\n <dc-icon class=\"spin-animation\" name=\"loading\"></dc-icon>\n }\n @if (!isLoading) {\n <dc-icon name=\"play\"></dc-icon>\n }\n </i>\n </ng-template>\n", styles: [".highlight{background-color:#ffff004d}i{cursor:pointer;display:inline-flex;align-items:center;margin-right:4px}.spin-animation{animation:spin 1s linear infinite}@keyframes spin{0%{transform:rotate(0)}to{transform:rotate(360deg)}}\n"] }]
|
|
1879
|
+
}], ctorParameters: () => [] });
|
|
1442
1880
|
|
|
1443
1881
|
function matchTranscription(originalText, transcription) {
|
|
1444
1882
|
const result = [];
|
|
@@ -1485,65 +1923,104 @@ function extractAudioAndTranscription(message, audio) {
|
|
|
1485
1923
|
message.transcriptionTimestamps = matchTranscription(message.text || message.content, message.transcription.words);
|
|
1486
1924
|
}
|
|
1487
1925
|
}
|
|
1926
|
+
function buildObjectTTSRequest(message, settings = {}) {
|
|
1927
|
+
const generateTranscription = settings?.highlightWords ?? false;
|
|
1928
|
+
const speedRate = settings?.speedRate || 0;
|
|
1929
|
+
const speed = typeof settings?.speed === 'string' ? settings.speed : AudioSpeed$1.Regular;
|
|
1930
|
+
const text = removeEmojis(message.text || message.content);
|
|
1931
|
+
return {
|
|
1932
|
+
text,
|
|
1933
|
+
voice: message.voice || settings.voice,
|
|
1934
|
+
generateTranscription,
|
|
1935
|
+
speedRate,
|
|
1936
|
+
speed,
|
|
1937
|
+
};
|
|
1938
|
+
}
|
|
1488
1939
|
|
|
1489
1940
|
class ChatMessageComponent {
|
|
1490
|
-
constructor(
|
|
1491
|
-
this.audioService =
|
|
1492
|
-
this.agentCardService =
|
|
1493
|
-
this.audioTextSyncService =
|
|
1941
|
+
constructor() {
|
|
1942
|
+
this.audioService = inject(AudioService);
|
|
1943
|
+
this.agentCardService = inject(CONVERSATION_AI_TOKEN);
|
|
1944
|
+
this.audioTextSyncService = inject(AudioTextSyncService);
|
|
1945
|
+
this.chatMessage = input.required();
|
|
1946
|
+
this.chatUserSettings = input(null);
|
|
1947
|
+
this.audioMessage = signal(null);
|
|
1494
1948
|
this.destroyRef = inject(DestroyRef);
|
|
1495
1949
|
this.conversationChatSettings = signal(null);
|
|
1496
1950
|
this.isLoading = signal(false);
|
|
1951
|
+
// Field initializer for the effect - runs during component creation
|
|
1952
|
+
this.messageEffect = effect(() => {
|
|
1953
|
+
const message = this.chatMessage();
|
|
1954
|
+
if (!message)
|
|
1955
|
+
return;
|
|
1956
|
+
const settings$ = this.conversationChatSettings();
|
|
1957
|
+
// Skip processing if settings aren't loaded yet
|
|
1958
|
+
if (!settings$)
|
|
1959
|
+
return;
|
|
1960
|
+
if (message.role === ChatRole.AssistantHelper) {
|
|
1961
|
+
return;
|
|
1962
|
+
}
|
|
1963
|
+
if (message.role === ChatRole.User) {
|
|
1964
|
+
if (settings$.repeatRecording && settings$.synthVoice && message.audioUrl) {
|
|
1965
|
+
this.playMessage(message);
|
|
1966
|
+
}
|
|
1967
|
+
}
|
|
1968
|
+
else if (settings$.synthVoice) {
|
|
1969
|
+
if (message.multiMessages) {
|
|
1970
|
+
this.generateAndPlayAllAudios(message.multiMessages);
|
|
1971
|
+
}
|
|
1972
|
+
else {
|
|
1973
|
+
this.generateAndPlayAudio(message);
|
|
1974
|
+
}
|
|
1975
|
+
}
|
|
1976
|
+
});
|
|
1977
|
+
// Computed properties for easier access to signal values
|
|
1978
|
+
this.messageRole = computed(() => this.chatMessage()?.role);
|
|
1979
|
+
this.hasMultiMessages = computed(() => !!this.chatMessage()?.multiMessages);
|
|
1980
|
+
this.multiMessages = computed(() => this.chatMessage()?.multiMessages || []);
|
|
1981
|
+
this.messageContent = computed(() => this.chatMessage()?.content || '');
|
|
1982
|
+
this.messageTranslation = computed(() => this.chatMessage()?.translation);
|
|
1983
|
+
this.isUserMessage = computed(() => this.messageRole() === ChatRole.User);
|
|
1984
|
+
this.isAssistantMessage = computed(() => this.messageRole() === ChatRole.Assistant);
|
|
1985
|
+
this.isAssistantHelperMessage = computed(() => this.messageRole() === ChatRole.AssistantHelper);
|
|
1497
1986
|
}
|
|
1498
1987
|
async ngOnInit() {
|
|
1988
|
+
// Load settings - the effect will react to this change
|
|
1499
1989
|
const settings = await this.agentCardService.getConversationUserChatSettings();
|
|
1500
1990
|
this.conversationChatSettings.set(settings);
|
|
1501
|
-
|
|
1502
|
-
return;
|
|
1503
|
-
}
|
|
1504
|
-
const settings$ = this.conversationChatSettings();
|
|
1505
|
-
if (this.chatMessage.role === ChatRole.User) {
|
|
1506
|
-
if (settings$?.repeatRecording && settings$?.synthVoice) {
|
|
1507
|
-
this.playMessage(this.chatMessage);
|
|
1508
|
-
}
|
|
1509
|
-
}
|
|
1510
|
-
else if (settings$?.synthVoice) {
|
|
1511
|
-
if (this.chatMessage.multiMessages) {
|
|
1512
|
-
this.generateAndPlayAllAudios(this.chatMessage.multiMessages);
|
|
1513
|
-
}
|
|
1514
|
-
else {
|
|
1515
|
-
this.generateAndPlayAudio(this.chatMessage);
|
|
1516
|
-
}
|
|
1517
|
-
}
|
|
1991
|
+
// No need for effect here as it's now a field initializer
|
|
1518
1992
|
}
|
|
1519
1993
|
async generateAndPlayAudio(message, overwriteText = null) {
|
|
1520
|
-
this.isLoading.set(true);
|
|
1994
|
+
// this.isLoading.set(true);
|
|
1995
|
+
this.audioMessage.set({ ...message, isLoading: true });
|
|
1521
1996
|
try {
|
|
1522
1997
|
const text = overwriteText || message.content;
|
|
1523
|
-
const ttsObject =
|
|
1998
|
+
const ttsObject = buildObjectTTSRequest({ ...message, text }, this.conversationChatSettings());
|
|
1524
1999
|
if (message.ssml) {
|
|
1525
2000
|
ttsObject.ssml = message.ssml;
|
|
1526
2001
|
}
|
|
1527
2002
|
const speechAudio = await this.agentCardService.getTextAudioFile(ttsObject);
|
|
1528
2003
|
extractAudioAndTranscription(message, speechAudio);
|
|
1529
|
-
this.
|
|
2004
|
+
this.audioMessage.set({ ...message, isLoading: false, shouldPlayAudio: true });
|
|
2005
|
+
// this.playMessage(message);
|
|
1530
2006
|
}
|
|
1531
2007
|
finally {
|
|
1532
2008
|
this.isLoading.set(false);
|
|
1533
2009
|
}
|
|
1534
2010
|
}
|
|
1535
2011
|
playMessage(message) {
|
|
2012
|
+
debugger;
|
|
1536
2013
|
if (!message.audioUrl) {
|
|
1537
2014
|
return null;
|
|
1538
2015
|
}
|
|
1539
2016
|
const audioElement = this.audioService.playAudio(message.audioUrl);
|
|
1540
2017
|
message['audioHtml'] = audioElement;
|
|
1541
|
-
console.log('Playing message with transcription:', {
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
});
|
|
2018
|
+
// console.log('Playing message with transcription:', {
|
|
2019
|
+
// hasTranscription: !!message.transcriptionTimestamps,
|
|
2020
|
+
// transcriptionLength: message.transcriptionTimestamps?.length,
|
|
2021
|
+
// audioUrl: message.audioUrl,
|
|
2022
|
+
// audioElement,
|
|
2023
|
+
// });
|
|
1547
2024
|
if (message.transcriptionTimestamps) {
|
|
1548
2025
|
// Generate a unique ID for this message
|
|
1549
2026
|
const messageId = this.generateMessageId(message);
|
|
@@ -1554,7 +2031,7 @@ class ChatMessageComponent {
|
|
|
1554
2031
|
});
|
|
1555
2032
|
// Use the audio-text sync service instead of direct manipulation
|
|
1556
2033
|
// Pass the messageId to ensure each message has its own state
|
|
1557
|
-
this.audioTextSyncService.syncAudioWithText(audioElement, message.transcriptionTimestamps, messageId);
|
|
2034
|
+
// this.audioTextSyncService.syncAudioWithText(audioElement, message.transcriptionTimestamps, messageId);
|
|
1558
2035
|
}
|
|
1559
2036
|
return audioElement;
|
|
1560
2037
|
}
|
|
@@ -1574,387 +2051,107 @@ class ChatMessageComponent {
|
|
|
1574
2051
|
/*
|
|
1575
2052
|
private setupTranscriptionHighlighting(audioElement: HTMLAudioElement, transcriptionTimestamps: WordTimestamps[]): void {
|
|
1576
2053
|
// This functionality has been moved to AudioTextSyncService
|
|
1577
|
-
}
|
|
1578
|
-
*/
|
|
1579
|
-
generateAndPlayAllAudios(multiMessages) {
|
|
1580
|
-
if (!multiMessages.length)
|
|
1581
|
-
return;
|
|
1582
|
-
let currentIndex = 0;
|
|
1583
|
-
const playAudioSequentially = async () => {
|
|
1584
|
-
if (currentIndex >= multiMessages.length) {
|
|
1585
|
-
return;
|
|
1586
|
-
}
|
|
1587
|
-
const currentMessage = multiMessages[currentIndex];
|
|
1588
|
-
currentMessage.isLoading = true;
|
|
1589
|
-
try {
|
|
1590
|
-
// Process current message audio if needed
|
|
1591
|
-
if (!currentMessage.audioUrl) {
|
|
1592
|
-
if (currentMessage.audioPromise) {
|
|
1593
|
-
await currentMessage.audioPromise;
|
|
1594
|
-
}
|
|
1595
|
-
else {
|
|
1596
|
-
const request = this.buildObjectTTSRequest(currentMessage);
|
|
1597
|
-
currentMessage.audioPromise = this.agentCardService.getTextAudioFile(request);
|
|
1598
|
-
const audio = await currentMessage.audioPromise;
|
|
1599
|
-
extractAudioAndTranscription(currentMessage, audio);
|
|
1600
|
-
}
|
|
1601
|
-
}
|
|
1602
|
-
// Play the current message
|
|
1603
|
-
const audioElement = this.playMessage(currentMessage);
|
|
1604
|
-
if (audioElement) {
|
|
1605
|
-
audioElement.addEventListener('ended', () => {
|
|
1606
|
-
currentIndex++;
|
|
1607
|
-
playAudioSequentially();
|
|
1608
|
-
});
|
|
1609
|
-
}
|
|
1610
|
-
else {
|
|
1611
|
-
// If playback failed, move to next message
|
|
1612
|
-
currentIndex++;
|
|
1613
|
-
playAudioSequentially();
|
|
1614
|
-
}
|
|
1615
|
-
// Preload next message audio
|
|
1616
|
-
this.preloadNextMessageAudio(multiMessages, currentIndex);
|
|
1617
|
-
}
|
|
1618
|
-
finally {
|
|
1619
|
-
currentMessage.isLoading = false;
|
|
1620
|
-
}
|
|
1621
|
-
};
|
|
1622
|
-
// Start the sequential playback
|
|
1623
|
-
playAudioSequentially();
|
|
1624
|
-
}
|
|
1625
|
-
preloadNextMessageAudio(messages, currentIndex) {
|
|
1626
|
-
if (currentIndex + 1 < messages.length) {
|
|
1627
|
-
const nextMessage = messages[currentIndex + 1];
|
|
1628
|
-
if (!nextMessage.audioUrl && !nextMessage.audioPromise) {
|
|
1629
|
-
const request = this.buildObjectTTSRequest(nextMessage);
|
|
1630
|
-
nextMessage.isLoading = true;
|
|
1631
|
-
nextMessage.audioPromise = this.agentCardService.getTextAudioFile(request);
|
|
1632
|
-
nextMessage.audioPromise.then((audio) => {
|
|
1633
|
-
extractAudioAndTranscription(nextMessage, audio);
|
|
1634
|
-
nextMessage.isLoading = false;
|
|
1635
|
-
});
|
|
1636
|
-
}
|
|
1637
|
-
}
|
|
1638
|
-
}
|
|
1639
|
-
buildObjectTTSRequest(message) {
|
|
1640
|
-
const settings = this.conversationChatSettings();
|
|
1641
|
-
const generateTranscription = settings?.highlightWords ?? false;
|
|
1642
|
-
const speedRate = settings?.speedRate || 0;
|
|
1643
|
-
const speed = typeof settings?.speed === 'string' ? settings.speed : AudioSpeed.Regular;
|
|
1644
|
-
const text = removeEmojis(message.text || message.content);
|
|
1645
|
-
return {
|
|
1646
|
-
text,
|
|
1647
|
-
voice: message.voice || this.chatMessage.voice,
|
|
1648
|
-
generateTranscription,
|
|
1649
|
-
speedRate,
|
|
1650
|
-
speed,
|
|
1651
|
-
};
|
|
1652
|
-
}
|
|
1653
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.1.1", ngImport: i0, type: ChatMessageComponent, deps: [{ token: AudioService }, { token: CONVERSATION_AI_TOKEN }, { token: AudioTextSyncService }], target: i0.ɵɵFactoryTarget.Component }); }
|
|
1654
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.1.1", type: ChatMessageComponent, isStandalone: true, selector: "dc-chat-message", inputs: { chatMessage: "chatMessage", chatUserSettings: "chatUserSettings" }, ngImport: i0, template: "<!-- Multi-message display -->\n<span class=\"message-container\">\n @if (chatMessage?.multiMessages) {\n <!-- Single message display -->\n <dc-multi-message-content [messages]=\"chatMessage.multiMessages\" (playAudio)=\"playMessage($event)\"> </dc-multi-message-content>\n } @else {\n <dc-message-content [message]=\"chatMessage\" [isLoading]=\"isLoading()\" (playAudio)=\"playMessage($event)\"> </dc-message-content>\n }\n</span>\n\n@if (chatMessage.translation) {\n<!-- Translation display if available -->\n\n<div class=\"translation\">\n <hr class=\"divider\" />\n {{ chatMessage.translation }}\n</div>\n}\n", styles: [":host{display:block}.message-container{display:block;line-height:1.5}::ng-deep .em{color:#0d5878;font-style:italic}::ng-deep .strong{font-weight:700;color:#515151}::ng-deep .em_strong{font-weight:700;font-style:italic;color:#0d5878}.translation{margin-top:8px;font-size:small;line-height:1.6;color:#393744;font-style:italic}.divider{margin:.5rem 40px;border-top:1px solid #ffa77e}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: MessageContentComponent, selector: "dc-message-content", inputs: ["message", "isLoading"], outputs: ["playAudio"] }, { kind: "component", type: MultiMessageContentComponent, selector: "dc-multi-message-content", inputs: ["messages", "isLoading"], outputs: ["playAudio"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
1655
|
-
}
|
|
1656
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.1.1", ngImport: i0, type: ChatMessageComponent, decorators: [{
|
|
1657
|
-
type: Component,
|
|
1658
|
-
args: [{ selector: 'dc-chat-message', standalone: true, imports: [CommonModule, MessageContentComponent, MultiMessageContentComponent], changeDetection: ChangeDetectionStrategy.OnPush, template: "<!-- Multi-message display -->\n<span class=\"message-container\">\n @if (chatMessage?.multiMessages) {\n <!-- Single message display -->\n <dc-multi-message-content [messages]=\"chatMessage.multiMessages\" (playAudio)=\"playMessage($event)\"> </dc-multi-message-content>\n } @else {\n <dc-message-content [message]=\"chatMessage\" [isLoading]=\"isLoading()\" (playAudio)=\"playMessage($event)\"> </dc-message-content>\n }\n</span>\n\n@if (chatMessage.translation) {\n<!-- Translation display if available -->\n\n<div class=\"translation\">\n <hr class=\"divider\" />\n {{ chatMessage.translation }}\n</div>\n}\n", styles: [":host{display:block}.message-container{display:block;line-height:1.5}::ng-deep .em{color:#0d5878;font-style:italic}::ng-deep .strong{font-weight:700;color:#515151}::ng-deep .em_strong{font-weight:700;font-style:italic;color:#0d5878}.translation{margin-top:8px;font-size:small;line-height:1.6;color:#393744;font-style:italic}.divider{margin:.5rem 40px;border-top:1px solid #ffa77e}\n"] }]
|
|
1659
|
-
}], ctorParameters: () => [{ type: AudioService }, { type: AgentCardsAbstractService, decorators: [{
|
|
1660
|
-
type: Inject,
|
|
1661
|
-
args: [CONVERSATION_AI_TOKEN]
|
|
1662
|
-
}] }, { type: AudioTextSyncService }], propDecorators: { chatMessage: [{
|
|
1663
|
-
type: Input
|
|
1664
|
-
}], chatUserSettings: [{
|
|
1665
|
-
type: Input
|
|
1666
|
-
}] } });
|
|
1667
|
-
|
|
1668
|
-
class MessageProcessingService {
|
|
1669
|
-
constructor() { }
|
|
1670
|
-
// Process message for display
|
|
1671
|
-
processMessage(message, conversationSettings, mutate = false) {
|
|
1672
|
-
const defaultVoice = this.getVoice(conversationSettings.voice);
|
|
1673
|
-
let processedMessage;
|
|
1674
|
-
if (mutate) {
|
|
1675
|
-
// Modify the existing message object
|
|
1676
|
-
message.voice = defaultVoice;
|
|
1677
|
-
processedMessage = message;
|
|
1678
|
-
}
|
|
1679
|
-
else {
|
|
1680
|
-
// Create a new message object
|
|
1681
|
-
processedMessage = {
|
|
1682
|
-
content: message.content,
|
|
1683
|
-
role: message.role,
|
|
1684
|
-
voice: defaultVoice
|
|
1685
|
-
};
|
|
1686
|
-
}
|
|
1687
|
-
// Process based on text engine
|
|
1688
|
-
if (conversationSettings.textEngine === TextEngines.MarkdownMultiMessages) {
|
|
1689
|
-
this.processMultiMessages(processedMessage, conversationSettings);
|
|
1690
|
-
}
|
|
1691
|
-
else if (conversationSettings.textEngine === TextEngines.MarkdownSSML) {
|
|
1692
|
-
if (!conversationSettings.secondaryVoice) {
|
|
1693
|
-
throw new Error('Secondary voice is required for SSML');
|
|
1694
|
-
}
|
|
1695
|
-
const content = this.subsItalicsByTag(processedMessage.content, conversationSettings.secondaryVoice);
|
|
1696
|
-
processedMessage.ssml = '<speak>' + content + '</speak>';
|
|
1697
|
-
}
|
|
1698
|
-
return processedMessage;
|
|
1699
|
-
}
|
|
1700
|
-
// Process multi-messages from markdown
|
|
1701
|
-
processMultiMessages(message, settings) {
|
|
1702
|
-
// Convert markdown to HTML segments
|
|
1703
|
-
const mdToHtml = convertToHTML(message.content);
|
|
1704
|
-
// Map segments to multi-messages
|
|
1705
|
-
message.multiMessages = mdToHtml.map((val) => {
|
|
1706
|
-
// Get the narrator voice from conversation settings
|
|
1707
|
-
const narratorVoice = settings.secondaryVoice || 'en-US-News-L';
|
|
1708
|
-
// Determine if this is italicized text (narrator)
|
|
1709
|
-
const isItalics = val.tag === 'em';
|
|
1710
|
-
const voice = isItalics ? narratorVoice : message.voice;
|
|
1711
|
-
return {
|
|
1712
|
-
voice,
|
|
1713
|
-
content: val.content,
|
|
1714
|
-
audioUrl: null,
|
|
1715
|
-
audioPromise: null,
|
|
1716
|
-
text: val.text,
|
|
1717
|
-
tag: val.tag
|
|
1718
|
-
};
|
|
1719
|
-
});
|
|
1720
|
-
}
|
|
1721
|
-
// Replace italics with voice tags for SSML
|
|
1722
|
-
subsItalicsByTag(text, voiceId = 'it-IT-Neural2-A', tagName = 'voice') {
|
|
1723
|
-
const regex = /\*(.*?)\*/g;
|
|
1724
|
-
return text.replace(regex, (match, p1) => `<${tagName} name="${voiceId}">${p1}</${tagName}>`);
|
|
1725
|
-
}
|
|
1726
|
-
// Get appropriate voice based on settings
|
|
1727
|
-
getVoice(voice_value, targetLang = 'en') {
|
|
1728
|
-
if ([null, '', 'random'].includes(voice_value)) {
|
|
1729
|
-
const voiceCodes = VoiceTTSOptions.map((val) => val.id);
|
|
1730
|
-
return voiceCodes[Math.floor(Math.random() * voiceCodes.length)];
|
|
1731
|
-
}
|
|
1732
|
-
if (voice_value === 'randomMan') {
|
|
1733
|
-
const voiceCodes = VoiceTTSOptions.filter((voice) => voice.gender === 'male' && voice.lang.includes(targetLang)).map((val) => val.id);
|
|
1734
|
-
return voiceCodes[Math.floor(Math.random() * voiceCodes.length)];
|
|
1735
|
-
}
|
|
1736
|
-
else if (voice_value === 'randomWoman') {
|
|
1737
|
-
const voiceCodes = VoiceTTSOptions.filter((voice) => voice.gender === 'female' && voice.lang.includes(targetLang)).map((val) => val.id);
|
|
1738
|
-
return voiceCodes[Math.floor(Math.random() * voiceCodes.length)];
|
|
1739
|
-
}
|
|
1740
|
-
else {
|
|
1741
|
-
const voice = VoiceTTSOptions.find((voice) => voice.id === voice_value);
|
|
1742
|
-
if (voice) {
|
|
1743
|
-
return voice.id;
|
|
1744
|
-
}
|
|
1745
|
-
else {
|
|
1746
|
-
console.error('Voice not found getting something random', voice_value);
|
|
1747
|
-
return VoiceTTSOptions.find((voice) => voice.lang.includes(targetLang))?.id || '';
|
|
1748
|
-
}
|
|
1749
|
-
}
|
|
1750
|
-
}
|
|
1751
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.1.1", ngImport: i0, type: MessageProcessingService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
1752
|
-
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.1.1", ngImport: i0, type: MessageProcessingService, providedIn: 'root' }); }
|
|
1753
|
-
}
|
|
1754
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.1.1", ngImport: i0, type: MessageProcessingService, decorators: [{
|
|
1755
|
-
type: Injectable,
|
|
1756
|
-
args: [{
|
|
1757
|
-
providedIn: 'root',
|
|
1758
|
-
}]
|
|
1759
|
-
}], ctorParameters: () => [] });
|
|
1760
|
-
|
|
1761
|
-
class ConversationService {
|
|
1762
|
-
constructor(agentCardService, messageProcessingService) {
|
|
1763
|
-
this.agentCardService = agentCardService;
|
|
1764
|
-
this.messageProcessingService = messageProcessingService;
|
|
1765
|
-
this.messagesSignal = signal([]);
|
|
1766
|
-
this.isThinkingSignal = signal(false);
|
|
1767
|
-
this.conversationSettingsSignal = signal(null);
|
|
1768
|
-
this.agentCardSignal = signal(null);
|
|
1769
|
-
this.isDestroyedSignal = signal(false);
|
|
1770
|
-
}
|
|
1771
|
-
// Get messages as a signal
|
|
1772
|
-
getMessages() {
|
|
1773
|
-
return this.messagesSignal;
|
|
1774
|
-
}
|
|
1775
|
-
// Get thinking state as a signal
|
|
1776
|
-
isThinking() {
|
|
1777
|
-
return this.isThinkingSignal;
|
|
1778
|
-
}
|
|
1779
|
-
// Get conversation settings as a signal
|
|
1780
|
-
getConversationSettings() {
|
|
1781
|
-
return this.conversationSettingsSignal;
|
|
1782
|
-
}
|
|
1783
|
-
// Get agent card as a signal
|
|
1784
|
-
getAgentCard() {
|
|
1785
|
-
return this.agentCardSignal;
|
|
1786
|
-
}
|
|
1787
|
-
// Set destroyed state
|
|
1788
|
-
setDestroyed(value) {
|
|
1789
|
-
this.isDestroyedSignal.set(value);
|
|
1790
|
-
}
|
|
1791
|
-
// Initialize conversation
|
|
1792
|
-
async initConversation(agentCard, conversationBuilder, parseDict) {
|
|
1793
|
-
if (!agentCard?.conversationSettings) {
|
|
1794
|
-
throw new Error('Conversation settings are required');
|
|
1795
|
-
}
|
|
1796
|
-
this.agentCardSignal.set(agentCard);
|
|
1797
|
-
// Build conversation settings
|
|
1798
|
-
const conversationSettings = conversationBuilder.buildConversationSettings(agentCard, parseDict);
|
|
1799
|
-
this.conversationSettingsSignal.set(conversationSettings);
|
|
1800
|
-
// Update agent card with conversation settings
|
|
1801
|
-
const updatedAgentCard = { ...agentCard, conversationSettings };
|
|
1802
|
-
this.agentCardSignal.set(updatedAgentCard);
|
|
1803
|
-
if (!conversationSettings.messages) {
|
|
1804
|
-
throw new Error('conversationSettings.messages is required in proper format to start conversation');
|
|
1805
|
-
}
|
|
1806
|
-
// Set initial messages
|
|
1807
|
-
this.messagesSignal.set(conversationSettings.messages);
|
|
1808
|
-
// Find first assistant message
|
|
1809
|
-
const firstAssistantMsg = conversationSettings.messages.find((message) => message.role === ChatRole.Assistant);
|
|
1810
|
-
if (firstAssistantMsg) {
|
|
1811
|
-
// Process the first assistant message
|
|
1812
|
-
this.processAssistantMessage(firstAssistantMsg, true);
|
|
1813
|
-
}
|
|
1814
|
-
else if (agentCard.conversationSettings.autoStart) {
|
|
1815
|
-
// Auto-start conversation if configured
|
|
1816
|
-
await this.sendCurrentConversation();
|
|
1817
|
-
}
|
|
1818
|
-
}
|
|
1819
|
-
// Send user message
|
|
1820
|
-
async sendUserMessage(message) {
|
|
1821
|
-
if (this.isThinkingSignal()) {
|
|
1822
|
-
return;
|
|
1823
|
-
}
|
|
1824
|
-
// Add message to conversation
|
|
1825
|
-
this.addMessage(message);
|
|
1826
|
-
// Set thinking state
|
|
1827
|
-
this.isThinkingSignal.set(true);
|
|
1828
|
-
try {
|
|
1829
|
-
if (message.audioUrl && this.getAudioPlaybackSetting()) {
|
|
1830
|
-
// Wait for audio to finish playing before sending response
|
|
1831
|
-
await new Promise((resolve) => {
|
|
1832
|
-
if (message.audioHtml) {
|
|
1833
|
-
message.audioHtml.addEventListener('ended', () => {
|
|
1834
|
-
resolve();
|
|
1835
|
-
});
|
|
2054
|
+
}
|
|
2055
|
+
*/
|
|
2056
|
+
generateAndPlayAllAudios(multiMessages) {
|
|
2057
|
+
if (!multiMessages.length)
|
|
2058
|
+
return;
|
|
2059
|
+
let currentIndex = 0;
|
|
2060
|
+
const playAudioSequentially = async () => {
|
|
2061
|
+
if (currentIndex >= multiMessages.length) {
|
|
2062
|
+
return;
|
|
2063
|
+
}
|
|
2064
|
+
const currentMessage = multiMessages[currentIndex];
|
|
2065
|
+
currentMessage.isLoading = true;
|
|
2066
|
+
try {
|
|
2067
|
+
// Process current message audio if needed
|
|
2068
|
+
if (!currentMessage.audioUrl) {
|
|
2069
|
+
if (currentMessage.audioPromise) {
|
|
2070
|
+
await currentMessage.audioPromise;
|
|
1836
2071
|
}
|
|
1837
2072
|
else {
|
|
1838
|
-
|
|
2073
|
+
const request = buildObjectTTSRequest(currentMessage, this.conversationChatSettings());
|
|
2074
|
+
currentMessage.audioPromise = this.agentCardService.getTextAudioFile(request);
|
|
2075
|
+
const audio = await currentMessage.audioPromise;
|
|
2076
|
+
extractAudioAndTranscription(currentMessage, audio);
|
|
1839
2077
|
}
|
|
1840
|
-
}
|
|
2078
|
+
}
|
|
2079
|
+
// Play the current message
|
|
2080
|
+
const audioElement = this.playMessage(currentMessage);
|
|
2081
|
+
if (audioElement) {
|
|
2082
|
+
audioElement.addEventListener('ended', () => {
|
|
2083
|
+
currentIndex++;
|
|
2084
|
+
playAudioSequentially();
|
|
2085
|
+
});
|
|
2086
|
+
}
|
|
2087
|
+
else {
|
|
2088
|
+
// If playback failed, move to next message
|
|
2089
|
+
currentIndex++;
|
|
2090
|
+
playAudioSequentially();
|
|
2091
|
+
}
|
|
2092
|
+
// Preload next message audio
|
|
2093
|
+
this.preloadNextMessageAudio(multiMessages, currentIndex);
|
|
2094
|
+
}
|
|
2095
|
+
finally {
|
|
2096
|
+
currentMessage.isLoading = false;
|
|
1841
2097
|
}
|
|
1842
|
-
// Send to AI service
|
|
1843
|
-
await this.sendCurrentConversation();
|
|
1844
|
-
}
|
|
1845
|
-
finally {
|
|
1846
|
-
this.isThinkingSignal.set(false);
|
|
1847
|
-
}
|
|
1848
|
-
}
|
|
1849
|
-
// Add message to conversation
|
|
1850
|
-
addMessage(message) {
|
|
1851
|
-
this.messagesSignal.update((messages) => [...messages, message]);
|
|
1852
|
-
}
|
|
1853
|
-
// Process assistant message
|
|
1854
|
-
processAssistantMessage(message, mutate = false) {
|
|
1855
|
-
const conversationSettings = this.conversationSettingsSignal();
|
|
1856
|
-
if (!conversationSettings) {
|
|
1857
|
-
throw new Error('Conversation settings not initialized');
|
|
1858
|
-
}
|
|
1859
|
-
return this.messageProcessingService.processMessage(message, conversationSettings, mutate);
|
|
1860
|
-
}
|
|
1861
|
-
// Send current conversation to AI
|
|
1862
|
-
async sendCurrentConversation() {
|
|
1863
|
-
if (this.isDestroyedSignal()) {
|
|
1864
|
-
return;
|
|
1865
|
-
}
|
|
1866
|
-
const messages = this.messagesSignal();
|
|
1867
|
-
const conversationSettings = this.conversationSettingsSignal();
|
|
1868
|
-
const agentCard = this.agentCardSignal();
|
|
1869
|
-
if (!conversationSettings || !agentCard) {
|
|
1870
|
-
throw new Error('Conversation not properly initialized');
|
|
1871
|
-
}
|
|
1872
|
-
if (messages.length > 31) {
|
|
1873
|
-
// Safety limit to prevent infinite conversations
|
|
1874
|
-
return;
|
|
1875
|
-
}
|
|
1876
|
-
let conversationMessages = messages;
|
|
1877
|
-
// Add last prompt if available
|
|
1878
|
-
if (conversationSettings.last_prompt) {
|
|
1879
|
-
conversationMessages = [
|
|
1880
|
-
...messages,
|
|
1881
|
-
{ content: conversationSettings.last_prompt, role: ChatRole.System },
|
|
1882
|
-
];
|
|
1883
|
-
}
|
|
1884
|
-
// Prepare conversation DTO
|
|
1885
|
-
const conversation = {
|
|
1886
|
-
messages: conversationMessages,
|
|
1887
|
-
conversationType: conversationSettings.conversationType,
|
|
1888
|
-
textEngine: conversationSettings.textEngine,
|
|
1889
|
-
model: agentCard.model,
|
|
1890
2098
|
};
|
|
1891
|
-
//
|
|
1892
|
-
|
|
1893
|
-
if (!response) {
|
|
1894
|
-
console.error('No message returned from AI, is your service working?');
|
|
1895
|
-
throw new Error('No message returned from AI');
|
|
1896
|
-
}
|
|
1897
|
-
// Process response
|
|
1898
|
-
const newMessage = this.processAssistantMessage(response);
|
|
1899
|
-
// Add to messages
|
|
1900
|
-
this.addMessage(newMessage);
|
|
1901
|
-
this.isThinkingSignal.set(false);
|
|
1902
|
-
}
|
|
1903
|
-
// Helper to get audio playback setting
|
|
1904
|
-
getAudioPlaybackSetting() {
|
|
1905
|
-
const agentCard = this.agentCardSignal();
|
|
1906
|
-
return agentCard?.conversationSettings?.repeatRecording || false;
|
|
2099
|
+
// Start the sequential playback
|
|
2100
|
+
playAudioSequentially();
|
|
1907
2101
|
}
|
|
1908
|
-
|
|
1909
|
-
|
|
1910
|
-
|
|
1911
|
-
|
|
1912
|
-
|
|
1913
|
-
|
|
1914
|
-
|
|
1915
|
-
|
|
1916
|
-
|
|
1917
|
-
|
|
1918
|
-
|
|
1919
|
-
|
|
2102
|
+
preloadNextMessageAudio(messages, currentIndex) {
|
|
2103
|
+
if (currentIndex + 1 < messages.length) {
|
|
2104
|
+
const nextMessage = messages[currentIndex + 1];
|
|
2105
|
+
if (!nextMessage.audioUrl && !nextMessage.audioPromise) {
|
|
2106
|
+
const request = buildObjectTTSRequest(nextMessage, this.conversationChatSettings());
|
|
2107
|
+
nextMessage.isLoading = true;
|
|
2108
|
+
nextMessage.audioPromise = this.agentCardService.getTextAudioFile(request);
|
|
2109
|
+
nextMessage.audioPromise.then((audio) => {
|
|
2110
|
+
extractAudioAndTranscription(nextMessage, audio);
|
|
2111
|
+
nextMessage.isLoading = false;
|
|
2112
|
+
});
|
|
2113
|
+
}
|
|
1920
2114
|
}
|
|
1921
2115
|
}
|
|
1922
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.
|
|
1923
|
-
static { this.ɵ
|
|
2116
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: ChatMessageComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
2117
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.4", type: ChatMessageComponent, isStandalone: true, selector: "dc-chat-message", inputs: { chatMessage: { classPropertyName: "chatMessage", publicName: "chatMessage", isSignal: true, isRequired: true, transformFunction: null }, chatUserSettings: { classPropertyName: "chatUserSettings", publicName: "chatUserSettings", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: "<div class=\"message-wrapper\" [ngClass]=\"{ 'user-message': isUserMessage(), 'assistant-message': !isUserMessage() }\">\n <div class=\"message-container\">\n <!-- Avatar for assistant messages -->\n\n @if (!isUserMessage()) {\n <div class=\"avatar-container\">\n <div class=\"avatar\">\n <span class=\"avatar-initial\">A</span>\n </div>\n </div>\n }\n\n <!-- Message content -->\n <div class=\"message-bubble\">\n @if (hasMultiMessages()) {\n <dc-multi-message-content [messages]=\"multiMessages()\" (playAudio)=\"playMessage($event)\"></dc-multi-message-content>\n } @else {\n <dc-standalone-audio-text-sync [message]=\"audioMessage()\"></dc-standalone-audio-text-sync>\n }\n\n <!-- Translation if available -->\n @if (messageTranslation()) {\n <div class=\"translation\">\n <hr class=\"divider\" />\n {{ messageTranslation() }}\n </div>\n }\n </div>\n\n <!-- Avatar for user messages -->\n @if (isUserMessage()) {\n <div class=\"avatar-container\">\n <div class=\"avatar user-avatar\">\n <span class=\"avatar-initial\">U</span>\n </div>\n </div>\n }\n </div>\n</div>\n", styles: [":host{display:block;margin-bottom:16px}.message-wrapper{display:flex;width:100%;margin-bottom:12px}.message-container{display:flex;max-width:85%;line-height:1.5}.user-message{justify-content:flex-end}.user-message .message-container{flex-direction:row-reverse}.user-message .message-bubble{background-color:#0d5878;color:#fff;border-radius:18px 18px 0;margin-right:8px}.assistant-message{justify-content:flex-start}.assistant-message .message-container{flex-direction:row}.assistant-message .message-bubble{background-color:#f0f0f0;color:#333;border-radius:18px 18px 18px 0;margin-left:8px}.message-bubble{padding:12px 16px;box-shadow:0 1px 2px #0000001a;max-width:calc(100% - 50px);word-wrap:break-word;overflow-wrap:break-word;word-break:break-word;hyphens:auto;min-width:0}.avatar-container{display:flex;align-items:flex-end}.avatar{width:36px;height:36px;border-radius:50%;background-color:#0d5878;display:flex;align-items:center;justify-content:center;color:#fff;font-weight:700;font-size:14px}.user-avatar{background-color:#ffa77e}::ng-deep .em{color:inherit;font-style:italic}::ng-deep .strong{font-weight:700;color:inherit}::ng-deep .em_strong{font-weight:700;font-style:italic;color:inherit}.translation{margin-top:8px;font-size:small;line-height:1.6;color:#393744;font-style:italic}.divider{margin:.5rem 40px;border-top:1px solid #ffa77e}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "component", type: MultiMessageContentComponent, selector: "dc-multi-message-content", inputs: ["messages", "isLoading"], outputs: ["playAudio"] }, { kind: "component", type: StandaloneAudioTextSyncComponent, selector: "dc-standalone-audio-text-sync", inputs: ["message"], outputs: ["playAudio", "audioCompleted"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
1924
2118
|
}
|
|
1925
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.
|
|
1926
|
-
type:
|
|
1927
|
-
args: [{
|
|
1928
|
-
|
|
1929
|
-
}]
|
|
1930
|
-
}], ctorParameters: () => [{ type: AgentCardsAbstractService, decorators: [{
|
|
1931
|
-
type: Inject,
|
|
1932
|
-
args: [CONVERSATION_AI_TOKEN]
|
|
1933
|
-
}] }, { type: MessageProcessingService }] });
|
|
2119
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: ChatMessageComponent, decorators: [{
|
|
2120
|
+
type: Component,
|
|
2121
|
+
args: [{ selector: 'dc-chat-message', standalone: true, imports: [CommonModule, MultiMessageContentComponent, StandaloneAudioTextSyncComponent], changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"message-wrapper\" [ngClass]=\"{ 'user-message': isUserMessage(), 'assistant-message': !isUserMessage() }\">\n <div class=\"message-container\">\n <!-- Avatar for assistant messages -->\n\n @if (!isUserMessage()) {\n <div class=\"avatar-container\">\n <div class=\"avatar\">\n <span class=\"avatar-initial\">A</span>\n </div>\n </div>\n }\n\n <!-- Message content -->\n <div class=\"message-bubble\">\n @if (hasMultiMessages()) {\n <dc-multi-message-content [messages]=\"multiMessages()\" (playAudio)=\"playMessage($event)\"></dc-multi-message-content>\n } @else {\n <dc-standalone-audio-text-sync [message]=\"audioMessage()\"></dc-standalone-audio-text-sync>\n }\n\n <!-- Translation if available -->\n @if (messageTranslation()) {\n <div class=\"translation\">\n <hr class=\"divider\" />\n {{ messageTranslation() }}\n </div>\n }\n </div>\n\n <!-- Avatar for user messages -->\n @if (isUserMessage()) {\n <div class=\"avatar-container\">\n <div class=\"avatar user-avatar\">\n <span class=\"avatar-initial\">U</span>\n </div>\n </div>\n }\n </div>\n</div>\n", styles: [":host{display:block;margin-bottom:16px}.message-wrapper{display:flex;width:100%;margin-bottom:12px}.message-container{display:flex;max-width:85%;line-height:1.5}.user-message{justify-content:flex-end}.user-message .message-container{flex-direction:row-reverse}.user-message .message-bubble{background-color:#0d5878;color:#fff;border-radius:18px 18px 0;margin-right:8px}.assistant-message{justify-content:flex-start}.assistant-message .message-container{flex-direction:row}.assistant-message .message-bubble{background-color:#f0f0f0;color:#333;border-radius:18px 18px 18px 0;margin-left:8px}.message-bubble{padding:12px 16px;box-shadow:0 1px 2px #0000001a;max-width:calc(100% - 50px);word-wrap:break-word;overflow-wrap:break-word;word-break:break-word;hyphens:auto;min-width:0}.avatar-container{display:flex;align-items:flex-end}.avatar{width:36px;height:36px;border-radius:50%;background-color:#0d5878;display:flex;align-items:center;justify-content:center;color:#fff;font-weight:700;font-size:14px}.user-avatar{background-color:#ffa77e}::ng-deep .em{color:inherit;font-style:italic}::ng-deep .strong{font-weight:700;color:inherit}::ng-deep .em_strong{font-weight:700;font-style:italic;color:inherit}.translation{margin-top:8px;font-size:small;line-height:1.6;color:#393744;font-style:italic}.divider{margin:.5rem 40px;border-top:1px solid #ffa77e}\n"] }]
|
|
2122
|
+
}], ctorParameters: () => [] });
|
|
1934
2123
|
|
|
1935
2124
|
class ChatMessagesListComponent {
|
|
1936
2125
|
constructor() {
|
|
2126
|
+
this.chatUserSettings = input.required();
|
|
2127
|
+
this.thinkingImg = input.required();
|
|
1937
2128
|
this.aiIcon = 'assets/default/ai.png';
|
|
1938
2129
|
this.conversationService = inject(ConversationService);
|
|
2130
|
+
// Create a signal for the filter text
|
|
2131
|
+
this.filterText = signal('');
|
|
1939
2132
|
// Get messages and thinking state from the conversation service
|
|
1940
|
-
this.messages =
|
|
2133
|
+
this.messages = computed(() => {
|
|
2134
|
+
// Get the actual array of messages from the signal by calling it as a function
|
|
2135
|
+
const allMessages = this.conversationService.getMessages()();
|
|
2136
|
+
return allMessages.filter((message) => message.role !== 'system');
|
|
2137
|
+
});
|
|
1941
2138
|
this.isThinking = this.conversationService.isThinking();
|
|
1942
2139
|
}
|
|
2140
|
+
// Method to update the filter text
|
|
2141
|
+
updateFilter(value) {
|
|
2142
|
+
this.filterText.set(value);
|
|
2143
|
+
}
|
|
1943
2144
|
// Track messages by their content and role for efficient rendering
|
|
1944
2145
|
trackByMessage(index, message) {
|
|
1945
2146
|
return `${message.role}-${index}-${message.content.substring(0, 20)}`;
|
|
1946
2147
|
}
|
|
1947
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.
|
|
1948
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.
|
|
2148
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: ChatMessagesListComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
2149
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.4", type: ChatMessagesListComponent, isStandalone: true, selector: "dc-chat-messages-list", inputs: { chatUserSettings: { classPropertyName: "chatUserSettings", publicName: "chatUserSettings", isSignal: true, isRequired: true, transformFunction: null }, thinkingImg: { classPropertyName: "thinkingImg", publicName: "thinkingImg", isSignal: true, isRequired: true, transformFunction: null } }, ngImport: i0, template: "<div class=\"messages-container\">\n @for (message of messages(); track trackByMessage($index, message)) {\n <dc-chat-message [chatMessage]=\"message\" [chatUserSettings]=\"chatUserSettings()\"> </dc-chat-message>\n } @if (isThinking()) {\n <div class=\"thinking-container\">\n <div class=\"thinking-message\">\n <div class=\"thinking-avatar\">\n <img [src]=\"aiIcon\" alt=\"AI thinking\" class=\"avatar-img\" />\n </div>\n <div class=\"thinking-content\">\n <p-skeleton width=\"80%\" height=\"2rem\"></p-skeleton>\n <p-skeleton width=\"60%\" height=\"2rem\"></p-skeleton>\n </div>\n </div>\n </div>\n }\n</div>\n", styles: [".messages-container{display:flex;flex-direction:column;gap:1rem;padding:1rem;overflow-y:auto;max-height:calc(100vh - 200px)}.thinking-container{padding:.5rem 0}.thinking-message{display:flex;gap:1rem;align-items:flex-start}.thinking-avatar{width:40px;height:40px;border-radius:50%;overflow:hidden;flex-shrink:0}.avatar-img{width:100%;height:100%;object-fit:cover}.thinking-content{flex:1;display:flex;flex-direction:column;gap:.5rem}\n"], dependencies: [{ kind: "component", type: ChatMessageComponent, selector: "dc-chat-message", inputs: ["chatMessage", "chatUserSettings"] }, { kind: "ngmodule", type: SkeletonModule }, { kind: "component", type: i1$2.Skeleton, selector: "p-skeleton", inputs: ["styleClass", "style", "shape", "animation", "borderRadius", "size", "width", "height"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
1949
2150
|
}
|
|
1950
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.
|
|
2151
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: ChatMessagesListComponent, decorators: [{
|
|
1951
2152
|
type: Component,
|
|
1952
|
-
args: [{ selector: 'dc-chat-messages-list', standalone: true, imports: [
|
|
1953
|
-
}]
|
|
1954
|
-
type: Input
|
|
1955
|
-
}], thinkingImg: [{
|
|
1956
|
-
type: Input
|
|
1957
|
-
}] } });
|
|
2153
|
+
args: [{ selector: 'dc-chat-messages-list', standalone: true, imports: [ChatMessageComponent, SkeletonModule], changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"messages-container\">\n @for (message of messages(); track trackByMessage($index, message)) {\n <dc-chat-message [chatMessage]=\"message\" [chatUserSettings]=\"chatUserSettings()\"> </dc-chat-message>\n } @if (isThinking()) {\n <div class=\"thinking-container\">\n <div class=\"thinking-message\">\n <div class=\"thinking-avatar\">\n <img [src]=\"aiIcon\" alt=\"AI thinking\" class=\"avatar-img\" />\n </div>\n <div class=\"thinking-content\">\n <p-skeleton width=\"80%\" height=\"2rem\"></p-skeleton>\n <p-skeleton width=\"60%\" height=\"2rem\"></p-skeleton>\n </div>\n </div>\n </div>\n }\n</div>\n", styles: [".messages-container{display:flex;flex-direction:column;gap:1rem;padding:1rem;overflow-y:auto;max-height:calc(100vh - 200px)}.thinking-container{padding:.5rem 0}.thinking-message{display:flex;gap:1rem;align-items:flex-start}.thinking-avatar{width:40px;height:40px;border-radius:50%;overflow:hidden;flex-shrink:0}.avatar-img{width:100%;height:100%;object-fit:cover}.thinking-content{flex:1;display:flex;flex-direction:column;gap:.5rem}\n"] }]
|
|
2154
|
+
}] });
|
|
1958
2155
|
|
|
1959
2156
|
const SpeedDescription = {
|
|
1960
2157
|
1: 'Muy Lento',
|
|
@@ -1967,10 +2164,10 @@ class SpeedDescPipe {
|
|
|
1967
2164
|
transform(speed) {
|
|
1968
2165
|
return SpeedDescription[speed];
|
|
1969
2166
|
}
|
|
1970
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.
|
|
1971
|
-
static { this.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "19.
|
|
2167
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: SpeedDescPipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe }); }
|
|
2168
|
+
static { this.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "19.2.4", ngImport: i0, type: SpeedDescPipe, isStandalone: true, name: "speedDisplay" }); }
|
|
1972
2169
|
}
|
|
1973
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.
|
|
2170
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: SpeedDescPipe, decorators: [{
|
|
1974
2171
|
type: Pipe,
|
|
1975
2172
|
args: [{
|
|
1976
2173
|
name: 'speedDisplay',
|
|
@@ -1987,10 +2184,10 @@ class TruncatePipe {
|
|
|
1987
2184
|
}
|
|
1988
2185
|
return value.substring(0, length) + suffix;
|
|
1989
2186
|
}
|
|
1990
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.
|
|
1991
|
-
static { this.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "19.
|
|
2187
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: TruncatePipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe }); }
|
|
2188
|
+
static { this.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "19.2.4", ngImport: i0, type: TruncatePipe, isStandalone: true, name: "truncate" }); }
|
|
1992
2189
|
}
|
|
1993
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.
|
|
2190
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: TruncatePipe, decorators: [{
|
|
1994
2191
|
type: Pipe,
|
|
1995
2192
|
args: [{
|
|
1996
2193
|
name: 'truncate',
|
|
@@ -2012,9 +2209,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.1.1", ngImpor
|
|
|
2012
2209
|
* ```
|
|
2013
2210
|
*/
|
|
2014
2211
|
class ProviderSelectorComponent {
|
|
2015
|
-
constructor(
|
|
2016
|
-
this.conversationCardAIService =
|
|
2017
|
-
this.cdr =
|
|
2212
|
+
constructor() {
|
|
2213
|
+
this.conversationCardAIService = inject(CONVERSATION_AI_TOKEN);
|
|
2214
|
+
this.cdr = inject(ChangeDetectorRef);
|
|
2018
2215
|
/** Flag indicating whether the models are currently being loaded */
|
|
2019
2216
|
this.isLoadingModels = true;
|
|
2020
2217
|
/** Array of available models for the selected provider */
|
|
@@ -2045,26 +2242,23 @@ class ProviderSelectorComponent {
|
|
|
2045
2242
|
});
|
|
2046
2243
|
}
|
|
2047
2244
|
}
|
|
2048
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.
|
|
2049
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.
|
|
2245
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: ProviderSelectorComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
2246
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.4", type: ProviderSelectorComponent, isStandalone: true, selector: "dc-provider-selector", inputs: { parentForm: "parentForm" }, ngImport: i0, template: "<div>\n <hr />\n <b>Admin Section</b>\n <br />\n\n <b>Providers:</b>\n <div style=\"display: flex; gap: 10px\" [formGroup]=\"parentForm\">\n <div class=\"space\">\n <p-radioButton value=\"groq\" formControlName=\"provider\"></p-radioButton>\n <label class=\"space\">Groq</label>\n </div>\n\n <div class=\"space\">\n <p-radioButton value=\"openai\" formControlName=\"provider\"></p-radioButton>\n <label>Open AI</label>\n </div>\n\n <div class=\"space\">\n <p-radioButton value=\"google\" formControlName=\"provider\"></p-radioButton>\n <label class=\"space\">Google</label>\n </div>\n\n <div class=\"space\">\n <p-radioButton value=\"openrouter\" formControlName=\"provider\"></p-radioButton>\n <label class=\"space\">Open Router</label>\n </div>\n </div>\n\n <b>Modelo: </b>\n <span pTooltip=\"Modelo Seleccionado\">{{ parentForm.controls.modelName.value }}</span>\n @if (parentForm.controls.provider.value) { @if(isLoadingModels) {\n <p-skeleton height=\"200px\" width=\"100%\"></p-skeleton>\n } @else {\n <p-table [value]=\"modelnames\" stripedRows [size]=\"'small'\" [paginator]=\"true\" [rows]=\"12\" [formGroup]=\"parentForm\">\n <ng-template pTemplate=\"header\">\n <tr>\n <th></th>\n <th>Name</th>\n <th>$$Prompt M</th>\n <th>$$Completion M</th>\n <th>$$Cost 90/10 M</th>\n <th>Created</th>\n </tr>\n </ng-template>\n <ng-template pTemplate=\"body\" let-model>\n <tr [pTooltip]=\"model.description | truncate : 200\" tooltipPosition=\"top\">\n <td><p-radioButton [value]=\"model.id\" formControlName=\"modelName\"></p-radioButton></td>\n <td>{{ model.name }}</td>\n <td>${{ +model.pricing?.prompt * 1000000 | number : '1.2-2' }}</td>\n <td>${{ +model.pricing?.completion * 1000000 | number : '1.2-2' }}</td>\n <td>${{ +model.pricing?.prompt * 1000000 * 0.9 + +model.pricing?.completion * 1000000 * 0.1 | number : '1.2-2' }}</td>\n <td>{{ model.created * 1000 | date : 'dd/MM/yyyy' }}</td>\n </tr>\n </ng-template>\n </p-table>\n } }\n</div>\n", styles: [":host{display:block}.space{display:flex;gap:2px}\n"], dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "ngmodule", type: RadioButtonModule }, { kind: "component", type: i2$2.RadioButton, selector: "p-radioButton, p-radiobutton, p-radio-button", inputs: ["value", "formControlName", "name", "disabled", "variant", "size", "tabindex", "inputId", "ariaLabelledBy", "ariaLabel", "style", "styleClass", "autofocus", "binary"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "directive", type: i3$1.PrimeTemplate, selector: "[pTemplate]", inputs: ["type", "pTemplate"] }, { kind: "ngmodule", type: TableModule }, { kind: "component", type: i4.Table, selector: "p-table", inputs: ["frozenColumns", "frozenValue", "style", "styleClass", "tableStyle", "tableStyleClass", "paginator", "pageLinks", "rowsPerPageOptions", "alwaysShowPaginator", "paginatorPosition", "paginatorStyleClass", "paginatorDropdownAppendTo", "paginatorDropdownScrollHeight", "currentPageReportTemplate", "showCurrentPageReport", "showJumpToPageDropdown", "showJumpToPageInput", "showFirstLastIcon", "showPageLinks", "defaultSortOrder", "sortMode", "resetPageOnSort", "selectionMode", "selectionPageOnly", "contextMenuSelection", "contextMenuSelectionMode", "dataKey", "metaKeySelection", "rowSelectable", "rowTrackBy", "lazy", "lazyLoadOnInit", "compareSelectionBy", "csvSeparator", "exportFilename", "filters", "globalFilterFields", "filterDelay", "filterLocale", "expandedRowKeys", "editingRowKeys", "rowExpandMode", "scrollable", "scrollDirection", "rowGroupMode", "scrollHeight", "virtualScroll", "virtualScrollItemSize", "virtualScrollOptions", "virtualScrollDelay", "frozenWidth", "responsive", "contextMenu", "resizableColumns", "columnResizeMode", "reorderableColumns", "loading", "loadingIcon", "showLoader", "rowHover", "customSort", "showInitialSortBadge", "autoLayout", "exportFunction", "exportHeader", "stateKey", "stateStorage", "editMode", "groupRowsBy", "size", "showGridlines", "stripedRows", "groupRowsByOrder", "responsiveLayout", "breakpoint", "paginatorLocale", "value", "columns", "first", "rows", "totalRecords", "sortField", "sortOrder", "multiSortMeta", "selection", "virtualRowHeight", "selectAll"], outputs: ["contextMenuSelectionChange", "selectAllChange", "selectionChange", "onRowSelect", "onRowUnselect", "onPage", "onSort", "onFilter", "onLazyLoad", "onRowExpand", "onRowCollapse", "onContextMenuSelect", "onColResize", "onColReorder", "onRowReorder", "onEditInit", "onEditComplete", "onEditCancel", "onHeaderCheckboxToggle", "sortFunction", "firstChange", "rowsChange", "onStateSave", "onStateRestore"] }, { kind: "ngmodule", type: SkeletonModule }, { kind: "component", type: i1$2.Skeleton, selector: "p-skeleton", inputs: ["styleClass", "style", "shape", "animation", "borderRadius", "size", "width", "height"] }, { kind: "ngmodule", type: TooltipModule }, { kind: "directive", type: i6.Tooltip, selector: "[pTooltip]", inputs: ["tooltipPosition", "tooltipEvent", "appendTo", "positionStyle", "tooltipStyleClass", "tooltipZIndex", "escape", "showDelay", "hideDelay", "life", "positionTop", "positionLeft", "autoHide", "fitContent", "hideOnEscape", "pTooltip", "tooltipDisabled", "tooltipOptions"] }, { kind: "pipe", type: TruncatePipe, name: "truncate" }, { kind: "pipe", type: DatePipe, name: "date" }, { kind: "pipe", type: DecimalPipe, name: "number" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
2050
2247
|
}
|
|
2051
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.
|
|
2248
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: ProviderSelectorComponent, decorators: [{
|
|
2052
2249
|
type: Component,
|
|
2053
|
-
args: [{ selector: 'dc-provider-selector', standalone: true, imports: [
|
|
2054
|
-
}], ctorParameters: () => [
|
|
2055
|
-
type: Inject,
|
|
2056
|
-
args: [CONVERSATION_AI_TOKEN]
|
|
2057
|
-
}] }, { type: i0.ChangeDetectorRef }], propDecorators: { parentForm: [{
|
|
2250
|
+
args: [{ selector: 'dc-provider-selector', standalone: true, imports: [ReactiveFormsModule, RadioButtonModule, TableModule, SkeletonModule, TooltipModule, TruncatePipe, DatePipe, DecimalPipe], changeDetection: ChangeDetectionStrategy.OnPush, template: "<div>\n <hr />\n <b>Admin Section</b>\n <br />\n\n <b>Providers:</b>\n <div style=\"display: flex; gap: 10px\" [formGroup]=\"parentForm\">\n <div class=\"space\">\n <p-radioButton value=\"groq\" formControlName=\"provider\"></p-radioButton>\n <label class=\"space\">Groq</label>\n </div>\n\n <div class=\"space\">\n <p-radioButton value=\"openai\" formControlName=\"provider\"></p-radioButton>\n <label>Open AI</label>\n </div>\n\n <div class=\"space\">\n <p-radioButton value=\"google\" formControlName=\"provider\"></p-radioButton>\n <label class=\"space\">Google</label>\n </div>\n\n <div class=\"space\">\n <p-radioButton value=\"openrouter\" formControlName=\"provider\"></p-radioButton>\n <label class=\"space\">Open Router</label>\n </div>\n </div>\n\n <b>Modelo: </b>\n <span pTooltip=\"Modelo Seleccionado\">{{ parentForm.controls.modelName.value }}</span>\n @if (parentForm.controls.provider.value) { @if(isLoadingModels) {\n <p-skeleton height=\"200px\" width=\"100%\"></p-skeleton>\n } @else {\n <p-table [value]=\"modelnames\" stripedRows [size]=\"'small'\" [paginator]=\"true\" [rows]=\"12\" [formGroup]=\"parentForm\">\n <ng-template pTemplate=\"header\">\n <tr>\n <th></th>\n <th>Name</th>\n <th>$$Prompt M</th>\n <th>$$Completion M</th>\n <th>$$Cost 90/10 M</th>\n <th>Created</th>\n </tr>\n </ng-template>\n <ng-template pTemplate=\"body\" let-model>\n <tr [pTooltip]=\"model.description | truncate : 200\" tooltipPosition=\"top\">\n <td><p-radioButton [value]=\"model.id\" formControlName=\"modelName\"></p-radioButton></td>\n <td>{{ model.name }}</td>\n <td>${{ +model.pricing?.prompt * 1000000 | number : '1.2-2' }}</td>\n <td>${{ +model.pricing?.completion * 1000000 | number : '1.2-2' }}</td>\n <td>${{ +model.pricing?.prompt * 1000000 * 0.9 + +model.pricing?.completion * 1000000 * 0.1 | number : '1.2-2' }}</td>\n <td>{{ model.created * 1000 | date : 'dd/MM/yyyy' }}</td>\n </tr>\n </ng-template>\n </p-table>\n } }\n</div>\n", styles: [":host{display:block}.space{display:flex;gap:2px}\n"] }]
|
|
2251
|
+
}], ctorParameters: () => [], propDecorators: { parentForm: [{
|
|
2058
2252
|
type: Input
|
|
2059
2253
|
}] } });
|
|
2060
2254
|
|
|
2061
2255
|
class DCConversationUserChatSettingsComponent {
|
|
2062
|
-
constructor(
|
|
2063
|
-
this.dialogRef =
|
|
2064
|
-
this.fb =
|
|
2065
|
-
this.conversationCardAIService =
|
|
2256
|
+
constructor() {
|
|
2257
|
+
this.dialogRef = inject(DynamicDialogRef);
|
|
2258
|
+
this.fb = inject(FormBuilder);
|
|
2259
|
+
this.conversationCardAIService = inject(CONVERSATION_AI_TOKEN);
|
|
2066
2260
|
this.isLoadingModels = true;
|
|
2067
|
-
this.showFeature = {
|
|
2261
|
+
this.showFeature = input({
|
|
2068
2262
|
synthVoice: true,
|
|
2069
2263
|
highlightWords: true,
|
|
2070
2264
|
speed: true,
|
|
@@ -2072,8 +2266,8 @@ class DCConversationUserChatSettingsComponent {
|
|
|
2072
2266
|
superHearing: true,
|
|
2073
2267
|
fixGrammar: true,
|
|
2074
2268
|
autoTranslate: true,
|
|
2075
|
-
};
|
|
2076
|
-
this.onSettingsChange =
|
|
2269
|
+
});
|
|
2270
|
+
this.onSettingsChange = output();
|
|
2077
2271
|
this.textEngines = Object.values(TextEngines);
|
|
2078
2272
|
this.voiceTTSOptions = Object.values(VoiceTTSOptions);
|
|
2079
2273
|
this.textEngineOptions = TextEngineOptions;
|
|
@@ -2110,157 +2304,61 @@ class DCConversationUserChatSettingsComponent {
|
|
|
2110
2304
|
}
|
|
2111
2305
|
}
|
|
2112
2306
|
close() {
|
|
2113
|
-
this.dialogRef.close();
|
|
2114
|
-
}
|
|
2115
|
-
async saveSettings() {
|
|
2116
|
-
if (this.form.valid) {
|
|
2117
|
-
await this.conversationCardAIService.saveConversationUserChatSettings(this.form.value);
|
|
2118
|
-
this.dialogRef.close(this.form.value);
|
|
2119
|
-
}
|
|
2120
|
-
}
|
|
2121
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.1.1", ngImport: i0, type: DCConversationUserChatSettingsComponent, deps: [{ token: i1$3.DynamicDialogRef }, { token: i1.FormBuilder }, { token: CONVERSATION_AI_TOKEN }], target: i0.ɵɵFactoryTarget.Component }); }
|
|
2122
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.1.1", type: DCConversationUserChatSettingsComponent, isStandalone: true, selector: "dc-chat-settings-dialog", inputs: { showFeature: "showFeature" }, outputs: { onSettingsChange: "onSettingsChange" }, viewQueries: [{ propertyName: "tooltipRef", first: true, predicate: ["tooltipRef"], descendants: true }], ngImport: i0, template: "<div class=\"dialog-container\">\n <form [formGroup]=\"form\">\n <div class=\"settings-section\" *ngIf=\"showFeature.synthVoice\">\n <p>\n <p-checkbox class=\"mr-2\" formControlName=\"synthVoice\" [binary]=\"true\" inputId=\"binary\"></p-checkbox>\n <span [class.cross]=\"form.controls.synthVoice.disabled\">Escuchar Voz</span>\n <br />\n <small>Desmarca si solo quieres leer texto</small>\n </p>\n </div>\n\n <div class=\"settings-section\" *ngIf=\"showFeature.highlightWords\">\n <p>\n <p-checkbox class=\"mr-2\" formControlName=\"highlightWords\" [binary]=\"true\" inputId=\"binary\"></p-checkbox>\n <span>Narraci\u00F3n de texto</span>\n <br />\n <small>Remarca las palabras como se van pronuncionando</small>\n </p>\n </div>\n\n <div class=\"settings-section\" *ngIf=\"showFeature.speed\">\n <p>\n Velocidad ({{ form.controls.speed.value | speedDisplay }})\n <br />\n <p-rating formControlName=\"speed\">\n <ng-template pTemplate=\"onicon\">\n <i class=\"pi pi-caret-right\"></i>\n </ng-template>\n <ng-template pTemplate=\"officon\">\n <i class=\"pi pi-circle\"></i>\n </ng-template>\n </p-rating>\n </p>\n </div>\n\n <div class=\"settings-section\" *ngIf=\"showFeature.realTime\">\n <p>\n <p-checkbox class=\"mr-2\" formControlName=\"realTime\" [binary]=\"true\" inputId=\"binary\"></p-checkbox>\n <span [class.cross]=\"form.controls.realTime.disabled\">Tiempo real</span>\n <br />\n <small>No tienes que presionar el microphono, comenzar\u00E1 a grabar en cuanto la AI termine de hablar, cierra el chat para finalizar conversaci\u00F3n.</small>\n </p>\n </div>\n\n <div class=\"settings-section\" *ngIf=\"showFeature.realTime\">\n <p>\n <p-checkbox class=\"mr-2\" formControlName=\"repeatRecording\" [binary]=\"true\" inputId=\"binary\"></p-checkbox>\n <span>Reproducir mi grabaci\u00F3n</span>\n <br />\n <small>Escucha tu dialogo, despu\u00E9s de grabar, te ayudar\u00E1 a notar tus errores.</small>\n </p>\n </div>\n\n <div class=\"settings-section\" *ngIf=\"showFeature.superHearing\">\n <p>\n <p-checkbox class=\"mr-2\" formControlName=\"superHearing\" [binary]=\"true\" inputId=\"binary\"></p-checkbox>\n <span>Super O\u00EDdo \uD83E\uDDBE</span>\n <br />\n <small>Tu audio se procesa en el servidor para mejor efectividad, si no usa el navegador.</small>\n </p>\n </div>\n\n <div class=\"settings-section\" *ngIf=\"showFeature.fixGrammar\">\n <p>\n <p-checkbox class=\"mr-2\" formControlName=\"fixGrammar\" [binary]=\"true\" inputId=\"binary\"></p-checkbox>\n <span [class.cross]=\"form.controls.fixGrammar.disabled\">Corregir gram\u00E1tica</span>\n <br />\n <small>La ai corrige tu forma de hablar/escribir y te retrolimenta de tus errores</small>\n </p>\n </div>\n\n <div class=\"settings-section\" *ngIf=\"showFeature.autoTranslate\">\n <p>\n <p-checkbox class=\"mr-2\" formControlName=\"autoTranslate\" [binary]=\"true\" inputId=\"binary\"></p-checkbox>\n <span [class.cross]=\"form.controls.autoTranslate.disabled\">Mostrar Traducciones</span>\n <br />\n <small>Texto adicional con la traducci\u00F3n</small>\n </p>\n </div>\n\n <div class=\"voice-selection\" *ngIf=\"showFeature.autoTranslate\">\n <span>Voz Preferencial:</span>\n <br />\n <p-radioButton value=\"random\" formControlName=\"voice\"></p-radioButton>\n <label class=\"space\">Aleatorio</label>\n\n <p-radioButton value=\"randomMan\" formControlName=\"voice\"></p-radioButton>\n <label class=\"space\">Hombre</label>\n\n <p-radioButton value=\"randomWoman\" formControlName=\"voice\"></p-radioButton>\n <label class=\"space\">Mujer</label>\n </div>\n\n @if(isAdmin) {\n <div>\n <hr />\n <b>Admin Section</b>\n <br />\n\n <b>Modelo:</b>\n\n <dc-provider-selector [parentForm]=\"form.controls.model\"></dc-provider-selector>\n </div>\n }\n\n <div class=\"button-group\">\n <p-button (click)=\"saveSettings()\" label=\"Guardar cambios\"></p-button>\n <p-button (click)=\"close()\" label=\"Cancelar\" styleClass=\"p-button-secondary\"></p-button>\n </div>\n </form>\n</div>\n", styles: [".dialog-container{padding:20px;background:#fff;border-radius:8px;min-width:300px;max-width:500px}.dialog-content{margin:20px 0}.dialog-actions{display:flex;justify-content:flex-end}.settings-section{margin-bottom:20px}.settings-section label{display:block;margin-bottom:5px;font-weight:700}.settings-section small{display:block;color:#666;margin-top:2px}.voice-selection{margin:15px 0}.voice-selection label{margin-right:15px}.button-group{margin-top:20px;display:flex;gap:10px;justify-content:flex-end}button{padding:8px 16px;border-radius:4px;border:none;cursor:pointer}button:first-child{background-color:#007bff;color:#fff}button:last-child{background-color:#6c757d;color:#fff}.space{margin-left:3px}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "ngmodule", type: CheckboxModule }, { kind: "component", type: i4$2.Checkbox, selector: "p-checkbox, p-checkBox, p-check-box", inputs: ["value", "name", "disabled", "binary", "ariaLabelledBy", "ariaLabel", "tabindex", "inputId", "style", "inputStyle", "styleClass", "inputClass", "indeterminate", "size", "formControl", "checkboxIcon", "readonly", "required", "autofocus", "trueValue", "falseValue", "variant"], outputs: ["onChange", "onFocus", "onBlur"] }, { kind: "directive", type: i4$1.PrimeTemplate, selector: "[pTemplate]", inputs: ["type", "pTemplate"] }, { kind: "ngmodule", type: SliderModule }, { kind: "ngmodule", type: RadioButtonModule }, { kind: "component", type: i3$1.RadioButton, selector: "p-radioButton, p-radiobutton, p-radio-button", inputs: ["value", "formControlName", "name", "disabled", "variant", "size", "tabindex", "inputId", "ariaLabelledBy", "ariaLabel", "style", "styleClass", "autofocus", "binary"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "ngmodule", type: ButtonModule }, { kind: "component", type: i7.Button, selector: "p-button", inputs: ["type", "iconPos", "icon", "badge", "label", "disabled", "loading", "loadingIcon", "raised", "rounded", "text", "plain", "severity", "outlined", "link", "tabindex", "size", "variant", "style", "styleClass", "badgeClass", "badgeSeverity", "ariaLabel", "autofocus", "fluid", "buttonProps"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "pipe", type: SpeedDescPipe, name: "speedDisplay" }, { kind: "ngmodule", type: RatingModule }, { kind: "component", type: i8.Rating, selector: "p-rating", inputs: ["disabled", "readonly", "stars", "iconOnClass", "iconOnStyle", "iconOffClass", "iconOffStyle", "autofocus"], outputs: ["onRate", "onCancel", "onFocus", "onBlur"] }, { kind: "ngmodule", type: TableModule }, { kind: "ngmodule", type: BadgeModule }, { kind: "ngmodule", type: SkeletonModule }, { kind: "ngmodule", type: TooltipModule }, { kind: "component", type: ProviderSelectorComponent, selector: "dc-provider-selector", inputs: ["parentForm"] }] }); }
|
|
2123
|
-
}
|
|
2124
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.1.1", ngImport: i0, type: DCConversationUserChatSettingsComponent, decorators: [{
|
|
2125
|
-
type: Component,
|
|
2126
|
-
args: [{ selector: 'dc-chat-settings-dialog', standalone: true, imports: [
|
|
2127
|
-
CommonModule,
|
|
2128
|
-
ReactiveFormsModule,
|
|
2129
|
-
CheckboxModule,
|
|
2130
|
-
SliderModule,
|
|
2131
|
-
RadioButtonModule,
|
|
2132
|
-
ButtonModule,
|
|
2133
|
-
SpeedDescPipe,
|
|
2134
|
-
RatingModule,
|
|
2135
|
-
TableModule,
|
|
2136
|
-
BadgeModule,
|
|
2137
|
-
SkeletonModule,
|
|
2138
|
-
TooltipModule,
|
|
2139
|
-
ProviderSelectorComponent,
|
|
2140
|
-
], template: "<div class=\"dialog-container\">\n <form [formGroup]=\"form\">\n <div class=\"settings-section\" *ngIf=\"showFeature.synthVoice\">\n <p>\n <p-checkbox class=\"mr-2\" formControlName=\"synthVoice\" [binary]=\"true\" inputId=\"binary\"></p-checkbox>\n <span [class.cross]=\"form.controls.synthVoice.disabled\">Escuchar Voz</span>\n <br />\n <small>Desmarca si solo quieres leer texto</small>\n </p>\n </div>\n\n <div class=\"settings-section\" *ngIf=\"showFeature.highlightWords\">\n <p>\n <p-checkbox class=\"mr-2\" formControlName=\"highlightWords\" [binary]=\"true\" inputId=\"binary\"></p-checkbox>\n <span>Narraci\u00F3n de texto</span>\n <br />\n <small>Remarca las palabras como se van pronuncionando</small>\n </p>\n </div>\n\n <div class=\"settings-section\" *ngIf=\"showFeature.speed\">\n <p>\n Velocidad ({{ form.controls.speed.value | speedDisplay }})\n <br />\n <p-rating formControlName=\"speed\">\n <ng-template pTemplate=\"onicon\">\n <i class=\"pi pi-caret-right\"></i>\n </ng-template>\n <ng-template pTemplate=\"officon\">\n <i class=\"pi pi-circle\"></i>\n </ng-template>\n </p-rating>\n </p>\n </div>\n\n <div class=\"settings-section\" *ngIf=\"showFeature.realTime\">\n <p>\n <p-checkbox class=\"mr-2\" formControlName=\"realTime\" [binary]=\"true\" inputId=\"binary\"></p-checkbox>\n <span [class.cross]=\"form.controls.realTime.disabled\">Tiempo real</span>\n <br />\n <small>No tienes que presionar el microphono, comenzar\u00E1 a grabar en cuanto la AI termine de hablar, cierra el chat para finalizar conversaci\u00F3n.</small>\n </p>\n </div>\n\n <div class=\"settings-section\" *ngIf=\"showFeature.realTime\">\n <p>\n <p-checkbox class=\"mr-2\" formControlName=\"repeatRecording\" [binary]=\"true\" inputId=\"binary\"></p-checkbox>\n <span>Reproducir mi grabaci\u00F3n</span>\n <br />\n <small>Escucha tu dialogo, despu\u00E9s de grabar, te ayudar\u00E1 a notar tus errores.</small>\n </p>\n </div>\n\n <div class=\"settings-section\" *ngIf=\"showFeature.superHearing\">\n <p>\n <p-checkbox class=\"mr-2\" formControlName=\"superHearing\" [binary]=\"true\" inputId=\"binary\"></p-checkbox>\n <span>Super O\u00EDdo \uD83E\uDDBE</span>\n <br />\n <small>Tu audio se procesa en el servidor para mejor efectividad, si no usa el navegador.</small>\n </p>\n </div>\n\n <div class=\"settings-section\" *ngIf=\"showFeature.fixGrammar\">\n <p>\n <p-checkbox class=\"mr-2\" formControlName=\"fixGrammar\" [binary]=\"true\" inputId=\"binary\"></p-checkbox>\n <span [class.cross]=\"form.controls.fixGrammar.disabled\">Corregir gram\u00E1tica</span>\n <br />\n <small>La ai corrige tu forma de hablar/escribir y te retrolimenta de tus errores</small>\n </p>\n </div>\n\n <div class=\"settings-section\" *ngIf=\"showFeature.autoTranslate\">\n <p>\n <p-checkbox class=\"mr-2\" formControlName=\"autoTranslate\" [binary]=\"true\" inputId=\"binary\"></p-checkbox>\n <span [class.cross]=\"form.controls.autoTranslate.disabled\">Mostrar Traducciones</span>\n <br />\n <small>Texto adicional con la traducci\u00F3n</small>\n </p>\n </div>\n\n <div class=\"voice-selection\" *ngIf=\"showFeature.autoTranslate\">\n <span>Voz Preferencial:</span>\n <br />\n <p-radioButton value=\"random\" formControlName=\"voice\"></p-radioButton>\n <label class=\"space\">Aleatorio</label>\n\n <p-radioButton value=\"randomMan\" formControlName=\"voice\"></p-radioButton>\n <label class=\"space\">Hombre</label>\n\n <p-radioButton value=\"randomWoman\" formControlName=\"voice\"></p-radioButton>\n <label class=\"space\">Mujer</label>\n </div>\n\n @if(isAdmin) {\n <div>\n <hr />\n <b>Admin Section</b>\n <br />\n\n <b>Modelo:</b>\n\n <dc-provider-selector [parentForm]=\"form.controls.model\"></dc-provider-selector>\n </div>\n }\n\n <div class=\"button-group\">\n <p-button (click)=\"saveSettings()\" label=\"Guardar cambios\"></p-button>\n <p-button (click)=\"close()\" label=\"Cancelar\" styleClass=\"p-button-secondary\"></p-button>\n </div>\n </form>\n</div>\n", styles: [".dialog-container{padding:20px;background:#fff;border-radius:8px;min-width:300px;max-width:500px}.dialog-content{margin:20px 0}.dialog-actions{display:flex;justify-content:flex-end}.settings-section{margin-bottom:20px}.settings-section label{display:block;margin-bottom:5px;font-weight:700}.settings-section small{display:block;color:#666;margin-top:2px}.voice-selection{margin:15px 0}.voice-selection label{margin-right:15px}.button-group{margin-top:20px;display:flex;gap:10px;justify-content:flex-end}button{padding:8px 16px;border-radius:4px;border:none;cursor:pointer}button:first-child{background-color:#007bff;color:#fff}button:last-child{background-color:#6c757d;color:#fff}.space{margin-left:3px}\n"] }]
|
|
2141
|
-
}], ctorParameters: () => [{ type: i1$3.DynamicDialogRef }, { type: i1.FormBuilder }, { type: AgentCardsAbstractService, decorators: [{
|
|
2142
|
-
type: Inject,
|
|
2143
|
-
args: [CONVERSATION_AI_TOKEN]
|
|
2144
|
-
}] }], propDecorators: { showFeature: [{
|
|
2145
|
-
type: Input
|
|
2146
|
-
}], onSettingsChange: [{
|
|
2147
|
-
type: Output
|
|
2148
|
-
}], tooltipRef: [{
|
|
2149
|
-
type: ViewChild,
|
|
2150
|
-
args: ['tooltipRef']
|
|
2151
|
-
}] } });
|
|
2152
|
-
|
|
2153
|
-
const EvalResultStringDefinition = `
|
|
2154
|
-
interface EvalResult {
|
|
2155
|
-
score: number; // Score of the user's response 0 to 3
|
|
2156
|
-
feedback: string; // Feedback of the user's understanding of the conversation
|
|
2157
|
-
}`;
|
|
2158
|
-
const DefaultEvaluatorAgentCard = {
|
|
2159
|
-
task: 'Evaluate the user understanding of the lesson',
|
|
2160
|
-
messages: [],
|
|
2161
|
-
expectedResponseType: EvalResultStringDefinition,
|
|
2162
|
-
model: { id: 'gpt-4o-mini', provider: 'openai' },
|
|
2163
|
-
sources: [],
|
|
2164
|
-
};
|
|
2165
|
-
|
|
2166
|
-
function extractJsonFromResponse(content) {
|
|
2167
|
-
const jsonMatch = content.match(/\{[\s\S]*?\}/); // Match everything between first { and }
|
|
2168
|
-
if (!jsonMatch)
|
|
2169
|
-
return null;
|
|
2170
|
-
try {
|
|
2171
|
-
return JSON.parse(jsonMatch[0]);
|
|
2172
|
-
}
|
|
2173
|
-
catch (error) {
|
|
2174
|
-
console.error('Error parsing JSON:', error);
|
|
2175
|
-
return null;
|
|
2176
|
-
}
|
|
2177
|
-
}
|
|
2178
|
-
|
|
2179
|
-
class EvaluationService {
|
|
2180
|
-
constructor(agentCardService) {
|
|
2181
|
-
this.agentCardService = agentCardService;
|
|
2182
|
-
this.scoreSignal = signal(10);
|
|
2183
|
-
this.evaluationResultSignal = signal(null);
|
|
2184
|
-
}
|
|
2185
|
-
// Get score as a signal
|
|
2186
|
-
getScore() {
|
|
2187
|
-
return this.scoreSignal;
|
|
2188
|
-
}
|
|
2189
|
-
// Get evaluation result as a signal
|
|
2190
|
-
getEvaluationResult() {
|
|
2191
|
-
return this.evaluationResultSignal;
|
|
2192
|
-
}
|
|
2193
|
-
// Evaluate conversation
|
|
2194
|
-
async evaluateConversation(messages, evaluator) {
|
|
2195
|
-
// Filter conversation to only include user and assistant messages
|
|
2196
|
-
const conversationMessages = messages.filter((message) => [ChatRole.User, ChatRole.Assistant].includes(message.role));
|
|
2197
|
-
// Check if there are any user messages to evaluate
|
|
2198
|
-
const userMessages = conversationMessages.filter((message) => message.role === ChatRole.User);
|
|
2199
|
-
if (userMessages.length <= 0) {
|
|
2200
|
-
console.log('No messages to evaluate', conversationMessages);
|
|
2201
|
-
return;
|
|
2202
|
-
}
|
|
2203
|
-
// Format conversation for evaluation
|
|
2204
|
-
const conversationMessagesString = conversationMessages
|
|
2205
|
-
.map((message) => `${message.role}: ${message.content}`)
|
|
2206
|
-
.join('\n');
|
|
2207
|
-
// Create evaluation prompt
|
|
2208
|
-
const instructions = `
|
|
2209
|
-
Please replay to this task:
|
|
2210
|
-
${evaluator.task}
|
|
2211
|
-
This is the conversation history:
|
|
2212
|
-
${conversationMessagesString}
|
|
2213
|
-
and give the response in next JSON format.
|
|
2214
|
-
${evaluator.expectedResponseType}
|
|
2215
|
-
`;
|
|
2216
|
-
// Send evaluation request
|
|
2217
|
-
const evaluationMessages = [{ content: instructions, role: ChatRole.User }];
|
|
2218
|
-
const response = await this.agentCardService.callChatCompletion({ messages: evaluationMessages });
|
|
2219
|
-
// Extract JSON from response
|
|
2220
|
-
const jsonData = extractJsonFromResponse(response.content);
|
|
2221
|
-
this.evaluationResultSignal.set(jsonData);
|
|
2222
|
-
// Update score if available
|
|
2223
|
-
if (jsonData.score) {
|
|
2224
|
-
if (jsonData.score <= 3) {
|
|
2225
|
-
this.updateScore(jsonData.score * 10);
|
|
2226
|
-
}
|
|
2227
|
-
}
|
|
2228
|
-
}
|
|
2229
|
-
// Update score with limits
|
|
2230
|
-
updateScore(additionalScore) {
|
|
2231
|
-
this.scoreSignal.update(currentScore => {
|
|
2232
|
-
const newScore = currentScore + additionalScore;
|
|
2233
|
-
return newScore > 100 ? 100 : newScore;
|
|
2234
|
-
});
|
|
2307
|
+
this.dialogRef.close();
|
|
2235
2308
|
}
|
|
2236
|
-
|
|
2237
|
-
|
|
2238
|
-
|
|
2309
|
+
async saveSettings() {
|
|
2310
|
+
if (this.form.valid) {
|
|
2311
|
+
await this.conversationCardAIService.saveConversationUserChatSettings(this.form.value);
|
|
2312
|
+
this.dialogRef.close(this.form.value);
|
|
2313
|
+
}
|
|
2239
2314
|
}
|
|
2240
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.
|
|
2241
|
-
static { this.ɵ
|
|
2315
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: DCConversationUserChatSettingsComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
2316
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.4", type: DCConversationUserChatSettingsComponent, isStandalone: true, selector: "dc-chat-settings-dialog", inputs: { showFeature: { classPropertyName: "showFeature", publicName: "showFeature", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { onSettingsChange: "onSettingsChange" }, viewQueries: [{ propertyName: "tooltipRef", first: true, predicate: ["tooltipRef"], descendants: true }], ngImport: i0, template: "<div class=\"dialog-container\">\n <form [formGroup]=\"form\">\n @if (showFeature().synthVoice) {\n <div class=\"settings-section\">\n <p>\n <p-checkbox class=\"mr-2\" formControlName=\"synthVoice\" [binary]=\"true\" inputId=\"binary\"></p-checkbox>\n <span [class.cross]=\"form.controls.synthVoice.disabled\">Escuchar Voz</span>\n <br />\n <small>Desmarca si solo quieres leer texto</small>\n </p>\n </div>\n }\n\n @if (showFeature().highlightWords) {\n <div class=\"settings-section\">\n <p>\n <p-checkbox class=\"mr-2\" formControlName=\"highlightWords\" [binary]=\"true\" inputId=\"binary\"></p-checkbox>\n <span>Narraci\u00F3n de texto</span>\n <br />\n <small>Remarca las palabras como se van pronuncionando</small>\n </p>\n </div>\n }\n\n @if (showFeature().speed) {\n <div class=\"settings-section\">\n <p>\n Velocidad ({{ form.controls.speed.value | speedDisplay }})\n <br />\n <p-rating formControlName=\"speed\">\n <ng-template pTemplate=\"onicon\">\n <i class=\"pi pi-caret-right\"></i>\n </ng-template>\n <ng-template pTemplate=\"officon\">\n <i class=\"pi pi-circle\"></i>\n </ng-template>\n </p-rating>\n </p>\n </div>\n }\n\n @if (showFeature().realTime) {\n <div class=\"settings-section\">\n <p>\n <p-checkbox class=\"mr-2\" formControlName=\"realTime\" [binary]=\"true\" inputId=\"binary\"></p-checkbox>\n <span [class.cross]=\"form.controls.realTime.disabled\">Tiempo real</span>\n <br />\n <small>No tienes que presionar el microphono, comenzar\u00E1 a grabar en cuanto la AI termine de hablar, cierra el chat para finalizar conversaci\u00F3n.</small>\n </p>\n </div>\n }\n\n @if (showFeature().realTime) {\n <div class=\"settings-section\">\n <p>\n <p-checkbox class=\"mr-2\" formControlName=\"repeatRecording\" [binary]=\"true\" inputId=\"binary\"></p-checkbox>\n <span>Reproducir mi grabaci\u00F3n</span>\n <br />\n <small>Escucha tu dialogo, despu\u00E9s de grabar, te ayudar\u00E1 a notar tus errores.</small>\n </p>\n </div>\n }\n\n @if (showFeature().superHearing) {\n <div class=\"settings-section\">\n <p>\n <p-checkbox class=\"mr-2\" formControlName=\"superHearing\" [binary]=\"true\" inputId=\"binary\"></p-checkbox>\n <span>Super O\u00EDdo \uD83E\uDDBE</span>\n <br />\n <small>Tu audio se procesa en el servidor para mejor efectividad, si no usa el navegador.</small>\n </p>\n </div>\n }\n\n @if (showFeature().fixGrammar) {\n <div class=\"settings-section\">\n <p>\n <p-checkbox class=\"mr-2\" formControlName=\"fixGrammar\" [binary]=\"true\" inputId=\"binary\"></p-checkbox>\n <span [class.cross]=\"form.controls.fixGrammar.disabled\">Corregir gram\u00E1tica</span>\n <br />\n <small>La ai corrige tu forma de hablar/escribir y te retrolimenta de tus errores</small>\n </p>\n </div>\n }\n\n @if (showFeature().autoTranslate) {\n <div class=\"settings-section\">\n <p>\n <p-checkbox class=\"mr-2\" formControlName=\"autoTranslate\" [binary]=\"true\" inputId=\"binary\"></p-checkbox>\n <span [class.cross]=\"form.controls.autoTranslate.disabled\">Mostrar Traducciones</span>\n <br />\n <small>Texto adicional con la traducci\u00F3n</small>\n </p>\n </div>\n }\n\n @if (showFeature().autoTranslate) {\n <div class=\"voice-selection\">\n <span>Voz Preferencial:</span>\n <br />\n <p-radioButton value=\"random\" formControlName=\"voice\"></p-radioButton>\n <label class=\"space\">Aleatorio</label>\n <p-radioButton value=\"randomMan\" formControlName=\"voice\"></p-radioButton>\n <label class=\"space\">Hombre</label>\n <p-radioButton value=\"randomWoman\" formControlName=\"voice\"></p-radioButton>\n <label class=\"space\">Mujer</label>\n </div>\n }\n\n @if(isAdmin) {\n <div>\n <hr />\n <b>Admin Section</b>\n <br />\n\n <b>Modelo:</b>\n\n <dc-provider-selector [parentForm]=\"form.controls.model\"></dc-provider-selector>\n </div>\n }\n\n <div class=\"button-group\">\n <p-button (click)=\"saveSettings()\" label=\"Guardar cambios\"></p-button>\n <p-button (click)=\"close()\" label=\"Cancelar\" styleClass=\"p-button-secondary\"></p-button>\n </div>\n </form>\n</div>\n", styles: [".dialog-container{padding:20px;background:#fff;border-radius:8px;min-width:300px;max-width:500px}.dialog-content{margin:20px 0}.dialog-actions{display:flex;justify-content:flex-end}.settings-section{margin-bottom:20px}.settings-section label{display:block;margin-bottom:5px;font-weight:700}.settings-section small{display:block;color:#666;margin-top:2px}.voice-selection{margin:15px 0}.voice-selection label{margin-right:15px}.button-group{margin-top:20px;display:flex;gap:10px;justify-content:flex-end}button{padding:8px 16px;border-radius:4px;border:none;cursor:pointer}button:first-child{background-color:#007bff;color:#fff}button:last-child{background-color:#6c757d;color:#fff}.space{margin-left:3px}\n"], dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "ngmodule", type: CheckboxModule }, { kind: "component", type: i2$3.Checkbox, selector: "p-checkbox, p-checkBox, p-check-box", inputs: ["value", "name", "disabled", "binary", "ariaLabelledBy", "ariaLabel", "tabindex", "inputId", "style", "inputStyle", "styleClass", "inputClass", "indeterminate", "size", "formControl", "checkboxIcon", "readonly", "required", "autofocus", "trueValue", "falseValue", "variant"], outputs: ["onChange", "onFocus", "onBlur"] }, { kind: "directive", type: i3$1.PrimeTemplate, selector: "[pTemplate]", inputs: ["type", "pTemplate"] }, { kind: "ngmodule", type: SliderModule }, { kind: "ngmodule", type: RadioButtonModule }, { kind: "component", type: i2$2.RadioButton, selector: "p-radioButton, p-radiobutton, p-radio-button", inputs: ["value", "formControlName", "name", "disabled", "variant", "size", "tabindex", "inputId", "ariaLabelledBy", "ariaLabel", "style", "styleClass", "autofocus", "binary"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "ngmodule", type: ButtonModule }, { kind: "component", type: i2$1.Button, selector: "p-button", inputs: ["type", "iconPos", "icon", "badge", "label", "disabled", "loading", "loadingIcon", "raised", "rounded", "text", "plain", "severity", "outlined", "link", "tabindex", "size", "variant", "style", "styleClass", "badgeClass", "badgeSeverity", "ariaLabel", "autofocus", "fluid", "buttonProps"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "pipe", type: SpeedDescPipe, name: "speedDisplay" }, { kind: "ngmodule", type: RatingModule }, { kind: "component", type: i6$1.Rating, selector: "p-rating", inputs: ["disabled", "readonly", "stars", "iconOnClass", "iconOnStyle", "iconOffClass", "iconOffStyle", "autofocus"], outputs: ["onRate", "onCancel", "onFocus", "onBlur"] }, { kind: "ngmodule", type: TableModule }, { kind: "ngmodule", type: BadgeModule }, { kind: "ngmodule", type: SkeletonModule }, { kind: "ngmodule", type: TooltipModule }, { kind: "component", type: ProviderSelectorComponent, selector: "dc-provider-selector", inputs: ["parentForm"] }] }); }
|
|
2242
2317
|
}
|
|
2243
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.
|
|
2244
|
-
type:
|
|
2245
|
-
args: [{
|
|
2246
|
-
|
|
2247
|
-
|
|
2248
|
-
|
|
2249
|
-
|
|
2250
|
-
|
|
2251
|
-
|
|
2318
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: DCConversationUserChatSettingsComponent, decorators: [{
|
|
2319
|
+
type: Component,
|
|
2320
|
+
args: [{ selector: 'dc-chat-settings-dialog', standalone: true, imports: [
|
|
2321
|
+
ReactiveFormsModule,
|
|
2322
|
+
CheckboxModule,
|
|
2323
|
+
SliderModule,
|
|
2324
|
+
RadioButtonModule,
|
|
2325
|
+
ButtonModule,
|
|
2326
|
+
SpeedDescPipe,
|
|
2327
|
+
RatingModule,
|
|
2328
|
+
TableModule,
|
|
2329
|
+
BadgeModule,
|
|
2330
|
+
SkeletonModule,
|
|
2331
|
+
TooltipModule,
|
|
2332
|
+
ProviderSelectorComponent
|
|
2333
|
+
], template: "<div class=\"dialog-container\">\n <form [formGroup]=\"form\">\n @if (showFeature().synthVoice) {\n <div class=\"settings-section\">\n <p>\n <p-checkbox class=\"mr-2\" formControlName=\"synthVoice\" [binary]=\"true\" inputId=\"binary\"></p-checkbox>\n <span [class.cross]=\"form.controls.synthVoice.disabled\">Escuchar Voz</span>\n <br />\n <small>Desmarca si solo quieres leer texto</small>\n </p>\n </div>\n }\n\n @if (showFeature().highlightWords) {\n <div class=\"settings-section\">\n <p>\n <p-checkbox class=\"mr-2\" formControlName=\"highlightWords\" [binary]=\"true\" inputId=\"binary\"></p-checkbox>\n <span>Narraci\u00F3n de texto</span>\n <br />\n <small>Remarca las palabras como se van pronuncionando</small>\n </p>\n </div>\n }\n\n @if (showFeature().speed) {\n <div class=\"settings-section\">\n <p>\n Velocidad ({{ form.controls.speed.value | speedDisplay }})\n <br />\n <p-rating formControlName=\"speed\">\n <ng-template pTemplate=\"onicon\">\n <i class=\"pi pi-caret-right\"></i>\n </ng-template>\n <ng-template pTemplate=\"officon\">\n <i class=\"pi pi-circle\"></i>\n </ng-template>\n </p-rating>\n </p>\n </div>\n }\n\n @if (showFeature().realTime) {\n <div class=\"settings-section\">\n <p>\n <p-checkbox class=\"mr-2\" formControlName=\"realTime\" [binary]=\"true\" inputId=\"binary\"></p-checkbox>\n <span [class.cross]=\"form.controls.realTime.disabled\">Tiempo real</span>\n <br />\n <small>No tienes que presionar el microphono, comenzar\u00E1 a grabar en cuanto la AI termine de hablar, cierra el chat para finalizar conversaci\u00F3n.</small>\n </p>\n </div>\n }\n\n @if (showFeature().realTime) {\n <div class=\"settings-section\">\n <p>\n <p-checkbox class=\"mr-2\" formControlName=\"repeatRecording\" [binary]=\"true\" inputId=\"binary\"></p-checkbox>\n <span>Reproducir mi grabaci\u00F3n</span>\n <br />\n <small>Escucha tu dialogo, despu\u00E9s de grabar, te ayudar\u00E1 a notar tus errores.</small>\n </p>\n </div>\n }\n\n @if (showFeature().superHearing) {\n <div class=\"settings-section\">\n <p>\n <p-checkbox class=\"mr-2\" formControlName=\"superHearing\" [binary]=\"true\" inputId=\"binary\"></p-checkbox>\n <span>Super O\u00EDdo \uD83E\uDDBE</span>\n <br />\n <small>Tu audio se procesa en el servidor para mejor efectividad, si no usa el navegador.</small>\n </p>\n </div>\n }\n\n @if (showFeature().fixGrammar) {\n <div class=\"settings-section\">\n <p>\n <p-checkbox class=\"mr-2\" formControlName=\"fixGrammar\" [binary]=\"true\" inputId=\"binary\"></p-checkbox>\n <span [class.cross]=\"form.controls.fixGrammar.disabled\">Corregir gram\u00E1tica</span>\n <br />\n <small>La ai corrige tu forma de hablar/escribir y te retrolimenta de tus errores</small>\n </p>\n </div>\n }\n\n @if (showFeature().autoTranslate) {\n <div class=\"settings-section\">\n <p>\n <p-checkbox class=\"mr-2\" formControlName=\"autoTranslate\" [binary]=\"true\" inputId=\"binary\"></p-checkbox>\n <span [class.cross]=\"form.controls.autoTranslate.disabled\">Mostrar Traducciones</span>\n <br />\n <small>Texto adicional con la traducci\u00F3n</small>\n </p>\n </div>\n }\n\n @if (showFeature().autoTranslate) {\n <div class=\"voice-selection\">\n <span>Voz Preferencial:</span>\n <br />\n <p-radioButton value=\"random\" formControlName=\"voice\"></p-radioButton>\n <label class=\"space\">Aleatorio</label>\n <p-radioButton value=\"randomMan\" formControlName=\"voice\"></p-radioButton>\n <label class=\"space\">Hombre</label>\n <p-radioButton value=\"randomWoman\" formControlName=\"voice\"></p-radioButton>\n <label class=\"space\">Mujer</label>\n </div>\n }\n\n @if(isAdmin) {\n <div>\n <hr />\n <b>Admin Section</b>\n <br />\n\n <b>Modelo:</b>\n\n <dc-provider-selector [parentForm]=\"form.controls.model\"></dc-provider-selector>\n </div>\n }\n\n <div class=\"button-group\">\n <p-button (click)=\"saveSettings()\" label=\"Guardar cambios\"></p-button>\n <p-button (click)=\"close()\" label=\"Cancelar\" styleClass=\"p-button-secondary\"></p-button>\n </div>\n </form>\n</div>\n", styles: [".dialog-container{padding:20px;background:#fff;border-radius:8px;min-width:300px;max-width:500px}.dialog-content{margin:20px 0}.dialog-actions{display:flex;justify-content:flex-end}.settings-section{margin-bottom:20px}.settings-section label{display:block;margin-bottom:5px;font-weight:700}.settings-section small{display:block;color:#666;margin-top:2px}.voice-selection{margin:15px 0}.voice-selection label{margin-right:15px}.button-group{margin-top:20px;display:flex;gap:10px;justify-content:flex-end}button{padding:8px 16px;border-radius:4px;border:none;cursor:pointer}button:first-child{background-color:#007bff;color:#fff}button:last-child{background-color:#6c757d;color:#fff}.space{margin-left:3px}\n"] }]
|
|
2334
|
+
}], ctorParameters: () => [], propDecorators: { tooltipRef: [{
|
|
2335
|
+
type: ViewChild,
|
|
2336
|
+
args: ['tooltipRef']
|
|
2337
|
+
}] } });
|
|
2338
|
+
|
|
2339
|
+
const EvalResultStringDefinition = `
|
|
2340
|
+
interface EvalResult {
|
|
2341
|
+
score: number; // Score of the user's response 0 to 3
|
|
2342
|
+
feedback: string; // Feedback of the user's understanding of the conversation
|
|
2343
|
+
}`;
|
|
2344
|
+
const DefaultEvaluatorAgentCard = {
|
|
2345
|
+
task: 'Evaluate the user understanding of the lesson',
|
|
2346
|
+
messages: [],
|
|
2347
|
+
expectedResponseType: EvalResultStringDefinition,
|
|
2348
|
+
model: { id: 'gpt-4o-mini', provider: 'openai' },
|
|
2349
|
+
sources: [],
|
|
2350
|
+
};
|
|
2252
2351
|
|
|
2253
|
-
class
|
|
2254
|
-
constructor(
|
|
2255
|
-
this.agentCardService =
|
|
2256
|
-
this.
|
|
2257
|
-
this.
|
|
2258
|
-
this.
|
|
2259
|
-
this.
|
|
2260
|
-
this.
|
|
2261
|
-
this.
|
|
2262
|
-
this.
|
|
2263
|
-
this.sendMessage = new EventEmitter();
|
|
2352
|
+
class DCChatComponent {
|
|
2353
|
+
constructor() {
|
|
2354
|
+
this.agentCardService = inject(CONVERSATION_AI_TOKEN);
|
|
2355
|
+
this.conversationBuilder = inject(DCConversationPromptBuilderService);
|
|
2356
|
+
this.dialogService = inject(DialogService);
|
|
2357
|
+
this.conversationService = inject(ConversationService);
|
|
2358
|
+
this.evaluationService = inject(EvaluationService);
|
|
2359
|
+
this.evaluatorAgentCard = input(DefaultEvaluatorAgentCard);
|
|
2360
|
+
this.parseDict = input({});
|
|
2361
|
+
this.sendMessage = output();
|
|
2264
2362
|
this.micSettings = { useWhisper: true, lang: 'en' };
|
|
2265
2363
|
this.imageUser = `assets/default/user.svg`;
|
|
2266
2364
|
this.thinkingImg = `assets/default/thinking.svg`;
|
|
@@ -2276,29 +2374,18 @@ class ChatContainerComponent {
|
|
|
2276
2374
|
this.score = this.evaluationService.getScore();
|
|
2277
2375
|
}
|
|
2278
2376
|
async ngOnInit() {
|
|
2279
|
-
debugger;
|
|
2280
2377
|
// Get user settings if not provided
|
|
2281
2378
|
if (!this.chatUserSettings) {
|
|
2282
2379
|
this.chatUserSettings = await this.agentCardService.getConversationUserChatSettings();
|
|
2283
2380
|
}
|
|
2284
2381
|
// Initialize conversation
|
|
2285
|
-
await this.conversationService.initConversation(this.agentCard, this.conversationBuilder, this.parseDict);
|
|
2382
|
+
await this.conversationService.initConversation(this.agentCard, this.conversationBuilder, this.parseDict());
|
|
2286
2383
|
}
|
|
2287
2384
|
ngOnDestroy() {
|
|
2288
2385
|
// Mark conversation as destroyed to prevent async operations
|
|
2289
2386
|
this.conversationService.setDestroyed(true);
|
|
2290
2387
|
}
|
|
2291
|
-
|
|
2292
|
-
* Handle user message from chat footer
|
|
2293
|
-
*/
|
|
2294
|
-
async onUserMessage(message) {
|
|
2295
|
-
if (this.isThinking()) {
|
|
2296
|
-
return;
|
|
2297
|
-
}
|
|
2298
|
-
await this.conversationService.sendUserMessage(message);
|
|
2299
|
-
// Evaluate conversation after sending message
|
|
2300
|
-
this.evaluateConversation();
|
|
2301
|
-
}
|
|
2388
|
+
// onUserMessage method has been moved to the chat-footer component
|
|
2302
2389
|
/**
|
|
2303
2390
|
* Handle microphone input finished
|
|
2304
2391
|
*/
|
|
@@ -2328,9 +2415,8 @@ class ChatContainerComponent {
|
|
|
2328
2415
|
};
|
|
2329
2416
|
message['audioUrl'] = URL.createObjectURL(eventBlob);
|
|
2330
2417
|
// Send message to conversation
|
|
2418
|
+
// The evaluation will happen automatically in the conversation service
|
|
2331
2419
|
await this.conversationService.sendUserMessage(message);
|
|
2332
|
-
// Evaluate conversation
|
|
2333
|
-
this.evaluateConversation();
|
|
2334
2420
|
}
|
|
2335
2421
|
finally {
|
|
2336
2422
|
this.isGettingTranscription = false;
|
|
@@ -2367,372 +2453,16 @@ class ChatContainerComponent {
|
|
|
2367
2453
|
}
|
|
2368
2454
|
await this.ngOnInit();
|
|
2369
2455
|
}
|
|
2370
|
-
|
|
2371
|
-
|
|
2372
|
-
*/
|
|
2373
|
-
async evaluateConversation() {
|
|
2374
|
-
const messages = this.messages();
|
|
2375
|
-
await this.evaluationService.evaluateConversation(messages, this.evaluatorAgentCard);
|
|
2376
|
-
}
|
|
2377
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.1.1", ngImport: i0, type: ChatContainerComponent, deps: [{ token: CONVERSATION_AI_TOKEN }, { token: TOAST_ALERTS_TOKEN }, { token: DCConversationPromptBuilderService }, { token: i1$3.DialogService }, { token: ConversationService }, { token: EvaluationService }], target: i0.ɵɵFactoryTarget.Component }); }
|
|
2378
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.1.1", type: ChatContainerComponent, isStandalone: true, selector: "dc-chat", inputs: { chatUserSettings: "chatUserSettings", agentCard: "agentCard", evaluatorAgentCard: "evaluatorAgentCard", parseDict: "parseDict" }, outputs: { sendMessage: "sendMessage" }, providers: [DialogService], ngImport: i0, template: "<div class=\"chat-container\">\n <!-- Chat Header -->\n <dc-chat-header\n [agentCard]=\"agentCard\"\n [isAdmin]=\"isAdmin\"\n (showInfoEvent)=\"showInfo()\"\n (settingsClickEvent)=\"changeUserChatSettings()\"\n (restartConversationEvent)=\"restartConversation($event)\">\n </dc-chat-header>\n\n <!-- Messages List -->\n <dc-chat-messages-list [chatUserSettings]=\"chatUserSettings\" [thinkingImg]=\"thinkingImg\"> </dc-chat-messages-list>\n\n <!-- Chat Footer -->\n <dc-chat-footer [isAIThinking]=\"isThinking()\" [micSettings]=\"micSettings\" (sendMessage)=\"onUserMessage($event)\" (micFinishedEvent)=\"onMicFinished($event)\">\n </dc-chat-footer>\n\n <!-- Progress Bar for Score -->\n <div class=\"score-container\" *ngIf=\"score() > 0\">\n <p-progressBar [value]=\"score()\" [showValue]=\"true\"></p-progressBar>\n </div>\n\n <!-- Info Dialog -->\n <p-dialog [(visible)]=\"isInfoVisible\" [modal]=\"true\" [draggable]=\"false\" [resizable]=\"false\" header=\"Conversation Info\">\n <div class=\"info-content\">\n <h3>Agent Card</h3>\n <pre>{{ agentCard | json }}</pre>\n\n <h3>User Settings</h3>\n <pre>{{ chatUserSettings | json }}</pre>\n </div>\n </p-dialog>\n</div>\n", styles: [".chat-container{display:flex;flex-direction:column;height:100%;max-height:100vh;overflow:hidden;background-color:var(--surface-ground, #f8f9fa)}dc-chat-messages-list{flex:1;overflow-y:auto;padding:.5rem}.score-container{padding:.5rem 1rem;background-color:var(--surface-card, #ffffff);border-top:1px solid var(--surface-border, #dee2e6)}.info-content{max-height:70vh;overflow-y:auto}.info-content h3{margin-top:1rem;margin-bottom:.5rem;font-size:1.2rem}.info-content pre{background-color:var(--surface-hover, #f1f1f1);padding:1rem;border-radius:4px;overflow-x:auto;font-size:.9rem}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "pipe", type: i1$2.JsonPipe, name: "json" }, { kind: "component", type: ChatHeaderComponent, selector: "dc-chat-header", inputs: ["isAdmin", "alternativeConversation", "agentCard"], outputs: ["restartConversationEvent", "showInfoEvent", "settingsClickEvent"] }, { kind: "component", type: ChatFooterComponent, selector: "dc-chat-footer", inputs: ["isAIThinking", "score", "micSettings"], outputs: ["sendMessage", "textInputChanged", "micFinishedEvent"] }, { kind: "component", type: ChatMessagesListComponent, selector: "dc-chat-messages-list", inputs: ["chatUserSettings", "thinkingImg"] }, { kind: "ngmodule", type: DialogModule }, { kind: "component", type: i6.Dialog, selector: "p-dialog", inputs: ["header", "draggable", "resizable", "positionLeft", "positionTop", "contentStyle", "contentStyleClass", "modal", "closeOnEscape", "dismissableMask", "rtl", "closable", "responsive", "appendTo", "breakpoints", "styleClass", "maskStyleClass", "maskStyle", "showHeader", "breakpoint", "blockScroll", "autoZIndex", "baseZIndex", "minX", "minY", "focusOnShow", "maximizable", "keepInViewport", "focusTrap", "transitionOptions", "closeIcon", "closeAriaLabel", "closeTabindex", "minimizeIcon", "maximizeIcon", "closeButtonProps", "maximizeButtonProps", "visible", "style", "position", "role", "content", "contentTemplate", "footerTemplate", "closeIconTemplate", "maximizeIconTemplate", "minimizeIconTemplate", "headlessTemplate"], outputs: ["onShow", "onHide", "visibleChange", "onResizeInit", "onResizeEnd", "onDragEnd", "onMaximize"] }, { kind: "ngmodule", type: ProgressBarModule }, { kind: "component", type: i2.ProgressBar, selector: "p-progressBar, p-progressbar, p-progress-bar", inputs: ["value", "showValue", "styleClass", "valueStyleClass", "style", "unit", "mode", "color"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
2379
|
-
}
|
|
2380
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.1.1", ngImport: i0, type: ChatContainerComponent, decorators: [{
|
|
2381
|
-
type: Component,
|
|
2382
|
-
args: [{ selector: 'dc-chat', standalone: true, imports: [CommonModule, ChatHeaderComponent, ChatFooterComponent, ChatMessagesListComponent, DialogModule, ProgressBarModule], changeDetection: ChangeDetectionStrategy.OnPush, providers: [DialogService], template: "<div class=\"chat-container\">\n <!-- Chat Header -->\n <dc-chat-header\n [agentCard]=\"agentCard\"\n [isAdmin]=\"isAdmin\"\n (showInfoEvent)=\"showInfo()\"\n (settingsClickEvent)=\"changeUserChatSettings()\"\n (restartConversationEvent)=\"restartConversation($event)\">\n </dc-chat-header>\n\n <!-- Messages List -->\n <dc-chat-messages-list [chatUserSettings]=\"chatUserSettings\" [thinkingImg]=\"thinkingImg\"> </dc-chat-messages-list>\n\n <!-- Chat Footer -->\n <dc-chat-footer [isAIThinking]=\"isThinking()\" [micSettings]=\"micSettings\" (sendMessage)=\"onUserMessage($event)\" (micFinishedEvent)=\"onMicFinished($event)\">\n </dc-chat-footer>\n\n <!-- Progress Bar for Score -->\n <div class=\"score-container\" *ngIf=\"score() > 0\">\n <p-progressBar [value]=\"score()\" [showValue]=\"true\"></p-progressBar>\n </div>\n\n <!-- Info Dialog -->\n <p-dialog [(visible)]=\"isInfoVisible\" [modal]=\"true\" [draggable]=\"false\" [resizable]=\"false\" header=\"Conversation Info\">\n <div class=\"info-content\">\n <h3>Agent Card</h3>\n <pre>{{ agentCard | json }}</pre>\n\n <h3>User Settings</h3>\n <pre>{{ chatUserSettings | json }}</pre>\n </div>\n </p-dialog>\n</div>\n", styles: [".chat-container{display:flex;flex-direction:column;height:100%;max-height:100vh;overflow:hidden;background-color:var(--surface-ground, #f8f9fa)}dc-chat-messages-list{flex:1;overflow-y:auto;padding:.5rem}.score-container{padding:.5rem 1rem;background-color:var(--surface-card, #ffffff);border-top:1px solid var(--surface-border, #dee2e6)}.info-content{max-height:70vh;overflow-y:auto}.info-content h3{margin-top:1rem;margin-bottom:.5rem;font-size:1.2rem}.info-content pre{background-color:var(--surface-hover, #f1f1f1);padding:1rem;border-radius:4px;overflow-x:auto;font-size:.9rem}\n"] }]
|
|
2383
|
-
}], ctorParameters: () => [{ type: AgentCardsAbstractService, decorators: [{
|
|
2384
|
-
type: Inject,
|
|
2385
|
-
args: [CONVERSATION_AI_TOKEN]
|
|
2386
|
-
}] }, { type: i6$1.ToastAlertsAbstractService, decorators: [{
|
|
2387
|
-
type: Inject,
|
|
2388
|
-
args: [TOAST_ALERTS_TOKEN]
|
|
2389
|
-
}] }, { type: DCConversationPromptBuilderService }, { type: i1$3.DialogService }, { type: ConversationService }, { type: EvaluationService }], propDecorators: { chatUserSettings: [{
|
|
2390
|
-
type: Input
|
|
2391
|
-
}], agentCard: [{
|
|
2392
|
-
type: Input
|
|
2393
|
-
}], evaluatorAgentCard: [{
|
|
2394
|
-
type: Input
|
|
2395
|
-
}], parseDict: [{
|
|
2396
|
-
type: Input
|
|
2397
|
-
}], sendMessage: [{
|
|
2398
|
-
type: Output
|
|
2399
|
-
}] } });
|
|
2400
|
-
|
|
2401
|
-
class DCChatComponent {
|
|
2402
|
-
constructor(agentCardService, toastService, conversationBuilder, dialogService, cdr) {
|
|
2403
|
-
this.agentCardService = agentCardService;
|
|
2404
|
-
this.toastService = toastService;
|
|
2405
|
-
this.conversationBuilder = conversationBuilder;
|
|
2406
|
-
this.dialogService = dialogService;
|
|
2407
|
-
this.cdr = cdr;
|
|
2408
|
-
this.sendMessage = new EventEmitter();
|
|
2409
|
-
this.micSettings = { useWhisper: true, lang: 'en' };
|
|
2410
|
-
this.messages = [];
|
|
2411
|
-
this.imageUser = `assets/default/user.svg`;
|
|
2412
|
-
this.thinkingImg = `assets/default/thinking.svg`;
|
|
2413
|
-
this.aiIcon = `assets/default/ai.svg`;
|
|
2414
|
-
this.isDestroyed = false;
|
|
2415
|
-
this.score = 10;
|
|
2416
|
-
this.isAdmin = true;
|
|
2417
|
-
this.isInfoVisible = false;
|
|
2418
|
-
this.isChatSettingsVisible = false;
|
|
2419
|
-
// chatInputControl moved to ChatFooterComponent
|
|
2420
|
-
this.isAIThinking = false;
|
|
2421
|
-
this.isUserTalking = false;
|
|
2422
|
-
this.user = null; // TODO: remove user.
|
|
2423
|
-
this.isGettingTranscription = false;
|
|
2424
|
-
}
|
|
2425
|
-
async ngOnInit() {
|
|
2426
|
-
if (!this.agentCard?.conversationSettings) {
|
|
2427
|
-
throw new Error('Conversation settings are required');
|
|
2428
|
-
}
|
|
2429
|
-
this.defaultVoice = this.getVoice(this.agentCard?.conversationSettings?.voice);
|
|
2430
|
-
// This method overrides the conversationSettings of the agentCard so message will be there.
|
|
2431
|
-
this.conversationSettings = this.conversationBuilder.buildConversationSettings(this.agentCard, this.parseDict);
|
|
2432
|
-
this.agentCard.conversationSettings = this.conversationSettings; // Save a copy here.
|
|
2433
|
-
if (!this.chatUserSettings) {
|
|
2434
|
-
console.log('looking for conversation user chat settings');
|
|
2435
|
-
this.chatUserSettings = await this.agentCardService.getConversationUserChatSettings();
|
|
2436
|
-
}
|
|
2437
|
-
if (!this.conversationSettings.messages) {
|
|
2438
|
-
throw new Error('conversationSettings.messages is required in propert format to start conversation');
|
|
2439
|
-
}
|
|
2440
|
-
console.log(this.conversationSettings.messages);
|
|
2441
|
-
this.messages = this.conversationSettings.messages;
|
|
2442
|
-
const firstAssistantMsn = this.messages.find((message) => message.role == ChatRole.Assistant);
|
|
2443
|
-
if (firstAssistantMsn) {
|
|
2444
|
-
if (this.chatUserSettings?.autoTranslate) {
|
|
2445
|
-
}
|
|
2446
|
-
this.buildChatMessage(firstAssistantMsn, true);
|
|
2447
|
-
}
|
|
2448
|
-
else if (this.agentCard.conversationSettings.autoStart) {
|
|
2449
|
-
this.sendCurrentConversation();
|
|
2450
|
-
}
|
|
2451
|
-
}
|
|
2452
|
-
buildChatMessage(message, mutate = false) {
|
|
2453
|
-
// De la respuesta del backend configura el mensaje para ser rendiriado y procesado en la interfaz
|
|
2454
|
-
if (mutate) {
|
|
2455
|
-
message.voice = this.defaultVoice;
|
|
2456
|
-
}
|
|
2457
|
-
else {
|
|
2458
|
-
message = { content: message.content, role: message.role, voice: this.defaultVoice };
|
|
2459
|
-
}
|
|
2460
|
-
if (this.conversationSettings.textEngine == TextEngines.MarkdownMultiMessages) {
|
|
2461
|
-
this.processMultiMessages(message);
|
|
2462
|
-
}
|
|
2463
|
-
else if (this.conversationSettings.textEngine == TextEngines.MarkdownSSML) {
|
|
2464
|
-
if (!this.conversationSettings.secondaryVoice) {
|
|
2465
|
-
throw new Error('Secondary voice is required for SSML');
|
|
2466
|
-
}
|
|
2467
|
-
const content = this.subsItalicsByTag(message.content, this.conversationSettings.secondaryVoice);
|
|
2468
|
-
message.ssml = '<speak>' + content + '</speak>';
|
|
2469
|
-
}
|
|
2470
|
-
return message;
|
|
2471
|
-
}
|
|
2472
|
-
changeUserChatSettings() {
|
|
2473
|
-
this.dialogService
|
|
2474
|
-
.open(DCConversationUserChatSettingsComponent, {
|
|
2475
|
-
width: '90vw',
|
|
2476
|
-
header: 'Chat Settings',
|
|
2477
|
-
styleClass: 'settings-dialog',
|
|
2478
|
-
closable: true,
|
|
2479
|
-
closeOnEscape: true,
|
|
2480
|
-
})
|
|
2481
|
-
.onClose.subscribe(async () => {
|
|
2482
|
-
this.chatUserSettings = await this.agentCardService.getConversationUserChatSettings();
|
|
2483
|
-
console.log('chatUserSettings', this.chatUserSettings);
|
|
2484
|
-
});
|
|
2485
|
-
}
|
|
2486
|
-
// if (this.useRealTimeMode) {
|
|
2487
|
-
// // this.chatInputControl.disable();
|
|
2488
|
-
// }
|
|
2489
|
-
ngOnDestroy() {
|
|
2490
|
-
// this.chatboxService.notifyChatComponentDestroyed(this.messages);
|
|
2491
|
-
// this.micServiceService.stopMicrophone();
|
|
2492
|
-
// this.speechRecognizerService.stop();
|
|
2493
|
-
// Por la interfaz es muy pobable que destruya este elemento, no tengo subscripciones abiertas pero si funciones asyncronas
|
|
2494
|
-
// con esto puedo forzar que las ultimas funcionalides de las funciones asincronas no se ejecuten, otra solucion sería crear todo con observables
|
|
2495
|
-
this.isDestroyed = true;
|
|
2496
|
-
}
|
|
2497
|
-
setAIthinking() {
|
|
2498
|
-
this.isAIThinking = true;
|
|
2499
|
-
this.cdr.detectChanges();
|
|
2500
|
-
}
|
|
2501
|
-
async sendUserMessage(message = null) {
|
|
2502
|
-
if (this.isAIThinking) {
|
|
2503
|
-
return;
|
|
2504
|
-
}
|
|
2505
|
-
if (!message) {
|
|
2506
|
-
return;
|
|
2507
|
-
}
|
|
2508
|
-
this.messages.push(message); // This activates the render of the message
|
|
2509
|
-
this.setAIthinking();
|
|
2510
|
-
try {
|
|
2511
|
-
if (message.audioUrl && this.chatUserSettings.repeatRecording) {
|
|
2512
|
-
// Esto significa que el usuario grabo un audio y tengo que esperar a que termine para seguir con la conversación
|
|
2513
|
-
await new Promise((resolve) => setTimeout(resolve, 500));
|
|
2514
|
-
if (message.audioHtml) {
|
|
2515
|
-
message.audioHtml.addEventListener('ended', () => {
|
|
2516
|
-
this.sendCurrentConversation();
|
|
2517
|
-
});
|
|
2518
|
-
}
|
|
2519
|
-
}
|
|
2520
|
-
else {
|
|
2521
|
-
await this.sendCurrentConversation();
|
|
2522
|
-
}
|
|
2523
|
-
}
|
|
2524
|
-
finally {
|
|
2525
|
-
this.isAIThinking = false;
|
|
2526
|
-
}
|
|
2527
|
-
}
|
|
2528
|
-
async sendCurrentConversation() {
|
|
2529
|
-
if (this.isDestroyed)
|
|
2530
|
-
return; // user can close the chatbox at anytime, and leave this in background. somehow destry component keep process until they finished.
|
|
2531
|
-
if (this.messages.length > 31) {
|
|
2532
|
-
// Seguro para no tener conversaciones infinitas.
|
|
2533
|
-
this.toastService.warn({ title: 'Eso es todo, vuelve a practicar', subtitle: 'Esta conversación llegó a su límite de dialogos' });
|
|
2534
|
-
return;
|
|
2535
|
-
}
|
|
2536
|
-
let messages = this.messages;
|
|
2537
|
-
if (this.conversationSettings.last_prompt) {
|
|
2538
|
-
messages = [...this.messages, { content: this.conversationSettings.last_prompt, role: ChatRole.System }];
|
|
2539
|
-
}
|
|
2540
|
-
console.log('messages', this.chatUserSettings?.model, this.agentCard.model);
|
|
2541
|
-
const conversation = {
|
|
2542
|
-
messages: messages,
|
|
2543
|
-
conversationType: this.conversationSettings.conversationType,
|
|
2544
|
-
textEngine: this.conversationSettings.textEngine,
|
|
2545
|
-
model: this.chatUserSettings?.model || this.agentCard.model,
|
|
2546
|
-
};
|
|
2547
|
-
// if (this.settings.conversationType == ConversationType.LearningExample) {
|
|
2548
|
-
// // To save count, i created a taks to refactor learning examples include word.
|
|
2549
|
-
// conversation['word'] = this.chatboxService.item['word'];
|
|
2550
|
-
// }
|
|
2551
|
-
const response = await this.agentCardService.callChatCompletion(conversation);
|
|
2552
|
-
if (!response) {
|
|
2553
|
-
console.error('No message returned from AI, is your service working?');
|
|
2554
|
-
throw new Error('No message returned from AI');
|
|
2555
|
-
}
|
|
2556
|
-
console.log('newMessage', response);
|
|
2557
|
-
const newMessage = this.buildChatMessage(response);
|
|
2558
|
-
// TODO: think on how to include this as pluggin. this.conversationSettings?.autoTranslate
|
|
2559
|
-
this.messages.push(newMessage); // This point ChatMessage will be rendered then ngOnInit will handle everything.
|
|
2560
|
-
// Aqui se debe evaluar si el mensaje es un evaluador, si es así, se debe llamar a la función de evaluación.
|
|
2561
|
-
this.evaluateConversation();
|
|
2562
|
-
this.isAIThinking = false;
|
|
2563
|
-
this.cdr.detectChanges();
|
|
2564
|
-
if (this.chatUserSettings.realTime) {
|
|
2565
|
-
if (this.isDestroyed)
|
|
2566
|
-
return;
|
|
2567
|
-
// this.chatBroker.currentMessagePlayed$.pipe(first()).subscribe(() => {
|
|
2568
|
-
// this.isUserTalking = true;
|
|
2569
|
-
// this.micComponent.initStopMicState();
|
|
2570
|
-
// });
|
|
2571
|
-
}
|
|
2572
|
-
}
|
|
2573
|
-
getVoice(voice_value, targetLang = 'en') {
|
|
2574
|
-
if ([null, '', 'random'].includes(voice_value)) {
|
|
2575
|
-
const voiceCodes = VoiceTTSOptions.map((val) => val.id);
|
|
2576
|
-
return voiceCodes[Math.floor(Math.random() * voiceCodes.length)];
|
|
2577
|
-
}
|
|
2578
|
-
if (voice_value == 'randomMan') {
|
|
2579
|
-
const voiceCodes = VoiceTTSOptions.filter((voice) => voice.gender == 'male' && voice.lang.includes(targetLang)).map((val) => val.id);
|
|
2580
|
-
return voiceCodes[Math.floor(Math.random() * voiceCodes.length)];
|
|
2581
|
-
}
|
|
2582
|
-
else if (voice_value == 'randomWoman') {
|
|
2583
|
-
const voiceCodes = VoiceTTSOptions.filter((voice) => voice.gender == 'female' && voice.lang.includes(targetLang)).map((val) => val.id);
|
|
2584
|
-
return voiceCodes[Math.floor(Math.random() * voiceCodes.length)];
|
|
2585
|
-
}
|
|
2586
|
-
else {
|
|
2587
|
-
const voice = VoiceTTSOptions.find((voice) => voice.id == voice_value);
|
|
2588
|
-
if (voice) {
|
|
2589
|
-
return voice.id;
|
|
2590
|
-
}
|
|
2591
|
-
else {
|
|
2592
|
-
console.error('Voice not found getting something random', voice_value);
|
|
2593
|
-
return VoiceTTSOptions.find((voice) => voice.lang.includes(targetLang)).id;
|
|
2594
|
-
}
|
|
2595
|
-
}
|
|
2596
|
-
}
|
|
2597
|
-
setInputText(text) {
|
|
2598
|
-
// Method now receives text from the chat-footer component
|
|
2599
|
-
// No need to set the value as it's handled in the child component
|
|
2600
|
-
}
|
|
2601
|
-
async micFinished(eventBlob) {
|
|
2602
|
-
if (eventBlob instanceof Blob) {
|
|
2603
|
-
// Se asume tambien que conversationSettings.superHearing es true es la unica razon para que se grabe un audio
|
|
2604
|
-
if (eventBlob) {
|
|
2605
|
-
if (eventBlob.size < 10000) {
|
|
2606
|
-
// this.toastService.warn('El audio es muy pequeño', 'graba una respuesta más larga');
|
|
2607
|
-
return;
|
|
2608
|
-
}
|
|
2609
|
-
}
|
|
2610
|
-
// tODO: Probably rename to userMicState isUserTalking isGettingTranscription
|
|
2611
|
-
this.isUserTalking = false;
|
|
2612
|
-
this.isGettingTranscription = true;
|
|
2613
|
-
const transcription = await this.agentCardService.getAudioTranscriptions(eventBlob, { conversationId: this.agentCard._id });
|
|
2614
|
-
let message = { content: transcription.text, role: ChatRole.User, transcriptionTimestamps: transcription.words };
|
|
2615
|
-
message['audioUrl'] = URL.createObjectURL(eventBlob);
|
|
2616
|
-
this.isGettingTranscription = false;
|
|
2617
|
-
this.sendUserMessage(message);
|
|
2618
|
-
}
|
|
2619
|
-
else {
|
|
2620
|
-
if (!this.chatUserSettings.superHearing) {
|
|
2621
|
-
this.isUserTalking = false;
|
|
2622
|
-
this.sendUserMessage();
|
|
2623
|
-
}
|
|
2624
|
-
}
|
|
2625
|
-
}
|
|
2626
|
-
// // when you click on profile this is activated
|
|
2627
|
-
playMessage(message) {
|
|
2628
|
-
console.log('playMessage', message);
|
|
2629
|
-
}
|
|
2630
|
-
subsItalicsByTag(text, voiceId = 'it-IT-Neural2-A', tagName = 'voice') {
|
|
2631
|
-
const regex = /\*(.*?)\*/g;
|
|
2632
|
-
return text.replace(regex, (match, p1) => `<${tagName} name="${voiceId}">${p1}</${tagName}>`);
|
|
2633
|
-
}
|
|
2634
|
-
processMultiMessages(message) {
|
|
2635
|
-
// TODO! no esta funcionando como quisiera el metodo markdownToHtml
|
|
2636
|
-
let mdToHtml = convertToHTML(message.content);
|
|
2637
|
-
message.multiMessages = mdToHtml.map((val) => {
|
|
2638
|
-
// la voz del narrador viene de las opciones de conversación
|
|
2639
|
-
const narratorVoice = this.agentCard.conversationSettings.secondaryVoice || 'en-US-News-L';
|
|
2640
|
-
// la voz del persoje viene del scenario
|
|
2641
|
-
// const mainVoice = this.getVoice(this.agentCard.conversationSettings.voice) || 'en-US-Journey-F';
|
|
2642
|
-
const isItalics = val.tag == 'em';
|
|
2643
|
-
const voice = isItalics ? narratorVoice : this.defaultVoice;
|
|
2644
|
-
return { voice, content: val.content, audioUrl: null, audioPromise: null, text: val.text, tag: val.tag };
|
|
2645
|
-
});
|
|
2646
|
-
console.log('message', message.multiMessages);
|
|
2647
|
-
}
|
|
2648
|
-
showInfo() {
|
|
2649
|
-
console.log('showInfo', this.conversationSettings);
|
|
2650
|
-
console.log('agentCard', this.agentCard);
|
|
2651
|
-
console.log('parseDict', this.parseDict);
|
|
2652
|
-
console.log('chatUserSettings', this.chatUserSettings);
|
|
2653
|
-
this.isInfoVisible = true;
|
|
2654
|
-
}
|
|
2655
|
-
async restartConversation(conversation = null) {
|
|
2656
|
-
if (conversation) {
|
|
2657
|
-
this.agentCard = conversation;
|
|
2658
|
-
}
|
|
2659
|
-
await this.ngOnInit();
|
|
2660
|
-
}
|
|
2661
|
-
async evaluateConversation() {
|
|
2662
|
-
const evaluator = this.evaluatorAgentCard || DefaultEvaluatorAgentCard;
|
|
2663
|
-
const conversationMessages = this.messages.filter((message) => [ChatRole.User, ChatRole.Assistant].includes(message.role));
|
|
2664
|
-
const userMessages = conversationMessages.filter((message) => message.role == ChatRole.User);
|
|
2665
|
-
if (userMessages.length <= 0) {
|
|
2666
|
-
console.log('No hay mensajes para evaluar', conversationMessages);
|
|
2667
|
-
return;
|
|
2668
|
-
}
|
|
2669
|
-
const conversationMessagesString = conversationMessages.map((message) => `${message.role}: ${message.content}`).join('\n');
|
|
2670
|
-
const instructions = `
|
|
2671
|
-
Please replay to this task:
|
|
2672
|
-
${evaluator.task}
|
|
2673
|
-
This is the conversation history:
|
|
2674
|
-
${conversationMessagesString}
|
|
2675
|
-
and give the response in next JSON format.
|
|
2676
|
-
${evaluator.expectedResponseType}
|
|
2677
|
-
`;
|
|
2678
|
-
console.log('instructions', instructions);
|
|
2679
|
-
const messages = [{ content: instructions, role: ChatRole.User }];
|
|
2680
|
-
const response = await this.agentCardService.callChatCompletion({ messages });
|
|
2681
|
-
// parse the response to the expected response type
|
|
2682
|
-
const jsonData = extractJsonFromResponse(response.content);
|
|
2683
|
-
if (jsonData.score) {
|
|
2684
|
-
if (jsonData.score <= 3) {
|
|
2685
|
-
console.log('score', this.score);
|
|
2686
|
-
this.score += jsonData.score * 10;
|
|
2687
|
-
if (this.score > 100) {
|
|
2688
|
-
this.score = 100;
|
|
2689
|
-
}
|
|
2690
|
-
console.log('score', this.score);
|
|
2691
|
-
this.cdr.detectChanges();
|
|
2692
|
-
}
|
|
2693
|
-
this.toastService.success({
|
|
2694
|
-
title: `Tu puntuación es ${jsonData.score} tu puntaje final es ${this.score}`,
|
|
2695
|
-
subtitle: `tu feedback es ${jsonData.feedback}`,
|
|
2696
|
-
});
|
|
2697
|
-
}
|
|
2698
|
-
else {
|
|
2699
|
-
console.log('No se pudo parsear el json', response.content);
|
|
2700
|
-
this.toastService.error({ title: 'No se pudo parsear el json', subtitle: response.content });
|
|
2701
|
-
}
|
|
2702
|
-
console.log('response', response);
|
|
2703
|
-
}
|
|
2704
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.1.1", ngImport: i0, type: DCChatComponent, deps: [{ token: CONVERSATION_AI_TOKEN }, { token: TOAST_ALERTS_TOKEN }, { token: DCConversationPromptBuilderService }, { token: i1$3.DialogService }, { token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Component }); }
|
|
2705
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.1.1", type: DCChatComponent, isStandalone: true, selector: "dc-chat", inputs: { chatUserSettings: "chatUserSettings", agentCard: "agentCard", evaluatorAgentCard: "evaluatorAgentCard", parseDict: "parseDict" }, outputs: { sendMessage: "sendMessage" }, providers: [DialogService], ngImport: i0, template: "<dc-chat-header\n [isAdmin]=\"isAdmin\"\n [alternativeConversation]=\"alternativeConversation\"\n (restartConversationEvent)=\"restartConversation($event || null)\"\n (showInfoEvent)=\"showInfo()\"\n (settingsClickEvent)=\"changeUserChatSettings()\">\n</dc-chat-header>\n\n<main class=\"chat-container\" (touchmove)=\"$event.stopPropagation()\">\n <div class=\"chat\" aria-live=\"polite\">\n @for (message of messages; track message) { @switch (message.role) { @case ('assistant') {\n <div class=\"message left\">\n <img class=\"logo fit-top-image\" [src]=\"agentCard?.assets?.image?.url ?? imageUser\" (click)=\"playMessage(message)\" />\n <dc-chat-message [chatUserSettings]=\"chatUserSettings\" [chatMessage]=\"message\" appTextSelectable> </dc-chat-message>\n </div>\n } @case ('assistantHelper') {\n <div class=\"message-helper right\">\n <dc-chat-message [chatUserSettings]=\"chatUserSettings\" [chatMessage]=\"message\" appTextSelectable> </dc-chat-message>\n </div>\n } @case ('user') {\n <div class=\"message right\">\n <img class=\"logo fit-top-image\" [src]=\"user?.urlPicture || 'assets/defaults/avatar.jpg'\" [alt]=\"'User avatar'\" (click)=\"playMessage(message)\" />\n <dc-chat-message [chatMessage]=\"message\" appTextSelectable> </dc-chat-message>\n </div>\n } } }\n\n <!-- AI thinking indicator -->\n @if (isAIThinking) {\n <div class=\"message message--assistant message--thinking\">\n <span class=\"thinking-emoji\" aria-hidden=\"true\">\uD83E\uDD14</span>\n <div class=\"thinking-indicator\">\n <div class=\"skeleton-container\">\n <p-skeleton width=\"100%\" />\n <br />\n <p-skeleton width=\"75%\" />\n </div>\n </div>\n </div>\n }\n\n <!-- User typing or transcribing indicator -->\n @if (isUserTalking || isGettingTranscription) {\n <div class=\"message message--user message--activity\">\n <img class=\"avatar avatar--user\" [src]=\"user?.urlPicture || imageUser\" [alt]=\"'User avatar'\" />\n @if (isUserTalking) {\n <p class=\"typing-indicator\" aria-label=\"User is speaking\">...</p>\n } @if (isGettingTranscription) {\n <i class=\"pi pi-spin pi-spinner-dotted\" aria-label=\"Transcribing speech\"></i>\n }\n </div>\n }\n </div>\n</main>\n\n<dc-chat-footer\n [isAIThinking]=\"isAIThinking\"\n [score]=\"score\"\n [micSettings]=\"micSettings\"\n (sendMessage)=\"sendUserMessage($event)\"\n (textInputChanged)=\"setInputText($event)\"\n (micFinishedEvent)=\"micFinished($event)\">\n</dc-chat-footer>\n\n<!-- Information dialog -->\n<p-dialog\n header=\"Informaci\u00F3n de la conversaci\u00F3n\"\n [(visible)]=\"isInfoVisible\"\n [modal]=\"true\"\n [responsive]=\"true\"\n styleClass=\"conversation-info-dialog\"\n aria-labelledby=\"conversation-info-title\">\n <div class=\"dialog-content\">\n <section class=\"model-info\">\n <h3 id=\"conversation-info-title\" class=\"sr-only\">Informaci\u00F3n de la conversaci\u00F3n</h3>\n <dl class=\"info-list\">\n <dt>Modelo:</dt>\n <dd>{{ chatUserSettings?.model?.provider }} - {{ chatUserSettings?.model?.modelName }}</dd>\n\n <dt>Tipo:</dt>\n <dd>{{ agentCard?.conversationSettings?.conversationType }}</dd>\n\n <dt>Text Engine:</dt>\n <dd>{{ agentCard?.conversationSettings?.textEngine }}</dd>\n\n <dt>Language:</dt>\n <dd>{{ agentCard?.lang }}</dd>\n\n <dt>TTS:</dt>\n <dd>{{ agentCard?.conversationSettings?.tts?.voice }}</dd>\n\n <dt>Secondary TTS:</dt>\n <dd>{{ agentCard?.conversationSettings?.tts?.secondaryVoice }}</dd>\n </dl>\n\n <div class=\"parameters\">\n <h4>Par\u00E1metros:</h4>\n <pre>{{ parseDict | json }}</pre>\n </div>\n </section>\n\n <section class=\"conversation-history\">\n <h4>Conversaci\u00F3n hasta ahora</h4>\n <div class=\"message-history-list\">\n @for (message of conversationSettings.messages; track message) {\n <div class=\"message-history-item\">\n <span class=\"message-role\">{{ message.role }}:</span>\n <span class=\"message-content\">{{ message.content }}</span>\n </div>\n }\n </div>\n </section>\n </div>\n</p-dialog>\n", styles: [":host{display:flex;flex-direction:column;height:100%}*{box-sizing:border-box}.chat-container{border-radius:10px;overflow:hidden;padding:15px;position:relative;max-width:100%;height:100%;display:flex;flex-direction:column}.chat{display:flex;flex-direction:column;list-style-type:none;padding:0;margin:0;overflow-y:auto;flex:1}.logo{width:30px;height:30px}.logo.fit-top-image{object-fit:cover;object-position:top;border-radius:50%}.message{background-color:#f6f6f6f2;border-radius:25px;box-shadow:0 5px 15px #0000001a;position:relative;margin-bottom:20px;transition:all .2s ease}.message .logo{position:absolute;top:50%;transform:translateY(-50%);border-radius:50%;cursor:pointer}.message .logo:hover{transform:translateY(-50%) scale(1.05)}.message.left{padding:12px 15px 12px 45px;margin-right:20%}.message.left .logo{left:5px}.message.right{align-self:flex-end;padding:12px 45px 12px 15px;margin-left:20%;background-color:#e6f0fff2}.message.right .logo{right:5px}.message--thinking,.message--activity{opacity:.7}.message-helper{background-color:#ffffffe6;box-shadow:0 3px 10px #0000001a;position:relative;margin-bottom:20px;border-radius:20px}.message-helper.right{align-self:flex-end;padding:8px 15px;margin-left:20%}.thinking-indicator{padding:5px}.thinking-indicator .skeleton-container{width:200px}::ng-deep .cdk-overlay-container,::ng-deep .cdk-global-overlay-wrapper{z-index:1400}::ng-deep .dialog-backdrop{background:#00000080;z-index:1299}::ng-deep .conversation-info-dialog .dialog-content{max-height:70vh;overflow-y:auto}::ng-deep .conversation-info-dialog .info-list{display:grid;grid-template-columns:auto 1fr;gap:8px;margin-bottom:20px}::ng-deep .conversation-info-dialog .info-list dt{font-weight:700}::ng-deep .conversation-info-dialog .message-history-list{max-height:300px;overflow-y:auto;border:1px solid #eee;border-radius:5px;padding:10px}::ng-deep .conversation-info-dialog .message-history-list .message-history-item{padding:5px 0;border-bottom:1px solid #f0f0f0}::ng-deep .conversation-info-dialog .message-history-list .message-history-item:last-child{border-bottom:none}::ng-deep .conversation-info-dialog .message-history-list .message-history-item .message-role{font-weight:700;margin-right:5px}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "pipe", type: i1$2.JsonPipe, name: "json" }, { kind: "ngmodule", type: FormsModule }, { kind: "component", type: ChatMessageComponent, selector: "dc-chat-message", inputs: ["chatMessage", "chatUserSettings"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "ngmodule", type: SkeletonModule }, { kind: "component", type: i4.Skeleton, selector: "p-skeleton", inputs: ["styleClass", "style", "shape", "animation", "borderRadius", "size", "width", "height"] }, { kind: "ngmodule", type: DialogModule }, { kind: "component", type: i6.Dialog, selector: "p-dialog", inputs: ["header", "draggable", "resizable", "positionLeft", "positionTop", "contentStyle", "contentStyleClass", "modal", "closeOnEscape", "dismissableMask", "rtl", "closable", "responsive", "appendTo", "breakpoints", "styleClass", "maskStyleClass", "maskStyle", "showHeader", "breakpoint", "blockScroll", "autoZIndex", "baseZIndex", "minX", "minY", "focusOnShow", "maximizable", "keepInViewport", "focusTrap", "transitionOptions", "closeIcon", "closeAriaLabel", "closeTabindex", "minimizeIcon", "maximizeIcon", "closeButtonProps", "maximizeButtonProps", "visible", "style", "position", "role", "content", "contentTemplate", "footerTemplate", "closeIconTemplate", "maximizeIconTemplate", "minimizeIconTemplate", "headlessTemplate"], outputs: ["onShow", "onHide", "visibleChange", "onResizeInit", "onResizeEnd", "onDragEnd", "onMaximize"] }, { kind: "ngmodule", type: ProgressBarModule }, { kind: "component", type: ChatHeaderComponent, selector: "dc-chat-header", inputs: ["isAdmin", "alternativeConversation", "agentCard"], outputs: ["restartConversationEvent", "showInfoEvent", "settingsClickEvent"] }, { kind: "component", type: ChatFooterComponent, selector: "dc-chat-footer", inputs: ["isAIThinking", "score", "micSettings"], outputs: ["sendMessage", "textInputChanged", "micFinishedEvent"] }] }); }
|
|
2456
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: DCChatComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
2457
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "19.2.4", type: DCChatComponent, isStandalone: true, selector: "dc-chat", inputs: { chatUserSettings: { classPropertyName: "chatUserSettings", publicName: "chatUserSettings", isSignal: false, isRequired: false, transformFunction: null }, agentCard: { classPropertyName: "agentCard", publicName: "agentCard", isSignal: false, isRequired: false, transformFunction: null }, evaluatorAgentCard: { classPropertyName: "evaluatorAgentCard", publicName: "evaluatorAgentCard", isSignal: true, isRequired: false, transformFunction: null }, parseDict: { classPropertyName: "parseDict", publicName: "parseDict", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { sendMessage: "sendMessage" }, providers: [DialogService], ngImport: i0, template: "<div class=\"chat-container\">\n <!-- Chat Header -->\n <dc-chat-header\n [agentCard]=\"agentCard\"\n [isAdmin]=\"isAdmin\"\n (showInfoEvent)=\"showInfo()\"\n (settingsClickEvent)=\"changeUserChatSettings()\"\n (restartConversationEvent)=\"restartConversation($event)\">\n </dc-chat-header>\n\n <!-- Messages List -->\n <dc-chat-messages-list [chatUserSettings]=\"chatUserSettings\" [thinkingImg]=\"thinkingImg\"> </dc-chat-messages-list>\n\n <!-- Chat Footer -->\n <dc-chat-footer \n [isAIThinking]=\"isThinking()\" \n [micSettings]=\"micSettings\" \n [evaluatorAgentCard]=\"evaluatorAgentCard()\" \n (micFinishedEvent)=\"onMicFinished($event)\">\n </dc-chat-footer>\n\n <!-- Progress Bar for Score -->\n <!-- @if (score() > 0) {\n <div class=\"score-container\">\n <p-progressBar [value]=\"score()\" [showValue]=\"true\"></p-progressBar>\n </div>\n } -->\n\n <!-- Info Dialog -->\n <p-dialog [(visible)]=\"isInfoVisible\" [modal]=\"true\" [draggable]=\"false\" [resizable]=\"false\" header=\"Conversation Info\">\n <div class=\"info-content\">\n <h3>Agent Card</h3>\n <pre>{{ agentCard | json }}</pre>\n\n <h3>User Settings</h3>\n <pre>{{ chatUserSettings | json }}</pre>\n </div>\n </p-dialog>\n</div>\n", styles: [".chat-container{display:flex;flex-direction:column;height:100%;max-height:100vh;overflow:hidden;background-color:transparent}dc-chat-messages-list{flex:1;overflow-y:auto;padding:.5rem}.score-container{padding:.5rem 1rem;background-color:var(--surface-card, #ffffff);border-top:1px solid var(--surface-border, #dee2e6)}.info-content{max-height:70vh;overflow-y:auto}.info-content h3{margin-top:1rem;margin-bottom:.5rem;font-size:1.2rem}.info-content pre{background-color:var(--surface-hover, #f1f1f1);padding:1rem;border-radius:4px;overflow-x:auto;font-size:.9rem}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "pipe", type: i1$1.JsonPipe, name: "json" }, { kind: "component", type: ChatHeaderComponent, selector: "dc-chat-header", inputs: ["isAdmin", "alternativeConversation", "agentCard"], outputs: ["restartConversationEvent", "showInfoEvent", "settingsClickEvent"] }, { kind: "component", type: ChatFooterComponent, selector: "dc-chat-footer", inputs: ["isAIThinking", "evaluatorAgentCard", "micSettings"], outputs: ["sendMessage", "textInputChanged", "micFinishedEvent"] }, { kind: "component", type: ChatMessagesListComponent, selector: "dc-chat-messages-list", inputs: ["chatUserSettings", "thinkingImg"] }, { kind: "ngmodule", type: DialogModule }, { kind: "component", type: i2$4.Dialog, selector: "p-dialog", inputs: ["header", "draggable", "resizable", "positionLeft", "positionTop", "contentStyle", "contentStyleClass", "modal", "closeOnEscape", "dismissableMask", "rtl", "closable", "responsive", "appendTo", "breakpoints", "styleClass", "maskStyleClass", "maskStyle", "showHeader", "breakpoint", "blockScroll", "autoZIndex", "baseZIndex", "minX", "minY", "focusOnShow", "maximizable", "keepInViewport", "focusTrap", "transitionOptions", "closeIcon", "closeAriaLabel", "closeTabindex", "minimizeIcon", "maximizeIcon", "closeButtonProps", "maximizeButtonProps", "visible", "style", "position", "role", "content", "contentTemplate", "footerTemplate", "closeIconTemplate", "maximizeIconTemplate", "minimizeIconTemplate", "headlessTemplate"], outputs: ["onShow", "onHide", "visibleChange", "onResizeInit", "onResizeEnd", "onDragEnd", "onMaximize"] }, { kind: "ngmodule", type: ProgressBarModule }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
2706
2458
|
}
|
|
2707
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.
|
|
2459
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: DCChatComponent, decorators: [{
|
|
2708
2460
|
type: Component,
|
|
2709
|
-
args: [{ selector: 'dc-chat', standalone: true, imports: [
|
|
2710
|
-
|
|
2711
|
-
FormsModule,
|
|
2712
|
-
ChatMessageComponent,
|
|
2713
|
-
ReactiveFormsModule,
|
|
2714
|
-
SkeletonModule,
|
|
2715
|
-
DialogModule,
|
|
2716
|
-
ProgressBarModule,
|
|
2717
|
-
ChatHeaderComponent,
|
|
2718
|
-
ChatFooterComponent,
|
|
2719
|
-
], providers: [DialogService], template: "<dc-chat-header\n [isAdmin]=\"isAdmin\"\n [alternativeConversation]=\"alternativeConversation\"\n (restartConversationEvent)=\"restartConversation($event || null)\"\n (showInfoEvent)=\"showInfo()\"\n (settingsClickEvent)=\"changeUserChatSettings()\">\n</dc-chat-header>\n\n<main class=\"chat-container\" (touchmove)=\"$event.stopPropagation()\">\n <div class=\"chat\" aria-live=\"polite\">\n @for (message of messages; track message) { @switch (message.role) { @case ('assistant') {\n <div class=\"message left\">\n <img class=\"logo fit-top-image\" [src]=\"agentCard?.assets?.image?.url ?? imageUser\" (click)=\"playMessage(message)\" />\n <dc-chat-message [chatUserSettings]=\"chatUserSettings\" [chatMessage]=\"message\" appTextSelectable> </dc-chat-message>\n </div>\n } @case ('assistantHelper') {\n <div class=\"message-helper right\">\n <dc-chat-message [chatUserSettings]=\"chatUserSettings\" [chatMessage]=\"message\" appTextSelectable> </dc-chat-message>\n </div>\n } @case ('user') {\n <div class=\"message right\">\n <img class=\"logo fit-top-image\" [src]=\"user?.urlPicture || 'assets/defaults/avatar.jpg'\" [alt]=\"'User avatar'\" (click)=\"playMessage(message)\" />\n <dc-chat-message [chatMessage]=\"message\" appTextSelectable> </dc-chat-message>\n </div>\n } } }\n\n <!-- AI thinking indicator -->\n @if (isAIThinking) {\n <div class=\"message message--assistant message--thinking\">\n <span class=\"thinking-emoji\" aria-hidden=\"true\">\uD83E\uDD14</span>\n <div class=\"thinking-indicator\">\n <div class=\"skeleton-container\">\n <p-skeleton width=\"100%\" />\n <br />\n <p-skeleton width=\"75%\" />\n </div>\n </div>\n </div>\n }\n\n <!-- User typing or transcribing indicator -->\n @if (isUserTalking || isGettingTranscription) {\n <div class=\"message message--user message--activity\">\n <img class=\"avatar avatar--user\" [src]=\"user?.urlPicture || imageUser\" [alt]=\"'User avatar'\" />\n @if (isUserTalking) {\n <p class=\"typing-indicator\" aria-label=\"User is speaking\">...</p>\n } @if (isGettingTranscription) {\n <i class=\"pi pi-spin pi-spinner-dotted\" aria-label=\"Transcribing speech\"></i>\n }\n </div>\n }\n </div>\n</main>\n\n<dc-chat-footer\n [isAIThinking]=\"isAIThinking\"\n [score]=\"score\"\n [micSettings]=\"micSettings\"\n (sendMessage)=\"sendUserMessage($event)\"\n (textInputChanged)=\"setInputText($event)\"\n (micFinishedEvent)=\"micFinished($event)\">\n</dc-chat-footer>\n\n<!-- Information dialog -->\n<p-dialog\n header=\"Informaci\u00F3n de la conversaci\u00F3n\"\n [(visible)]=\"isInfoVisible\"\n [modal]=\"true\"\n [responsive]=\"true\"\n styleClass=\"conversation-info-dialog\"\n aria-labelledby=\"conversation-info-title\">\n <div class=\"dialog-content\">\n <section class=\"model-info\">\n <h3 id=\"conversation-info-title\" class=\"sr-only\">Informaci\u00F3n de la conversaci\u00F3n</h3>\n <dl class=\"info-list\">\n <dt>Modelo:</dt>\n <dd>{{ chatUserSettings?.model?.provider }} - {{ chatUserSettings?.model?.modelName }}</dd>\n\n <dt>Tipo:</dt>\n <dd>{{ agentCard?.conversationSettings?.conversationType }}</dd>\n\n <dt>Text Engine:</dt>\n <dd>{{ agentCard?.conversationSettings?.textEngine }}</dd>\n\n <dt>Language:</dt>\n <dd>{{ agentCard?.lang }}</dd>\n\n <dt>TTS:</dt>\n <dd>{{ agentCard?.conversationSettings?.tts?.voice }}</dd>\n\n <dt>Secondary TTS:</dt>\n <dd>{{ agentCard?.conversationSettings?.tts?.secondaryVoice }}</dd>\n </dl>\n\n <div class=\"parameters\">\n <h4>Par\u00E1metros:</h4>\n <pre>{{ parseDict | json }}</pre>\n </div>\n </section>\n\n <section class=\"conversation-history\">\n <h4>Conversaci\u00F3n hasta ahora</h4>\n <div class=\"message-history-list\">\n @for (message of conversationSettings.messages; track message) {\n <div class=\"message-history-item\">\n <span class=\"message-role\">{{ message.role }}:</span>\n <span class=\"message-content\">{{ message.content }}</span>\n </div>\n }\n </div>\n </section>\n </div>\n</p-dialog>\n", styles: [":host{display:flex;flex-direction:column;height:100%}*{box-sizing:border-box}.chat-container{border-radius:10px;overflow:hidden;padding:15px;position:relative;max-width:100%;height:100%;display:flex;flex-direction:column}.chat{display:flex;flex-direction:column;list-style-type:none;padding:0;margin:0;overflow-y:auto;flex:1}.logo{width:30px;height:30px}.logo.fit-top-image{object-fit:cover;object-position:top;border-radius:50%}.message{background-color:#f6f6f6f2;border-radius:25px;box-shadow:0 5px 15px #0000001a;position:relative;margin-bottom:20px;transition:all .2s ease}.message .logo{position:absolute;top:50%;transform:translateY(-50%);border-radius:50%;cursor:pointer}.message .logo:hover{transform:translateY(-50%) scale(1.05)}.message.left{padding:12px 15px 12px 45px;margin-right:20%}.message.left .logo{left:5px}.message.right{align-self:flex-end;padding:12px 45px 12px 15px;margin-left:20%;background-color:#e6f0fff2}.message.right .logo{right:5px}.message--thinking,.message--activity{opacity:.7}.message-helper{background-color:#ffffffe6;box-shadow:0 3px 10px #0000001a;position:relative;margin-bottom:20px;border-radius:20px}.message-helper.right{align-self:flex-end;padding:8px 15px;margin-left:20%}.thinking-indicator{padding:5px}.thinking-indicator .skeleton-container{width:200px}::ng-deep .cdk-overlay-container,::ng-deep .cdk-global-overlay-wrapper{z-index:1400}::ng-deep .dialog-backdrop{background:#00000080;z-index:1299}::ng-deep .conversation-info-dialog .dialog-content{max-height:70vh;overflow-y:auto}::ng-deep .conversation-info-dialog .info-list{display:grid;grid-template-columns:auto 1fr;gap:8px;margin-bottom:20px}::ng-deep .conversation-info-dialog .info-list dt{font-weight:700}::ng-deep .conversation-info-dialog .message-history-list{max-height:300px;overflow-y:auto;border:1px solid #eee;border-radius:5px;padding:10px}::ng-deep .conversation-info-dialog .message-history-list .message-history-item{padding:5px 0;border-bottom:1px solid #f0f0f0}::ng-deep .conversation-info-dialog .message-history-list .message-history-item:last-child{border-bottom:none}::ng-deep .conversation-info-dialog .message-history-list .message-history-item .message-role{font-weight:700;margin-right:5px}\n"] }]
|
|
2720
|
-
}], ctorParameters: () => [{ type: AgentCardsAbstractService, decorators: [{
|
|
2721
|
-
type: Inject,
|
|
2722
|
-
args: [CONVERSATION_AI_TOKEN]
|
|
2723
|
-
}] }, { type: i6$1.ToastAlertsAbstractService, decorators: [{
|
|
2724
|
-
type: Inject,
|
|
2725
|
-
args: [TOAST_ALERTS_TOKEN]
|
|
2726
|
-
}] }, { type: DCConversationPromptBuilderService }, { type: i1$3.DialogService }, { type: i0.ChangeDetectorRef }], propDecorators: { chatUserSettings: [{
|
|
2461
|
+
args: [{ selector: 'dc-chat', standalone: true, imports: [CommonModule, ChatHeaderComponent, ChatFooterComponent, ChatMessagesListComponent, DialogModule, ProgressBarModule], changeDetection: ChangeDetectionStrategy.OnPush, providers: [DialogService], template: "<div class=\"chat-container\">\n <!-- Chat Header -->\n <dc-chat-header\n [agentCard]=\"agentCard\"\n [isAdmin]=\"isAdmin\"\n (showInfoEvent)=\"showInfo()\"\n (settingsClickEvent)=\"changeUserChatSettings()\"\n (restartConversationEvent)=\"restartConversation($event)\">\n </dc-chat-header>\n\n <!-- Messages List -->\n <dc-chat-messages-list [chatUserSettings]=\"chatUserSettings\" [thinkingImg]=\"thinkingImg\"> </dc-chat-messages-list>\n\n <!-- Chat Footer -->\n <dc-chat-footer \n [isAIThinking]=\"isThinking()\" \n [micSettings]=\"micSettings\" \n [evaluatorAgentCard]=\"evaluatorAgentCard()\" \n (micFinishedEvent)=\"onMicFinished($event)\">\n </dc-chat-footer>\n\n <!-- Progress Bar for Score -->\n <!-- @if (score() > 0) {\n <div class=\"score-container\">\n <p-progressBar [value]=\"score()\" [showValue]=\"true\"></p-progressBar>\n </div>\n } -->\n\n <!-- Info Dialog -->\n <p-dialog [(visible)]=\"isInfoVisible\" [modal]=\"true\" [draggable]=\"false\" [resizable]=\"false\" header=\"Conversation Info\">\n <div class=\"info-content\">\n <h3>Agent Card</h3>\n <pre>{{ agentCard | json }}</pre>\n\n <h3>User Settings</h3>\n <pre>{{ chatUserSettings | json }}</pre>\n </div>\n </p-dialog>\n</div>\n", styles: [".chat-container{display:flex;flex-direction:column;height:100%;max-height:100vh;overflow:hidden;background-color:transparent}dc-chat-messages-list{flex:1;overflow-y:auto;padding:.5rem}.score-container{padding:.5rem 1rem;background-color:var(--surface-card, #ffffff);border-top:1px solid var(--surface-border, #dee2e6)}.info-content{max-height:70vh;overflow-y:auto}.info-content h3{margin-top:1rem;margin-bottom:.5rem;font-size:1.2rem}.info-content pre{background-color:var(--surface-hover, #f1f1f1);padding:1rem;border-radius:4px;overflow-x:auto;font-size:.9rem}\n"] }]
|
|
2462
|
+
}], ctorParameters: () => [], propDecorators: { chatUserSettings: [{
|
|
2727
2463
|
type: Input
|
|
2728
2464
|
}], agentCard: [{
|
|
2729
2465
|
type: Input
|
|
2730
|
-
}], evaluatorAgentCard: [{
|
|
2731
|
-
type: Input
|
|
2732
|
-
}], parseDict: [{
|
|
2733
|
-
type: Input
|
|
2734
|
-
}], sendMessage: [{
|
|
2735
|
-
type: Output
|
|
2736
2466
|
}] } });
|
|
2737
2467
|
|
|
2738
2468
|
async function getCharacterData(file) {
|
|
@@ -2866,32 +2596,32 @@ function getFileBuffer(file) {
|
|
|
2866
2596
|
}
|
|
2867
2597
|
|
|
2868
2598
|
class PromptPreviewDialogComponent {
|
|
2869
|
-
constructor(
|
|
2870
|
-
this.dynamicDialogConfig =
|
|
2871
|
-
this.dialogRef =
|
|
2599
|
+
constructor() {
|
|
2600
|
+
this.dynamicDialogConfig = inject(DynamicDialogConfig);
|
|
2601
|
+
this.dialogRef = inject(DynamicDialogRef);
|
|
2872
2602
|
this.data = this.dynamicDialogConfig.data;
|
|
2873
2603
|
}
|
|
2874
2604
|
close() {
|
|
2875
2605
|
this.dialogRef.close();
|
|
2876
2606
|
}
|
|
2877
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.
|
|
2878
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.
|
|
2607
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: PromptPreviewDialogComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
2608
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.4", type: PromptPreviewDialogComponent, isStandalone: true, selector: "dc-prompt-preview-dialog", ngImport: i0, template: ` <div class="prompt-preview-content" [innerHTML]="data.html"></div> `, isInline: true, dependencies: [{ kind: "ngmodule", type: DialogModule }] }); }
|
|
2879
2609
|
}
|
|
2880
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.
|
|
2610
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: PromptPreviewDialogComponent, decorators: [{
|
|
2881
2611
|
type: Component,
|
|
2882
2612
|
args: [{
|
|
2883
2613
|
selector: 'dc-prompt-preview-dialog',
|
|
2884
2614
|
standalone: true,
|
|
2885
|
-
imports: [
|
|
2615
|
+
imports: [DialogModule],
|
|
2886
2616
|
template: ` <div class="prompt-preview-content" [innerHTML]="data.html"></div> `,
|
|
2887
2617
|
}]
|
|
2888
|
-
}], ctorParameters: () => [
|
|
2618
|
+
}], ctorParameters: () => [] });
|
|
2889
2619
|
|
|
2890
2620
|
class TranslateDialogComponent {
|
|
2891
|
-
constructor(
|
|
2892
|
-
this.fb =
|
|
2893
|
-
this.dialogRef =
|
|
2894
|
-
this.data =
|
|
2621
|
+
constructor() {
|
|
2622
|
+
this.fb = inject(FormBuilder);
|
|
2623
|
+
this.dialogRef = inject(DialogRef);
|
|
2624
|
+
this.data = inject(DIALOG_DATA);
|
|
2895
2625
|
this.languages = Object.entries(LangCodeDescriptionEs).map(([code, description]) => ({
|
|
2896
2626
|
code,
|
|
2897
2627
|
description,
|
|
@@ -2907,17 +2637,19 @@ class TranslateDialogComponent {
|
|
|
2907
2637
|
onConfirm() {
|
|
2908
2638
|
this.dialogRef.close(this.form.value.targetLang);
|
|
2909
2639
|
}
|
|
2910
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.
|
|
2911
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "
|
|
2640
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: TranslateDialogComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
2641
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.4", type: TranslateDialogComponent, isStandalone: true, selector: "dc-translate-dialog", ngImport: i0, template: `
|
|
2912
2642
|
<div class="translate-dialog">
|
|
2913
2643
|
<h4>Tu idioma actual es: {{ data.currentLang }}</h4>
|
|
2914
2644
|
<h2>Selecciona el idioma al que quieres traducir</h2>
|
|
2915
2645
|
<form [formGroup]="form">
|
|
2916
2646
|
<select formControlName="targetLang">
|
|
2917
2647
|
<option value="">Select language...</option>
|
|
2918
|
-
|
|
2919
|
-
|
|
2920
|
-
|
|
2648
|
+
@for (lang of languages; track lang) {
|
|
2649
|
+
<option [value]="lang.code">
|
|
2650
|
+
{{ lang.description }}
|
|
2651
|
+
</option>
|
|
2652
|
+
}
|
|
2921
2653
|
</select>
|
|
2922
2654
|
</form>
|
|
2923
2655
|
<div class="actions">
|
|
@@ -2925,20 +2657,22 @@ class TranslateDialogComponent {
|
|
|
2925
2657
|
<button (click)="onConfirm()" [disabled]="!form.value.targetLang"> Translate </button>
|
|
2926
2658
|
</div>
|
|
2927
2659
|
</div>
|
|
2928
|
-
|
|
2660
|
+
`, isInline: true, styles: [".translate-dialog{padding:20px;background:#fff;border-radius:8px;box-shadow:0 2px 8px #00000026}.actions{margin-top:20px;display:flex;justify-content:flex-end;gap:10px}select{width:100%;padding:8px;margin-top:10px;border:1px solid #ccc;border-radius:4px}button{padding:8px 16px;border:none;border-radius:4px;cursor:pointer}button:first-child{background:#f0f0f0}button:last-child{background:#007bff;color:#fff}button:disabled{background:#ccc;cursor:not-allowed}h2{margin:0 0 16px;color:#333}\n"], dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1.SelectControlValueAccessor, selector: "select:not([multiple])[formControlName],select:not([multiple])[formControl],select:not([multiple])[ngModel]", inputs: ["compareWith"] }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }] }); }
|
|
2929
2661
|
}
|
|
2930
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.
|
|
2662
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: TranslateDialogComponent, decorators: [{
|
|
2931
2663
|
type: Component,
|
|
2932
|
-
args: [{ selector: 'dc-translate-dialog', standalone: true, imports: [
|
|
2664
|
+
args: [{ selector: 'dc-translate-dialog', standalone: true, imports: [ReactiveFormsModule], template: `
|
|
2933
2665
|
<div class="translate-dialog">
|
|
2934
2666
|
<h4>Tu idioma actual es: {{ data.currentLang }}</h4>
|
|
2935
2667
|
<h2>Selecciona el idioma al que quieres traducir</h2>
|
|
2936
2668
|
<form [formGroup]="form">
|
|
2937
2669
|
<select formControlName="targetLang">
|
|
2938
2670
|
<option value="">Select language...</option>
|
|
2939
|
-
|
|
2940
|
-
|
|
2941
|
-
|
|
2671
|
+
@for (lang of languages; track lang) {
|
|
2672
|
+
<option [value]="lang.code">
|
|
2673
|
+
{{ lang.description }}
|
|
2674
|
+
</option>
|
|
2675
|
+
}
|
|
2942
2676
|
</select>
|
|
2943
2677
|
</form>
|
|
2944
2678
|
<div class="actions">
|
|
@@ -2946,11 +2680,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.1.1", ngImpor
|
|
|
2946
2680
|
<button (click)="onConfirm()" [disabled]="!form.value.targetLang"> Translate </button>
|
|
2947
2681
|
</div>
|
|
2948
2682
|
</div>
|
|
2949
|
-
|
|
2950
|
-
}], ctorParameters: () => [
|
|
2951
|
-
type: Inject,
|
|
2952
|
-
args: [DIALOG_DATA]
|
|
2953
|
-
}] }] });
|
|
2683
|
+
`, styles: [".translate-dialog{padding:20px;background:#fff;border-radius:8px;box-shadow:0 2px 8px #00000026}.actions{margin-top:20px;display:flex;justify-content:flex-end;gap:10px}select{width:100%;padding:8px;margin-top:10px;border:1px solid #ccc;border-radius:4px}button{padding:8px 16px;border:none;border-radius:4px;cursor:pointer}button:first-child{background:#f0f0f0}button:last-child{background:#007bff;color:#fff}button:disabled{background:#ccc;cursor:not-allowed}h2{margin:0 0 16px;color:#333}\n"] }]
|
|
2684
|
+
}], ctorParameters: () => [] });
|
|
2954
2685
|
|
|
2955
2686
|
function markdownToHTML(markdownText) {
|
|
2956
2687
|
// Convert italics-bold (***text***)
|
|
@@ -2967,10 +2698,10 @@ class SimpleMdToHtmlPipe {
|
|
|
2967
2698
|
transform(text) {
|
|
2968
2699
|
return markdownToHTML(text);
|
|
2969
2700
|
}
|
|
2970
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.
|
|
2971
|
-
static { this.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "19.
|
|
2701
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: SimpleMdToHtmlPipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe }); }
|
|
2702
|
+
static { this.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "19.2.4", ngImport: i0, type: SimpleMdToHtmlPipe, isStandalone: true, name: "simpleMdToHtml" }); }
|
|
2972
2703
|
}
|
|
2973
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.
|
|
2704
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: SimpleMdToHtmlPipe, decorators: [{
|
|
2974
2705
|
type: Pipe,
|
|
2975
2706
|
args: [{
|
|
2976
2707
|
name: 'simpleMdToHtml',
|
|
@@ -2984,10 +2715,10 @@ class StartDivToHtmlPipe {
|
|
|
2984
2715
|
text = text.replace(/\n/g, '<br>');
|
|
2985
2716
|
return text;
|
|
2986
2717
|
}
|
|
2987
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.
|
|
2988
|
-
static { this.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "19.
|
|
2718
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: StartDivToHtmlPipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe }); }
|
|
2719
|
+
static { this.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "19.2.4", ngImport: i0, type: StartDivToHtmlPipe, isStandalone: true, name: "startDividerToHtml" }); }
|
|
2989
2720
|
}
|
|
2990
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.
|
|
2721
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: StartDivToHtmlPipe, decorators: [{
|
|
2991
2722
|
type: Pipe,
|
|
2992
2723
|
args: [{
|
|
2993
2724
|
name: 'startDividerToHtml',
|
|
@@ -3000,10 +2731,10 @@ class MdToHtmlArrayPipe {
|
|
|
3000
2731
|
const htmlArray = convertToHTML(text);
|
|
3001
2732
|
return htmlArray.map((val) => val.content).join('');
|
|
3002
2733
|
}
|
|
3003
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.
|
|
3004
|
-
static { this.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "19.
|
|
2734
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: MdToHtmlArrayPipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe }); }
|
|
2735
|
+
static { this.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "19.2.4", ngImport: i0, type: MdToHtmlArrayPipe, isStandalone: true, name: "mdToHtmlArray" }); }
|
|
3005
2736
|
}
|
|
3006
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.
|
|
2737
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: MdToHtmlArrayPipe, decorators: [{
|
|
3007
2738
|
type: Pipe,
|
|
3008
2739
|
args: [{
|
|
3009
2740
|
name: 'mdToHtmlArray',
|
|
@@ -3012,11 +2743,11 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.1.1", ngImpor
|
|
|
3012
2743
|
}] });
|
|
3013
2744
|
|
|
3014
2745
|
class AccountPlatformForm {
|
|
3015
|
-
constructor(
|
|
3016
|
-
this.route =
|
|
3017
|
-
this.fb =
|
|
3018
|
-
this.router =
|
|
3019
|
-
this.toastService =
|
|
2746
|
+
constructor() {
|
|
2747
|
+
this.route = inject(ActivatedRoute);
|
|
2748
|
+
this.fb = inject(FormBuilder);
|
|
2749
|
+
this.router = inject(Router);
|
|
2750
|
+
this.toastService = inject(TOAST_ALERTS_TOKEN);
|
|
3020
2751
|
// Format the platform options for dropdown display
|
|
3021
2752
|
this.platformOptions = Object.values(EAccountsPlatform).map((value) => ({
|
|
3022
2753
|
label: value.charAt(0).toUpperCase() + value.slice(1), // Capitalize first letter
|
|
@@ -3071,16 +2802,13 @@ class AccountPlatformForm {
|
|
|
3071
2802
|
this.toastService.warn(infoToast);
|
|
3072
2803
|
}
|
|
3073
2804
|
}
|
|
3074
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.
|
|
3075
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.
|
|
2805
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: AccountPlatformForm, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
2806
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.4", type: AccountPlatformForm, isStandalone: true, selector: "account-platform-form", inputs: { formArray: "formArray" }, ngImport: i0, template: "<div class=\"source-form-card\">\n <p-card header=\"Cuenta\">\n @for (formAccount of formArray.controls; track formAccount) {\n <form [formGroup]=\"$any(formAccount)\">\n <div class=\"form-field\">\n <label class=\"block\">Platform</label>\n <p-dropdown [options]=\"platformOptions\" formControlName=\"platform\" optionLabel=\"label\" optionValue=\"value\" placeholder=\"Select a platform\"></p-dropdown>\n </div>\n\n <div class=\"form-field\">\n <label for=\"name\" class=\"block\">Username</label>\n <input pInputText id=\"name\" type=\"text\" formControlName=\"name\" placeholder=\"Enter name\" class=\"w-full\" />\n </div>\n\n <div class=\"form-field\">\n <label class=\"block\">Email</label>\n <input pInputText type=\"text\" formControlName=\"email\" placeholder=\"Enter name\" class=\"w-full\" />\n </div>\n </form>\n }\n </p-card>\n</div>\n", styles: [":host{display:block;padding:1rem}.source-form-card{max-width:800px;margin:0 auto}.form-field{margin-bottom:1.5rem;display:flex;flex-direction:column}.form-field label{margin-bottom:.5rem;font-weight:500;color:#495057}.form-field input,.form-field textarea,.form-field ::ng-deep .p-element{margin-top:.25rem}:host ::ng-deep .p-card .p-card-content>div:last-child{margin-top:1.5rem;display:flex;justify-content:flex-end}:host ::ng-deep .p-card .p-card-header{background-color:#f8f9fa;padding:1rem;border-bottom:1px solid #dee2e6}h3{color:#495057;margin-bottom:1.5rem;text-align:center}\n"], dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1.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.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "ngmodule", type: CardModule }, { kind: "component", type: i2$5.Card, selector: "p-card", inputs: ["header", "subheader", "style", "styleClass"] }, { kind: "ngmodule", type: TextareaModule }, { kind: "ngmodule", type: DropdownModule }, { kind: "component", type: i3$2.Dropdown, selector: "p-dropdown", inputs: ["id", "scrollHeight", "filter", "name", "style", "panelStyle", "styleClass", "panelStyleClass", "readonly", "required", "editable", "appendTo", "tabindex", "placeholder", "loadingIcon", "filterPlaceholder", "filterLocale", "variant", "inputId", "dataKey", "filterBy", "filterFields", "autofocus", "resetFilterOnHide", "checkmark", "dropdownIcon", "loading", "optionLabel", "optionValue", "optionDisabled", "optionGroupLabel", "optionGroupChildren", "autoDisplayFirst", "group", "showClear", "emptyFilterMessage", "emptyMessage", "lazy", "virtualScroll", "virtualScrollItemSize", "virtualScrollOptions", "overlayOptions", "ariaFilterLabel", "ariaLabel", "ariaLabelledBy", "filterMatchMode", "maxlength", "tooltip", "tooltipPosition", "tooltipPositionStyle", "tooltipStyleClass", "focusOnHover", "selectOnFocus", "autoOptionFocus", "autofocusFilter", "fluid", "disabled", "itemSize", "autoZIndex", "baseZIndex", "showTransitionOptions", "hideTransitionOptions", "filterValue", "options"], outputs: ["onChange", "onFilter", "onFocus", "onBlur", "onClick", "onShow", "onHide", "onClear", "onLazyLoad"] }, { kind: "ngmodule", type: ButtonModule }, { kind: "ngmodule", type: SelectModule }, { kind: "ngmodule", type: InputTextModule }, { kind: "directive", type: i4$1.InputText, selector: "[pInputText]", inputs: ["variant", "fluid", "pSize"] }, { kind: "ngmodule", type: ChipModule }, { kind: "ngmodule", type: TooltipModule }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
3076
2807
|
}
|
|
3077
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.
|
|
2808
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: AccountPlatformForm, decorators: [{
|
|
3078
2809
|
type: Component,
|
|
3079
2810
|
args: [{ selector: 'account-platform-form', imports: [ReactiveFormsModule, CardModule, TextareaModule, DropdownModule, ButtonModule, SelectModule, InputTextModule, ChipModule, TooltipModule], changeDetection: ChangeDetectionStrategy.OnPush, standalone: true, template: "<div class=\"source-form-card\">\n <p-card header=\"Cuenta\">\n @for (formAccount of formArray.controls; track formAccount) {\n <form [formGroup]=\"$any(formAccount)\">\n <div class=\"form-field\">\n <label class=\"block\">Platform</label>\n <p-dropdown [options]=\"platformOptions\" formControlName=\"platform\" optionLabel=\"label\" optionValue=\"value\" placeholder=\"Select a platform\"></p-dropdown>\n </div>\n\n <div class=\"form-field\">\n <label for=\"name\" class=\"block\">Username</label>\n <input pInputText id=\"name\" type=\"text\" formControlName=\"name\" placeholder=\"Enter name\" class=\"w-full\" />\n </div>\n\n <div class=\"form-field\">\n <label class=\"block\">Email</label>\n <input pInputText type=\"text\" formControlName=\"email\" placeholder=\"Enter name\" class=\"w-full\" />\n </div>\n </form>\n }\n </p-card>\n</div>\n", styles: [":host{display:block;padding:1rem}.source-form-card{max-width:800px;margin:0 auto}.form-field{margin-bottom:1.5rem;display:flex;flex-direction:column}.form-field label{margin-bottom:.5rem;font-weight:500;color:#495057}.form-field input,.form-field textarea,.form-field ::ng-deep .p-element{margin-top:.25rem}:host ::ng-deep .p-card .p-card-content>div:last-child{margin-top:1.5rem;display:flex;justify-content:flex-end}:host ::ng-deep .p-card .p-card-header{background-color:#f8f9fa;padding:1rem;border-bottom:1px solid #dee2e6}h3{color:#495057;margin-bottom:1.5rem;text-align:center}\n"] }]
|
|
3080
|
-
}], ctorParameters: () => [
|
|
3081
|
-
type: Inject,
|
|
3082
|
-
args: [TOAST_ALERTS_TOKEN]
|
|
3083
|
-
}] }], propDecorators: { formArray: [{
|
|
2811
|
+
}], ctorParameters: () => [], propDecorators: { formArray: [{
|
|
3084
2812
|
type: Input
|
|
3085
2813
|
}] } });
|
|
3086
2814
|
|
|
@@ -3093,16 +2821,16 @@ class DCAgentCardFormComponent {
|
|
|
3093
2821
|
};
|
|
3094
2822
|
return imageSettings;
|
|
3095
2823
|
}
|
|
3096
|
-
constructor(
|
|
3097
|
-
this.fb =
|
|
3098
|
-
this.storageService =
|
|
3099
|
-
this.agentCardService =
|
|
3100
|
-
this.cdr =
|
|
3101
|
-
this.router =
|
|
3102
|
-
this.activatedRoute =
|
|
3103
|
-
this.dialogService =
|
|
3104
|
-
this.promptBuilder =
|
|
3105
|
-
this.toastService =
|
|
2824
|
+
constructor() {
|
|
2825
|
+
this.fb = inject(FormBuilder);
|
|
2826
|
+
this.storageService = inject(MultiImagesStorageService);
|
|
2827
|
+
this.agentCardService = inject(CONVERSATION_AI_TOKEN);
|
|
2828
|
+
this.cdr = inject(ChangeDetectorRef);
|
|
2829
|
+
this.router = inject(Router);
|
|
2830
|
+
this.activatedRoute = inject(ActivatedRoute);
|
|
2831
|
+
this.dialogService = inject(DialogService);
|
|
2832
|
+
this.promptBuilder = inject(DCConversationPromptBuilderService);
|
|
2833
|
+
this.toastService = inject(TOAST_ALERTS_TOKEN, { optional: true });
|
|
3106
2834
|
// select options
|
|
3107
2835
|
this.textEngines = Object.values(TextEngines);
|
|
3108
2836
|
this.conversationOptions = ConversationTypeOptions;
|
|
@@ -3112,26 +2840,26 @@ class DCAgentCardFormComponent {
|
|
|
3112
2840
|
this.languageOptions = Object.entries(LangCodeDescriptionEs).map(([value, label]) => ({ value, label }));
|
|
3113
2841
|
this.agentCardId = this.activatedRoute.snapshot.paramMap.get('id');
|
|
3114
2842
|
this.audioSpeedOptions = Object.values(AudioSpeed$1).map((speed) => ({ label: speed, value: speed }));
|
|
3115
|
-
this.storageSettings = this.getSettings();
|
|
3116
|
-
this.bannerImgSettings = {
|
|
2843
|
+
this.storageSettings = input(this.getSettings());
|
|
2844
|
+
this.bannerImgSettings = input({
|
|
3117
2845
|
path: 'conversation-cards/' + this.agentCardId,
|
|
3118
2846
|
fileName: null,
|
|
3119
2847
|
cropSettings: { aspectRatio: AspectType.Rectangle, resolutions: [ResolutionType.MediumLarge], resizeToWidth: 700 },
|
|
3120
|
-
};
|
|
3121
|
-
this.imageStorageSettings = {
|
|
2848
|
+
});
|
|
2849
|
+
this.imageStorageSettings = input({
|
|
3122
2850
|
path: 'conversation-cards/' + this.agentCardId,
|
|
3123
2851
|
fileName: null,
|
|
3124
2852
|
cropSettings: { aspectRatio: AspectType.Vertical_9_16, resolutions: [ResolutionType.MediumLarge], resizeToWidth: 500 },
|
|
3125
|
-
};
|
|
2853
|
+
});
|
|
3126
2854
|
this.stickerStorageSettings = {
|
|
3127
2855
|
path: `conversation-cards/${this.agentCardId}/stickers`,
|
|
3128
2856
|
fileName: null,
|
|
3129
2857
|
cropSettings: { aspectRatio: AspectType.Square, resolutions: [ResolutionType.MediumLarge], resizeToWidth: 400 },
|
|
3130
2858
|
};
|
|
3131
|
-
this.onImageLoaded =
|
|
3132
|
-
this.onSave =
|
|
3133
|
-
this.onGoDetails =
|
|
3134
|
-
this.onTranslate =
|
|
2859
|
+
this.onImageLoaded = output();
|
|
2860
|
+
this.onSave = output();
|
|
2861
|
+
this.onGoDetails = output();
|
|
2862
|
+
this.onTranslate = output();
|
|
3135
2863
|
this.markdownForm = this.fb.group({ seeMarkdown: [false] });
|
|
3136
2864
|
// Type is IAgentCard
|
|
3137
2865
|
this.form = this.fb.group({
|
|
@@ -3168,7 +2896,7 @@ class DCAgentCardFormComponent {
|
|
|
3168
2896
|
ngOnInit() {
|
|
3169
2897
|
this.form.controls.conversationSettings.controls.autoStart;
|
|
3170
2898
|
console.log('form', this.form.value, this.conversationOptions);
|
|
3171
|
-
this.imageSettings = this.storageSettings.cropSettings;
|
|
2899
|
+
this.imageSettings = this.storageSettings().cropSettings;
|
|
3172
2900
|
this.loadConversationCard();
|
|
3173
2901
|
this.markdownForm.get('checked')?.valueChanges.subscribe((value) => {
|
|
3174
2902
|
this.markdownForm.patchValue({ seeMarkdown: value }, { emitEvent: false });
|
|
@@ -3183,7 +2911,7 @@ class DCAgentCardFormComponent {
|
|
|
3183
2911
|
this.patchFormWithConversationData();
|
|
3184
2912
|
this.handleAlternateGreetings();
|
|
3185
2913
|
}
|
|
3186
|
-
this.storageSettings.path = 'conversation-cards/' + this.agentCardId;
|
|
2914
|
+
this.storageSettings().path = 'conversation-cards/' + this.agentCardId;
|
|
3187
2915
|
this.cdr.detectChanges();
|
|
3188
2916
|
}
|
|
3189
2917
|
catch (err) {
|
|
@@ -3501,13 +3229,12 @@ class DCAgentCardFormComponent {
|
|
|
3501
3229
|
this.toastService.success({ title: 'Sticker removed', subtitle: 'Sticker was removed' });
|
|
3502
3230
|
this.cdr.detectChanges();
|
|
3503
3231
|
}
|
|
3504
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.
|
|
3505
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.1.1", type: DCAgentCardFormComponent, isStandalone: true, selector: "dc-agent-form", inputs: { storageSettings: "storageSettings", bannerImgSettings: "bannerImgSettings", imageStorageSettings: "imageStorageSettings" }, outputs: { onImageLoaded: "onImageLoaded", onSave: "onSave", onGoDetails: "onGoDetails", onTranslate: "onTranslate" }, providers: [DialogService], ngImport: i0, template: "<div class=\"top-buttons\">\n <button pButton severity=\"info\" (click)=\"checkPrompt()\" label=\"\uD83D\uDC41\uFE0F Ver instrucciones finales \uD83D\uDCD3\"></button>\n\n <button pButton severity=\"info\" (click)=\"goToDetails()\" label=\"\uD83D\uDCAC Conversar\"></button>\n <button pButton severity=\"primary\" (click)=\"saveConversation()\" label=\"\uD83D\uDCBE Guardar cambios\"></button>\n</div>\n\n<div class=\"top-buttons\">\n <p-button severity=\"help\" (click)=\"translate()\" label=\"\uD83D\uDD04 Traducir\"></p-button>\n <p-button [loading]=\"isGenerating\" severity=\"help\" (click)=\"generateCharacter()\" label=\"Generar \uD83E\uDDBE\"></p-button>\n\n <p-button severity=\"info\" (click)=\"downloadConversation()\" label=\"\uD83D\uDCC1 Exportar \u2B07\uFE0F\"></p-button>\n <p-button severity=\"info\" (click)=\"importConversation()\" label=\"\uD83C\uDCCF Importar \u2B06\uFE0F\"></p-button>\n</div>\n\n<br />\n<br />\n<form [formGroup]=\"form\" class=\"conversation-form\">\n <div class=\"form-grid\">\n <div class=\"left-column\">\n <div style=\"display: flex; gap: 15px\">\n <div class=\"form-field\">\n <label for=\"version\">Version: {{ form.controls.version.value }} <span pTooltip=\"Version number of the conversation\">\u2139\uFE0F</span></label>\n </div>\n\n <div class=\"form-field\">\n <label for=\"id\"\n >ID: <span pTooltip=\"Unique identifier for this conversation\"> {{ form.controls.id.value }} \u2139\uFE0F</span></label\n >\n </div>\n </div>\n\n <div class=\"form-field\">\n <label for=\"title\">Title <span pTooltip=\"T\u00EDtulo de la conversaci\u00F3n\">\u2139\uFE0F</span></label>\n <input pInputText id=\"title\" type=\"text\" formControlName=\"title\" />\n @if(form.controls.title.errors?.['required'] && form.controls.title.touched){\n <div class=\"error\"> Title is required </div>\n }\n </div>\n\n <div class=\"form-field\">\n <label for=\"lang\">Language <span pTooltip=\"Select the primary language for the conversation\">\u2139\uFE0F</span></label>\n <p-select\n id=\"lang\"\n [options]=\"languageOptions\"\n formControlName=\"lang\"\n optionLabel=\"label\"\n optionValue=\"value\"\n [placeholder]=\"'Select Language'\"></p-select>\n </div>\n\n <div formGroupName=\"conversationSettings\" class=\"group\">\n <h3>Conversation Settings <span pTooltip=\"Additional information about the conversation\">\u2139\uFE0F</span></h3>\n\n <div class=\"form-field\">\n <label for=\"textEngine\">\n Text Engine\n <span\n class=\"cursor-pointer\"\n (click)=\"textEngineDialog.toggle($event)\"\n pTooltip=\"Sistema de generaci\u00F3n de texto y audios. Client: el cliente llama al servidor en cada dialogo de voz/personaje, es optimo para historias, Server SSML: se sintetiza todo el audio en uno solo con los distintos cambios de voz/personaje, util para la reflexi\u00F3n porque es bilingue, utiliza dialogos en ingles y espa\u00F1ol en el mismo dialogo/audio\"\n >\u2139\uFE0F</span\n >\n </label>\n\n <p-select\n id=\"textEngine\"\n [options]=\"textEngineOptions\"\n formControlName=\"textEngine\"\n optionLabel=\"label\"\n optionValue=\"value\"\n [placeholder]=\"'Select Text Engine'\"></p-select>\n </div>\n\n <div class=\"form-field\">\n <label for=\"conversationType\">Conversation Type <span pTooltip=\"Choose the type of conversation interaction\">\u2139\uFE0F</span></label>\n <p-select\n id=\"conversationType\"\n [options]=\"conversationOptions\"\n formControlName=\"conversationType\"\n optionLabel=\"label\"\n optionValue=\"value\"\n [placeholder]=\"'Select Conversation Type'\"></p-select>\n </div>\n\n <div class=\"form-field\">\n <label> Auto Start <span pTooltip=\"Start conversation automatically\">\u2139\uFE0F</span> </label>\n <p-toggleSwitch formControlName=\"autoStart\"> </p-toggleSwitch>\n </div>\n\n <div formGroupName=\"tts\" class=\"group\">\n <h3>TTS Settings <span pTooltip=\"Text-to-Speech configuration options\">\u2139\uFE0F</span></h3>\n\n <div class=\"form-field\">\n <label for=\"voice\">Voice <span pTooltip=\"Select the primary voice for text-to-speech\">\u2139\uFE0F</span></label>\n <p-select\n id=\"voice\"\n [options]=\"voiceTTSOptions\"\n formControlName=\"voice\"\n optionLabel=\"name\"\n optionValue=\"id\"\n [placeholder]=\"'Select Voice'\"></p-select>\n </div>\n\n <div class=\"form-field\">\n <label for=\"secondaryVoice\">Secondary Voice <span pTooltip=\"Select an alternative voice for text-to-speech\">\u2139\uFE0F</span></label>\n <p-select\n id=\"secondaryVoice\"\n [options]=\"voiceTTSOptions\"\n formControlName=\"secondaryVoice\"\n optionLabel=\"name\"\n optionValue=\"id\"\n [placeholder]=\"'Select Secondary Voice'\"></p-select>\n </div>\n\n <div class=\"form-field\">\n <label for=\"speed\">Speed <span pTooltip=\"Set the speech rate for text-to-speech conversion\">\u2139\uFE0F</span></label>\n <p-select\n id=\"speed\"\n [options]=\"audioSpeedOptions\"\n formControlName=\"speed\"\n optionLabel=\"label\"\n optionValue=\"value\"\n [placeholder]=\"'Select Speed'\"></p-select>\n </div>\n\n <div class=\"form-field\">\n <label for=\"speedRate\">Speed Rate <span pTooltip=\"Adjust the rate of speech delivery\">\u2139\uFE0F</span></label>\n <input pInputText id=\"speedRate\" type=\"number\" formControlName=\"speedRate\" step=\"0.1\" />\n </div>\n </div>\n </div>\n\n <div formGroupName=\"metaApp\" class=\"group\">\n <h3>Meta Information <span pTooltip=\"Additional information about the conversation\">\u2139\uFE0F</span></h3>\n <div class=\"form-field\">\n <label for=\"authorId\">Author ID <span pTooltip=\"Unique identifier for the conversation author\">\u2139\uFE0F</span></label>\n <input pInputText id=\"authorId\" type=\"text\" formControlName=\"authorId\" />\n </div>\n\n <div class=\"form-field\">\n <label for=\"authorEmail\">Author Email \u2139\uFE0F</label>\n <input pInputText id=\"authorEmail\" type=\"email\" formControlName=\"authorEmail\" />\n <div class=\"error\" *ngIf=\"form.get('metaApp.authorEmail')?.errors?.['email'] && form.get('metaApp.authorEmail')?.touched\">\n Please enter a valid email address\n </div>\n </div>\n\n <div class=\"form-field\">\n <label for=\"takenCount\"\n >Taken Count <span pTooltip=\"Es el contador de cuantas veces se ha tomado esta conversaci\u00F3n, no sirve por ahora\"> \u2139\uFE0F</span></label\n >\n <input pInputText id=\"takenCount\" type=\"number\" formControlName=\"takenCount\" />\n </div>\n\n <div class=\"form-field checkbox\">\n <label>\n <p-checkbox [binary]=\"true\" formControlName=\"isPublic\" />\n Public\n </label>\n </div>\n\n <div class=\"form-field checkbox\">\n <label>\n <p-checkbox [binary]=\"true\" formControlName=\"isPublished\" />\n Published\n </label>\n </div>\n </div>\n\n <div class=\"group\">\n <h4>Model Settings <span pTooltip=\"AI model configuration\">\u2139\uFE0F</span></h4>\n\n <dc-provider-selector [parentForm]=\"form.controls.model\"></dc-provider-selector>\n </div>\n\n <div class=\"group\">\n <h4>Gestion de cuentas</h4>\n @if(form.controls.accounts){\n <account-platform-form [formArray]=\"form.controls.accounts\"></account-platform-form>\n\n }\n </div>\n </div>\n\n <div class=\"right-column\">\n <div style=\"position: relative; min-height: 60px\">\n <img [src]=\"conversation?.assets?.bannerImg?.url || 'assets/images/default_banner.webp'\" class=\"main-banner-image-card\" />\n @if(!conversation?.assets?.bannerImg?.url && agentCardId) {\n\n <dc-cropper-modal\n style=\"position: absolute; bottom: 10px; right: 10px\"\n #cropperBanner\n id=\"cropperBanner\"\n [buttonLabel]=\"conversation?.assets?.bannerImg?.url ? 'Cambiar el banner' : 'Cargar un banner'\"\n [imgStorageSettings]=\"bannerImgSettings\"\n [currentStorage]=\"conversation?.assets?.bannerImg\"\n (onFileSelected)=\"onImageSelected($event)\"\n (imageUploaded)=\"onImageUploaded($event, 'bannerImg')\"></dc-cropper-modal>\n\n }\n </div>\n <div style=\"position: relative\">\n <img [src]=\"conversation?.assets?.image?.url || 'assets/images/default_2_3.webp'\" class=\"main-image-card\" />\n @if (!agentCardId) {\n <button pButton (click)=\"saveConversation()\"> Guarda el scenario para subir la imagen</button>\n } @else {\n\n <dc-cropper-modal\n style=\"position: absolute; bottom: 10px; left: 50%\"\n id=\"cropperCardImage\"\n #cropperCardImage\n [buttonLabel]=\"conversation?.assets?.image?.url ? 'Cambiar imagen' : 'Cargar una imagen'\"\n [imgStorageSettings]=\"imageStorageSettings\"\n (onFileSelected)=\"onImageSelected($event)\"\n (imageUploaded)=\"onImageUploaded($event, 'image')\"></dc-cropper-modal>\n }\n </div>\n\n <div>\n <h4>Agregar stickers</h4>\n\n <dc-cropper-modal\n id=\"cropperCardImage\"\n #cropperStickers\n [buttonLabel]=\"'agregar sticker'\"\n [imgStorageSettings]=\"stickerStorageSettings\"\n (onFileSelected)=\"onImageSelected($event)\"\n (imageUploaded)=\"onImageUploaded($event, 'sticker')\"></dc-cropper-modal>\n </div>\n\n <div style=\"display: flex; flex-wrap: wrap; gap: 10px\">\n @for (sticker of conversation?.assets?.stickers; track sticker.url) {\n <div style=\"position: relative\">\n <img width=\"100\" [src]=\"sticker.url\" alt=\"\" />\n <p-button (click)=\"removeSticker(sticker)\" class=\"remove-sticker\" icon=\"pi pi-times\" [rounded]=\"true\" [text]=\"true\" severity=\"danger\" />\n </div>\n }\n </div>\n\n <!-- <input pInputText type=\"file\" accept=\"image/*\" (change)=\"onImageSelected($event)\" /> -->\n\n <div formGroupName=\"characterCard\">\n <div formGroupName=\"data\" class=\"card-group\">\n <h3>Character Card <span pTooltip=\"Informaci\u00F3n de la ficha del personaje\">\u2139\uFE0F</span></h3>\n <div class=\"form-field\">\n <label for=\"cardName\">Name <span pTooltip=\"El nombre del personaje\">\u2139\uFE0F</span></label>\n <input pInputText id=\"cardName\" type=\"text\" formControlName=\"name\" />\n <div class=\"error\" *ngIf=\"form.get('characterCard.data.name')?.errors?.['required'] && form.get('characterCard.data.name')?.touched\">\n Name is required\n </div>\n </div>\n\n <div class=\"form-field\">\n <label for=\"cardDescription\">Description <span pTooltip=\"Descripci\u00F3n detallada del personaje\">\u2139\uFE0F</span></label>\n <textarea class=\"textmin\" rows=\"1\" pTextarea [autoResize]=\"true\" id=\"cardDescription\" formControlName=\"description\"></textarea>\n <div class=\"error\" *ngIf=\"form.get('characterCard.data.description')?.errors?.['required'] && form.get('characterCard.data.description')?.touched\">\n Description is required\n </div>\n </div>\n\n <div class=\"form-field\">\n <label for=\"cardScenario\">Scenario <span pTooltip=\"Describe the context or setting for the conversation\">\u2139\uFE0F</span></label>\n <textarea rows=\"1\" pTextarea [autoResize]=\"true\" id=\"cardScenario\" formControlName=\"scenario\"></textarea>\n </div>\n\n <div class=\"form-field\">\n <label for=\"cardFirstMessage\">\n First Message\n <span pTooltip=\"Es muy importante que la historia inicie bien, ya que es el patr\u00F3n inicial para la AI, respetar las convenciones de texto\"\n >\u2139\uFE0F</span\n >\n\n <p-togglebutton\n [formControl]=\"markdownForm.controls.seeMarkdown\"\n [onLabel]=\"'Editar'\"\n [offLabel]=\"'Ver Markdown Texto'\"\n size=\"small\"\n styleClass=\"min-w-16\"\n (onChange)=\"checkCdr()\" />\n </label>\n\n @if(markdownForm.controls.seeMarkdown.value){\n <div [innerHTML]=\"form.controls.characterCard.controls.data.controls.first_mes.value | mdToHtmlArray\"></div>\n }@else{\n <textarea rows=\"1\" pTextarea [autoResize]=\"true\" id=\"cardFirstMessage\" formControlName=\"first_mes\"> </textarea>\n }\n </div>\n\n <div class=\"form-field\">\n <label for=\"mes_example\">Mensajes de Ejemplo <span pTooltip=\"Importante para el estilo de la conversaci\u00F3n\">\u2139\uFE0F</span></label>\n <textarea rows=\"1\" pTextarea [autoResize]=\"true\" id=\"mes_example\" formControlName=\"mes_example\"></textarea>\n </div>\n\n <div class=\"form-field\">\n <label for=\"cardCreatorNotes\">Creator Notes <span pTooltip=\"son solo notas del creador, no afecta nada a la conversaci\u00F3n\">\u2139\uFE0F</span></label>\n <textarea rows=\"1\" pTextarea [autoResize]=\"true\" id=\"cardCreatorNotes\" formControlName=\"creator_notes\"></textarea>\n </div>\n\n <div class=\"form-field\">\n <label for=\"cardSystemPrompt\">System Prompt (Opcional) <span pTooltip=\"Instrucciones del sistema para la conversaci\u00F3n\">\u2139\uFE0F</span></label>\n <textarea rows=\"1\" pTextarea [autoResize]=\"true\" id=\"cardSystemPrompt\" formControlName=\"system_prompt\"></textarea>\n <div\n class=\"error\"\n *ngIf=\"form.get('characterCard.data.system_prompt')?.errors?.['required'] && form.get('characterCard.data.system_prompt')?.touched\">\n System prompt is required\n </div>\n </div>\n\n <div style=\"display: flex; flex-direction: column\">\n <label for=\"cardPostHistoryInstructions\"\n >Post-History Instructions (Opcional)\n <span\n pTooltip=\"Dejar en blanco, al menos que se sepa como funciona, esto se llama jailbreak, es para darle instrucciones finales y m\u00E1s importantes al modelo\"\n >\u2139\uFE0F</span\n ></label\n >\n <textarea rows=\"1\" pTextarea [autoResize]=\"true\" formControlName=\"post_history_instructions\"></textarea>\n </div>\n\n <div class=\"form-field\">\n <label for=\"cardAlternateGreetings\">Alternate Greetings <span pTooltip=\"Saludos alternativos para comenzar una historia diferente\">\u2139\uFE0F</span></label>\n <div class=\"array-field\">\n <div\n *ngFor=\"let greeting of form.controls.characterCard.controls.data.controls.alternate_greetings.controls; let i = index\"\n class=\"array-item\"\n style=\"position: relative\">\n <textarea\n pTextarea\n rows=\"1\"\n [autoResize]=\"true\"\n [id]=\"'cardAlternateGreeting' + i\"\n [formControl]=\"greeting\"\n (input)=\"updateArrayField('alternate_greetings', i, $event)\">\n </textarea>\n <button pButton severity=\"danger\" class=\"remove-button\" (click)=\"removeArrayItem('alternate_greetings', i)\">✖</button>\n </div>\n <button pButton severity=\"info\" (click)=\"addArrayItem('alternate_greetings')\">Add Greeting</button>\n </div>\n </div>\n\n <div class=\"form-field\">\n <label pTooltip=\"Agrega las categorias\" for=\"cardTags\">Tags \u2139\uFE0F</label>\n <div class=\"array-field\">\n <div *ngFor=\"let tag of form.controls.characterCard.controls.data.controls.tags.controls; let i = index\" class=\"array-item\">\n <input [id]=\"'cardTag' + i\" type=\"text\" [formControl]=\"tag\" (input)=\"updateArrayField('tags', i, $event)\" />\n <button pButton severity=\"danger\" (click)=\"removeArrayItem('tags', i)\">Remove</button>\n </div>\n <button pButton severity=\"info\" (click)=\"addArrayItem('tags')\">Add Tag</button>\n </div>\n </div>\n </div>\n </div>\n </div>\n </div>\n</form>\n\n<p-popover #textEngineDialog header=\"Text Engine Information\">\n <div class=\"p-4\">\n <h3>Text Engine Types</h3>\n <ul>\n <li> <strong>Texto Simple</strong> La conversaci\u00F3n es como chatgpt, preguntas y responde, es la m\u00E1s b\u00E1sica</li>\n\n <li\n ><strong>Multi Mensajes</strong> Utiliza markdown (recomendable entenderlo), sirve para darle formato al texto y sea m\u00E1s agradable de leer, el sistema\n puede partir dialogos que tienen distinto formato, como normal, cursiva y negritas, asi puede generar distintas voces y estilo para el narrador y\n personaje principal</li\n >\n <li\n ><strong>MD SSML :</strong> Markdown con Lenguaje de marcaci\u00F3n de s\u00EDntesis de voz (SSML), es tambien markdown pero a diferencia de multimessage, solo se\n presenta un mensaje. y la voz se genera para toda la linea,normalmente lo uso para conversaciones bilingues.</li\n >\n </ul>\n </div>\n</p-popover>\n\n<div class=\"float-button\">\n <p-button icon=\"pi pi-save\" (click)=\"saveConversation()\" severity=\"primary\" [rounded]=\"true\" [raised]=\"true\" pTooltip=\"Guardar (Ctrl + S)\"> </p-button>\n</div>\n", styles: [".textmin{min-width:36vw}.main-image-card{max-width:280px;display:block;margin:0 auto;border-radius:8px}.main-banner-image-card{border-radius:8px}.remove-sticker{position:absolute;top:5px;right:5px}.conversation-form{max-width:100%;padding:20px;background-color:#fff;border-radius:8px;box-shadow:0 2px 4px #0000001a}.conversation-form .card-group{background-color:#f8f9fa;padding:20px;border-radius:6px;margin-bottom:24px}.conversation-form .card-group h3{margin:0 0 20px;color:#2c3e50;font-size:1.25rem}.conversation-form .form-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:2rem;width:100%;max-width:100%}@media (max-width: 768px){.conversation-form .form-grid{grid-template-columns:1fr}}.conversation-form .form-field{margin-bottom:1.5rem;display:flex;flex-direction:column;gap:.5rem}.conversation-form .form-field label{font-weight:500}.conversation-form .form-field textarea{resize:vertical}.conversation-form .form-field.checkbox{flex-direction:row;align-items:center;gap:.5rem}.conversation-form .form-field.checkbox input[type=checkbox]{width:auto}.conversation-form .form-field .error{color:#dc3545;font-size:.875rem;margin-top:.25rem}.conversation-form .form-field .remove-button{position:absolute;border:none;border-radius:50%;width:20px;height:20px;display:flex;align-items:center;justify-content:center;cursor:pointer;top:-10px;right:-10px}.conversation-form .left-column,.conversation-form .right-column{display:flex;flex-direction:column;gap:1rem}.conversation-form .array-field{display:flex;flex-direction:column;gap:.5rem}.conversation-form .array-field .array-item{display:flex;gap:.5rem}.conversation-form .array-field .array-item input,.conversation-form .array-field .array-item textarea{flex:1}.conversation-form .array-field .array-item button{padding:.5rem}.conversation-form .array-field button[type=button]{background-color:#28a745;color:#fff;border:none;padding:8px 12px;border-radius:4px;cursor:pointer;transition:background-color .2s}.conversation-form .array-field button[type=button]:hover{background-color:#218838}.conversation-form .group,.conversation-form .meta-group,.conversation-form .card-group{background-color:#f8f9fa;padding:1rem;border-radius:4px;margin-bottom:1.5rem}.conversation-form .group h3,.conversation-form .meta-group h3,.conversation-form .card-group h3{margin-top:0;margin-bottom:1rem}.top-buttons{display:flex;justify-content:space-between;margin-bottom:2rem;gap:1rem}.top-buttons button{flex:1}::ng-deep em{font-weight:900;color:#014a93}.float-button{position:fixed;bottom:4rem;right:2rem;z-index:1000;display:flex;gap:1px}.float-button :host ::ng-deep .p-button{width:4rem;height:4rem;border-radius:50%}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$2.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1$2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1.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.NumberValueAccessor, selector: "input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "directive", type: i1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "directive", type: i1.FormGroupName, selector: "[formGroupName]", inputs: ["formGroupName"] }, { kind: "component", type: CropperComponentModal, selector: "dc-cropper-modal", inputs: ["imgStorageSettings", "buttonLabel", "currentStorage"], outputs: ["imageUploaded", "onImageCropped", "onFileSelected"] }, { kind: "ngmodule", type: OverlayModule }, { kind: "ngmodule", type: PortalModule }, { kind: "ngmodule", type: ButtonModule }, { kind: "directive", type: i7.ButtonDirective, selector: "[pButton]", inputs: ["iconPos", "loadingIcon", "loading", "severity", "raised", "rounded", "text", "outlined", "size", "plain", "fluid", "label", "icon", "buttonProps"] }, { kind: "component", type: i7.Button, selector: "p-button", inputs: ["type", "iconPos", "icon", "badge", "label", "disabled", "loading", "loadingIcon", "raised", "rounded", "text", "plain", "severity", "outlined", "link", "tabindex", "size", "variant", "style", "styleClass", "badgeClass", "badgeSeverity", "ariaLabel", "autofocus", "fluid", "buttonProps"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "ngmodule", type: TextareaModule }, { kind: "directive", type: i3.Textarea, selector: "[pTextarea]", inputs: ["autoResize", "variant", "fluid", "pSize"], outputs: ["onResize"] }, { kind: "ngmodule", type: InputTextModule }, { kind: "directive", type: i5$1.InputText, selector: "[pInputText]", inputs: ["variant", "fluid", "pSize"] }, { kind: "ngmodule", type: CheckboxModule }, { kind: "component", type: i4$2.Checkbox, selector: "p-checkbox, p-checkBox, p-check-box", inputs: ["value", "name", "disabled", "binary", "ariaLabelledBy", "ariaLabel", "tabindex", "inputId", "style", "inputStyle", "styleClass", "inputClass", "indeterminate", "size", "formControl", "checkboxIcon", "readonly", "required", "autofocus", "trueValue", "falseValue", "variant"], outputs: ["onChange", "onFocus", "onBlur"] }, { kind: "ngmodule", type: ToggleButtonModule }, { kind: "component", type: i11.ToggleButton, selector: "p-toggleButton, p-togglebutton, p-toggle-button", inputs: ["onLabel", "offLabel", "onIcon", "offIcon", "ariaLabel", "ariaLabelledBy", "disabled", "style", "styleClass", "inputId", "tabindex", "size", "iconPos", "autofocus", "allowEmpty"], outputs: ["onChange"] }, { kind: "ngmodule", type: FormsModule }, { kind: "ngmodule", type: TooltipModule }, { kind: "directive", type: i7$1.Tooltip, selector: "[pTooltip]", inputs: ["tooltipPosition", "tooltipEvent", "appendTo", "positionStyle", "tooltipStyleClass", "tooltipZIndex", "escape", "showDelay", "hideDelay", "life", "positionTop", "positionLeft", "autoHide", "fitContent", "hideOnEscape", "pTooltip", "tooltipDisabled", "tooltipOptions"] }, { kind: "ngmodule", type: ToggleSwitchModule }, { kind: "component", type: i13.ToggleSwitch, selector: "p-toggleswitch, p-toggleSwitch, p-toggle-switch", inputs: ["style", "styleClass", "tabindex", "inputId", "name", "disabled", "readonly", "trueValue", "falseValue", "ariaLabel", "ariaLabelledBy", "autofocus"], outputs: ["onChange"] }, { kind: "pipe", type: MdToHtmlArrayPipe, name: "mdToHtmlArray" }, { kind: "ngmodule", type: SelectModule }, { kind: "component", type: i14.Select, selector: "p-select", inputs: ["id", "scrollHeight", "filter", "name", "style", "panelStyle", "styleClass", "panelStyleClass", "readonly", "required", "editable", "appendTo", "tabindex", "placeholder", "loadingIcon", "filterPlaceholder", "filterLocale", "variant", "inputId", "dataKey", "filterBy", "filterFields", "autofocus", "resetFilterOnHide", "checkmark", "dropdownIcon", "loading", "optionLabel", "optionValue", "optionDisabled", "optionGroupLabel", "optionGroupChildren", "autoDisplayFirst", "group", "showClear", "emptyFilterMessage", "emptyMessage", "lazy", "virtualScroll", "virtualScrollItemSize", "virtualScrollOptions", "size", "overlayOptions", "ariaFilterLabel", "ariaLabel", "ariaLabelledBy", "filterMatchMode", "maxlength", "tooltip", "tooltipPosition", "tooltipPositionStyle", "tooltipStyleClass", "focusOnHover", "selectOnFocus", "autoOptionFocus", "autofocusFilter", "fluid", "disabled", "itemSize", "autoZIndex", "baseZIndex", "showTransitionOptions", "hideTransitionOptions", "filterValue", "options"], outputs: ["onChange", "onFilter", "onFocus", "onBlur", "onClick", "onShow", "onHide", "onClear", "onLazyLoad"] }, { kind: "ngmodule", type: DialogModule }, { kind: "ngmodule", type: DynamicDialogModule }, { kind: "ngmodule", type: PopoverModule }, { kind: "component", type: i15.Popover, selector: "p-popover", inputs: ["ariaLabel", "ariaLabelledBy", "dismissable", "style", "styleClass", "appendTo", "autoZIndex", "ariaCloseLabel", "baseZIndex", "focusOnShow", "showTransitionOptions", "hideTransitionOptions"], outputs: ["onShow", "onHide"] }, { kind: "component", type: ProviderSelectorComponent, selector: "dc-provider-selector", inputs: ["parentForm"] }, { kind: "component", type: AccountPlatformForm, selector: "account-platform-form", inputs: ["formArray"] }] }); }
|
|
3232
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: DCAgentCardFormComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
3233
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.4", type: DCAgentCardFormComponent, isStandalone: true, selector: "dc-agent-form", inputs: { storageSettings: { classPropertyName: "storageSettings", publicName: "storageSettings", isSignal: true, isRequired: false, transformFunction: null }, bannerImgSettings: { classPropertyName: "bannerImgSettings", publicName: "bannerImgSettings", isSignal: true, isRequired: false, transformFunction: null }, imageStorageSettings: { classPropertyName: "imageStorageSettings", publicName: "imageStorageSettings", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { onImageLoaded: "onImageLoaded", onSave: "onSave", onGoDetails: "onGoDetails", onTranslate: "onTranslate" }, providers: [DialogService], ngImport: i0, template: "<div class=\"top-buttons\">\n <button pButton severity=\"info\" (click)=\"checkPrompt()\" label=\"\uD83D\uDC41\uFE0F Ver instrucciones finales \uD83D\uDCD3\"></button>\n\n <button pButton severity=\"info\" (click)=\"goToDetails()\" label=\"\uD83D\uDCAC Conversar\"></button>\n <button pButton severity=\"primary\" (click)=\"saveConversation()\" label=\"\uD83D\uDCBE Guardar cambios\"></button>\n</div>\n\n<div class=\"top-buttons\">\n <p-button severity=\"help\" (click)=\"translate()\" label=\"\uD83D\uDD04 Traducir\"></p-button>\n <p-button [loading]=\"isGenerating\" severity=\"help\" (click)=\"generateCharacter()\" label=\"Generar \uD83E\uDDBE\"></p-button>\n\n <p-button severity=\"info\" (click)=\"downloadConversation()\" label=\"\uD83D\uDCC1 Exportar \u2B07\uFE0F\"></p-button>\n <p-button severity=\"info\" (click)=\"importConversation()\" label=\"\uD83C\uDCCF Importar \u2B06\uFE0F\"></p-button>\n</div>\n\n<br />\n<br />\n<form [formGroup]=\"form\" class=\"conversation-form\">\n <div class=\"form-grid\">\n <div class=\"left-column\">\n <div style=\"display: flex; gap: 15px\">\n <div class=\"form-field\">\n <label for=\"version\">Version: {{ form.controls.version.value }} <span pTooltip=\"Version number of the conversation\">\u2139\uFE0F</span></label>\n </div>\n\n <div class=\"form-field\">\n <label for=\"id\"\n >ID: <span pTooltip=\"Unique identifier for this conversation\"> {{ form.controls.id.value }} \u2139\uFE0F</span></label\n >\n </div>\n </div>\n\n <div class=\"form-field\">\n <label for=\"title\">Title <span pTooltip=\"T\u00EDtulo de la conversaci\u00F3n\">\u2139\uFE0F</span></label>\n <input pInputText id=\"title\" type=\"text\" formControlName=\"title\" />\n @if(form.controls.title.errors?.['required'] && form.controls.title.touched){\n <div class=\"error\"> Title is required </div>\n }\n </div>\n\n <div class=\"form-field\">\n <label for=\"lang\">Language <span pTooltip=\"Select the primary language for the conversation\">\u2139\uFE0F</span></label>\n <p-select\n id=\"lang\"\n [options]=\"languageOptions\"\n formControlName=\"lang\"\n optionLabel=\"label\"\n optionValue=\"value\"\n [placeholder]=\"'Select Language'\"></p-select>\n </div>\n\n <div formGroupName=\"conversationSettings\" class=\"group\">\n <h3>Conversation Settings <span pTooltip=\"Additional information about the conversation\">\u2139\uFE0F</span></h3>\n\n <div class=\"form-field\">\n <label for=\"textEngine\">\n Text Engine\n <span\n class=\"cursor-pointer\"\n (click)=\"textEngineDialog.toggle($event)\"\n pTooltip=\"Sistema de generaci\u00F3n de texto y audios. Client: el cliente llama al servidor en cada dialogo de voz/personaje, es optimo para historias, Server SSML: se sintetiza todo el audio en uno solo con los distintos cambios de voz/personaje, util para la reflexi\u00F3n porque es bilingue, utiliza dialogos en ingles y espa\u00F1ol en el mismo dialogo/audio\"\n >\u2139\uFE0F</span\n >\n </label>\n\n <p-select\n id=\"textEngine\"\n [options]=\"textEngineOptions\"\n formControlName=\"textEngine\"\n optionLabel=\"label\"\n optionValue=\"value\"\n [placeholder]=\"'Select Text Engine'\"></p-select>\n </div>\n\n <div class=\"form-field\">\n <label for=\"conversationType\">Conversation Type <span pTooltip=\"Choose the type of conversation interaction\">\u2139\uFE0F</span></label>\n <p-select\n id=\"conversationType\"\n [options]=\"conversationOptions\"\n formControlName=\"conversationType\"\n optionLabel=\"label\"\n optionValue=\"value\"\n [placeholder]=\"'Select Conversation Type'\"></p-select>\n </div>\n\n <div class=\"form-field\">\n <label> Auto Start <span pTooltip=\"Start conversation automatically\">\u2139\uFE0F</span> </label>\n <p-toggleSwitch formControlName=\"autoStart\"> </p-toggleSwitch>\n </div>\n\n <div formGroupName=\"tts\" class=\"group\">\n <h3>TTS Settings <span pTooltip=\"Text-to-Speech configuration options\">\u2139\uFE0F</span></h3>\n\n <div class=\"form-field\">\n <label for=\"voice\">Voice <span pTooltip=\"Select the primary voice for text-to-speech\">\u2139\uFE0F</span></label>\n <p-select\n id=\"voice\"\n [options]=\"voiceTTSOptions\"\n formControlName=\"voice\"\n optionLabel=\"name\"\n optionValue=\"id\"\n [placeholder]=\"'Select Voice'\"></p-select>\n </div>\n\n <div class=\"form-field\">\n <label for=\"secondaryVoice\">Secondary Voice <span pTooltip=\"Select an alternative voice for text-to-speech\">\u2139\uFE0F</span></label>\n <p-select\n id=\"secondaryVoice\"\n [options]=\"voiceTTSOptions\"\n formControlName=\"secondaryVoice\"\n optionLabel=\"name\"\n optionValue=\"id\"\n [placeholder]=\"'Select Secondary Voice'\"></p-select>\n </div>\n\n <div class=\"form-field\">\n <label for=\"speed\">Speed <span pTooltip=\"Set the speech rate for text-to-speech conversion\">\u2139\uFE0F</span></label>\n <p-select\n id=\"speed\"\n [options]=\"audioSpeedOptions\"\n formControlName=\"speed\"\n optionLabel=\"label\"\n optionValue=\"value\"\n [placeholder]=\"'Select Speed'\"></p-select>\n </div>\n\n <div class=\"form-field\">\n <label for=\"speedRate\">Speed Rate <span pTooltip=\"Adjust the rate of speech delivery\">\u2139\uFE0F</span></label>\n <input pInputText id=\"speedRate\" type=\"number\" formControlName=\"speedRate\" step=\"0.1\" />\n </div>\n </div>\n </div>\n\n <div formGroupName=\"metaApp\" class=\"group\">\n <h3>Meta Information <span pTooltip=\"Additional information about the conversation\">\u2139\uFE0F</span></h3>\n <div class=\"form-field\">\n <label for=\"authorId\">Author ID <span pTooltip=\"Unique identifier for the conversation author\">\u2139\uFE0F</span></label>\n <input pInputText id=\"authorId\" type=\"text\" formControlName=\"authorId\" />\n </div>\n\n <div class=\"form-field\">\n <label for=\"authorEmail\">Author Email \u2139\uFE0F</label>\n <input pInputText id=\"authorEmail\" type=\"email\" formControlName=\"authorEmail\" />\n @if (form.get('metaApp.authorEmail')?.errors?.['email'] && form.get('metaApp.authorEmail')?.touched) {\n <div class=\"error\">\n Please enter a valid email address\n </div>\n }\n </div>\n\n <div class=\"form-field\">\n <label for=\"takenCount\"\n >Taken Count <span pTooltip=\"Es el contador de cuantas veces se ha tomado esta conversaci\u00F3n, no sirve por ahora\"> \u2139\uFE0F</span></label\n >\n <input pInputText id=\"takenCount\" type=\"number\" formControlName=\"takenCount\" />\n </div>\n\n <div class=\"form-field checkbox\">\n <label>\n <p-checkbox [binary]=\"true\" formControlName=\"isPublic\" />\n Public\n </label>\n </div>\n\n <div class=\"form-field checkbox\">\n <label>\n <p-checkbox [binary]=\"true\" formControlName=\"isPublished\" />\n Published\n </label>\n </div>\n </div>\n\n <div class=\"group\">\n <h4>Model Settings <span pTooltip=\"AI model configuration\">\u2139\uFE0F</span></h4>\n\n <dc-provider-selector [parentForm]=\"form.controls.model\"></dc-provider-selector>\n </div>\n\n <div class=\"group\">\n <h4>Gestion de cuentas</h4>\n @if(form.controls.accounts){\n <account-platform-form [formArray]=\"form.controls.accounts\"></account-platform-form>\n\n }\n </div>\n </div>\n\n <div class=\"right-column\">\n <div style=\"position: relative; min-height: 60px\">\n <img [src]=\"conversation?.assets?.bannerImg?.url || 'assets/images/default_banner.webp'\" class=\"main-banner-image-card\" />\n @if(!conversation?.assets?.bannerImg?.url && agentCardId) {\n\n <dc-cropper-modal\n style=\"position: absolute; bottom: 10px; right: 10px\"\n #cropperBanner\n id=\"cropperBanner\"\n [buttonLabel]=\"conversation?.assets?.bannerImg?.url ? 'Cambiar el banner' : 'Cargar un banner'\"\n [imgStorageSettings]=\"bannerImgSettings()\"\n [currentStorage]=\"conversation?.assets?.bannerImg\"\n (onFileSelected)=\"onImageSelected($event)\"\n (imageUploaded)=\"onImageUploaded($event, 'bannerImg')\"></dc-cropper-modal>\n\n }\n </div>\n <div style=\"position: relative\">\n <img [src]=\"conversation?.assets?.image?.url || 'assets/images/default_2_3.webp'\" class=\"main-image-card\" />\n @if (!agentCardId) {\n <button pButton (click)=\"saveConversation()\"> Guarda el scenario para subir la imagen</button>\n } @else {\n\n <dc-cropper-modal\n style=\"position: absolute; bottom: 10px; left: 50%\"\n id=\"cropperCardImage\"\n #cropperCardImage\n [buttonLabel]=\"conversation?.assets?.image?.url ? 'Cambiar imagen' : 'Cargar una imagen'\"\n [imgStorageSettings]=\"imageStorageSettings()\"\n (onFileSelected)=\"onImageSelected($event)\"\n (imageUploaded)=\"onImageUploaded($event, 'image')\"></dc-cropper-modal>\n }\n </div>\n\n <div>\n <h4>Agregar stickers</h4>\n\n <dc-cropper-modal\n id=\"cropperCardImage\"\n #cropperStickers\n [buttonLabel]=\"'agregar sticker'\"\n [imgStorageSettings]=\"stickerStorageSettings\"\n (onFileSelected)=\"onImageSelected($event)\"\n (imageUploaded)=\"onImageUploaded($event, 'sticker')\"></dc-cropper-modal>\n </div>\n\n <div style=\"display: flex; flex-wrap: wrap; gap: 10px\">\n @for (sticker of conversation?.assets?.stickers; track sticker.url) {\n <div style=\"position: relative\">\n <img width=\"100\" [src]=\"sticker.url\" alt=\"\" />\n <p-button (click)=\"removeSticker(sticker)\" class=\"remove-sticker\" icon=\"pi pi-times\" [rounded]=\"true\" [text]=\"true\" severity=\"danger\" />\n </div>\n }\n </div>\n\n <!-- <input pInputText type=\"file\" accept=\"image/*\" (change)=\"onImageSelected($event)\" /> -->\n\n <div formGroupName=\"characterCard\">\n <div formGroupName=\"data\" class=\"card-group\">\n <h3>Character Card <span pTooltip=\"Informaci\u00F3n de la ficha del personaje\">\u2139\uFE0F</span></h3>\n <div class=\"form-field\">\n <label for=\"cardName\">Name <span pTooltip=\"El nombre del personaje\">\u2139\uFE0F</span></label>\n <input pInputText id=\"cardName\" type=\"text\" formControlName=\"name\" />\n @if (form.get('characterCard.data.name')?.errors?.['required'] && form.get('characterCard.data.name')?.touched) {\n <div class=\"error\">\n Name is required\n </div>\n }\n </div>\n\n <div class=\"form-field\">\n <label for=\"cardDescription\">Description <span pTooltip=\"Descripci\u00F3n detallada del personaje\">\u2139\uFE0F</span></label>\n <textarea class=\"textmin\" rows=\"1\" pTextarea [autoResize]=\"true\" id=\"cardDescription\" formControlName=\"description\"></textarea>\n @if (form.get('characterCard.data.description')?.errors?.['required'] && form.get('characterCard.data.description')?.touched) {\n <div class=\"error\">\n Description is required\n </div>\n }\n </div>\n\n <div class=\"form-field\">\n <label for=\"cardScenario\">Scenario <span pTooltip=\"Describe the context or setting for the conversation\">\u2139\uFE0F</span></label>\n <textarea rows=\"1\" pTextarea [autoResize]=\"true\" id=\"cardScenario\" formControlName=\"scenario\"></textarea>\n </div>\n\n <div class=\"form-field\">\n <label for=\"cardFirstMessage\">\n First Message\n <span pTooltip=\"Es muy importante que la historia inicie bien, ya que es el patr\u00F3n inicial para la AI, respetar las convenciones de texto\"\n >\u2139\uFE0F</span\n >\n\n <p-togglebutton\n [formControl]=\"markdownForm.controls.seeMarkdown\"\n [onLabel]=\"'Editar'\"\n [offLabel]=\"'Ver Markdown Texto'\"\n size=\"small\"\n styleClass=\"min-w-16\"\n (onChange)=\"checkCdr()\" />\n </label>\n\n @if(markdownForm.controls.seeMarkdown.value){\n <div [innerHTML]=\"form.controls.characterCard.controls.data.controls.first_mes.value | mdToHtmlArray\"></div>\n }@else{\n <textarea rows=\"1\" pTextarea [autoResize]=\"true\" id=\"cardFirstMessage\" formControlName=\"first_mes\"> </textarea>\n }\n </div>\n\n <div class=\"form-field\">\n <label for=\"mes_example\">Mensajes de Ejemplo <span pTooltip=\"Importante para el estilo de la conversaci\u00F3n\">\u2139\uFE0F</span></label>\n <textarea rows=\"1\" pTextarea [autoResize]=\"true\" id=\"mes_example\" formControlName=\"mes_example\"></textarea>\n </div>\n\n <div class=\"form-field\">\n <label for=\"cardCreatorNotes\">Creator Notes <span pTooltip=\"son solo notas del creador, no afecta nada a la conversaci\u00F3n\">\u2139\uFE0F</span></label>\n <textarea rows=\"1\" pTextarea [autoResize]=\"true\" id=\"cardCreatorNotes\" formControlName=\"creator_notes\"></textarea>\n </div>\n\n <div class=\"form-field\">\n <label for=\"cardSystemPrompt\">System Prompt (Opcional) <span pTooltip=\"Instrucciones del sistema para la conversaci\u00F3n\">\u2139\uFE0F</span></label>\n <textarea rows=\"1\" pTextarea [autoResize]=\"true\" id=\"cardSystemPrompt\" formControlName=\"system_prompt\"></textarea>\n @if (form.get('characterCard.data.system_prompt')?.errors?.['required'] && form.get('characterCard.data.system_prompt')?.touched) {\n <div\n class=\"error\"\n >\n System prompt is required\n </div>\n }\n </div>\n\n <div style=\"display: flex; flex-direction: column\">\n <label for=\"cardPostHistoryInstructions\"\n >Post-History Instructions (Opcional)\n <span\n pTooltip=\"Dejar en blanco, al menos que se sepa como funciona, esto se llama jailbreak, es para darle instrucciones finales y m\u00E1s importantes al modelo\"\n >\u2139\uFE0F</span\n ></label\n >\n <textarea rows=\"1\" pTextarea [autoResize]=\"true\" formControlName=\"post_history_instructions\"></textarea>\n </div>\n\n <div class=\"form-field\">\n <label for=\"cardAlternateGreetings\">Alternate Greetings <span pTooltip=\"Saludos alternativos para comenzar una historia diferente\">\u2139\uFE0F</span></label>\n <div class=\"array-field\">\n @for (greeting of form.controls.characterCard.controls.data.controls.alternate_greetings.controls; track greeting; let i = $index) {\n <div\n class=\"array-item\"\n style=\"position: relative\">\n <textarea\n pTextarea\n rows=\"1\"\n [autoResize]=\"true\"\n [id]=\"'cardAlternateGreeting' + i\"\n [formControl]=\"greeting\"\n (input)=\"updateArrayField('alternate_greetings', i, $event)\">\n </textarea>\n <button pButton severity=\"danger\" class=\"remove-button\" (click)=\"removeArrayItem('alternate_greetings', i)\">✖</button>\n </div>\n }\n <button pButton severity=\"info\" (click)=\"addArrayItem('alternate_greetings')\">Add Greeting</button>\n </div>\n </div>\n\n <div class=\"form-field\">\n <label pTooltip=\"Agrega las categorias\" for=\"cardTags\">Tags \u2139\uFE0F</label>\n <div class=\"array-field\">\n @for (tag of form.controls.characterCard.controls.data.controls.tags.controls; track tag; let i = $index) {\n <div class=\"array-item\">\n <input [id]=\"'cardTag' + i\" type=\"text\" [formControl]=\"tag\" (input)=\"updateArrayField('tags', i, $event)\" />\n <button pButton severity=\"danger\" (click)=\"removeArrayItem('tags', i)\">Remove</button>\n </div>\n }\n <button pButton severity=\"info\" (click)=\"addArrayItem('tags')\">Add Tag</button>\n </div>\n </div>\n </div>\n </div>\n </div>\n </div>\n </form>\n\n <p-popover #textEngineDialog header=\"Text Engine Information\">\n <div class=\"p-4\">\n <h3>Text Engine Types</h3>\n <ul>\n <li> <strong>Texto Simple</strong> La conversaci\u00F3n es como chatgpt, preguntas y responde, es la m\u00E1s b\u00E1sica</li>\n\n <li\n ><strong>Multi Mensajes</strong> Utiliza markdown (recomendable entenderlo), sirve para darle formato al texto y sea m\u00E1s agradable de leer, el sistema\n puede partir dialogos que tienen distinto formato, como normal, cursiva y negritas, asi puede generar distintas voces y estilo para el narrador y\n personaje principal</li\n >\n <li\n ><strong>MD SSML :</strong> Markdown con Lenguaje de marcaci\u00F3n de s\u00EDntesis de voz (SSML), es tambien markdown pero a diferencia de multimessage, solo se\n presenta un mensaje. y la voz se genera para toda la linea,normalmente lo uso para conversaciones bilingues.</li\n >\n </ul>\n </div>\n </p-popover>\n\n <div class=\"float-button\">\n <p-button icon=\"pi pi-save\" (click)=\"saveConversation()\" severity=\"primary\" [rounded]=\"true\" [raised]=\"true\" pTooltip=\"Guardar (Ctrl + S)\"> </p-button>\n </div>\n", styles: [".textmin{min-width:36vw}.main-image-card{max-width:280px;display:block;margin:0 auto;border-radius:8px}.main-banner-image-card{border-radius:8px}.remove-sticker{position:absolute;top:5px;right:5px}.conversation-form{max-width:100%;padding:20px;background-color:#fff;border-radius:8px;box-shadow:0 2px 4px #0000001a}.conversation-form .card-group{background-color:#f8f9fa;padding:20px;border-radius:6px;margin-bottom:24px}.conversation-form .card-group h3{margin:0 0 20px;color:#2c3e50;font-size:1.25rem}.conversation-form .form-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:2rem;width:100%;max-width:100%}@media (max-width: 768px){.conversation-form .form-grid{grid-template-columns:1fr}}.conversation-form .form-field{margin-bottom:1.5rem;display:flex;flex-direction:column;gap:.5rem}.conversation-form .form-field label{font-weight:500}.conversation-form .form-field textarea{resize:vertical}.conversation-form .form-field.checkbox{flex-direction:row;align-items:center;gap:.5rem}.conversation-form .form-field.checkbox input[type=checkbox]{width:auto}.conversation-form .form-field .error{color:#dc3545;font-size:.875rem;margin-top:.25rem}.conversation-form .form-field .remove-button{position:absolute;border:none;border-radius:50%;width:20px;height:20px;display:flex;align-items:center;justify-content:center;cursor:pointer;top:-10px;right:-10px}.conversation-form .left-column,.conversation-form .right-column{display:flex;flex-direction:column;gap:1rem}.conversation-form .array-field{display:flex;flex-direction:column;gap:.5rem}.conversation-form .array-field .array-item{display:flex;gap:.5rem}.conversation-form .array-field .array-item input,.conversation-form .array-field .array-item textarea{flex:1}.conversation-form .array-field .array-item button{padding:.5rem}.conversation-form .array-field button[type=button]{background-color:#28a745;color:#fff;border:none;padding:8px 12px;border-radius:4px;cursor:pointer;transition:background-color .2s}.conversation-form .array-field button[type=button]:hover{background-color:#218838}.conversation-form .group,.conversation-form .meta-group,.conversation-form .card-group{background-color:#f8f9fa;padding:1rem;border-radius:4px;margin-bottom:1.5rem}.conversation-form .group h3,.conversation-form .meta-group h3,.conversation-form .card-group h3{margin-top:0;margin-bottom:1rem}.top-buttons{display:flex;justify-content:space-between;margin-bottom:2rem;gap:1rem}.top-buttons button{flex:1}::ng-deep em{font-weight:900;color:#014a93}.float-button{position:fixed;bottom:4rem;right:2rem;z-index:1000;display:flex;gap:1px}.float-button :host ::ng-deep .p-button{width:4rem;height:4rem;border-radius:50%}\n"], dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1.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.NumberValueAccessor, selector: "input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "directive", type: i1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "directive", type: i1.FormGroupName, selector: "[formGroupName]", inputs: ["formGroupName"] }, { kind: "component", type: CropperComponentModal, selector: "dc-cropper-modal", inputs: ["imgStorageSettings", "buttonLabel", "currentStorage"], outputs: ["imageUploaded", "onImageCropped", "onFileSelected"] }, { kind: "ngmodule", type: OverlayModule }, { kind: "ngmodule", type: PortalModule }, { kind: "ngmodule", type: ButtonModule }, { kind: "directive", type: i2$1.ButtonDirective, selector: "[pButton]", inputs: ["iconPos", "loadingIcon", "loading", "severity", "raised", "rounded", "text", "outlined", "size", "plain", "fluid", "label", "icon", "buttonProps"] }, { kind: "component", type: i2$1.Button, selector: "p-button", inputs: ["type", "iconPos", "icon", "badge", "label", "disabled", "loading", "loadingIcon", "raised", "rounded", "text", "plain", "severity", "outlined", "link", "tabindex", "size", "variant", "style", "styleClass", "badgeClass", "badgeSeverity", "ariaLabel", "autofocus", "fluid", "buttonProps"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "ngmodule", type: TextareaModule }, { kind: "directive", type: i3.Textarea, selector: "[pTextarea]", inputs: ["autoResize", "variant", "fluid", "pSize"], outputs: ["onResize"] }, { kind: "ngmodule", type: InputTextModule }, { kind: "directive", type: i4$1.InputText, selector: "[pInputText]", inputs: ["variant", "fluid", "pSize"] }, { kind: "ngmodule", type: CheckboxModule }, { kind: "component", type: i2$3.Checkbox, selector: "p-checkbox, p-checkBox, p-check-box", inputs: ["value", "name", "disabled", "binary", "ariaLabelledBy", "ariaLabel", "tabindex", "inputId", "style", "inputStyle", "styleClass", "inputClass", "indeterminate", "size", "formControl", "checkboxIcon", "readonly", "required", "autofocus", "trueValue", "falseValue", "variant"], outputs: ["onChange", "onFocus", "onBlur"] }, { kind: "ngmodule", type: ToggleButtonModule }, { kind: "component", type: i6$2.ToggleButton, selector: "p-toggleButton, p-togglebutton, p-toggle-button", inputs: ["onLabel", "offLabel", "onIcon", "offIcon", "ariaLabel", "ariaLabelledBy", "disabled", "style", "styleClass", "inputId", "tabindex", "size", "iconPos", "autofocus", "allowEmpty"], outputs: ["onChange"] }, { kind: "ngmodule", type: FormsModule }, { kind: "ngmodule", type: TooltipModule }, { kind: "directive", type: i6.Tooltip, selector: "[pTooltip]", inputs: ["tooltipPosition", "tooltipEvent", "appendTo", "positionStyle", "tooltipStyleClass", "tooltipZIndex", "escape", "showDelay", "hideDelay", "life", "positionTop", "positionLeft", "autoHide", "fitContent", "hideOnEscape", "pTooltip", "tooltipDisabled", "tooltipOptions"] }, { kind: "ngmodule", type: ToggleSwitchModule }, { kind: "component", type: i8.ToggleSwitch, selector: "p-toggleswitch, p-toggleSwitch, p-toggle-switch", inputs: ["style", "styleClass", "tabindex", "inputId", "name", "disabled", "readonly", "trueValue", "falseValue", "ariaLabel", "ariaLabelledBy", "autofocus"], outputs: ["onChange"] }, { kind: "pipe", type: MdToHtmlArrayPipe, name: "mdToHtmlArray" }, { kind: "ngmodule", type: SelectModule }, { kind: "component", type: i9.Select, selector: "p-select", inputs: ["id", "scrollHeight", "filter", "name", "style", "panelStyle", "styleClass", "panelStyleClass", "readonly", "required", "editable", "appendTo", "tabindex", "placeholder", "loadingIcon", "filterPlaceholder", "filterLocale", "variant", "inputId", "dataKey", "filterBy", "filterFields", "autofocus", "resetFilterOnHide", "checkmark", "dropdownIcon", "loading", "optionLabel", "optionValue", "optionDisabled", "optionGroupLabel", "optionGroupChildren", "autoDisplayFirst", "group", "showClear", "emptyFilterMessage", "emptyMessage", "lazy", "virtualScroll", "virtualScrollItemSize", "virtualScrollOptions", "size", "overlayOptions", "ariaFilterLabel", "ariaLabel", "ariaLabelledBy", "filterMatchMode", "maxlength", "tooltip", "tooltipPosition", "tooltipPositionStyle", "tooltipStyleClass", "focusOnHover", "selectOnFocus", "autoOptionFocus", "autofocusFilter", "fluid", "disabled", "itemSize", "autoZIndex", "baseZIndex", "showTransitionOptions", "hideTransitionOptions", "filterValue", "options"], outputs: ["onChange", "onFilter", "onFocus", "onBlur", "onClick", "onShow", "onHide", "onClear", "onLazyLoad"] }, { kind: "ngmodule", type: DialogModule }, { kind: "ngmodule", type: DynamicDialogModule }, { kind: "ngmodule", type: PopoverModule }, { kind: "component", type: i10.Popover, selector: "p-popover", inputs: ["ariaLabel", "ariaLabelledBy", "dismissable", "style", "styleClass", "appendTo", "autoZIndex", "ariaCloseLabel", "baseZIndex", "focusOnShow", "showTransitionOptions", "hideTransitionOptions"], outputs: ["onShow", "onHide"] }, { kind: "component", type: ProviderSelectorComponent, selector: "dc-provider-selector", inputs: ["parentForm"] }, { kind: "component", type: AccountPlatformForm, selector: "account-platform-form", inputs: ["formArray"] }] }); }
|
|
3506
3234
|
}
|
|
3507
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.
|
|
3235
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: DCAgentCardFormComponent, decorators: [{
|
|
3508
3236
|
type: Component,
|
|
3509
3237
|
args: [{ selector: 'dc-agent-form', standalone: true, providers: [DialogService], imports: [
|
|
3510
|
-
CommonModule,
|
|
3511
3238
|
ReactiveFormsModule,
|
|
3512
3239
|
CropperComponentModal,
|
|
3513
3240
|
OverlayModule,
|
|
@@ -3526,31 +3253,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.1.1", ngImpor
|
|
|
3526
3253
|
DynamicDialogModule,
|
|
3527
3254
|
PopoverModule,
|
|
3528
3255
|
ProviderSelectorComponent,
|
|
3529
|
-
AccountPlatformForm
|
|
3530
|
-
], template: "<div class=\"top-buttons\">\n <button pButton severity=\"info\" (click)=\"checkPrompt()\" label=\"\uD83D\uDC41\uFE0F Ver instrucciones finales \uD83D\uDCD3\"></button>\n\n <button pButton severity=\"info\" (click)=\"goToDetails()\" label=\"\uD83D\uDCAC Conversar\"></button>\n <button pButton severity=\"primary\" (click)=\"saveConversation()\" label=\"\uD83D\uDCBE Guardar cambios\"></button>\n</div>\n\n<div class=\"top-buttons\">\n <p-button severity=\"help\" (click)=\"translate()\" label=\"\uD83D\uDD04 Traducir\"></p-button>\n <p-button [loading]=\"isGenerating\" severity=\"help\" (click)=\"generateCharacter()\" label=\"Generar \uD83E\uDDBE\"></p-button>\n\n <p-button severity=\"info\" (click)=\"downloadConversation()\" label=\"\uD83D\uDCC1 Exportar \u2B07\uFE0F\"></p-button>\n <p-button severity=\"info\" (click)=\"importConversation()\" label=\"\uD83C\uDCCF Importar \u2B06\uFE0F\"></p-button>\n</div>\n\n<br />\n<br />\n<form [formGroup]=\"form\" class=\"conversation-form\">\n <div class=\"form-grid\">\n <div class=\"left-column\">\n <div style=\"display: flex; gap: 15px\">\n <div class=\"form-field\">\n <label for=\"version\">Version: {{ form.controls.version.value }} <span pTooltip=\"Version number of the conversation\">\u2139\uFE0F</span></label>\n </div>\n\n <div class=\"form-field\">\n <label for=\"id\"\n >ID: <span pTooltip=\"Unique identifier for this conversation\"> {{ form.controls.id.value }} \u2139\uFE0F</span></label\n >\n </div>\n </div>\n\n <div class=\"form-field\">\n <label for=\"title\">Title <span pTooltip=\"T\u00EDtulo de la conversaci\u00F3n\">\u2139\uFE0F</span></label>\n <input pInputText id=\"title\" type=\"text\" formControlName=\"title\" />\n @if(form.controls.title.errors?.['required'] && form.controls.title.touched){\n <div class=\"error\"> Title is required </div>\n }\n </div>\n\n <div class=\"form-field\">\n <label for=\"lang\">Language <span pTooltip=\"Select the primary language for the conversation\">\u2139\uFE0F</span></label>\n <p-select\n id=\"lang\"\n [options]=\"languageOptions\"\n formControlName=\"lang\"\n optionLabel=\"label\"\n optionValue=\"value\"\n [placeholder]=\"'Select Language'\"></p-select>\n </div>\n\n <div formGroupName=\"conversationSettings\" class=\"group\">\n <h3>Conversation Settings <span pTooltip=\"Additional information about the conversation\">\u2139\uFE0F</span></h3>\n\n <div class=\"form-field\">\n <label for=\"textEngine\">\n Text Engine\n <span\n class=\"cursor-pointer\"\n (click)=\"textEngineDialog.toggle($event)\"\n pTooltip=\"Sistema de generaci\u00F3n de texto y audios. Client: el cliente llama al servidor en cada dialogo de voz/personaje, es optimo para historias, Server SSML: se sintetiza todo el audio en uno solo con los distintos cambios de voz/personaje, util para la reflexi\u00F3n porque es bilingue, utiliza dialogos en ingles y espa\u00F1ol en el mismo dialogo/audio\"\n >\u2139\uFE0F</span\n >\n </label>\n\n <p-select\n id=\"textEngine\"\n [options]=\"textEngineOptions\"\n formControlName=\"textEngine\"\n optionLabel=\"label\"\n optionValue=\"value\"\n [placeholder]=\"'Select Text Engine'\"></p-select>\n </div>\n\n <div class=\"form-field\">\n <label for=\"conversationType\">Conversation Type <span pTooltip=\"Choose the type of conversation interaction\">\u2139\uFE0F</span></label>\n <p-select\n id=\"conversationType\"\n [options]=\"conversationOptions\"\n formControlName=\"conversationType\"\n optionLabel=\"label\"\n optionValue=\"value\"\n [placeholder]=\"'Select Conversation Type'\"></p-select>\n </div>\n\n <div class=\"form-field\">\n <label> Auto Start <span pTooltip=\"Start conversation automatically\">\u2139\uFE0F</span> </label>\n <p-toggleSwitch formControlName=\"autoStart\"> </p-toggleSwitch>\n </div>\n\n <div formGroupName=\"tts\" class=\"group\">\n <h3>TTS Settings <span pTooltip=\"Text-to-Speech configuration options\">\u2139\uFE0F</span></h3>\n\n <div class=\"form-field\">\n <label for=\"voice\">Voice <span pTooltip=\"Select the primary voice for text-to-speech\">\u2139\uFE0F</span></label>\n <p-select\n id=\"voice\"\n [options]=\"voiceTTSOptions\"\n formControlName=\"voice\"\n optionLabel=\"name\"\n optionValue=\"id\"\n [placeholder]=\"'Select Voice'\"></p-select>\n </div>\n\n <div class=\"form-field\">\n <label for=\"secondaryVoice\">Secondary Voice <span pTooltip=\"Select an alternative voice for text-to-speech\">\u2139\uFE0F</span></label>\n <p-select\n id=\"secondaryVoice\"\n [options]=\"voiceTTSOptions\"\n formControlName=\"secondaryVoice\"\n optionLabel=\"name\"\n optionValue=\"id\"\n [placeholder]=\"'Select Secondary Voice'\"></p-select>\n </div>\n\n <div class=\"form-field\">\n <label for=\"speed\">Speed <span pTooltip=\"Set the speech rate for text-to-speech conversion\">\u2139\uFE0F</span></label>\n <p-select\n id=\"speed\"\n [options]=\"audioSpeedOptions\"\n formControlName=\"speed\"\n optionLabel=\"label\"\n optionValue=\"value\"\n [placeholder]=\"'Select Speed'\"></p-select>\n </div>\n\n <div class=\"form-field\">\n <label for=\"speedRate\">Speed Rate <span pTooltip=\"Adjust the rate of speech delivery\">\u2139\uFE0F</span></label>\n <input pInputText id=\"speedRate\" type=\"number\" formControlName=\"speedRate\" step=\"0.1\" />\n </div>\n </div>\n </div>\n\n <div formGroupName=\"metaApp\" class=\"group\">\n <h3>Meta Information <span pTooltip=\"Additional information about the conversation\">\u2139\uFE0F</span></h3>\n <div class=\"form-field\">\n <label for=\"authorId\">Author ID <span pTooltip=\"Unique identifier for the conversation author\">\u2139\uFE0F</span></label>\n <input pInputText id=\"authorId\" type=\"text\" formControlName=\"authorId\" />\n </div>\n\n <div class=\"form-field\">\n <label for=\"authorEmail\">Author Email \u2139\uFE0F</label>\n <input pInputText id=\"authorEmail\" type=\"email\" formControlName=\"authorEmail\" />\n <div class=\"error\" *ngIf=\"form.get('metaApp.authorEmail')?.errors?.['email'] && form.get('metaApp.authorEmail')?.touched\">\n Please enter a valid email address\n </div>\n </div>\n\n <div class=\"form-field\">\n <label for=\"takenCount\"\n >Taken Count <span pTooltip=\"Es el contador de cuantas veces se ha tomado esta conversaci\u00F3n, no sirve por ahora\"> \u2139\uFE0F</span></label\n >\n <input pInputText id=\"takenCount\" type=\"number\" formControlName=\"takenCount\" />\n </div>\n\n <div class=\"form-field checkbox\">\n <label>\n <p-checkbox [binary]=\"true\" formControlName=\"isPublic\" />\n Public\n </label>\n </div>\n\n <div class=\"form-field checkbox\">\n <label>\n <p-checkbox [binary]=\"true\" formControlName=\"isPublished\" />\n Published\n </label>\n </div>\n </div>\n\n <div class=\"group\">\n <h4>Model Settings <span pTooltip=\"AI model configuration\">\u2139\uFE0F</span></h4>\n\n <dc-provider-selector [parentForm]=\"form.controls.model\"></dc-provider-selector>\n </div>\n\n <div class=\"group\">\n <h4>Gestion de cuentas</h4>\n @if(form.controls.accounts){\n <account-platform-form [formArray]=\"form.controls.accounts\"></account-platform-form>\n\n }\n </div>\n </div>\n\n <div class=\"right-column\">\n <div style=\"position: relative; min-height: 60px\">\n <img [src]=\"conversation?.assets?.bannerImg?.url || 'assets/images/default_banner.webp'\" class=\"main-banner-image-card\" />\n @if(!conversation?.assets?.bannerImg?.url && agentCardId) {\n\n <dc-cropper-modal\n style=\"position: absolute; bottom: 10px; right: 10px\"\n #cropperBanner\n id=\"cropperBanner\"\n [buttonLabel]=\"conversation?.assets?.bannerImg?.url ? 'Cambiar el banner' : 'Cargar un banner'\"\n [imgStorageSettings]=\"bannerImgSettings\"\n [currentStorage]=\"conversation?.assets?.bannerImg\"\n (onFileSelected)=\"onImageSelected($event)\"\n (imageUploaded)=\"onImageUploaded($event, 'bannerImg')\"></dc-cropper-modal>\n\n }\n </div>\n <div style=\"position: relative\">\n <img [src]=\"conversation?.assets?.image?.url || 'assets/images/default_2_3.webp'\" class=\"main-image-card\" />\n @if (!agentCardId) {\n <button pButton (click)=\"saveConversation()\"> Guarda el scenario para subir la imagen</button>\n } @else {\n\n <dc-cropper-modal\n style=\"position: absolute; bottom: 10px; left: 50%\"\n id=\"cropperCardImage\"\n #cropperCardImage\n [buttonLabel]=\"conversation?.assets?.image?.url ? 'Cambiar imagen' : 'Cargar una imagen'\"\n [imgStorageSettings]=\"imageStorageSettings\"\n (onFileSelected)=\"onImageSelected($event)\"\n (imageUploaded)=\"onImageUploaded($event, 'image')\"></dc-cropper-modal>\n }\n </div>\n\n <div>\n <h4>Agregar stickers</h4>\n\n <dc-cropper-modal\n id=\"cropperCardImage\"\n #cropperStickers\n [buttonLabel]=\"'agregar sticker'\"\n [imgStorageSettings]=\"stickerStorageSettings\"\n (onFileSelected)=\"onImageSelected($event)\"\n (imageUploaded)=\"onImageUploaded($event, 'sticker')\"></dc-cropper-modal>\n </div>\n\n <div style=\"display: flex; flex-wrap: wrap; gap: 10px\">\n @for (sticker of conversation?.assets?.stickers; track sticker.url) {\n <div style=\"position: relative\">\n <img width=\"100\" [src]=\"sticker.url\" alt=\"\" />\n <p-button (click)=\"removeSticker(sticker)\" class=\"remove-sticker\" icon=\"pi pi-times\" [rounded]=\"true\" [text]=\"true\" severity=\"danger\" />\n </div>\n }\n </div>\n\n <!-- <input pInputText type=\"file\" accept=\"image/*\" (change)=\"onImageSelected($event)\" /> -->\n\n <div formGroupName=\"characterCard\">\n <div formGroupName=\"data\" class=\"card-group\">\n <h3>Character Card <span pTooltip=\"Informaci\u00F3n de la ficha del personaje\">\u2139\uFE0F</span></h3>\n <div class=\"form-field\">\n <label for=\"cardName\">Name <span pTooltip=\"El nombre del personaje\">\u2139\uFE0F</span></label>\n <input pInputText id=\"cardName\" type=\"text\" formControlName=\"name\" />\n <div class=\"error\" *ngIf=\"form.get('characterCard.data.name')?.errors?.['required'] && form.get('characterCard.data.name')?.touched\">\n Name is required\n </div>\n </div>\n\n <div class=\"form-field\">\n <label for=\"cardDescription\">Description <span pTooltip=\"Descripci\u00F3n detallada del personaje\">\u2139\uFE0F</span></label>\n <textarea class=\"textmin\" rows=\"1\" pTextarea [autoResize]=\"true\" id=\"cardDescription\" formControlName=\"description\"></textarea>\n <div class=\"error\" *ngIf=\"form.get('characterCard.data.description')?.errors?.['required'] && form.get('characterCard.data.description')?.touched\">\n Description is required\n </div>\n </div>\n\n <div class=\"form-field\">\n <label for=\"cardScenario\">Scenario <span pTooltip=\"Describe the context or setting for the conversation\">\u2139\uFE0F</span></label>\n <textarea rows=\"1\" pTextarea [autoResize]=\"true\" id=\"cardScenario\" formControlName=\"scenario\"></textarea>\n </div>\n\n <div class=\"form-field\">\n <label for=\"cardFirstMessage\">\n First Message\n <span pTooltip=\"Es muy importante que la historia inicie bien, ya que es el patr\u00F3n inicial para la AI, respetar las convenciones de texto\"\n >\u2139\uFE0F</span\n >\n\n <p-togglebutton\n [formControl]=\"markdownForm.controls.seeMarkdown\"\n [onLabel]=\"'Editar'\"\n [offLabel]=\"'Ver Markdown Texto'\"\n size=\"small\"\n styleClass=\"min-w-16\"\n (onChange)=\"checkCdr()\" />\n </label>\n\n @if(markdownForm.controls.seeMarkdown.value){\n <div [innerHTML]=\"form.controls.characterCard.controls.data.controls.first_mes.value | mdToHtmlArray\"></div>\n }@else{\n <textarea rows=\"1\" pTextarea [autoResize]=\"true\" id=\"cardFirstMessage\" formControlName=\"first_mes\"> </textarea>\n }\n </div>\n\n <div class=\"form-field\">\n <label for=\"mes_example\">Mensajes de Ejemplo <span pTooltip=\"Importante para el estilo de la conversaci\u00F3n\">\u2139\uFE0F</span></label>\n <textarea rows=\"1\" pTextarea [autoResize]=\"true\" id=\"mes_example\" formControlName=\"mes_example\"></textarea>\n </div>\n\n <div class=\"form-field\">\n <label for=\"cardCreatorNotes\">Creator Notes <span pTooltip=\"son solo notas del creador, no afecta nada a la conversaci\u00F3n\">\u2139\uFE0F</span></label>\n <textarea rows=\"1\" pTextarea [autoResize]=\"true\" id=\"cardCreatorNotes\" formControlName=\"creator_notes\"></textarea>\n </div>\n\n <div class=\"form-field\">\n <label for=\"cardSystemPrompt\">System Prompt (Opcional) <span pTooltip=\"Instrucciones del sistema para la conversaci\u00F3n\">\u2139\uFE0F</span></label>\n <textarea rows=\"1\" pTextarea [autoResize]=\"true\" id=\"cardSystemPrompt\" formControlName=\"system_prompt\"></textarea>\n <div\n class=\"error\"\n *ngIf=\"form.get('characterCard.data.system_prompt')?.errors?.['required'] && form.get('characterCard.data.system_prompt')?.touched\">\n System prompt is required\n </div>\n </div>\n\n <div style=\"display: flex; flex-direction: column\">\n <label for=\"cardPostHistoryInstructions\"\n >Post-History Instructions (Opcional)\n <span\n pTooltip=\"Dejar en blanco, al menos que se sepa como funciona, esto se llama jailbreak, es para darle instrucciones finales y m\u00E1s importantes al modelo\"\n >\u2139\uFE0F</span\n ></label\n >\n <textarea rows=\"1\" pTextarea [autoResize]=\"true\" formControlName=\"post_history_instructions\"></textarea>\n </div>\n\n <div class=\"form-field\">\n <label for=\"cardAlternateGreetings\">Alternate Greetings <span pTooltip=\"Saludos alternativos para comenzar una historia diferente\">\u2139\uFE0F</span></label>\n <div class=\"array-field\">\n <div\n *ngFor=\"let greeting of form.controls.characterCard.controls.data.controls.alternate_greetings.controls; let i = index\"\n class=\"array-item\"\n style=\"position: relative\">\n <textarea\n pTextarea\n rows=\"1\"\n [autoResize]=\"true\"\n [id]=\"'cardAlternateGreeting' + i\"\n [formControl]=\"greeting\"\n (input)=\"updateArrayField('alternate_greetings', i, $event)\">\n </textarea>\n <button pButton severity=\"danger\" class=\"remove-button\" (click)=\"removeArrayItem('alternate_greetings', i)\">✖</button>\n </div>\n <button pButton severity=\"info\" (click)=\"addArrayItem('alternate_greetings')\">Add Greeting</button>\n </div>\n </div>\n\n <div class=\"form-field\">\n <label pTooltip=\"Agrega las categorias\" for=\"cardTags\">Tags \u2139\uFE0F</label>\n <div class=\"array-field\">\n <div *ngFor=\"let tag of form.controls.characterCard.controls.data.controls.tags.controls; let i = index\" class=\"array-item\">\n <input [id]=\"'cardTag' + i\" type=\"text\" [formControl]=\"tag\" (input)=\"updateArrayField('tags', i, $event)\" />\n <button pButton severity=\"danger\" (click)=\"removeArrayItem('tags', i)\">Remove</button>\n </div>\n <button pButton severity=\"info\" (click)=\"addArrayItem('tags')\">Add Tag</button>\n </div>\n </div>\n </div>\n </div>\n </div>\n </div>\n</form>\n\n<p-popover #textEngineDialog header=\"Text Engine Information\">\n <div class=\"p-4\">\n <h3>Text Engine Types</h3>\n <ul>\n <li> <strong>Texto Simple</strong> La conversaci\u00F3n es como chatgpt, preguntas y responde, es la m\u00E1s b\u00E1sica</li>\n\n <li\n ><strong>Multi Mensajes</strong> Utiliza markdown (recomendable entenderlo), sirve para darle formato al texto y sea m\u00E1s agradable de leer, el sistema\n puede partir dialogos que tienen distinto formato, como normal, cursiva y negritas, asi puede generar distintas voces y estilo para el narrador y\n personaje principal</li\n >\n <li\n ><strong>MD SSML :</strong> Markdown con Lenguaje de marcaci\u00F3n de s\u00EDntesis de voz (SSML), es tambien markdown pero a diferencia de multimessage, solo se\n presenta un mensaje. y la voz se genera para toda la linea,normalmente lo uso para conversaciones bilingues.</li\n >\n </ul>\n </div>\n</p-popover>\n\n<div class=\"float-button\">\n <p-button icon=\"pi pi-save\" (click)=\"saveConversation()\" severity=\"primary\" [rounded]=\"true\" [raised]=\"true\" pTooltip=\"Guardar (Ctrl + S)\"> </p-button>\n</div>\n", styles: [".textmin{min-width:36vw}.main-image-card{max-width:280px;display:block;margin:0 auto;border-radius:8px}.main-banner-image-card{border-radius:8px}.remove-sticker{position:absolute;top:5px;right:5px}.conversation-form{max-width:100%;padding:20px;background-color:#fff;border-radius:8px;box-shadow:0 2px 4px #0000001a}.conversation-form .card-group{background-color:#f8f9fa;padding:20px;border-radius:6px;margin-bottom:24px}.conversation-form .card-group h3{margin:0 0 20px;color:#2c3e50;font-size:1.25rem}.conversation-form .form-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:2rem;width:100%;max-width:100%}@media (max-width: 768px){.conversation-form .form-grid{grid-template-columns:1fr}}.conversation-form .form-field{margin-bottom:1.5rem;display:flex;flex-direction:column;gap:.5rem}.conversation-form .form-field label{font-weight:500}.conversation-form .form-field textarea{resize:vertical}.conversation-form .form-field.checkbox{flex-direction:row;align-items:center;gap:.5rem}.conversation-form .form-field.checkbox input[type=checkbox]{width:auto}.conversation-form .form-field .error{color:#dc3545;font-size:.875rem;margin-top:.25rem}.conversation-form .form-field .remove-button{position:absolute;border:none;border-radius:50%;width:20px;height:20px;display:flex;align-items:center;justify-content:center;cursor:pointer;top:-10px;right:-10px}.conversation-form .left-column,.conversation-form .right-column{display:flex;flex-direction:column;gap:1rem}.conversation-form .array-field{display:flex;flex-direction:column;gap:.5rem}.conversation-form .array-field .array-item{display:flex;gap:.5rem}.conversation-form .array-field .array-item input,.conversation-form .array-field .array-item textarea{flex:1}.conversation-form .array-field .array-item button{padding:.5rem}.conversation-form .array-field button[type=button]{background-color:#28a745;color:#fff;border:none;padding:8px 12px;border-radius:4px;cursor:pointer;transition:background-color .2s}.conversation-form .array-field button[type=button]:hover{background-color:#218838}.conversation-form .group,.conversation-form .meta-group,.conversation-form .card-group{background-color:#f8f9fa;padding:1rem;border-radius:4px;margin-bottom:1.5rem}.conversation-form .group h3,.conversation-form .meta-group h3,.conversation-form .card-group h3{margin-top:0;margin-bottom:1rem}.top-buttons{display:flex;justify-content:space-between;margin-bottom:2rem;gap:1rem}.top-buttons button{flex:1}::ng-deep em{font-weight:900;color:#014a93}.float-button{position:fixed;bottom:4rem;right:2rem;z-index:1000;display:flex;gap:1px}.float-button :host ::ng-deep .p-button{width:4rem;height:4rem;border-radius:50%}\n"] }]
|
|
3531
|
-
}], ctorParameters: () => [
|
|
3532
|
-
type: Inject,
|
|
3533
|
-
args: [CONVERSATION_AI_TOKEN]
|
|
3534
|
-
}] }, { type: i0.ChangeDetectorRef }, { type: i1$4.Router }, { type: i1$4.ActivatedRoute }, { type: i1$3.DialogService }, { type: DCConversationPromptBuilderService }, { type: i6$1.ToastAlertsAbstractService, decorators: [{
|
|
3535
|
-
type: Optional
|
|
3536
|
-
}, {
|
|
3537
|
-
type: Inject,
|
|
3538
|
-
args: [TOAST_ALERTS_TOKEN]
|
|
3539
|
-
}] }], propDecorators: { storageSettings: [{
|
|
3540
|
-
type: Input
|
|
3541
|
-
}], bannerImgSettings: [{
|
|
3542
|
-
type: Input
|
|
3543
|
-
}], imageStorageSettings: [{
|
|
3544
|
-
type: Input
|
|
3545
|
-
}], onImageLoaded: [{
|
|
3546
|
-
type: Output
|
|
3547
|
-
}], onSave: [{
|
|
3548
|
-
type: Output
|
|
3549
|
-
}], onGoDetails: [{
|
|
3550
|
-
type: Output
|
|
3551
|
-
}], onTranslate: [{
|
|
3552
|
-
type: Output
|
|
3553
|
-
}] } });
|
|
3256
|
+
AccountPlatformForm
|
|
3257
|
+
], template: "<div class=\"top-buttons\">\n <button pButton severity=\"info\" (click)=\"checkPrompt()\" label=\"\uD83D\uDC41\uFE0F Ver instrucciones finales \uD83D\uDCD3\"></button>\n\n <button pButton severity=\"info\" (click)=\"goToDetails()\" label=\"\uD83D\uDCAC Conversar\"></button>\n <button pButton severity=\"primary\" (click)=\"saveConversation()\" label=\"\uD83D\uDCBE Guardar cambios\"></button>\n</div>\n\n<div class=\"top-buttons\">\n <p-button severity=\"help\" (click)=\"translate()\" label=\"\uD83D\uDD04 Traducir\"></p-button>\n <p-button [loading]=\"isGenerating\" severity=\"help\" (click)=\"generateCharacter()\" label=\"Generar \uD83E\uDDBE\"></p-button>\n\n <p-button severity=\"info\" (click)=\"downloadConversation()\" label=\"\uD83D\uDCC1 Exportar \u2B07\uFE0F\"></p-button>\n <p-button severity=\"info\" (click)=\"importConversation()\" label=\"\uD83C\uDCCF Importar \u2B06\uFE0F\"></p-button>\n</div>\n\n<br />\n<br />\n<form [formGroup]=\"form\" class=\"conversation-form\">\n <div class=\"form-grid\">\n <div class=\"left-column\">\n <div style=\"display: flex; gap: 15px\">\n <div class=\"form-field\">\n <label for=\"version\">Version: {{ form.controls.version.value }} <span pTooltip=\"Version number of the conversation\">\u2139\uFE0F</span></label>\n </div>\n\n <div class=\"form-field\">\n <label for=\"id\"\n >ID: <span pTooltip=\"Unique identifier for this conversation\"> {{ form.controls.id.value }} \u2139\uFE0F</span></label\n >\n </div>\n </div>\n\n <div class=\"form-field\">\n <label for=\"title\">Title <span pTooltip=\"T\u00EDtulo de la conversaci\u00F3n\">\u2139\uFE0F</span></label>\n <input pInputText id=\"title\" type=\"text\" formControlName=\"title\" />\n @if(form.controls.title.errors?.['required'] && form.controls.title.touched){\n <div class=\"error\"> Title is required </div>\n }\n </div>\n\n <div class=\"form-field\">\n <label for=\"lang\">Language <span pTooltip=\"Select the primary language for the conversation\">\u2139\uFE0F</span></label>\n <p-select\n id=\"lang\"\n [options]=\"languageOptions\"\n formControlName=\"lang\"\n optionLabel=\"label\"\n optionValue=\"value\"\n [placeholder]=\"'Select Language'\"></p-select>\n </div>\n\n <div formGroupName=\"conversationSettings\" class=\"group\">\n <h3>Conversation Settings <span pTooltip=\"Additional information about the conversation\">\u2139\uFE0F</span></h3>\n\n <div class=\"form-field\">\n <label for=\"textEngine\">\n Text Engine\n <span\n class=\"cursor-pointer\"\n (click)=\"textEngineDialog.toggle($event)\"\n pTooltip=\"Sistema de generaci\u00F3n de texto y audios. Client: el cliente llama al servidor en cada dialogo de voz/personaje, es optimo para historias, Server SSML: se sintetiza todo el audio en uno solo con los distintos cambios de voz/personaje, util para la reflexi\u00F3n porque es bilingue, utiliza dialogos en ingles y espa\u00F1ol en el mismo dialogo/audio\"\n >\u2139\uFE0F</span\n >\n </label>\n\n <p-select\n id=\"textEngine\"\n [options]=\"textEngineOptions\"\n formControlName=\"textEngine\"\n optionLabel=\"label\"\n optionValue=\"value\"\n [placeholder]=\"'Select Text Engine'\"></p-select>\n </div>\n\n <div class=\"form-field\">\n <label for=\"conversationType\">Conversation Type <span pTooltip=\"Choose the type of conversation interaction\">\u2139\uFE0F</span></label>\n <p-select\n id=\"conversationType\"\n [options]=\"conversationOptions\"\n formControlName=\"conversationType\"\n optionLabel=\"label\"\n optionValue=\"value\"\n [placeholder]=\"'Select Conversation Type'\"></p-select>\n </div>\n\n <div class=\"form-field\">\n <label> Auto Start <span pTooltip=\"Start conversation automatically\">\u2139\uFE0F</span> </label>\n <p-toggleSwitch formControlName=\"autoStart\"> </p-toggleSwitch>\n </div>\n\n <div formGroupName=\"tts\" class=\"group\">\n <h3>TTS Settings <span pTooltip=\"Text-to-Speech configuration options\">\u2139\uFE0F</span></h3>\n\n <div class=\"form-field\">\n <label for=\"voice\">Voice <span pTooltip=\"Select the primary voice for text-to-speech\">\u2139\uFE0F</span></label>\n <p-select\n id=\"voice\"\n [options]=\"voiceTTSOptions\"\n formControlName=\"voice\"\n optionLabel=\"name\"\n optionValue=\"id\"\n [placeholder]=\"'Select Voice'\"></p-select>\n </div>\n\n <div class=\"form-field\">\n <label for=\"secondaryVoice\">Secondary Voice <span pTooltip=\"Select an alternative voice for text-to-speech\">\u2139\uFE0F</span></label>\n <p-select\n id=\"secondaryVoice\"\n [options]=\"voiceTTSOptions\"\n formControlName=\"secondaryVoice\"\n optionLabel=\"name\"\n optionValue=\"id\"\n [placeholder]=\"'Select Secondary Voice'\"></p-select>\n </div>\n\n <div class=\"form-field\">\n <label for=\"speed\">Speed <span pTooltip=\"Set the speech rate for text-to-speech conversion\">\u2139\uFE0F</span></label>\n <p-select\n id=\"speed\"\n [options]=\"audioSpeedOptions\"\n formControlName=\"speed\"\n optionLabel=\"label\"\n optionValue=\"value\"\n [placeholder]=\"'Select Speed'\"></p-select>\n </div>\n\n <div class=\"form-field\">\n <label for=\"speedRate\">Speed Rate <span pTooltip=\"Adjust the rate of speech delivery\">\u2139\uFE0F</span></label>\n <input pInputText id=\"speedRate\" type=\"number\" formControlName=\"speedRate\" step=\"0.1\" />\n </div>\n </div>\n </div>\n\n <div formGroupName=\"metaApp\" class=\"group\">\n <h3>Meta Information <span pTooltip=\"Additional information about the conversation\">\u2139\uFE0F</span></h3>\n <div class=\"form-field\">\n <label for=\"authorId\">Author ID <span pTooltip=\"Unique identifier for the conversation author\">\u2139\uFE0F</span></label>\n <input pInputText id=\"authorId\" type=\"text\" formControlName=\"authorId\" />\n </div>\n\n <div class=\"form-field\">\n <label for=\"authorEmail\">Author Email \u2139\uFE0F</label>\n <input pInputText id=\"authorEmail\" type=\"email\" formControlName=\"authorEmail\" />\n @if (form.get('metaApp.authorEmail')?.errors?.['email'] && form.get('metaApp.authorEmail')?.touched) {\n <div class=\"error\">\n Please enter a valid email address\n </div>\n }\n </div>\n\n <div class=\"form-field\">\n <label for=\"takenCount\"\n >Taken Count <span pTooltip=\"Es el contador de cuantas veces se ha tomado esta conversaci\u00F3n, no sirve por ahora\"> \u2139\uFE0F</span></label\n >\n <input pInputText id=\"takenCount\" type=\"number\" formControlName=\"takenCount\" />\n </div>\n\n <div class=\"form-field checkbox\">\n <label>\n <p-checkbox [binary]=\"true\" formControlName=\"isPublic\" />\n Public\n </label>\n </div>\n\n <div class=\"form-field checkbox\">\n <label>\n <p-checkbox [binary]=\"true\" formControlName=\"isPublished\" />\n Published\n </label>\n </div>\n </div>\n\n <div class=\"group\">\n <h4>Model Settings <span pTooltip=\"AI model configuration\">\u2139\uFE0F</span></h4>\n\n <dc-provider-selector [parentForm]=\"form.controls.model\"></dc-provider-selector>\n </div>\n\n <div class=\"group\">\n <h4>Gestion de cuentas</h4>\n @if(form.controls.accounts){\n <account-platform-form [formArray]=\"form.controls.accounts\"></account-platform-form>\n\n }\n </div>\n </div>\n\n <div class=\"right-column\">\n <div style=\"position: relative; min-height: 60px\">\n <img [src]=\"conversation?.assets?.bannerImg?.url || 'assets/images/default_banner.webp'\" class=\"main-banner-image-card\" />\n @if(!conversation?.assets?.bannerImg?.url && agentCardId) {\n\n <dc-cropper-modal\n style=\"position: absolute; bottom: 10px; right: 10px\"\n #cropperBanner\n id=\"cropperBanner\"\n [buttonLabel]=\"conversation?.assets?.bannerImg?.url ? 'Cambiar el banner' : 'Cargar un banner'\"\n [imgStorageSettings]=\"bannerImgSettings()\"\n [currentStorage]=\"conversation?.assets?.bannerImg\"\n (onFileSelected)=\"onImageSelected($event)\"\n (imageUploaded)=\"onImageUploaded($event, 'bannerImg')\"></dc-cropper-modal>\n\n }\n </div>\n <div style=\"position: relative\">\n <img [src]=\"conversation?.assets?.image?.url || 'assets/images/default_2_3.webp'\" class=\"main-image-card\" />\n @if (!agentCardId) {\n <button pButton (click)=\"saveConversation()\"> Guarda el scenario para subir la imagen</button>\n } @else {\n\n <dc-cropper-modal\n style=\"position: absolute; bottom: 10px; left: 50%\"\n id=\"cropperCardImage\"\n #cropperCardImage\n [buttonLabel]=\"conversation?.assets?.image?.url ? 'Cambiar imagen' : 'Cargar una imagen'\"\n [imgStorageSettings]=\"imageStorageSettings()\"\n (onFileSelected)=\"onImageSelected($event)\"\n (imageUploaded)=\"onImageUploaded($event, 'image')\"></dc-cropper-modal>\n }\n </div>\n\n <div>\n <h4>Agregar stickers</h4>\n\n <dc-cropper-modal\n id=\"cropperCardImage\"\n #cropperStickers\n [buttonLabel]=\"'agregar sticker'\"\n [imgStorageSettings]=\"stickerStorageSettings\"\n (onFileSelected)=\"onImageSelected($event)\"\n (imageUploaded)=\"onImageUploaded($event, 'sticker')\"></dc-cropper-modal>\n </div>\n\n <div style=\"display: flex; flex-wrap: wrap; gap: 10px\">\n @for (sticker of conversation?.assets?.stickers; track sticker.url) {\n <div style=\"position: relative\">\n <img width=\"100\" [src]=\"sticker.url\" alt=\"\" />\n <p-button (click)=\"removeSticker(sticker)\" class=\"remove-sticker\" icon=\"pi pi-times\" [rounded]=\"true\" [text]=\"true\" severity=\"danger\" />\n </div>\n }\n </div>\n\n <!-- <input pInputText type=\"file\" accept=\"image/*\" (change)=\"onImageSelected($event)\" /> -->\n\n <div formGroupName=\"characterCard\">\n <div formGroupName=\"data\" class=\"card-group\">\n <h3>Character Card <span pTooltip=\"Informaci\u00F3n de la ficha del personaje\">\u2139\uFE0F</span></h3>\n <div class=\"form-field\">\n <label for=\"cardName\">Name <span pTooltip=\"El nombre del personaje\">\u2139\uFE0F</span></label>\n <input pInputText id=\"cardName\" type=\"text\" formControlName=\"name\" />\n @if (form.get('characterCard.data.name')?.errors?.['required'] && form.get('characterCard.data.name')?.touched) {\n <div class=\"error\">\n Name is required\n </div>\n }\n </div>\n\n <div class=\"form-field\">\n <label for=\"cardDescription\">Description <span pTooltip=\"Descripci\u00F3n detallada del personaje\">\u2139\uFE0F</span></label>\n <textarea class=\"textmin\" rows=\"1\" pTextarea [autoResize]=\"true\" id=\"cardDescription\" formControlName=\"description\"></textarea>\n @if (form.get('characterCard.data.description')?.errors?.['required'] && form.get('characterCard.data.description')?.touched) {\n <div class=\"error\">\n Description is required\n </div>\n }\n </div>\n\n <div class=\"form-field\">\n <label for=\"cardScenario\">Scenario <span pTooltip=\"Describe the context or setting for the conversation\">\u2139\uFE0F</span></label>\n <textarea rows=\"1\" pTextarea [autoResize]=\"true\" id=\"cardScenario\" formControlName=\"scenario\"></textarea>\n </div>\n\n <div class=\"form-field\">\n <label for=\"cardFirstMessage\">\n First Message\n <span pTooltip=\"Es muy importante que la historia inicie bien, ya que es el patr\u00F3n inicial para la AI, respetar las convenciones de texto\"\n >\u2139\uFE0F</span\n >\n\n <p-togglebutton\n [formControl]=\"markdownForm.controls.seeMarkdown\"\n [onLabel]=\"'Editar'\"\n [offLabel]=\"'Ver Markdown Texto'\"\n size=\"small\"\n styleClass=\"min-w-16\"\n (onChange)=\"checkCdr()\" />\n </label>\n\n @if(markdownForm.controls.seeMarkdown.value){\n <div [innerHTML]=\"form.controls.characterCard.controls.data.controls.first_mes.value | mdToHtmlArray\"></div>\n }@else{\n <textarea rows=\"1\" pTextarea [autoResize]=\"true\" id=\"cardFirstMessage\" formControlName=\"first_mes\"> </textarea>\n }\n </div>\n\n <div class=\"form-field\">\n <label for=\"mes_example\">Mensajes de Ejemplo <span pTooltip=\"Importante para el estilo de la conversaci\u00F3n\">\u2139\uFE0F</span></label>\n <textarea rows=\"1\" pTextarea [autoResize]=\"true\" id=\"mes_example\" formControlName=\"mes_example\"></textarea>\n </div>\n\n <div class=\"form-field\">\n <label for=\"cardCreatorNotes\">Creator Notes <span pTooltip=\"son solo notas del creador, no afecta nada a la conversaci\u00F3n\">\u2139\uFE0F</span></label>\n <textarea rows=\"1\" pTextarea [autoResize]=\"true\" id=\"cardCreatorNotes\" formControlName=\"creator_notes\"></textarea>\n </div>\n\n <div class=\"form-field\">\n <label for=\"cardSystemPrompt\">System Prompt (Opcional) <span pTooltip=\"Instrucciones del sistema para la conversaci\u00F3n\">\u2139\uFE0F</span></label>\n <textarea rows=\"1\" pTextarea [autoResize]=\"true\" id=\"cardSystemPrompt\" formControlName=\"system_prompt\"></textarea>\n @if (form.get('characterCard.data.system_prompt')?.errors?.['required'] && form.get('characterCard.data.system_prompt')?.touched) {\n <div\n class=\"error\"\n >\n System prompt is required\n </div>\n }\n </div>\n\n <div style=\"display: flex; flex-direction: column\">\n <label for=\"cardPostHistoryInstructions\"\n >Post-History Instructions (Opcional)\n <span\n pTooltip=\"Dejar en blanco, al menos que se sepa como funciona, esto se llama jailbreak, es para darle instrucciones finales y m\u00E1s importantes al modelo\"\n >\u2139\uFE0F</span\n ></label\n >\n <textarea rows=\"1\" pTextarea [autoResize]=\"true\" formControlName=\"post_history_instructions\"></textarea>\n </div>\n\n <div class=\"form-field\">\n <label for=\"cardAlternateGreetings\">Alternate Greetings <span pTooltip=\"Saludos alternativos para comenzar una historia diferente\">\u2139\uFE0F</span></label>\n <div class=\"array-field\">\n @for (greeting of form.controls.characterCard.controls.data.controls.alternate_greetings.controls; track greeting; let i = $index) {\n <div\n class=\"array-item\"\n style=\"position: relative\">\n <textarea\n pTextarea\n rows=\"1\"\n [autoResize]=\"true\"\n [id]=\"'cardAlternateGreeting' + i\"\n [formControl]=\"greeting\"\n (input)=\"updateArrayField('alternate_greetings', i, $event)\">\n </textarea>\n <button pButton severity=\"danger\" class=\"remove-button\" (click)=\"removeArrayItem('alternate_greetings', i)\">✖</button>\n </div>\n }\n <button pButton severity=\"info\" (click)=\"addArrayItem('alternate_greetings')\">Add Greeting</button>\n </div>\n </div>\n\n <div class=\"form-field\">\n <label pTooltip=\"Agrega las categorias\" for=\"cardTags\">Tags \u2139\uFE0F</label>\n <div class=\"array-field\">\n @for (tag of form.controls.characterCard.controls.data.controls.tags.controls; track tag; let i = $index) {\n <div class=\"array-item\">\n <input [id]=\"'cardTag' + i\" type=\"text\" [formControl]=\"tag\" (input)=\"updateArrayField('tags', i, $event)\" />\n <button pButton severity=\"danger\" (click)=\"removeArrayItem('tags', i)\">Remove</button>\n </div>\n }\n <button pButton severity=\"info\" (click)=\"addArrayItem('tags')\">Add Tag</button>\n </div>\n </div>\n </div>\n </div>\n </div>\n </div>\n </form>\n\n <p-popover #textEngineDialog header=\"Text Engine Information\">\n <div class=\"p-4\">\n <h3>Text Engine Types</h3>\n <ul>\n <li> <strong>Texto Simple</strong> La conversaci\u00F3n es como chatgpt, preguntas y responde, es la m\u00E1s b\u00E1sica</li>\n\n <li\n ><strong>Multi Mensajes</strong> Utiliza markdown (recomendable entenderlo), sirve para darle formato al texto y sea m\u00E1s agradable de leer, el sistema\n puede partir dialogos que tienen distinto formato, como normal, cursiva y negritas, asi puede generar distintas voces y estilo para el narrador y\n personaje principal</li\n >\n <li\n ><strong>MD SSML :</strong> Markdown con Lenguaje de marcaci\u00F3n de s\u00EDntesis de voz (SSML), es tambien markdown pero a diferencia de multimessage, solo se\n presenta un mensaje. y la voz se genera para toda la linea,normalmente lo uso para conversaciones bilingues.</li\n >\n </ul>\n </div>\n </p-popover>\n\n <div class=\"float-button\">\n <p-button icon=\"pi pi-save\" (click)=\"saveConversation()\" severity=\"primary\" [rounded]=\"true\" [raised]=\"true\" pTooltip=\"Guardar (Ctrl + S)\"> </p-button>\n </div>\n", styles: [".textmin{min-width:36vw}.main-image-card{max-width:280px;display:block;margin:0 auto;border-radius:8px}.main-banner-image-card{border-radius:8px}.remove-sticker{position:absolute;top:5px;right:5px}.conversation-form{max-width:100%;padding:20px;background-color:#fff;border-radius:8px;box-shadow:0 2px 4px #0000001a}.conversation-form .card-group{background-color:#f8f9fa;padding:20px;border-radius:6px;margin-bottom:24px}.conversation-form .card-group h3{margin:0 0 20px;color:#2c3e50;font-size:1.25rem}.conversation-form .form-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:2rem;width:100%;max-width:100%}@media (max-width: 768px){.conversation-form .form-grid{grid-template-columns:1fr}}.conversation-form .form-field{margin-bottom:1.5rem;display:flex;flex-direction:column;gap:.5rem}.conversation-form .form-field label{font-weight:500}.conversation-form .form-field textarea{resize:vertical}.conversation-form .form-field.checkbox{flex-direction:row;align-items:center;gap:.5rem}.conversation-form .form-field.checkbox input[type=checkbox]{width:auto}.conversation-form .form-field .error{color:#dc3545;font-size:.875rem;margin-top:.25rem}.conversation-form .form-field .remove-button{position:absolute;border:none;border-radius:50%;width:20px;height:20px;display:flex;align-items:center;justify-content:center;cursor:pointer;top:-10px;right:-10px}.conversation-form .left-column,.conversation-form .right-column{display:flex;flex-direction:column;gap:1rem}.conversation-form .array-field{display:flex;flex-direction:column;gap:.5rem}.conversation-form .array-field .array-item{display:flex;gap:.5rem}.conversation-form .array-field .array-item input,.conversation-form .array-field .array-item textarea{flex:1}.conversation-form .array-field .array-item button{padding:.5rem}.conversation-form .array-field button[type=button]{background-color:#28a745;color:#fff;border:none;padding:8px 12px;border-radius:4px;cursor:pointer;transition:background-color .2s}.conversation-form .array-field button[type=button]:hover{background-color:#218838}.conversation-form .group,.conversation-form .meta-group,.conversation-form .card-group{background-color:#f8f9fa;padding:1rem;border-radius:4px;margin-bottom:1.5rem}.conversation-form .group h3,.conversation-form .meta-group h3,.conversation-form .card-group h3{margin-top:0;margin-bottom:1rem}.top-buttons{display:flex;justify-content:space-between;margin-bottom:2rem;gap:1rem}.top-buttons button{flex:1}::ng-deep em{font-weight:900;color:#014a93}.float-button{position:fixed;bottom:4rem;right:2rem;z-index:1000;display:flex;gap:1px}.float-button :host ::ng-deep .p-button{width:4rem;height:4rem;border-radius:50%}\n"] }]
|
|
3258
|
+
}], ctorParameters: () => [] });
|
|
3554
3259
|
|
|
3555
3260
|
class DCConversationCardUIComponent {
|
|
3556
3261
|
constructor() {
|
|
@@ -3559,36 +3264,31 @@ class DCConversationCardUIComponent {
|
|
|
3559
3264
|
{ label: 'Delete', icon: 'pi pi-trash', title: 'delete', severity: 'danger', command: () => this.onDelete() },
|
|
3560
3265
|
{ label: 'Select', icon: 'pi pi-check', title: 'select', severity: 'success', command: () => this.onDetails() },
|
|
3561
3266
|
];
|
|
3562
|
-
this.
|
|
3563
|
-
this.
|
|
3267
|
+
this.card = input(undefined);
|
|
3268
|
+
this.showOptions = input(true);
|
|
3269
|
+
this.onCardAction = output();
|
|
3564
3270
|
}
|
|
3565
3271
|
ngOnInit() {
|
|
3566
|
-
const name = this.card.characterCard.data.name;
|
|
3567
|
-
const description = this.card.characterCard.data.description;
|
|
3568
|
-
this.card.characterCard.data.description = description.replace(/{{char}}/g, name);
|
|
3272
|
+
const name = this.card().characterCard.data.name;
|
|
3273
|
+
const description = this.card().characterCard.data.description;
|
|
3274
|
+
this.card().characterCard.data.description = description.replace(/{{char}}/g, name);
|
|
3569
3275
|
}
|
|
3570
3276
|
onDetails() {
|
|
3571
|
-
this.onCardAction.emit({ event: 'details', card: this.card });
|
|
3277
|
+
this.onCardAction.emit({ event: 'details', card: this.card() });
|
|
3572
3278
|
}
|
|
3573
3279
|
onEdit() {
|
|
3574
|
-
this.onCardAction.emit({ event: 'edit', card: this.card });
|
|
3280
|
+
this.onCardAction.emit({ event: 'edit', card: this.card() });
|
|
3575
3281
|
}
|
|
3576
3282
|
onDelete() {
|
|
3577
|
-
this.onCardAction.emit({ event: 'delete', card: this.card });
|
|
3283
|
+
this.onCardAction.emit({ event: 'delete', card: this.card() });
|
|
3578
3284
|
}
|
|
3579
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.
|
|
3580
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.
|
|
3285
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: DCConversationCardUIComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
3286
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.4", type: DCConversationCardUIComponent, isStandalone: true, selector: "dc-agent-card-default-ui", inputs: { card: { classPropertyName: "card", publicName: "card", isSignal: true, isRequired: false, transformFunction: null }, showOptions: { classPropertyName: "showOptions", publicName: "showOptions", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { onCardAction: "onCardAction" }, ngImport: i0, template: "<p-card class=\"card-image\">\n @if(showOptions()) {\n <div style=\"position: absolute; top: 5px; right: 5px; z-index: 1000\">\n <p-speeddial\n [model]=\"speedDialModel\"\n [radius]=\"70\"\n type=\"quarter-circle\"\n direction=\"down-left\"\n [buttonProps]=\"{ severity: 'primary', rounded: true, outlined: true, raised: true }\" />\n </div>\n }\n\n <img [src]=\"card()?.assets?.image?.url || 'assets/images/default_conversation_card.webp'\" alt=\"\" />\n\n <div (click)=\"onDetails()\" class=\"content\">\n <h2 class=\"title-text\">{{ card().title }}</h2>\n\n <h5 class=\"title\">\n <span [innerHTML]=\"card().characterCard?.data.description | truncate : 100\"></span>\n </h5>\n\n <p-button\n (click)=\"onDetails()\"\n [style]=\"{ position: 'absolute', bottom: '10px', right: '10px' }\"\n icon=\"pi pi-comment\"\n [rounded]=\"true\"\n severity=\"info\"\n [outlined]=\"true\"\n [raised]=\"true\" />\n </div>\n</p-card>\n", styles: [":host{display:block}:host ::ng-deep .p-card{height:100%}:host ::ng-deep .p-card-body{height:100%;padding:0!important}.card-image{width:280px;height:380px;position:relative;align-items:center;display:block;padding:-10px}.card-image img{position:absolute;z-index:3;width:100%;height:100%;opacity:.75;object-fit:cover;transition:opacity .5s}.content{position:absolute;inset:0;z-index:4;padding:1.5rem;color:#fff;background:linear-gradient(to bottom,#0003,#0000001a);height:100%;display:flex;flex-direction:column}.content:hover{background:linear-gradient(to bottom,color-mix(in srgb,var(--p-primary-color) 20%,transparent),color-mix(in srgb,black 10%,transparent));cursor:pointer}\n"], dependencies: [{ kind: "ngmodule", type: PopoverModule }, { kind: "pipe", type: TruncatePipe, name: "truncate" }, { kind: "ngmodule", type: ButtonModule }, { kind: "component", type: i2$1.Button, selector: "p-button", inputs: ["type", "iconPos", "icon", "badge", "label", "disabled", "loading", "loadingIcon", "raised", "rounded", "text", "plain", "severity", "outlined", "link", "tabindex", "size", "variant", "style", "styleClass", "badgeClass", "badgeSeverity", "ariaLabel", "autofocus", "fluid", "buttonProps"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "ngmodule", type: SpeedDialModule }, { kind: "component", type: i2$6.SpeedDial, selector: "p-speeddial, p-speedDial, p-speed-dial", inputs: ["id", "model", "visible", "style", "className", "direction", "transitionDelay", "type", "radius", "mask", "disabled", "hideOnClickOutside", "buttonStyle", "buttonClassName", "maskStyle", "maskClassName", "showIcon", "hideIcon", "rotateAnimation", "ariaLabel", "ariaLabelledBy", "tooltipOptions", "buttonProps"], outputs: ["onVisibleChange", "visibleChange", "onClick", "onShow", "onHide"] }, { kind: "ngmodule", type: CardModule }, { kind: "component", type: i2$5.Card, selector: "p-card", inputs: ["header", "subheader", "style", "styleClass"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
3581
3287
|
}
|
|
3582
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.
|
|
3288
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: DCConversationCardUIComponent, decorators: [{
|
|
3583
3289
|
type: Component,
|
|
3584
|
-
args: [{ selector: 'dc-agent-card-default-ui', imports: [PopoverModule, TruncatePipe, ButtonModule, SpeedDialModule, CardModule], changeDetection: ChangeDetectionStrategy.OnPush, template: "<p-card class=\"card-image\">\n @if(showOptions) {\n <div style=\"position: absolute; top: 5px; right: 5px; z-index: 1000\">\n <p-speeddial\n [model]=\"speedDialModel\"\n [radius]=\"70\"\n type=\"quarter-circle\"\n direction=\"down-left\"\n [buttonProps]=\"{ severity: 'primary', rounded: true, outlined: true, raised: true }\" />\n </div>\n }\n\n <img [src]=\"card?.assets?.image?.url || 'assets/images/default_conversation_card.webp'\" alt=\"\" />\n\n <div (click)=\"onDetails()\" class=\"content\">\n <h2 class=\"title-text\">{{ card.title }}</h2>\n\n <h5 class=\"title\">\n <span [innerHTML]=\"card.characterCard?.data.description | truncate : 100\"></span>\n </h5>\n\n <p-button\n (click)=\"onDetails()\"\n [style]=\"{ position: 'absolute', bottom: '10px', right: '10px' }\"\n icon=\"pi pi-comment\"\n [rounded]=\"true\"\n severity=\"info\"\n [outlined]=\"true\"\n [raised]=\"true\" />\n </div>\n</p-card>\n", styles: [":host{display:block}:host ::ng-deep .p-card{height:100%}:host ::ng-deep .p-card-body{height:100%;padding:0!important}.card-image{width:280px;height:380px;position:relative;align-items:center;display:block;padding:-10px}.card-image img{position:absolute;z-index:3;width:100%;height:100%;opacity:.75;object-fit:cover;transition:opacity .5s}.content{position:absolute;inset:0;z-index:4;padding:1.5rem;color:#fff;background:linear-gradient(to bottom,#0003,#0000001a);height:100%;display:flex;flex-direction:column}.content:hover{background:linear-gradient(to bottom,color-mix(in srgb,var(--p-primary-color) 20%,transparent),color-mix(in srgb,black 10%,transparent));cursor:pointer}\n"] }]
|
|
3585
|
-
}]
|
|
3586
|
-
type: Input
|
|
3587
|
-
}], showOptions: [{
|
|
3588
|
-
type: Input
|
|
3589
|
-
}], onCardAction: [{
|
|
3590
|
-
type: Output
|
|
3591
|
-
}] } });
|
|
3290
|
+
args: [{ selector: 'dc-agent-card-default-ui', imports: [PopoverModule, TruncatePipe, ButtonModule, SpeedDialModule, CardModule], changeDetection: ChangeDetectionStrategy.OnPush, template: "<p-card class=\"card-image\">\n @if(showOptions()) {\n <div style=\"position: absolute; top: 5px; right: 5px; z-index: 1000\">\n <p-speeddial\n [model]=\"speedDialModel\"\n [radius]=\"70\"\n type=\"quarter-circle\"\n direction=\"down-left\"\n [buttonProps]=\"{ severity: 'primary', rounded: true, outlined: true, raised: true }\" />\n </div>\n }\n\n <img [src]=\"card()?.assets?.image?.url || 'assets/images/default_conversation_card.webp'\" alt=\"\" />\n\n <div (click)=\"onDetails()\" class=\"content\">\n <h2 class=\"title-text\">{{ card().title }}</h2>\n\n <h5 class=\"title\">\n <span [innerHTML]=\"card().characterCard?.data.description | truncate : 100\"></span>\n </h5>\n\n <p-button\n (click)=\"onDetails()\"\n [style]=\"{ position: 'absolute', bottom: '10px', right: '10px' }\"\n icon=\"pi pi-comment\"\n [rounded]=\"true\"\n severity=\"info\"\n [outlined]=\"true\"\n [raised]=\"true\" />\n </div>\n</p-card>\n", styles: [":host{display:block}:host ::ng-deep .p-card{height:100%}:host ::ng-deep .p-card-body{height:100%;padding:0!important}.card-image{width:280px;height:380px;position:relative;align-items:center;display:block;padding:-10px}.card-image img{position:absolute;z-index:3;width:100%;height:100%;opacity:.75;object-fit:cover;transition:opacity .5s}.content{position:absolute;inset:0;z-index:4;padding:1.5rem;color:#fff;background:linear-gradient(to bottom,#0003,#0000001a);height:100%;display:flex;flex-direction:column}.content:hover{background:linear-gradient(to bottom,color-mix(in srgb,var(--p-primary-color) 20%,transparent),color-mix(in srgb,black 10%,transparent));cursor:pointer}\n"] }]
|
|
3291
|
+
}] });
|
|
3592
3292
|
|
|
3593
3293
|
// This component contains a really avanced strategy to dinamically render Conversation Cards Details so every app can implement it with their own Style and Behavior
|
|
3594
3294
|
// The trick is use NGFOR with ngComponentOutlet and pass input, problem is, is not possible to get Outpus events, Option one is use injector with ngComponentOutlet and pass context function to handle , but somehow it inject html and due to sanity im not able to see popups
|
|
@@ -3599,14 +3299,18 @@ const DefaultColumns = [
|
|
|
3599
3299
|
{ field: 'title', header: 'Title', type: 'text' },
|
|
3600
3300
|
];
|
|
3601
3301
|
class AgentCardListComponent extends PaginationBase {
|
|
3602
|
-
constructor(
|
|
3302
|
+
constructor() {
|
|
3303
|
+
const route = inject(ActivatedRoute);
|
|
3304
|
+
const router = inject(Router);
|
|
3603
3305
|
super(route, router);
|
|
3604
|
-
this.agentCardService =
|
|
3605
|
-
this.toastService =
|
|
3606
|
-
this.cdr =
|
|
3306
|
+
this.agentCardService = inject(CONVERSATION_AI_TOKEN);
|
|
3307
|
+
this.toastService = inject(TOAST_ALERTS_TOKEN);
|
|
3308
|
+
this.cdr = inject(ChangeDetectorRef);
|
|
3607
3309
|
this.viewMode = 'cards';
|
|
3608
|
-
this.
|
|
3609
|
-
this.
|
|
3310
|
+
this.customCardComponent = input(undefined);
|
|
3311
|
+
this.showOptions = input(true);
|
|
3312
|
+
this.gridLayout = input(true);
|
|
3313
|
+
this.getCustomButtons = input();
|
|
3610
3314
|
this.agentCards = [];
|
|
3611
3315
|
this.cardEventSubs = [];
|
|
3612
3316
|
this.cardComponent = null;
|
|
@@ -3616,7 +3320,7 @@ class AgentCardListComponent extends PaginationBase {
|
|
|
3616
3320
|
// this.buttonActions = DefaultActions;
|
|
3617
3321
|
this.filterConfig.returnProps = { _id: 1, title: 1, assets: 1, description: 1, 'characterCard.data.description': 1, 'characterCard.data.name': 1 };
|
|
3618
3322
|
this.loadConversationCards();
|
|
3619
|
-
this.cardComponent = this.customCardComponent || DCConversationCardUIComponent;
|
|
3323
|
+
this.cardComponent = this.customCardComponent() || DCConversationCardUIComponent;
|
|
3620
3324
|
}
|
|
3621
3325
|
subscribeDinamicInstantToEvents() {
|
|
3622
3326
|
// Clear previous cardEventSubs
|
|
@@ -3626,9 +3330,12 @@ class AgentCardListComponent extends PaginationBase {
|
|
|
3626
3330
|
subscribeToCardEvents() {
|
|
3627
3331
|
this.outlets.forEach((outlet) => {
|
|
3628
3332
|
const instance = outlet.componentInstance;
|
|
3629
|
-
|
|
3630
|
-
|
|
3631
|
-
|
|
3333
|
+
if (instance && instance.onCardAction) {
|
|
3334
|
+
const subscription = instance.onCardAction.subscribe((action) => {
|
|
3335
|
+
this.onCardAction({ action: action.event, item: action.card });
|
|
3336
|
+
});
|
|
3337
|
+
this.cardEventSubs.push(subscription);
|
|
3338
|
+
}
|
|
3632
3339
|
});
|
|
3633
3340
|
}
|
|
3634
3341
|
clearcardEventSubs() {
|
|
@@ -3722,13 +3429,12 @@ class AgentCardListComponent extends PaginationBase {
|
|
|
3722
3429
|
this.doAction(actionEvent); // handle by father.
|
|
3723
3430
|
}
|
|
3724
3431
|
}
|
|
3725
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.
|
|
3726
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.
|
|
3432
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: AgentCardListComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
3433
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.4", type: AgentCardListComponent, isStandalone: true, selector: "dc-agent-card-lists", inputs: { viewMode: { classPropertyName: "viewMode", publicName: "viewMode", isSignal: false, isRequired: false, transformFunction: null }, customCardComponent: { classPropertyName: "customCardComponent", publicName: "customCardComponent", isSignal: true, isRequired: false, transformFunction: null }, showOptions: { classPropertyName: "showOptions", publicName: "showOptions", isSignal: true, isRequired: false, transformFunction: null }, gridLayout: { classPropertyName: "gridLayout", publicName: "gridLayout", isSignal: true, isRequired: false, transformFunction: null }, getCustomButtons: { classPropertyName: "getCustomButtons", publicName: "getCustomButtons", isSignal: true, isRequired: false, transformFunction: null } }, viewQueries: [{ propertyName: "outlets", predicate: ["outlet"], descendants: true }], usesInheritance: true, ngImport: i0, template: "<dc-filter-bar [isAdmin]=\"true\" (onFilterAction)=\"doFilterBarAction($event)\"></dc-filter-bar>\n\n@if(viewMode === 'table'){\n<app-quick-table [columns]=\"columns\" [tableData]=\"agentCards\" [actions]=\"actions()\" (onAction)=\"onCardAction($event)\"></app-quick-table>\n\n}@else{\n\n<div class=\"conversation-card-lists\">\n @if(!isLoading) {\n <div [ngClass]=\"{ 'cards-container': gridLayout() }\">\n @for (card of agentCards; track card) {\n <div style=\"position: relative\">\n <ng-container #outlet=\"ngComponentOutlet\" [ngComponentOutlet]=\"cardComponent\" [ngComponentOutletInputs]=\"{ card: card, showOptions: showOptions() }\">\n </ng-container>\n </div>\n }\n </div>\n }\n</div>\n\n@if(isLoading) {\n<div>\n <p-skeleton styleClass=\"mb-2\" />\n <p-skeleton width=\"10rem\" styleClass=\"mb-2\" />\n <p-skeleton width=\"5rem\" styleClass=\"mb-2\" />\n <p-skeleton height=\"2rem\" styleClass=\"mb-2\" />\n <p-skeleton width=\"10rem\" height=\"4rem\" />\n</div>\n} @if(agentCards.length === 0) {\n<div>\n <p>No conversations found or no connection with server</p>\n</div>\n} }\n\n<p-paginator\n currentPageReportTemplate=\"{{ totalRecords }} conversations\"\n [showCurrentPageReport]=\"true\"\n (onPageChange)=\"onPageChange($event)\"\n [first]=\"paginatorFirst\"\n [rows]=\"paginatorRows\"\n [totalRecords]=\"totalRecords\"\n [rowsPerPageOptions]=\"[10, 20, 30]\">\n</p-paginator>\n", styles: [":host{display:block;height:100%}.options-icon{cursor:pointer;position:absolute;top:2px;right:3px;font-size:1.2rem;color:#dde9e9;background-color:#4f486281;border-radius:50%;padding:5px;z-index:1000}.conversation-card-lists{padding:1.5rem;width:100%;height:100%;display:flex;flex-direction:column}.conversation-card-lists .cards-container{display:flex;flex-wrap:wrap;gap:2rem;width:100%;justify-content:center;flex:1;overflow-y:auto;min-height:0}.conversation-card-lists .cards-container>div{flex:0 0 240px}.conversation-card-lists .dc-card{position:relative;background:#fff;border-radius:8px;box-shadow:0 2px 4px #0000001a;padding:.5rem;transition:transform .2s ease,box-shadow .2s ease;display:flex;flex-direction:column;gap:2px}.conversation-card-lists .dc-card:hover{transform:translateY(-2px);box-shadow:0 4px 8px #00000026}.conversation-card-lists .dc-card .dc-card-header{position:absolute;top:10px;left:5px;border-radius:5px;padding:5px}.conversation-card-lists .dc-card .dc-card-header:before{content:\"\";position:absolute;inset:0;background-color:#4d30db81;filter:blur(2px);border-radius:5px;z-index:0}.conversation-card-lists .dc-card .dc-card-header h3{margin:0;font-size:1.25rem;font-weight:600;color:#ece7e7;position:relative;z-index:1}.conversation-card-lists .dc-card .dc-card-content{flex:1}.conversation-card-lists .dc-card .dc-card-content p{margin:0;color:#666;line-height:1.5}.conversation-card-lists .dc-card button{padding:.5rem 1rem;border:none;border-radius:4px;background-color:#007bff;color:#fff;cursor:pointer;font-weight:500;transition:background-color .2s ease}.conversation-card-lists .dc-card button:hover{background-color:#0056b3}.conversation-card-lists .dc-card button:active{transform:translateY(1px)}:host{display:flex;flex-direction:column;height:100%}p-paginator{margin-top:1rem;flex-shrink:0}\n"], dependencies: [{ kind: "directive", type: NgComponentOutlet, selector: "[ngComponentOutlet]", inputs: ["ngComponentOutlet", "ngComponentOutletInputs", "ngComponentOutletInjector", "ngComponentOutletContent", "ngComponentOutletNgModule", "ngComponentOutletNgModuleFactory"], exportAs: ["ngComponentOutlet"] }, { kind: "ngmodule", type: PopoverModule }, { kind: "ngmodule", type: ButtonModule }, { kind: "ngmodule", type: PaginatorModule }, { kind: "component", type: i1$3.Paginator, selector: "p-paginator", inputs: ["pageLinkSize", "style", "styleClass", "alwaysShow", "dropdownAppendTo", "templateLeft", "templateRight", "appendTo", "dropdownScrollHeight", "currentPageReportTemplate", "showCurrentPageReport", "showFirstLastIcon", "totalRecords", "rows", "rowsPerPageOptions", "showJumpToPageDropdown", "showJumpToPageInput", "jumpToPageItemTemplate", "showPageLinks", "locale", "dropdownItemTemplate", "first"], outputs: ["onPageChange"] }, { kind: "component", type: DCFilterBarComponent, selector: "dc-filter-bar", inputs: ["isAdmin", "customFilters", "items"], outputs: ["onFilterAction", "onChangeSort"] }, { kind: "ngmodule", type: SkeletonModule }, { kind: "component", type: i1$2.Skeleton, selector: "p-skeleton", inputs: ["styleClass", "style", "shape", "animation", "borderRadius", "size", "width", "height"] }, { kind: "ngmodule", type: SpeedDialModule }, { kind: "component", type: QuickTableComponent, selector: "app-quick-table", inputs: ["onlyView", "columns", "tableData", "actions"], outputs: ["onAction"] }, { kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }] }); }
|
|
3727
3434
|
}
|
|
3728
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.
|
|
3435
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: AgentCardListComponent, decorators: [{
|
|
3729
3436
|
type: Component,
|
|
3730
3437
|
args: [{ selector: 'dc-agent-card-lists', imports: [
|
|
3731
|
-
CommonModule,
|
|
3732
3438
|
NgComponentOutlet,
|
|
3733
3439
|
PopoverModule,
|
|
3734
3440
|
ButtonModule,
|
|
@@ -3737,22 +3443,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.1.1", ngImpor
|
|
|
3737
3443
|
SkeletonModule,
|
|
3738
3444
|
SpeedDialModule,
|
|
3739
3445
|
QuickTableComponent,
|
|
3740
|
-
|
|
3741
|
-
|
|
3742
|
-
|
|
3743
|
-
args: [CONVERSATION_AI_TOKEN]
|
|
3744
|
-
}] }, { type: i6$1.ToastAlertsAbstractService, decorators: [{
|
|
3745
|
-
type: Inject,
|
|
3746
|
-
args: [TOAST_ALERTS_TOKEN]
|
|
3747
|
-
}] }, { type: i1$4.ActivatedRoute }, { type: i1$4.Router }, { type: i0.ChangeDetectorRef }], propDecorators: { viewMode: [{
|
|
3748
|
-
type: Input
|
|
3749
|
-
}], customCardComponent: [{
|
|
3750
|
-
type: Input
|
|
3751
|
-
}], showOptions: [{
|
|
3752
|
-
type: Input
|
|
3753
|
-
}], gridLayout: [{
|
|
3754
|
-
type: Input
|
|
3755
|
-
}], getCustomButtons: [{
|
|
3446
|
+
CommonModule,
|
|
3447
|
+
], standalone: true, template: "<dc-filter-bar [isAdmin]=\"true\" (onFilterAction)=\"doFilterBarAction($event)\"></dc-filter-bar>\n\n@if(viewMode === 'table'){\n<app-quick-table [columns]=\"columns\" [tableData]=\"agentCards\" [actions]=\"actions()\" (onAction)=\"onCardAction($event)\"></app-quick-table>\n\n}@else{\n\n<div class=\"conversation-card-lists\">\n @if(!isLoading) {\n <div [ngClass]=\"{ 'cards-container': gridLayout() }\">\n @for (card of agentCards; track card) {\n <div style=\"position: relative\">\n <ng-container #outlet=\"ngComponentOutlet\" [ngComponentOutlet]=\"cardComponent\" [ngComponentOutletInputs]=\"{ card: card, showOptions: showOptions() }\">\n </ng-container>\n </div>\n }\n </div>\n }\n</div>\n\n@if(isLoading) {\n<div>\n <p-skeleton styleClass=\"mb-2\" />\n <p-skeleton width=\"10rem\" styleClass=\"mb-2\" />\n <p-skeleton width=\"5rem\" styleClass=\"mb-2\" />\n <p-skeleton height=\"2rem\" styleClass=\"mb-2\" />\n <p-skeleton width=\"10rem\" height=\"4rem\" />\n</div>\n} @if(agentCards.length === 0) {\n<div>\n <p>No conversations found or no connection with server</p>\n</div>\n} }\n\n<p-paginator\n currentPageReportTemplate=\"{{ totalRecords }} conversations\"\n [showCurrentPageReport]=\"true\"\n (onPageChange)=\"onPageChange($event)\"\n [first]=\"paginatorFirst\"\n [rows]=\"paginatorRows\"\n [totalRecords]=\"totalRecords\"\n [rowsPerPageOptions]=\"[10, 20, 30]\">\n</p-paginator>\n", styles: [":host{display:block;height:100%}.options-icon{cursor:pointer;position:absolute;top:2px;right:3px;font-size:1.2rem;color:#dde9e9;background-color:#4f486281;border-radius:50%;padding:5px;z-index:1000}.conversation-card-lists{padding:1.5rem;width:100%;height:100%;display:flex;flex-direction:column}.conversation-card-lists .cards-container{display:flex;flex-wrap:wrap;gap:2rem;width:100%;justify-content:center;flex:1;overflow-y:auto;min-height:0}.conversation-card-lists .cards-container>div{flex:0 0 240px}.conversation-card-lists .dc-card{position:relative;background:#fff;border-radius:8px;box-shadow:0 2px 4px #0000001a;padding:.5rem;transition:transform .2s ease,box-shadow .2s ease;display:flex;flex-direction:column;gap:2px}.conversation-card-lists .dc-card:hover{transform:translateY(-2px);box-shadow:0 4px 8px #00000026}.conversation-card-lists .dc-card .dc-card-header{position:absolute;top:10px;left:5px;border-radius:5px;padding:5px}.conversation-card-lists .dc-card .dc-card-header:before{content:\"\";position:absolute;inset:0;background-color:#4d30db81;filter:blur(2px);border-radius:5px;z-index:0}.conversation-card-lists .dc-card .dc-card-header h3{margin:0;font-size:1.25rem;font-weight:600;color:#ece7e7;position:relative;z-index:1}.conversation-card-lists .dc-card .dc-card-content{flex:1}.conversation-card-lists .dc-card .dc-card-content p{margin:0;color:#666;line-height:1.5}.conversation-card-lists .dc-card button{padding:.5rem 1rem;border:none;border-radius:4px;background-color:#007bff;color:#fff;cursor:pointer;font-weight:500;transition:background-color .2s ease}.conversation-card-lists .dc-card button:hover{background-color:#0056b3}.conversation-card-lists .dc-card button:active{transform:translateY(1px)}:host{display:flex;flex-direction:column;height:100%}p-paginator{margin-top:1rem;flex-shrink:0}\n"] }]
|
|
3448
|
+
}], ctorParameters: () => [], propDecorators: { viewMode: [{
|
|
3756
3449
|
type: Input
|
|
3757
3450
|
}], outlets: [{
|
|
3758
3451
|
type: ViewChildren,
|
|
@@ -3773,10 +3466,10 @@ class ParseCardPipe {
|
|
|
3773
3466
|
const result = this.builderConversation.applyReplacements(text, parseDict);
|
|
3774
3467
|
return result;
|
|
3775
3468
|
}
|
|
3776
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.
|
|
3777
|
-
static { this.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "19.
|
|
3469
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: ParseCardPipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe }); }
|
|
3470
|
+
static { this.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "19.2.4", ngImport: i0, type: ParseCardPipe, isStandalone: true, name: "parseCard" }); }
|
|
3778
3471
|
}
|
|
3779
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.
|
|
3472
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: ParseCardPipe, decorators: [{
|
|
3780
3473
|
type: Pipe,
|
|
3781
3474
|
args: [{
|
|
3782
3475
|
name: 'parseCard',
|
|
@@ -3785,13 +3478,13 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.1.1", ngImpor
|
|
|
3785
3478
|
}] });
|
|
3786
3479
|
|
|
3787
3480
|
class DcAgentCardDetailsComponent {
|
|
3788
|
-
constructor(
|
|
3789
|
-
this.agentCardService =
|
|
3790
|
-
this.route =
|
|
3791
|
-
this.cdr =
|
|
3792
|
-
this.userDataExchange =
|
|
3481
|
+
constructor() {
|
|
3482
|
+
this.agentCardService = inject(CONVERSATION_AI_TOKEN);
|
|
3483
|
+
this.route = inject(ActivatedRoute);
|
|
3484
|
+
this.cdr = inject(ChangeDetectorRef);
|
|
3485
|
+
this.userDataExchange = inject(USER_DATA_EXCHANGE);
|
|
3793
3486
|
this.agentCardId = '';
|
|
3794
|
-
this.onStartConversation =
|
|
3487
|
+
this.onStartConversation = output();
|
|
3795
3488
|
this.showInfoLayer = false;
|
|
3796
3489
|
}
|
|
3797
3490
|
async ngOnInit() {
|
|
@@ -3817,22 +3510,14 @@ class DcAgentCardDetailsComponent {
|
|
|
3817
3510
|
this.showInfoLayer = !this.showInfoLayer;
|
|
3818
3511
|
this.cdr.markForCheck();
|
|
3819
3512
|
}
|
|
3820
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.
|
|
3821
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "
|
|
3513
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: DcAgentCardDetailsComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
3514
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.4", type: DcAgentCardDetailsComponent, isStandalone: true, selector: "dc-agent-card-details", inputs: { agentCardId: "agentCardId" }, outputs: { onStartConversation: "onStartConversation" }, ngImport: i0, template: "<div style=\"display: flex; justify-content: center; align-items: center\">\n <p-card>\n <div class=\"card-container\">\n <img class=\"card-image\" [src]=\"agentCard?.assets?.image?.url || 'assets/images/default_conversation_card.webp'\" alt=\"\" />\n\n <div class=\"info-button\" (click)=\"toggleInfoLayer()\">\n <p-button icon=\"pi pi-arrow-down-left\" [rounded]=\"true\" [raised]=\"true\" severity=\"primary\" [outlined]=\"true\" />\n </div>\n\n <div style=\"position: absolute; bottom: 20px; right: 50%; transform: translateX(50%); z-index: 3\">\n <p-button size=\"large\" label=\"Iniciar Conversaci\u00F3n\" [rounded]=\"true\" (click)=\"startConversation()\" />\n </div>\n\n <div class=\"info-layer\" [class.active]=\"showInfoLayer\">\n <div class=\"info-content\">\n <h1\n ><strong>{{ agentCard?.title }}</strong></h1\n >\n <p>{{ agentCard?.characterCard.data?.name }}</p>\n\n @if (agentCard?.characterCard.data?.scenario) {\n <div class=\"scenario\">\n <h4>Scenario</h4>\n <p>{{ agentCard?.characterCard.data.scenario | parseCard : agentCard }}</p>\n </div>\n }\n </div>\n </div>\n </div>\n </p-card>\n </div>\n\n <!-- <div class=\"dc-conversation-card-details\">\n <div class=\"header\">\n <h2>{{ agentCard?.title }}</h2>\n <span class=\"version\">v{{ agentCard?.version }}</span>\n </div>\n\n <div class=\"character-card\" *ngIf=\"agentCard?.characterCard\">\n <div class=\"character-header\">\n <h3>{{ agentCard?.characterCard.data.name }}</h3>\n <div class=\"tags\">\n <span class=\"tag\" *ngFor=\"let tag of agentCard?.characterCard.data?.tags\">{{ tag }}</span>\n </div>\n </div>\n\n <div class=\"image-wrapper\">\n <div class=\"image\">\n <img [src]=\"agentCard?.assets?.image?.url || 'assets/images/default_conversation_card.webp'\" alt=\"\" />\n <div class=\"actions\">\n <p-button label=\"Start Conversation\" (click)=\"startConversation()\"></p-button>\n </div>\n </div>\n </div>\n\n <div class=\"description\">\n <p>{{ agentCard?.characterCard.data?.description | parseCard : agentCard }}</p>\n </div>\n\n <div class=\"scenario\" *ngIf=\"agentCard?.characterCard.data?.scenario\">\n <h4>Scenario</h4>\n <p>{{ agentCard?.characterCard.data.scenario }}</p>\n </div>\n\n <div class=\"first-message\" *ngIf=\"agentCard?.characterCard.data?.first_mes\">\n <h4>First Message</h4>\n <p>{{ agentCard?.characterCard.data.first_mes }}</p>\n </div>\n\n <div class=\"alternate-greetings\" *ngIf=\"agentCard?.characterCard.data?.alternate_greetings?.length\">\n <h4>Alternate Greetings</h4>\n <ul>\n <li *ngFor=\"let greeting of agentCard?.characterCard.data.alternate_greetings\">{{ greeting }}</li>\n </ul>\n </div>\n </div>\n\n <div class=\"settings\">\n <div class=\"conversation-settings\">\n <h3>Conversation Settings</h3>\n <div class=\"setting-item\">\n <span class=\"label\">Type:</span>\n <span class=\"value\">{{ agentCard?.conversationSettings?.conversationType }}</span>\n </div>\n <div class=\"setting-item\">\n <span class=\"label\">Language:</span>\n <span class=\"value\">{{ agentCard?.lang }}</span>\n </div>\n <div class=\"setting-item\">\n <span class=\"label\">Text Engine:</span>\n <span class=\"value\">{{ agentCard?.conversationSettings?.textEngine }}</span>\n </div>\n </div>\n\n <div class=\"tts-settings\" *ngIf=\"agentCard?.tts\">\n <h3>TTS Settings</h3>\n <div class=\"setting-item\">\n <span class=\"label\">Primary Voice:</span>\n <span class=\"value\">{{ agentCard?.tts.voice }}</span>\n </div>\n <div class=\"setting-item\">\n <span class=\"label\">Secondary Voice:</span>\n <span class=\"value\">{{ agentCard?.tts.secondaryVoice }}</span>\n </div>\n <div class=\"setting-item\">\n <span class=\"label\">Speed:</span>\n <span class=\"value\">{{ agentCard?.tts.speed }} ({{ agentCard?.tts.speedRate }}x)</span>\n </div>\n </div>\n </div>\n</div> -->\n", styles: ["::ng-deep .p-card{width:420px;height:700px}::ng-deep .p-card .p-card-body{width:100%;height:100%}.card-image{height:100%;width:100%;object-fit:cover;object-position:center;position:absolute;top:0;left:0;transition:filter .3s ease}.info-button{position:absolute;top:15px;right:15px;z-index:3}.info-button:hover{transform:scale(1.1)}.info-layer{height:100%;width:100%;position:absolute;top:0;left:0;display:flex;justify-content:center;align-items:center;z-index:2;-webkit-backdrop-filter:blur(3px);backdrop-filter:blur(3px);background-color:#ed122833;color:#fff;opacity:1;clip-path:circle(0% at top right);transition:clip-path .5s cubic-bezier(.25,1,.5,1);pointer-events:none}.info-layer.active{clip-path:circle(150% at top right);pointer-events:auto}.info-content{padding:15px;text-align:center;max-width:90%}.info-content h1{margin-top:0;font-size:18px;margin-bottom:10px}.info-content p{font-size:12px;margin:0}\n"], dependencies: [{ kind: "ngmodule", type: ButtonModule }, { kind: "component", type: i2$1.Button, selector: "p-button", inputs: ["type", "iconPos", "icon", "badge", "label", "disabled", "loading", "loadingIcon", "raised", "rounded", "text", "plain", "severity", "outlined", "link", "tabindex", "size", "variant", "style", "styleClass", "badgeClass", "badgeSeverity", "ariaLabel", "autofocus", "fluid", "buttonProps"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "ngmodule", type: CardModule }, { kind: "component", type: i2$5.Card, selector: "p-card", inputs: ["header", "subheader", "style", "styleClass"] }, { kind: "pipe", type: ParseCardPipe, name: "parseCard" }] }); }
|
|
3822
3515
|
}
|
|
3823
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.
|
|
3516
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: DcAgentCardDetailsComponent, decorators: [{
|
|
3824
3517
|
type: Component,
|
|
3825
|
-
args: [{ selector: 'dc-agent-card-details', standalone: true, imports: [
|
|
3826
|
-
}], ctorParameters: () => [
|
|
3827
|
-
type: Inject,
|
|
3828
|
-
args: [CONVERSATION_AI_TOKEN]
|
|
3829
|
-
}] }, { type: i1$4.ActivatedRoute }, { type: i0.ChangeDetectorRef }, { type: UserDataExchangeAbstractService, decorators: [{
|
|
3830
|
-
type: Inject,
|
|
3831
|
-
args: [USER_DATA_EXCHANGE]
|
|
3832
|
-
}] }], propDecorators: { agentCardId: [{
|
|
3518
|
+
args: [{ selector: 'dc-agent-card-details', standalone: true, imports: [ButtonModule, CardModule, ParseCardPipe], template: "<div style=\"display: flex; justify-content: center; align-items: center\">\n <p-card>\n <div class=\"card-container\">\n <img class=\"card-image\" [src]=\"agentCard?.assets?.image?.url || 'assets/images/default_conversation_card.webp'\" alt=\"\" />\n\n <div class=\"info-button\" (click)=\"toggleInfoLayer()\">\n <p-button icon=\"pi pi-arrow-down-left\" [rounded]=\"true\" [raised]=\"true\" severity=\"primary\" [outlined]=\"true\" />\n </div>\n\n <div style=\"position: absolute; bottom: 20px; right: 50%; transform: translateX(50%); z-index: 3\">\n <p-button size=\"large\" label=\"Iniciar Conversaci\u00F3n\" [rounded]=\"true\" (click)=\"startConversation()\" />\n </div>\n\n <div class=\"info-layer\" [class.active]=\"showInfoLayer\">\n <div class=\"info-content\">\n <h1\n ><strong>{{ agentCard?.title }}</strong></h1\n >\n <p>{{ agentCard?.characterCard.data?.name }}</p>\n\n @if (agentCard?.characterCard.data?.scenario) {\n <div class=\"scenario\">\n <h4>Scenario</h4>\n <p>{{ agentCard?.characterCard.data.scenario | parseCard : agentCard }}</p>\n </div>\n }\n </div>\n </div>\n </div>\n </p-card>\n </div>\n\n <!-- <div class=\"dc-conversation-card-details\">\n <div class=\"header\">\n <h2>{{ agentCard?.title }}</h2>\n <span class=\"version\">v{{ agentCard?.version }}</span>\n </div>\n\n <div class=\"character-card\" *ngIf=\"agentCard?.characterCard\">\n <div class=\"character-header\">\n <h3>{{ agentCard?.characterCard.data.name }}</h3>\n <div class=\"tags\">\n <span class=\"tag\" *ngFor=\"let tag of agentCard?.characterCard.data?.tags\">{{ tag }}</span>\n </div>\n </div>\n\n <div class=\"image-wrapper\">\n <div class=\"image\">\n <img [src]=\"agentCard?.assets?.image?.url || 'assets/images/default_conversation_card.webp'\" alt=\"\" />\n <div class=\"actions\">\n <p-button label=\"Start Conversation\" (click)=\"startConversation()\"></p-button>\n </div>\n </div>\n </div>\n\n <div class=\"description\">\n <p>{{ agentCard?.characterCard.data?.description | parseCard : agentCard }}</p>\n </div>\n\n <div class=\"scenario\" *ngIf=\"agentCard?.characterCard.data?.scenario\">\n <h4>Scenario</h4>\n <p>{{ agentCard?.characterCard.data.scenario }}</p>\n </div>\n\n <div class=\"first-message\" *ngIf=\"agentCard?.characterCard.data?.first_mes\">\n <h4>First Message</h4>\n <p>{{ agentCard?.characterCard.data.first_mes }}</p>\n </div>\n\n <div class=\"alternate-greetings\" *ngIf=\"agentCard?.characterCard.data?.alternate_greetings?.length\">\n <h4>Alternate Greetings</h4>\n <ul>\n <li *ngFor=\"let greeting of agentCard?.characterCard.data.alternate_greetings\">{{ greeting }}</li>\n </ul>\n </div>\n </div>\n\n <div class=\"settings\">\n <div class=\"conversation-settings\">\n <h3>Conversation Settings</h3>\n <div class=\"setting-item\">\n <span class=\"label\">Type:</span>\n <span class=\"value\">{{ agentCard?.conversationSettings?.conversationType }}</span>\n </div>\n <div class=\"setting-item\">\n <span class=\"label\">Language:</span>\n <span class=\"value\">{{ agentCard?.lang }}</span>\n </div>\n <div class=\"setting-item\">\n <span class=\"label\">Text Engine:</span>\n <span class=\"value\">{{ agentCard?.conversationSettings?.textEngine }}</span>\n </div>\n </div>\n\n <div class=\"tts-settings\" *ngIf=\"agentCard?.tts\">\n <h3>TTS Settings</h3>\n <div class=\"setting-item\">\n <span class=\"label\">Primary Voice:</span>\n <span class=\"value\">{{ agentCard?.tts.voice }}</span>\n </div>\n <div class=\"setting-item\">\n <span class=\"label\">Secondary Voice:</span>\n <span class=\"value\">{{ agentCard?.tts.secondaryVoice }}</span>\n </div>\n <div class=\"setting-item\">\n <span class=\"label\">Speed:</span>\n <span class=\"value\">{{ agentCard?.tts.speed }} ({{ agentCard?.tts.speedRate }}x)</span>\n </div>\n </div>\n </div>\n</div> -->\n", styles: ["::ng-deep .p-card{width:420px;height:700px}::ng-deep .p-card .p-card-body{width:100%;height:100%}.card-image{height:100%;width:100%;object-fit:cover;object-position:center;position:absolute;top:0;left:0;transition:filter .3s ease}.info-button{position:absolute;top:15px;right:15px;z-index:3}.info-button:hover{transform:scale(1.1)}.info-layer{height:100%;width:100%;position:absolute;top:0;left:0;display:flex;justify-content:center;align-items:center;z-index:2;-webkit-backdrop-filter:blur(3px);backdrop-filter:blur(3px);background-color:#ed122833;color:#fff;opacity:1;clip-path:circle(0% at top right);transition:clip-path .5s cubic-bezier(.25,1,.5,1);pointer-events:none}.info-layer.active{clip-path:circle(150% at top right);pointer-events:auto}.info-content{padding:15px;text-align:center;max-width:90%}.info-content h1{margin-top:0;font-size:18px;margin-bottom:10px}.info-content p{font-size:12px;margin:0}\n"] }]
|
|
3519
|
+
}], ctorParameters: () => [], propDecorators: { agentCardId: [{
|
|
3833
3520
|
type: Input
|
|
3834
|
-
}], onStartConversation: [{
|
|
3835
|
-
type: Output
|
|
3836
3521
|
}] } });
|
|
3837
3522
|
|
|
3838
3523
|
/*
|
|
@@ -3844,5 +3529,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.1.1", ngImpor
|
|
|
3844
3529
|
* Generated bundle index. Do not edit.
|
|
3845
3530
|
*/
|
|
3846
3531
|
|
|
3847
|
-
export { AgentCardListComponent, AgentCardsAbstractService, AudioService, AudioSpeed, CONVERSATION_AI_TOKEN,
|
|
3532
|
+
export { AgentCardListComponent, AgentCardsAbstractService, AudioService, AudioSpeed, CONVERSATION_AI_TOKEN, ChatMessage, ChatMultiMessage, ChatRole, ChatUserSettings, ConversationDTO, ConversationMessagesDTO, ConversationType, ConversationTypeOptions, DCAgentCardFormComponent, DCChatComponent, DCConversationCardUIComponent, DCConversationPromptBuilderService, DcAgentCardDetailsComponent, DefaultEvaluatorAgentCard, EAccountsPlatform, LangCodeDescriptionEs, MessageAudio, ProviderSelectorComponent, StandaloneAudioTextSyncComponent, TextEngineOptions, TextEngines, USER_DATA_EXCHANGE, UserDataExchangeAbstractService, VoiceTTSOption, VoiceTTSOptions, WordTimestamps, buildObjectTTSRequest, characterCardStringDataDefinition, defaultconvUserSettings, extractAudioAndTranscription, extractJsonFromResponse, matchTranscription, provideChatAIService, provideUserDataExchange };
|
|
3848
3533
|
//# sourceMappingURL=dataclouder-ngx-agent-cards.mjs.map
|