@dataclouder/ngx-agent-cards 0.0.88 → 0.0.90
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/fesm2022/dataclouder-ngx-agent-cards.mjs +463 -671
- package/fesm2022/dataclouder-ngx-agent-cards.mjs.map +1 -1
- package/lib/components/chat-container/chat-container.component.d.ts +5 -19
- package/lib/components/chat-container/chat-footer/chat-footer.component.d.ts +7 -2
- package/lib/components/chat-container/chat-messages-list/chat-message/chat-message.component.d.ts +5 -28
- package/lib/components/chat-container/chat-messages-list/chat-message/chat-message.utils.d.ts +1 -1
- package/lib/components/chat-container/chat-messages-list/chat-messages-list.component.d.ts +8 -5
- package/lib/components/chat-container/chat-messages-list/message-orchestrator/message-orchestrator.component.d.ts +26 -0
- package/lib/components/chat-settings/dc-conversation-userchat-settings.component.d.ts +2 -3
- package/lib/components/icons/icons.component.d.ts +1 -1
- package/lib/components/{standalone-audio-text-sync/standalone-audio-text-sync.component.d.ts → text-highlighter/text-highlighter.d.ts} +10 -10
- package/lib/models/agent.models.d.ts +4 -7
- package/lib/models/conversation-ai.class.d.ts +3 -3
- package/lib/models/user-data-exchange.d.ts +2 -0
- package/lib/pipes/safe-json.pipe.d.ts +15 -0
- package/lib/services/conversation.service.d.ts +10 -13
- package/lib/services/dc-conversation-builder.service.d.ts +0 -2
- package/lib/services/message-processing.service.d.ts +2 -2
- package/package.json +1 -1
- package/public-api.d.ts +3 -1
- package/lib/components/chat-container/chat-messages-list/chat-message/multi-message-content/multi-message-content.d.ts +0 -33
- package/lib/services/audio-text-sync.service.d.ts +0 -57
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import * as i0 from '@angular/core';
|
|
2
|
-
import { InjectionToken, Injectable, inject, input, output, Input, Component, signal,
|
|
2
|
+
import { InjectionToken, Injectable, inject, Pipe, input, output, Input, Component, signal, computed, ChangeDetectorRef, DestroyRef, effect, ChangeDetectionStrategy, ElementRef, ViewChild, ViewChildren } from '@angular/core';
|
|
3
3
|
import * as i1$1 from '@angular/common';
|
|
4
4
|
import { CommonModule, DatePipe, DecimalPipe, NgComponentOutlet } from '@angular/common';
|
|
5
5
|
import { DynamicDialogRef, DialogService, DynamicDialogConfig, DynamicDialogModule } from 'primeng/dynamicdialog';
|
|
6
|
-
import * as
|
|
6
|
+
import * as i1$3 from 'primeng/dialog';
|
|
7
7
|
import { DialogModule } from 'primeng/dialog';
|
|
8
8
|
import * as i2 from 'primeng/progressbar';
|
|
9
9
|
import { ProgressBarModule } from 'primeng/progressbar';
|
|
@@ -14,11 +14,11 @@ import * as i3 from 'primeng/textarea';
|
|
|
14
14
|
import { TextareaModule } from 'primeng/textarea';
|
|
15
15
|
import * as i2$1 from 'primeng/button';
|
|
16
16
|
import { ButtonModule } from 'primeng/button';
|
|
17
|
+
import { AudioSpeed as AudioSpeed$1, MicVadComponent, TOAST_ALERTS_TOKEN, PaginationBase, DCFilterBarComponent, QuickTableComponent } from '@dataclouder/ngx-core';
|
|
17
18
|
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
|
|
18
|
-
import {
|
|
19
|
+
import { Subject, fromEvent, filter } from 'rxjs';
|
|
19
20
|
import { takeUntil, map } from 'rxjs/operators';
|
|
20
21
|
import { DomSanitizer } from '@angular/platform-browser';
|
|
21
|
-
import { AudioSpeed as AudioSpeed$1, TOAST_ALERTS_TOKEN, PaginationBase, DCFilterBarComponent, QuickTableComponent } from '@dataclouder/ngx-core';
|
|
22
22
|
import * as i1$2 from 'primeng/skeleton';
|
|
23
23
|
import { SkeletonModule } from 'primeng/skeleton';
|
|
24
24
|
import * as i2$3 from 'primeng/checkbox';
|
|
@@ -49,14 +49,14 @@ import * as i9 from 'primeng/select';
|
|
|
49
49
|
import { SelectModule } from 'primeng/select';
|
|
50
50
|
import * as i10 from 'primeng/popover';
|
|
51
51
|
import { PopoverModule } from 'primeng/popover';
|
|
52
|
-
import * as i2$
|
|
52
|
+
import * as i2$4 from 'primeng/card';
|
|
53
53
|
import { CardModule } from 'primeng/card';
|
|
54
54
|
import * as i3$2 from 'primeng/dropdown';
|
|
55
55
|
import { DropdownModule } from 'primeng/dropdown';
|
|
56
56
|
import { ChipModule } from 'primeng/chip';
|
|
57
|
-
import * as i1$
|
|
57
|
+
import * as i1$4 from 'primeng/paginator';
|
|
58
58
|
import { PaginatorModule } from 'primeng/paginator';
|
|
59
|
-
import * as i2$
|
|
59
|
+
import * as i2$5 from 'primeng/speeddial';
|
|
60
60
|
import { SpeedDialModule } from 'primeng/speeddial';
|
|
61
61
|
|
|
62
62
|
const characterCardStringDataDefinition = `
|
|
@@ -83,8 +83,6 @@ class WordTimestamps {
|
|
|
83
83
|
}
|
|
84
84
|
class MessageAudio {
|
|
85
85
|
}
|
|
86
|
-
class ChatMultiMessage extends MessageAudio {
|
|
87
|
-
}
|
|
88
86
|
var ChatRole;
|
|
89
87
|
(function (ChatRole) {
|
|
90
88
|
ChatRole["System"] = "system";
|
|
@@ -536,13 +534,13 @@ class DCConversationPromptBuilderService {
|
|
|
536
534
|
}
|
|
537
535
|
// For chat conversation i need inital settings.
|
|
538
536
|
buildConversationSettings(agentCard, parseDict = null) {
|
|
539
|
-
const converstionSettings = agentCard?.conversationSettings;
|
|
537
|
+
const converstionSettings = agentCard?.conversationSettings || {};
|
|
540
538
|
converstionSettings.messages = this.buildConversationMessages(agentCard, parseDict);
|
|
541
539
|
converstionSettings.last_prompt = this.getJailBrakePrompt(agentCard);
|
|
542
540
|
return converstionSettings;
|
|
543
541
|
}
|
|
544
542
|
buildConversationMessages(agentCard, parseDict = null) {
|
|
545
|
-
const initialConversation = this.buildInitialConversation(agentCard.characterCard, agentCard
|
|
543
|
+
const initialConversation = this.buildInitialConversation(agentCard.characterCard, agentCard?.conversationSettings?.conversationType);
|
|
546
544
|
parseDict = this.getDefaultParseDict(parseDict, agentCard);
|
|
547
545
|
// Si quiero agregar todo tipo de info, el parse dict es algo que debe hacer el cliente.
|
|
548
546
|
this.parseConversation(initialConversation, parseDict);
|
|
@@ -609,7 +607,7 @@ class DCConversationPromptBuilderService {
|
|
|
609
607
|
}
|
|
610
608
|
return prompt;
|
|
611
609
|
}
|
|
612
|
-
buildInitialConversation(characterCard, conversationType) {
|
|
610
|
+
buildInitialConversation(characterCard, conversationType = ConversationType.General) {
|
|
613
611
|
let systemPromptInstructions = this.getDefaultPromptByType(conversationType);
|
|
614
612
|
if (characterCard?.data?.system_prompt) {
|
|
615
613
|
systemPromptInstructions = characterCard.data.system_prompt;
|
|
@@ -682,7 +680,59 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImpor
|
|
|
682
680
|
args: [{
|
|
683
681
|
providedIn: 'root',
|
|
684
682
|
}]
|
|
685
|
-
}]
|
|
683
|
+
}] });
|
|
684
|
+
|
|
685
|
+
/**
|
|
686
|
+
* SafeJsonPipe - A pipe that safely stringifies objects with circular references
|
|
687
|
+
* This pipe handles circular references and DOM objects that can't be serialized
|
|
688
|
+
*/
|
|
689
|
+
class SafeJsonPipe {
|
|
690
|
+
transform(value) {
|
|
691
|
+
// Create a new object with only serializable properties
|
|
692
|
+
return this.stringifySafely(value);
|
|
693
|
+
}
|
|
694
|
+
/**
|
|
695
|
+
* Safely stringify an object, handling circular references
|
|
696
|
+
*/
|
|
697
|
+
stringifySafely(obj) {
|
|
698
|
+
const seen = new WeakSet();
|
|
699
|
+
return JSON.stringify(obj, (key, value) => {
|
|
700
|
+
// Skip properties that start with underscore (often internal properties)
|
|
701
|
+
if (key.startsWith('_')) {
|
|
702
|
+
return undefined;
|
|
703
|
+
}
|
|
704
|
+
// Handle DOM elements and other non-serializable objects
|
|
705
|
+
if (value instanceof HTMLElement || value instanceof Node) {
|
|
706
|
+
return '[DOM Element]';
|
|
707
|
+
}
|
|
708
|
+
// Handle audio elements specifically
|
|
709
|
+
if (key === 'audioHtml' || key === 'audioElement') {
|
|
710
|
+
return '[Audio Element]';
|
|
711
|
+
}
|
|
712
|
+
// Handle functions
|
|
713
|
+
if (typeof value === 'function') {
|
|
714
|
+
return '[Function]';
|
|
715
|
+
}
|
|
716
|
+
// Handle circular references
|
|
717
|
+
if (typeof value === 'object' && value !== null) {
|
|
718
|
+
if (seen.has(value)) {
|
|
719
|
+
return '[Circular Reference]';
|
|
720
|
+
}
|
|
721
|
+
seen.add(value);
|
|
722
|
+
}
|
|
723
|
+
return value;
|
|
724
|
+
}, 2);
|
|
725
|
+
}
|
|
726
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: SafeJsonPipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe }); }
|
|
727
|
+
static { this.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "19.2.4", ngImport: i0, type: SafeJsonPipe, isStandalone: true, name: "safeJson" }); }
|
|
728
|
+
}
|
|
729
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: SafeJsonPipe, decorators: [{
|
|
730
|
+
type: Pipe,
|
|
731
|
+
args: [{
|
|
732
|
+
name: 'safeJson',
|
|
733
|
+
standalone: true
|
|
734
|
+
}]
|
|
735
|
+
}] });
|
|
686
736
|
|
|
687
737
|
class ChatHeaderComponent {
|
|
688
738
|
constructor() {
|
|
@@ -932,22 +982,14 @@ function removeAllEmojis(text) {
|
|
|
932
982
|
class MessageProcessingService {
|
|
933
983
|
constructor() { }
|
|
934
984
|
// Process message for display
|
|
935
|
-
processMessage(message, conversationSettings
|
|
985
|
+
processMessage(message, conversationSettings) {
|
|
936
986
|
const defaultVoice = this.getVoice(conversationSettings.voice);
|
|
937
987
|
let processedMessage;
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
message.
|
|
941
|
-
|
|
942
|
-
}
|
|
943
|
-
else {
|
|
944
|
-
// Create a new message object
|
|
945
|
-
processedMessage = {
|
|
946
|
-
content: message.content,
|
|
947
|
-
role: message.role,
|
|
948
|
-
voice: defaultVoice
|
|
949
|
-
};
|
|
950
|
-
}
|
|
988
|
+
processedMessage = {
|
|
989
|
+
content: message.content,
|
|
990
|
+
role: message.role,
|
|
991
|
+
voice: defaultVoice,
|
|
992
|
+
};
|
|
951
993
|
// Process based on text engine
|
|
952
994
|
if (conversationSettings.textEngine === TextEngines.MarkdownMultiMessages) {
|
|
953
995
|
this.processMultiMessages(processedMessage, conversationSettings);
|
|
@@ -959,7 +1001,8 @@ class MessageProcessingService {
|
|
|
959
1001
|
const content = this.subsItalicsByTag(processedMessage.content, conversationSettings.secondaryVoice);
|
|
960
1002
|
processedMessage.ssml = '<speak>' + content + '</speak>';
|
|
961
1003
|
}
|
|
962
|
-
|
|
1004
|
+
console.log(processedMessage);
|
|
1005
|
+
return { ...message, ...processedMessage };
|
|
963
1006
|
}
|
|
964
1007
|
// Process multi-messages from markdown
|
|
965
1008
|
processMultiMessages(message, settings) {
|
|
@@ -978,7 +1021,7 @@ class MessageProcessingService {
|
|
|
978
1021
|
audioUrl: null,
|
|
979
1022
|
audioPromise: null,
|
|
980
1023
|
text: val.text,
|
|
981
|
-
tag: val.tag
|
|
1024
|
+
tag: val.tag,
|
|
982
1025
|
};
|
|
983
1026
|
});
|
|
984
1027
|
}
|
|
@@ -1022,64 +1065,131 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImpor
|
|
|
1022
1065
|
}]
|
|
1023
1066
|
}], ctorParameters: () => [] });
|
|
1024
1067
|
|
|
1068
|
+
function matchTranscription(originalText, transcription) {
|
|
1069
|
+
const result = [];
|
|
1070
|
+
const transcriptionMap = new Map();
|
|
1071
|
+
// Create a map of lowercase words to an array of their corresponding objects in transcription
|
|
1072
|
+
for (const obj of transcription) {
|
|
1073
|
+
const lowercaseWord = obj.word.trim().toLowerCase();
|
|
1074
|
+
if (transcriptionMap.has(lowercaseWord)) {
|
|
1075
|
+
transcriptionMap.get(lowercaseWord).push(obj);
|
|
1076
|
+
}
|
|
1077
|
+
else {
|
|
1078
|
+
transcriptionMap.set(lowercaseWord, [obj]);
|
|
1079
|
+
}
|
|
1080
|
+
}
|
|
1081
|
+
// Split the original text into an array of words
|
|
1082
|
+
const words = originalText.split(' ');
|
|
1083
|
+
let currentTime = 0;
|
|
1084
|
+
for (const word of words) {
|
|
1085
|
+
const lowercaseWord = word.toLowerCase();
|
|
1086
|
+
const matchedObjs = transcriptionMap.get(lowercaseWord);
|
|
1087
|
+
if (matchedObjs && matchedObjs.length > 0) {
|
|
1088
|
+
const matchedObj = matchedObjs.shift();
|
|
1089
|
+
result.push({
|
|
1090
|
+
word: word,
|
|
1091
|
+
start: Number(matchedObj.start.toFixed(3)),
|
|
1092
|
+
end: Number(matchedObj.end.toFixed(3)),
|
|
1093
|
+
});
|
|
1094
|
+
currentTime = matchedObj.end;
|
|
1095
|
+
}
|
|
1096
|
+
else {
|
|
1097
|
+
result.push({
|
|
1098
|
+
word: word,
|
|
1099
|
+
start: Number(currentTime.toFixed(3)),
|
|
1100
|
+
end: Number(currentTime.toFixed(3)),
|
|
1101
|
+
});
|
|
1102
|
+
}
|
|
1103
|
+
}
|
|
1104
|
+
return result;
|
|
1105
|
+
}
|
|
1106
|
+
function extractAudioAndTranscription(message, audio) {
|
|
1107
|
+
message.audioUrl = audio.blobUrl;
|
|
1108
|
+
message.transcription = audio.transcription;
|
|
1109
|
+
if (message.transcription) {
|
|
1110
|
+
message.transcriptionTimestamps = matchTranscription(message.text || message.content, message.transcription.words);
|
|
1111
|
+
}
|
|
1112
|
+
return message;
|
|
1113
|
+
}
|
|
1114
|
+
function buildObjectTTSRequest(message, settings = {}) {
|
|
1115
|
+
const generateTranscription = settings?.highlightWords ?? false;
|
|
1116
|
+
const speedRate = settings?.speedRate || 0;
|
|
1117
|
+
const speed = typeof settings?.speed === 'string' ? settings.speed : AudioSpeed$1.Regular;
|
|
1118
|
+
const text = removeEmojis(message.text || message.content);
|
|
1119
|
+
return {
|
|
1120
|
+
text,
|
|
1121
|
+
voice: message.voice || settings.voice,
|
|
1122
|
+
generateTranscription,
|
|
1123
|
+
speedRate,
|
|
1124
|
+
speed,
|
|
1125
|
+
};
|
|
1126
|
+
}
|
|
1127
|
+
|
|
1025
1128
|
class ConversationService {
|
|
1026
1129
|
constructor() {
|
|
1130
|
+
// Services
|
|
1027
1131
|
this.agentCardService = inject(CONVERSATION_AI_TOKEN);
|
|
1028
1132
|
this.messageProcessingService = inject(MessageProcessingService);
|
|
1133
|
+
this.conversationBuilder = inject(DCConversationPromptBuilderService);
|
|
1134
|
+
this.userDataExchange = inject(USER_DATA_EXCHANGE);
|
|
1135
|
+
// Signals
|
|
1029
1136
|
this.messagesSignal = signal([]);
|
|
1030
1137
|
this.isThinkingSignal = signal(false);
|
|
1031
1138
|
this.conversationSettingsSignal = signal(null);
|
|
1032
|
-
this.agentCardSignal = signal(null);
|
|
1033
1139
|
this.isDestroyedSignal = signal(false);
|
|
1034
1140
|
}
|
|
1035
1141
|
// Get messages as a signal
|
|
1036
|
-
|
|
1142
|
+
getMessagesSignal() {
|
|
1037
1143
|
return this.messagesSignal;
|
|
1038
1144
|
}
|
|
1145
|
+
// Add message to conversation
|
|
1146
|
+
addMessage(message) {
|
|
1147
|
+
this.messagesSignal.update((messages) => [...messages, message]);
|
|
1148
|
+
}
|
|
1039
1149
|
// Get thinking state as a signal
|
|
1040
1150
|
isThinking() {
|
|
1041
1151
|
return this.isThinkingSignal;
|
|
1042
1152
|
}
|
|
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
1153
|
// Set destroyed state
|
|
1052
1154
|
setDestroyed(value) {
|
|
1053
1155
|
this.isDestroyedSignal.set(value);
|
|
1054
1156
|
}
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
this.agentCardSignal.set(agentCard);
|
|
1061
|
-
// Build conversation settings
|
|
1062
|
-
const conversationSettings = conversationBuilder.buildConversationSettings(agentCard, parseDict);
|
|
1157
|
+
setupConversationWithAgentCard(agentCard) {
|
|
1158
|
+
const conversationSettings = this.conversationBuilder.buildConversationSettings(agentCard);
|
|
1159
|
+
this.conversationSettingsSignal.set(conversationSettings);
|
|
1160
|
+
}
|
|
1161
|
+
async initConversationWithSettings(conversationSettings) {
|
|
1063
1162
|
this.conversationSettingsSignal.set(conversationSettings);
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
this.messagesSignal.set(conversationSettings.messages);
|
|
1163
|
+
await this.initConversation();
|
|
1164
|
+
}
|
|
1165
|
+
async initConversation() {
|
|
1166
|
+
const conversationSettings = this.conversationSettingsSignal();
|
|
1167
|
+
for (const i in conversationSettings.messages) {
|
|
1168
|
+
conversationSettings.messages[i].messageId = 'msg_' + i;
|
|
1169
|
+
}
|
|
1072
1170
|
// Find first assistant message
|
|
1073
|
-
const firstAssistantMsg = conversationSettings
|
|
1171
|
+
const firstAssistantMsg = conversationSettings?.messages.find((message) => message.role === ChatRole.Assistant);
|
|
1172
|
+
this.messagesSignal.set(conversationSettings?.messages || []);
|
|
1074
1173
|
if (firstAssistantMsg) {
|
|
1075
1174
|
// Process the first assistant message
|
|
1076
|
-
this.
|
|
1175
|
+
const processedMessage = this.messageProcessingService.processMessage(firstAssistantMsg, this.conversationSettingsSignal());
|
|
1176
|
+
// Find the index of the message with the matching ID
|
|
1177
|
+
const messageIndex = conversationSettings.messages.findIndex((message) => message.messageId === firstAssistantMsg.messageId);
|
|
1178
|
+
// If found, replace the message at that index
|
|
1179
|
+
if (messageIndex !== -1) {
|
|
1180
|
+
conversationSettings.messages[messageIndex] = processedMessage;
|
|
1181
|
+
}
|
|
1077
1182
|
}
|
|
1078
|
-
else if (
|
|
1183
|
+
else if (conversationSettings?.autoStart) {
|
|
1079
1184
|
// Auto-start conversation if configured
|
|
1080
1185
|
await this.sendCurrentConversation();
|
|
1081
1186
|
}
|
|
1082
1187
|
}
|
|
1188
|
+
// Initialize conversation
|
|
1189
|
+
async initConversationWithAgentCard(agentCard) {
|
|
1190
|
+
this.setupConversationWithAgentCard(agentCard);
|
|
1191
|
+
await this.initConversation();
|
|
1192
|
+
}
|
|
1083
1193
|
// Send user message
|
|
1084
1194
|
async sendUserMessage(message) {
|
|
1085
1195
|
if (this.isThinkingSignal()) {
|
|
@@ -1090,19 +1200,6 @@ class ConversationService {
|
|
|
1090
1200
|
// Set thinking state
|
|
1091
1201
|
this.isThinkingSignal.set(true);
|
|
1092
1202
|
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
1203
|
// Send to AI service
|
|
1107
1204
|
await this.sendCurrentConversation();
|
|
1108
1205
|
}
|
|
@@ -1110,18 +1207,7 @@ class ConversationService {
|
|
|
1110
1207
|
this.isThinkingSignal.set(false);
|
|
1111
1208
|
}
|
|
1112
1209
|
}
|
|
1113
|
-
// Add message to conversation
|
|
1114
|
-
addMessage(message) {
|
|
1115
|
-
this.messagesSignal.update((messages) => [...messages, message]);
|
|
1116
|
-
}
|
|
1117
1210
|
// 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
1211
|
// Send current conversation to AI
|
|
1126
1212
|
async sendCurrentConversation() {
|
|
1127
1213
|
if (this.isDestroyedSignal()) {
|
|
@@ -1129,10 +1215,6 @@ class ConversationService {
|
|
|
1129
1215
|
}
|
|
1130
1216
|
const messages = this.messagesSignal();
|
|
1131
1217
|
const conversationSettings = this.conversationSettingsSignal();
|
|
1132
|
-
const agentCard = this.agentCardSignal();
|
|
1133
|
-
if (!conversationSettings || !agentCard) {
|
|
1134
|
-
throw new Error('Conversation not properly initialized');
|
|
1135
|
-
}
|
|
1136
1218
|
if (messages.length > 31) {
|
|
1137
1219
|
// Safety limit to prevent infinite conversations
|
|
1138
1220
|
return;
|
|
@@ -1140,17 +1222,14 @@ class ConversationService {
|
|
|
1140
1222
|
let conversationMessages = messages;
|
|
1141
1223
|
// Add last prompt if available
|
|
1142
1224
|
if (conversationSettings.last_prompt) {
|
|
1143
|
-
conversationMessages = [
|
|
1144
|
-
...messages,
|
|
1145
|
-
{ content: conversationSettings.last_prompt, role: ChatRole.System },
|
|
1146
|
-
];
|
|
1225
|
+
conversationMessages = [...messages, { content: conversationSettings.last_prompt, role: ChatRole.System }];
|
|
1147
1226
|
}
|
|
1148
1227
|
// Prepare conversation DTO
|
|
1149
1228
|
const conversation = {
|
|
1150
1229
|
messages: conversationMessages,
|
|
1151
1230
|
conversationType: conversationSettings.conversationType,
|
|
1152
1231
|
textEngine: conversationSettings.textEngine,
|
|
1153
|
-
model:
|
|
1232
|
+
model: conversationSettings.model || { modelName: '', provider: '' },
|
|
1154
1233
|
};
|
|
1155
1234
|
// Call AI service
|
|
1156
1235
|
const response = await this.agentCardService.callChatCompletion(conversation);
|
|
@@ -1159,29 +1238,20 @@ class ConversationService {
|
|
|
1159
1238
|
throw new Error('No message returned from AI');
|
|
1160
1239
|
}
|
|
1161
1240
|
// Process response
|
|
1162
|
-
const newMessage = this.
|
|
1241
|
+
const newMessage = this.messageProcessingService.processMessage(response, conversationSettings);
|
|
1163
1242
|
// Add to messages
|
|
1164
1243
|
this.addMessage(newMessage);
|
|
1165
1244
|
this.isThinkingSignal.set(false);
|
|
1166
1245
|
}
|
|
1167
|
-
// Helper to get audio playback setting
|
|
1168
|
-
getAudioPlaybackSetting() {
|
|
1169
|
-
const agentCard = this.agentCardSignal();
|
|
1170
|
-
return agentCard?.conversationSettings?.repeatRecording || false;
|
|
1171
|
-
}
|
|
1172
1246
|
// Reset conversation
|
|
1173
1247
|
async resetConversation(agentCard) {
|
|
1174
|
-
if (agentCard) {
|
|
1175
|
-
this.agentCardSignal.set(agentCard);
|
|
1176
|
-
}
|
|
1177
1248
|
// Clear messages
|
|
1178
1249
|
this.messagesSignal.set([]);
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
}
|
|
1250
|
+
}
|
|
1251
|
+
async getTTSFile(message) {
|
|
1252
|
+
const userChatSettings = await this.userDataExchange.getUserChatSettings();
|
|
1253
|
+
const ttsRequest = buildObjectTTSRequest(message, userChatSettings);
|
|
1254
|
+
return this.agentCardService.getTextAudioFile(ttsRequest);
|
|
1185
1255
|
}
|
|
1186
1256
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: ConversationService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
1187
1257
|
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: ConversationService, providedIn: 'root' }); }
|
|
@@ -1191,7 +1261,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImpor
|
|
|
1191
1261
|
args: [{
|
|
1192
1262
|
providedIn: 'root',
|
|
1193
1263
|
}]
|
|
1194
|
-
}]
|
|
1264
|
+
}] });
|
|
1195
1265
|
|
|
1196
1266
|
function extractJsonFromResponse(content) {
|
|
1197
1267
|
const jsonMatch = content.match(/\{[\s\S]*?\}/); // Match everything between first { and }
|
|
@@ -1279,14 +1349,20 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImpor
|
|
|
1279
1349
|
|
|
1280
1350
|
class ChatFooterComponent {
|
|
1281
1351
|
constructor() {
|
|
1352
|
+
// Services
|
|
1282
1353
|
this.conversationService = inject(ConversationService);
|
|
1283
1354
|
this.evaluationService = inject(EvaluationService);
|
|
1355
|
+
this.agentCardService = inject(CONVERSATION_AI_TOKEN);
|
|
1356
|
+
// Inputs
|
|
1284
1357
|
this.isAIThinking = input(false);
|
|
1285
1358
|
this.evaluatorAgentCard = input();
|
|
1286
1359
|
this.micSettings = input({ useWhisper: true, lang: 'en' });
|
|
1360
|
+
// Outputs
|
|
1287
1361
|
this.sendMessage = output();
|
|
1288
1362
|
this.textInputChanged = output();
|
|
1289
|
-
|
|
1363
|
+
// readonly micFinishedEvent = output<any>();
|
|
1364
|
+
this.isGettingTranscription = false;
|
|
1365
|
+
this.isUserTalking = false;
|
|
1290
1366
|
this.chatInputControl = new FormControl();
|
|
1291
1367
|
// Get score from evaluation service
|
|
1292
1368
|
this.score = this.evaluationService.getScore();
|
|
@@ -1304,7 +1380,7 @@ class ChatFooterComponent {
|
|
|
1304
1380
|
* @param eventBlob The blob event from the mic component
|
|
1305
1381
|
*/
|
|
1306
1382
|
micFinished(eventBlob) {
|
|
1307
|
-
this.micFinishedEvent.emit(eventBlob);
|
|
1383
|
+
// this.micFinishedEvent.emit(eventBlob);
|
|
1308
1384
|
}
|
|
1309
1385
|
/**
|
|
1310
1386
|
* Sends the user message
|
|
@@ -1319,7 +1395,7 @@ class ChatFooterComponent {
|
|
|
1319
1395
|
role: ChatRole.User,
|
|
1320
1396
|
};
|
|
1321
1397
|
// Emit the message for parent components that need it
|
|
1322
|
-
this.sendMessage.emit(message);
|
|
1398
|
+
// this.sendMessage.emit(message);
|
|
1323
1399
|
// Clear the input field
|
|
1324
1400
|
this.chatInputControl.setValue('');
|
|
1325
1401
|
// Send the user message to the conversation service
|
|
@@ -1331,182 +1407,48 @@ class ChatFooterComponent {
|
|
|
1331
1407
|
* Evaluate conversation using evaluator agent
|
|
1332
1408
|
*/
|
|
1333
1409
|
async evaluateConversation() {
|
|
1334
|
-
const messages = this.conversationService.
|
|
1410
|
+
const messages = this.conversationService.getMessagesSignal()();
|
|
1335
1411
|
if (this.evaluatorAgentCard()) {
|
|
1336
1412
|
await this.evaluationService.evaluateConversation(messages, this.evaluatorAgentCard());
|
|
1337
1413
|
}
|
|
1338
1414
|
}
|
|
1339
|
-
|
|
1340
|
-
|
|
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
|
|
1378
|
-
const cleanup$ = new Subject();
|
|
1379
|
-
this.cleanup$Map.set(messageId, cleanup$);
|
|
1380
|
-
// Store the active audio element
|
|
1381
|
-
this.activeAudioMap.set(messageId, audioElement);
|
|
1382
|
-
// Get the signal and subject for this message
|
|
1383
|
-
const messageSignal = this.highlightedWordsSignalMap.get(messageId);
|
|
1384
|
-
const messageSubject = this.highlightedWords$Map.get(messageId);
|
|
1385
|
-
// Initialize the highlighted words state
|
|
1386
|
-
const initialWords = transcriptionTimestamps.map((word, index) => ({
|
|
1387
|
-
word: word.word,
|
|
1388
|
-
index,
|
|
1389
|
-
isHighlighted: false,
|
|
1390
|
-
}));
|
|
1391
|
-
// Update both signal and observable
|
|
1392
|
-
messageSignal.set(initialWords);
|
|
1393
|
-
messageSubject.next(initialWords);
|
|
1394
|
-
// Listen to timeupdate events
|
|
1395
|
-
fromEvent(audioElement, 'timeupdate')
|
|
1396
|
-
.pipe(takeUntilDestroyed(this.destroyRef), takeUntil(cleanup$), map(() => audioElement.currentTime))
|
|
1397
|
-
.subscribe((currentTime) => {
|
|
1398
|
-
const updatedWords = transcriptionTimestamps.map((word, index) => {
|
|
1399
|
-
const isHighlighted = currentTime >= word.start - 0.15 && currentTime < word.end + 0.15;
|
|
1400
|
-
return {
|
|
1401
|
-
word: word.word,
|
|
1402
|
-
index,
|
|
1403
|
-
isHighlighted,
|
|
1404
|
-
};
|
|
1405
|
-
});
|
|
1406
|
-
// Update both signal and observable for this message
|
|
1407
|
-
messageSignal.set(updatedWords);
|
|
1408
|
-
messageSubject.next(updatedWords);
|
|
1409
|
-
});
|
|
1410
|
-
// Listen to ended event for cleanup
|
|
1411
|
-
fromEvent(audioElement, 'ended')
|
|
1412
|
-
.pipe(takeUntilDestroyed(this.destroyRef), takeUntil(cleanup$))
|
|
1413
|
-
.subscribe(() => {
|
|
1414
|
-
// Reset highlighting when audio ends
|
|
1415
|
-
const resetWords = initialWords.map((word) => ({
|
|
1416
|
-
...word,
|
|
1417
|
-
isHighlighted: false,
|
|
1418
|
-
}));
|
|
1419
|
-
messageSignal.set(resetWords);
|
|
1420
|
-
messageSubject.next(resetWords);
|
|
1421
|
-
});
|
|
1422
|
-
}
|
|
1423
|
-
/**
|
|
1424
|
-
* Stops the sync for a specific message and cleans up resources
|
|
1425
|
-
* @param messageId The ID of the message to stop syncing
|
|
1426
|
-
*/
|
|
1427
|
-
stopSync(messageId) {
|
|
1428
|
-
if (this.activeAudioMap.has(messageId)) {
|
|
1429
|
-
// Get the cleanup subject for this message
|
|
1430
|
-
const cleanup$ = this.cleanup$Map.get(messageId);
|
|
1431
|
-
if (cleanup$) {
|
|
1432
|
-
cleanup$.next();
|
|
1433
|
-
cleanup$.complete();
|
|
1434
|
-
this.cleanup$Map.delete(messageId);
|
|
1435
|
-
}
|
|
1436
|
-
this.activeAudioMap.delete(messageId);
|
|
1437
|
-
// Reset state for this message
|
|
1438
|
-
const messageSignal = this.highlightedWordsSignalMap.get(messageId);
|
|
1439
|
-
const messageSubject = this.highlightedWords$Map.get(messageId);
|
|
1440
|
-
if (messageSignal) {
|
|
1441
|
-
messageSignal.set([]);
|
|
1442
|
-
}
|
|
1443
|
-
if (messageSubject) {
|
|
1444
|
-
messageSubject.next([]);
|
|
1445
|
-
}
|
|
1446
|
-
}
|
|
1415
|
+
handleAudioRecorded(event) {
|
|
1416
|
+
console.log(event);
|
|
1417
|
+
this.onMicFinished(event.blob);
|
|
1447
1418
|
}
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
stopAllSyncs() {
|
|
1452
|
-
// Get all message IDs and stop each sync
|
|
1453
|
-
for (const messageId of this.activeAudioMap.keys()) {
|
|
1454
|
-
this.stopSync(messageId);
|
|
1419
|
+
async onMicFinished(eventBlob) {
|
|
1420
|
+
if (!(eventBlob instanceof Blob)) {
|
|
1421
|
+
return;
|
|
1455
1422
|
}
|
|
1456
|
-
|
|
1457
|
-
this.
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1423
|
+
this.isUserTalking = false;
|
|
1424
|
+
this.isGettingTranscription = true;
|
|
1425
|
+
try {
|
|
1426
|
+
// Get transcription from audio
|
|
1427
|
+
const transcription = await this.agentCardService.getAudioTranscriptions(eventBlob, null);
|
|
1428
|
+
const message = {
|
|
1429
|
+
content: '',
|
|
1430
|
+
role: ChatRole.User,
|
|
1431
|
+
audioUrl: URL.createObjectURL(eventBlob),
|
|
1432
|
+
};
|
|
1433
|
+
if (transcription) {
|
|
1434
|
+
message.content = transcription.text;
|
|
1435
|
+
message.transcription = transcription;
|
|
1436
|
+
}
|
|
1437
|
+
// Send message to conversation
|
|
1438
|
+
// The evaluation will happen automatically in the conversation service
|
|
1439
|
+
await this.conversationService.sendUserMessage(message);
|
|
1470
1440
|
}
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
/**
|
|
1474
|
-
* Returns the highlighted words observable for a specific message
|
|
1475
|
-
* @param messageId The ID of the message
|
|
1476
|
-
*/
|
|
1477
|
-
getHighlightedWords$(messageId) {
|
|
1478
|
-
// Create a new subject for this message if it doesn't exist
|
|
1479
|
-
if (!this.highlightedWords$Map.has(messageId)) {
|
|
1480
|
-
this.highlightedWords$Map.set(messageId, new BehaviorSubject([]));
|
|
1441
|
+
finally {
|
|
1442
|
+
this.isGettingTranscription = false;
|
|
1481
1443
|
}
|
|
1482
|
-
return this.highlightedWords$Map.get(messageId).asObservable();
|
|
1483
|
-
}
|
|
1484
|
-
/**
|
|
1485
|
-
* Checks if a word at a specific index is currently highlighted for a specific message
|
|
1486
|
-
* @param messageId The ID of the message
|
|
1487
|
-
* @param index The index of the word to check
|
|
1488
|
-
*/
|
|
1489
|
-
isWordHighlighted(messageId, index) {
|
|
1490
|
-
const messageSignal = this.getHighlightedWordsSignal(messageId);
|
|
1491
|
-
return messageSignal().some((word) => word.index === index && word.isHighlighted);
|
|
1492
|
-
}
|
|
1493
|
-
/**
|
|
1494
|
-
* Returns an observable that emits true when a word at a specific index is highlighted for a specific message
|
|
1495
|
-
* @param messageId The ID of the message
|
|
1496
|
-
* @param index The index of the word to observe
|
|
1497
|
-
*/
|
|
1498
|
-
isWordHighlighted$(messageId, index) {
|
|
1499
|
-
return this.getHighlightedWords$(messageId).pipe(map((words) => words.some((word) => word.index === index && word.isHighlighted)));
|
|
1500
1444
|
}
|
|
1501
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type:
|
|
1502
|
-
static { this.ɵ
|
|
1445
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: ChatFooterComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
1446
|
+
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" }, ngImport: i0, template: "<div class=\"progress-input\">\n <div class=\"input-container\">\n <app-mic-vad (audioRecorded)=\"handleAudioRecorded($event)\" />\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: "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"] }, { kind: "component", type: MicVadComponent, selector: "app-mic-vad", inputs: ["buttonLabel", "recordingLabel", "listeningLabel", "buttonIcon", "recordingIcon", "buttonClass"], outputs: ["recordingStarted", "recordingStopped", "speechDetected", "speechEnded", "audioRecorded", "error"] }] }); }
|
|
1503
1447
|
}
|
|
1504
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type:
|
|
1505
|
-
type:
|
|
1506
|
-
args: [{
|
|
1507
|
-
|
|
1508
|
-
}]
|
|
1509
|
-
}], ctorParameters: () => [] });
|
|
1448
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: ChatFooterComponent, decorators: [{
|
|
1449
|
+
type: Component,
|
|
1450
|
+
args: [{ selector: 'dc-chat-footer', standalone: true, imports: [ReactiveFormsModule, ProgressBarModule, DCMicComponent, TextareaModule, ButtonModule, MicVadComponent], template: "<div class=\"progress-input\">\n <div class=\"input-container\">\n <app-mic-vad (audioRecorded)=\"handleAudioRecorded($event)\" />\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"] }]
|
|
1451
|
+
}] });
|
|
1510
1452
|
|
|
1511
1453
|
const ICONS = {
|
|
1512
1454
|
chat: `<svg viewBox="0 0 24 24" fill="currentColor">
|
|
@@ -1585,40 +1527,48 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImpor
|
|
|
1585
1527
|
`, 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
1528
|
}], ctorParameters: () => [] });
|
|
1587
1529
|
|
|
1588
|
-
|
|
1589
|
-
* Standalone component for audio-text synchronization
|
|
1590
|
-
* This component can work independently from the chat component
|
|
1591
|
-
*/
|
|
1592
|
-
class StandaloneAudioTextSyncComponent {
|
|
1530
|
+
class TextHighlighterComponent {
|
|
1593
1531
|
constructor() {
|
|
1594
|
-
this.message = input(
|
|
1532
|
+
this.message = input.required(); // Or input.required<MessageAudio>() if always expected
|
|
1595
1533
|
this.highlightedWords = signal([]); // Signal for highlighted words
|
|
1596
|
-
this.
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
|
|
1534
|
+
this.isPlaying = signal(false); // Signal for play state
|
|
1535
|
+
// Signals State computed from the input message
|
|
1536
|
+
this.iconState = computed(() => {
|
|
1537
|
+
if (this.isLoading()) {
|
|
1538
|
+
return 'isLoading';
|
|
1539
|
+
}
|
|
1540
|
+
else if (this.isPlaying()) {
|
|
1541
|
+
return 'isPlaying';
|
|
1542
|
+
}
|
|
1543
|
+
else if (this.message()?.audioUrl) {
|
|
1544
|
+
return 'playable';
|
|
1545
|
+
}
|
|
1546
|
+
else {
|
|
1547
|
+
return 'idle';
|
|
1548
|
+
}
|
|
1601
1549
|
});
|
|
1602
|
-
this.
|
|
1603
|
-
this.
|
|
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);
|
|
1550
|
+
this.isLoading = computed(() => this.message()?.isLoading);
|
|
1551
|
+
this.shouldPlayAudio = computed(() => !!this.message() && this.message()?.shouldPlayAudio);
|
|
1609
1552
|
// Computed signal for message text
|
|
1610
1553
|
this.messageText = computed(() => {
|
|
1611
1554
|
const msg = this.message(); // Read the input signal
|
|
1612
|
-
return msg?.
|
|
1555
|
+
return msg?.text || msg?.content || 'N/A';
|
|
1613
1556
|
});
|
|
1557
|
+
this.classTag = computed(() => this.message()?.tag);
|
|
1614
1558
|
// Computed signal for transcription availability
|
|
1615
1559
|
this.hasTranscription = computed(() => {
|
|
1616
1560
|
const hasTranscription = !!this.message()?.transcriptionTimestamps && this.message()?.transcriptionTimestamps.length > 0;
|
|
1617
1561
|
return hasTranscription;
|
|
1618
1562
|
});
|
|
1563
|
+
this.playAudio = output();
|
|
1564
|
+
this.audioCompleted = output();
|
|
1565
|
+
this.audioElement = null; // Audio element for playback
|
|
1566
|
+
this.destroy$ = new Subject(); // Cleanup localsubject
|
|
1567
|
+
// Inject services
|
|
1568
|
+
this.cdr = inject(ChangeDetectorRef); // Keep for now, might remove if template fully signal-driven
|
|
1569
|
+
this.destroyRef = inject(DestroyRef);
|
|
1619
1570
|
// Effect to react to message changes and re-initialize
|
|
1620
1571
|
effect(() => {
|
|
1621
|
-
debugger;
|
|
1622
1572
|
const currentMsg = this.message(); // Read the input signal
|
|
1623
1573
|
console.log('Input message signal changed:', currentMsg);
|
|
1624
1574
|
if (currentMsg) {
|
|
@@ -1653,30 +1603,27 @@ class StandaloneAudioTextSyncComponent {
|
|
|
1653
1603
|
this.destroy$.complete();
|
|
1654
1604
|
this.cleanupAudio();
|
|
1655
1605
|
}
|
|
1656
|
-
// This method is called by the effect when the message signal changes
|
|
1657
1606
|
initializeBasedOnMessage(msg) {
|
|
1658
1607
|
// Initialize or cleanup audio based on URL presence and change
|
|
1659
1608
|
if (msg.audioUrl && (!this.audioElement || this.audioElement.src !== msg.audioUrl)) {
|
|
1660
1609
|
this.initializeAudio(msg.audioUrl); // Pass URL
|
|
1661
1610
|
}
|
|
1662
1611
|
else if (!msg.audioUrl && this.audioElement) {
|
|
1663
|
-
// Cleanup if URL removed and element exists
|
|
1664
1612
|
this.cleanupAudio();
|
|
1665
1613
|
}
|
|
1614
|
+
else if (!msg.audioUrl) {
|
|
1615
|
+
// No audio URL and no audio element - just log a warning instead of throwing an error
|
|
1616
|
+
console.warn('No audioUrl provided in the message. Audio playback will not be available.');
|
|
1617
|
+
}
|
|
1666
1618
|
// Initialize words and sync based on transcription presence
|
|
1667
1619
|
if (this.hasTranscription()) {
|
|
1668
1620
|
// Use computed signal
|
|
1669
1621
|
const timestamps = msg.transcriptionTimestamps || [];
|
|
1670
1622
|
this.initializeHighlightedWords(timestamps); // Pass timestamps
|
|
1671
|
-
//
|
|
1672
|
-
if (this.audioElement) {
|
|
1673
|
-
this.setupAudioSync(timestamps); // Pass timestamps
|
|
1674
|
-
}
|
|
1623
|
+
this.subcribeToAudioSync(timestamps); // Pass timestamps
|
|
1675
1624
|
}
|
|
1676
1625
|
else {
|
|
1677
|
-
|
|
1678
|
-
this.highlightedWords.set([]);
|
|
1679
|
-
this.destroy$.next(); // Signal existing listeners to stop
|
|
1626
|
+
this.subscribeToEndAudio();
|
|
1680
1627
|
}
|
|
1681
1628
|
}
|
|
1682
1629
|
/**
|
|
@@ -1701,7 +1648,7 @@ class StandaloneAudioTextSyncComponent {
|
|
|
1701
1648
|
/**
|
|
1702
1649
|
* Set up audio synchronization with text
|
|
1703
1650
|
*/
|
|
1704
|
-
|
|
1651
|
+
subcribeToAudioSync(timestamps) {
|
|
1705
1652
|
// Accept timestamps
|
|
1706
1653
|
if (!this.audioElement) {
|
|
1707
1654
|
// Guard against missing audio element
|
|
@@ -1733,6 +1680,8 @@ class StandaloneAudioTextSyncComponent {
|
|
|
1733
1680
|
isHighlighted: false,
|
|
1734
1681
|
}));
|
|
1735
1682
|
this.highlightedWords.set(resetWords);
|
|
1683
|
+
// Set isPlaying to false when audio finishes
|
|
1684
|
+
this.isPlaying.set(false);
|
|
1736
1685
|
// Emit audio completed event with the current message
|
|
1737
1686
|
const currentMsg = this.message();
|
|
1738
1687
|
if (currentMsg) {
|
|
@@ -1742,6 +1691,23 @@ class StandaloneAudioTextSyncComponent {
|
|
|
1742
1691
|
}
|
|
1743
1692
|
});
|
|
1744
1693
|
}
|
|
1694
|
+
subscribeToEndAudio() {
|
|
1695
|
+
if (!this.audioElement) {
|
|
1696
|
+
return;
|
|
1697
|
+
}
|
|
1698
|
+
fromEvent(this.audioElement, 'ended')
|
|
1699
|
+
.pipe(takeUntilDestroyed(this.destroyRef), takeUntil(this.destroy$))
|
|
1700
|
+
.subscribe(() => {
|
|
1701
|
+
// Set isPlaying to false when audio finishes
|
|
1702
|
+
this.isPlaying.set(false);
|
|
1703
|
+
// Emit audio completed event with the current message
|
|
1704
|
+
const currentMsg = this.message();
|
|
1705
|
+
if (currentMsg) {
|
|
1706
|
+
currentMsg.shouldPlayAudio = false;
|
|
1707
|
+
this.audioCompleted.emit(currentMsg);
|
|
1708
|
+
}
|
|
1709
|
+
});
|
|
1710
|
+
}
|
|
1745
1711
|
/**
|
|
1746
1712
|
* Clean up audio element and event listeners
|
|
1747
1713
|
*/
|
|
@@ -1763,6 +1729,7 @@ class StandaloneAudioTextSyncComponent {
|
|
|
1763
1729
|
* Play or pause the audio
|
|
1764
1730
|
*/
|
|
1765
1731
|
onPlayMessage() {
|
|
1732
|
+
console.log('Playing message audio', this.message().messageId);
|
|
1766
1733
|
const currentMsg = this.message(); // Read signal
|
|
1767
1734
|
if (this.audioElement) {
|
|
1768
1735
|
if (this.audioElement.paused) {
|
|
@@ -1778,6 +1745,7 @@ class StandaloneAudioTextSyncComponent {
|
|
|
1778
1745
|
// For simplicity, let's re-call initializeAudio here if needed,
|
|
1779
1746
|
// though ideally the effect handles it.
|
|
1780
1747
|
this.initializeAudio(currentMsg.audioUrl); // Ensure it's created if somehow missed
|
|
1748
|
+
// this.initializeBasedOnMessage(currentMsg);
|
|
1781
1749
|
this.startAudioPlayback();
|
|
1782
1750
|
}
|
|
1783
1751
|
else if (currentMsg) {
|
|
@@ -1795,363 +1763,228 @@ class StandaloneAudioTextSyncComponent {
|
|
|
1795
1763
|
this.audioElement.play().catch((error) => {
|
|
1796
1764
|
console.error('Error playing audio:', error);
|
|
1797
1765
|
});
|
|
1766
|
+
this.isPlaying.set(true); // Set play state to true
|
|
1798
1767
|
}
|
|
1799
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type:
|
|
1800
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.4", type:
|
|
1768
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: TextHighlighterComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
1769
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.4", type: TextHighlighterComponent, isStandalone: true, selector: "dc-text-highlighter", inputs: { message: { classPropertyName: "message", publicName: "message", isSignal: true, isRequired: true, transformFunction: null } }, outputs: { playAudio: "playAudio", audioCompleted: "audioCompleted" }, ngImport: i0, template: "<div class=\"audio-text-sync-container\">\n <div> </div>\n <i (click)=\"onPlayMessage()\" class=\"play-button\">\n @if (iconState() === 'isLoading') {\n <!-- <dc-icon class=\"spin-animation\" name=\"loading\"></dc-icon> -->\n <i class=\"spin-animation pi pi-spinner-dotted\"></i>\n } @else if (iconState() === 'isPlaying') {\n <i class=\"pi pi-volume-up\"></i>\n } @else if (iconState() === 'playable') {\n <!-- Display play icon when not playing -->\n <dc-icon name=\"play\"></dc-icon>\n } @else {\n <!-- Nothing-->\n <!-- <dc-icon name=\"play\"></dc-icon> -->\n }\n </i>\n\n <!-- Display transcription with highlighting -->\n <div [class]=\"'text-content ' + classTag()\">\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> {{ messageText() }}</span>\n }\n </div>\n</div>\n", styles: [":host{display:block}.audio-text-sync-container{display:flex;align-items:flex-start;gap:2px;align-items:center}.play-button{cursor:pointer;display:flex;align-items:center;justify-content:center;min-width:24px;margin-top:4px}.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)}}.em{font-style:italic;color:#6495ed}.strong{font-weight:700;color:inherit}.em_strong{font-weight:700;font-style:italic;color:inherit}\n"], dependencies: [{ kind: "component", type: IconsComponent, selector: "dc-icon", inputs: ["name", "size", "color"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
1801
1770
|
}
|
|
1802
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type:
|
|
1771
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: TextHighlighterComponent, decorators: [{
|
|
1803
1772
|
type: Component,
|
|
1804
|
-
args: [{ selector: 'dc-
|
|
1773
|
+
args: [{ selector: 'dc-text-highlighter', standalone: true, imports: [IconsComponent], changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"audio-text-sync-container\">\n <div> </div>\n <i (click)=\"onPlayMessage()\" class=\"play-button\">\n @if (iconState() === 'isLoading') {\n <!-- <dc-icon class=\"spin-animation\" name=\"loading\"></dc-icon> -->\n <i class=\"spin-animation pi pi-spinner-dotted\"></i>\n } @else if (iconState() === 'isPlaying') {\n <i class=\"pi pi-volume-up\"></i>\n } @else if (iconState() === 'playable') {\n <!-- Display play icon when not playing -->\n <dc-icon name=\"play\"></dc-icon>\n } @else {\n <!-- Nothing-->\n <!-- <dc-icon name=\"play\"></dc-icon> -->\n }\n </i>\n\n <!-- Display transcription with highlighting -->\n <div [class]=\"'text-content ' + classTag()\">\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> {{ messageText() }}</span>\n }\n </div>\n</div>\n", styles: [":host{display:block}.audio-text-sync-container{display:flex;align-items:flex-start;gap:2px;align-items:center}.play-button{cursor:pointer;display:flex;align-items:center;justify-content:center;min-width:24px;margin-top:4px}.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)}}.em{font-style:italic;color:#6495ed}.strong{font-weight:700;color:inherit}.em_strong{font-weight:700;font-style:italic;color:inherit}\n"] }]
|
|
1805
1774
|
}], ctorParameters: () => [] });
|
|
1806
1775
|
|
|
1807
|
-
class
|
|
1776
|
+
class MessageOrchestratorComponent {
|
|
1808
1777
|
constructor() {
|
|
1778
|
+
this.agentCardService = inject(CONVERSATION_AI_TOKEN);
|
|
1779
|
+
this.conversationService = inject(ConversationService);
|
|
1809
1780
|
this.messages = input.required();
|
|
1810
|
-
this.
|
|
1781
|
+
this.messageRole = input.required();
|
|
1782
|
+
this.messagesSignal = signal([]);
|
|
1811
1783
|
this.playAudio = output();
|
|
1812
|
-
|
|
1813
|
-
|
|
1814
|
-
|
|
1815
|
-
|
|
1816
|
-
|
|
1817
|
-
|
|
1818
|
-
*/
|
|
1819
|
-
hasTranscription(message) {
|
|
1820
|
-
//
|
|
1821
|
-
return !!message.transcriptionTimestamps;
|
|
1784
|
+
this.audioCompleted = output();
|
|
1785
|
+
// Audio queue management
|
|
1786
|
+
this.audioQueue = [];
|
|
1787
|
+
this.isGenerating = false;
|
|
1788
|
+
this.currentPlayingIndex = null;
|
|
1789
|
+
this.preGenerationInProgress = false;
|
|
1822
1790
|
}
|
|
1823
|
-
|
|
1824
|
-
|
|
1825
|
-
|
|
1826
|
-
|
|
1827
|
-
|
|
1828
|
-
|
|
1829
|
-
|
|
1830
|
-
|
|
1831
|
-
|
|
1791
|
+
ngOnInit() {
|
|
1792
|
+
if (this.messageRole() === ChatRole.Assistant) {
|
|
1793
|
+
console.log('MessageOrchestratorComponent initialized', this.messages());
|
|
1794
|
+
// Initialize the queue with all message indices
|
|
1795
|
+
this.initializeAudioQueue();
|
|
1796
|
+
// Start processing the queue - generate the first audio
|
|
1797
|
+
this.processNextInQueue();
|
|
1798
|
+
}
|
|
1799
|
+
else {
|
|
1800
|
+
// since user only have one message, just activate signal
|
|
1801
|
+
this.changeStates(0, this.messages()[0]);
|
|
1832
1802
|
}
|
|
1833
|
-
// Also emit to parent if needed
|
|
1834
|
-
this.playAudio.emit(message);
|
|
1835
1803
|
}
|
|
1836
|
-
|
|
1837
|
-
|
|
1838
|
-
* @param startIndex Index of the message to start playing from
|
|
1839
|
-
*/
|
|
1840
|
-
startSequentialPlayback(startIndex = 0) {
|
|
1841
|
-
this.currentPlayingIndex = startIndex;
|
|
1804
|
+
// Initialize the queue with all message indices
|
|
1805
|
+
initializeAudioQueue() {
|
|
1842
1806
|
const messages = this.messages();
|
|
1843
|
-
if (messages && messages.length >
|
|
1844
|
-
|
|
1845
|
-
messages.forEach((msg) => (msg.shouldPlayAudio = false));
|
|
1846
|
-
// Set the current message to play
|
|
1847
|
-
messages[startIndex].shouldPlayAudio = true;
|
|
1807
|
+
if (messages && messages.length > 0) {
|
|
1808
|
+
this.audioQueue = messages.map((_, index) => index);
|
|
1848
1809
|
}
|
|
1849
1810
|
}
|
|
1850
|
-
|
|
1851
|
-
|
|
1852
|
-
|
|
1853
|
-
|
|
1854
|
-
|
|
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;
|
|
1811
|
+
// Process the next message in the queue
|
|
1812
|
+
async processNextInQueue() {
|
|
1813
|
+
console.log('Processing next message in queue', this.audioQueue);
|
|
1814
|
+
if (this.audioQueue.length === 0 || this.isGenerating) {
|
|
1815
|
+
return;
|
|
1864
1816
|
}
|
|
1865
|
-
|
|
1866
|
-
|
|
1867
|
-
|
|
1817
|
+
// Get the next index from the queue
|
|
1818
|
+
const nextIndex = this.audioQueue.shift();
|
|
1819
|
+
// Start generating
|
|
1820
|
+
this.isGenerating = true;
|
|
1821
|
+
// Generate audio for the current message
|
|
1822
|
+
await this.generateAudioForIndex(nextIndex);
|
|
1823
|
+
// After generation, mark as not generating
|
|
1824
|
+
this.isGenerating = false;
|
|
1825
|
+
// If there's another message in the queue, pre-generate it
|
|
1826
|
+
this.preGenerateNextIfNeeded();
|
|
1827
|
+
}
|
|
1828
|
+
// Pre-generate the next audio if available
|
|
1829
|
+
async preGenerateNextIfNeeded() {
|
|
1830
|
+
if (this.audioQueue.length > 0 && !this.preGenerationInProgress) {
|
|
1831
|
+
const nextIndex = this.audioQueue[0]; // Peek at next index but don't remove
|
|
1832
|
+
// Mark pre-generation as in progress
|
|
1833
|
+
this.preGenerationInProgress = true;
|
|
1834
|
+
// Mark as loading but don't set shouldPlayAudio yet
|
|
1835
|
+
const messages = this.messages();
|
|
1836
|
+
const message = { ...messages[nextIndex], isLoading: true };
|
|
1837
|
+
this.changeStates(nextIndex, message);
|
|
1838
|
+
// Generate in background
|
|
1839
|
+
const messageAudio = await this.generateAudio(messages[nextIndex], null);
|
|
1840
|
+
messageAudio.isLoading = false;
|
|
1841
|
+
messageAudio.shouldPlayAudio = false; // Don't auto-play yet
|
|
1842
|
+
this.changeStates(nextIndex, messageAudio);
|
|
1843
|
+
// Reset pre-generation flag
|
|
1844
|
+
this.preGenerationInProgress = false;
|
|
1845
|
+
}
|
|
1846
|
+
}
|
|
1847
|
+
// Generate audio for a specific index
|
|
1848
|
+
async generateAudioForIndex(index) {
|
|
1849
|
+
const messages = this.messages();
|
|
1850
|
+
// Check if this message was already pre-generated (has audioUrl)
|
|
1851
|
+
if (messages[index].audioUrl) {
|
|
1852
|
+
// If it was pre-generated, just set it to play
|
|
1853
|
+
const messageAudio = { ...messages[index], shouldPlayAudio: true };
|
|
1854
|
+
this.changeStates(index, messageAudio);
|
|
1855
|
+
this.currentPlayingIndex = index;
|
|
1856
|
+
return;
|
|
1868
1857
|
}
|
|
1858
|
+
// Otherwise generate it now
|
|
1859
|
+
const message = { ...messages[index], isLoading: true };
|
|
1860
|
+
this.changeStates(index, message);
|
|
1861
|
+
const messageAudio = await this.generateAudio(messages[index], null);
|
|
1862
|
+
messageAudio.isLoading = false;
|
|
1863
|
+
messageAudio.shouldPlayAudio = true;
|
|
1864
|
+
this.changeStates(index, messageAudio);
|
|
1865
|
+
this.currentPlayingIndex = index;
|
|
1869
1866
|
}
|
|
1870
|
-
|
|
1871
|
-
|
|
1872
|
-
|
|
1873
|
-
|
|
1874
|
-
|
|
1875
|
-
|
|
1876
|
-
|
|
1877
|
-
|
|
1878
|
-
|
|
1879
|
-
}], ctorParameters: () => [] });
|
|
1880
|
-
|
|
1881
|
-
function matchTranscription(originalText, transcription) {
|
|
1882
|
-
const result = [];
|
|
1883
|
-
const transcriptionMap = new Map();
|
|
1884
|
-
// Create a map of lowercase words to an array of their corresponding objects in transcription
|
|
1885
|
-
for (const obj of transcription) {
|
|
1886
|
-
const lowercaseWord = obj.word.trim().toLowerCase();
|
|
1887
|
-
if (transcriptionMap.has(lowercaseWord)) {
|
|
1888
|
-
transcriptionMap.get(lowercaseWord).push(obj);
|
|
1889
|
-
}
|
|
1890
|
-
else {
|
|
1891
|
-
transcriptionMap.set(lowercaseWord, [obj]);
|
|
1867
|
+
// Handle audio completion event from text-highlighter
|
|
1868
|
+
onAudioCompleted(message) {
|
|
1869
|
+
// Forward the event
|
|
1870
|
+
this.audioCompleted.emit(message);
|
|
1871
|
+
// Reset current playing index
|
|
1872
|
+
this.currentPlayingIndex = null;
|
|
1873
|
+
// If there are more messages in the queue, process the next one
|
|
1874
|
+
if (this.audioQueue.length > 0) {
|
|
1875
|
+
this.processNextInQueue();
|
|
1892
1876
|
}
|
|
1893
1877
|
}
|
|
1894
|
-
|
|
1895
|
-
|
|
1896
|
-
|
|
1897
|
-
|
|
1898
|
-
|
|
1899
|
-
|
|
1900
|
-
|
|
1901
|
-
const
|
|
1902
|
-
|
|
1903
|
-
|
|
1904
|
-
|
|
1905
|
-
|
|
1906
|
-
});
|
|
1907
|
-
currentTime = matchedObj.end;
|
|
1878
|
+
changeStates(index, messageAudio) {
|
|
1879
|
+
const messages = this.messages();
|
|
1880
|
+
messages[index] = { ...messages[index], ...messageAudio };
|
|
1881
|
+
this.messagesSignal.set([...messages]);
|
|
1882
|
+
}
|
|
1883
|
+
async generateAudio(message, overwriteText = null) {
|
|
1884
|
+
try {
|
|
1885
|
+
const text = overwriteText || message.text || message.content;
|
|
1886
|
+
const ttsObject = buildObjectTTSRequest({ ...message, text }, { highlightWords: true });
|
|
1887
|
+
const speechAudio = await this.conversationService.getTTSFile(ttsObject);
|
|
1888
|
+
message = extractAudioAndTranscription(message, speechAudio);
|
|
1889
|
+
return message;
|
|
1908
1890
|
}
|
|
1909
|
-
|
|
1910
|
-
|
|
1911
|
-
word: word,
|
|
1912
|
-
start: Number(currentTime.toFixed(3)),
|
|
1913
|
-
end: Number(currentTime.toFixed(3)),
|
|
1914
|
-
});
|
|
1891
|
+
finally {
|
|
1892
|
+
// No longer emitting audioCompleted here as it's handled by the text-highlighter
|
|
1915
1893
|
}
|
|
1916
1894
|
}
|
|
1917
|
-
|
|
1918
|
-
}
|
|
1919
|
-
function extractAudioAndTranscription(message, audio) {
|
|
1920
|
-
message.audioUrl = audio.blobUrl;
|
|
1921
|
-
message.transcription = audio.transcription;
|
|
1922
|
-
if (message.transcription) {
|
|
1923
|
-
message.transcriptionTimestamps = matchTranscription(message.text || message.content, message.transcription.words);
|
|
1924
|
-
}
|
|
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
|
-
};
|
|
1895
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: MessageOrchestratorComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
1896
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.4", type: MessageOrchestratorComponent, isStandalone: true, selector: "dc-message-orchestrator", inputs: { messages: { classPropertyName: "messages", publicName: "messages", isSignal: true, isRequired: true, transformFunction: null }, messageRole: { classPropertyName: "messageRole", publicName: "messageRole", isSignal: true, isRequired: true, transformFunction: null } }, outputs: { playAudio: "playAudio", audioCompleted: "audioCompleted" }, ngImport: i0, template: "@for (message of messagesSignal(); track message.messageId) {\n<dc-text-highlighter [message]=\"message\" (playAudio)=\"playAudio.emit($event)\" (audioCompleted)=\"onAudioCompleted($event)\" />\n}\n", styles: [":host{display:block}\n"], dependencies: [{ kind: "component", type: TextHighlighterComponent, selector: "dc-text-highlighter", inputs: ["message"], outputs: ["playAudio", "audioCompleted"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
1938
1897
|
}
|
|
1898
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: MessageOrchestratorComponent, decorators: [{
|
|
1899
|
+
type: Component,
|
|
1900
|
+
args: [{ selector: 'dc-message-orchestrator', imports: [TextHighlighterComponent], changeDetection: ChangeDetectionStrategy.OnPush, template: "@for (message of messagesSignal(); track message.messageId) {\n<dc-text-highlighter [message]=\"message\" (playAudio)=\"playAudio.emit($event)\" (audioCompleted)=\"onAudioCompleted($event)\" />\n}\n", styles: [":host{display:block}\n"] }]
|
|
1901
|
+
}] });
|
|
1939
1902
|
|
|
1940
1903
|
class ChatMessageComponent {
|
|
1941
1904
|
constructor() {
|
|
1942
|
-
this.audioService = inject(AudioService);
|
|
1943
|
-
this.agentCardService = inject(CONVERSATION_AI_TOKEN);
|
|
1944
|
-
this.audioTextSyncService = inject(AudioTextSyncService);
|
|
1945
1905
|
this.chatMessage = input.required();
|
|
1946
1906
|
this.chatUserSettings = input(null);
|
|
1947
1907
|
this.audioMessage = signal(null);
|
|
1948
|
-
|
|
1949
|
-
this.
|
|
1950
|
-
this.
|
|
1908
|
+
// Computed properties for easier access to signal values
|
|
1909
|
+
this.hasMultiMessages = computed(() => !!this.chatMessage()?.multiMessages);
|
|
1910
|
+
this.multiMessages = computed(() => this.chatMessage()?.multiMessages || []);
|
|
1911
|
+
this.messageTranslation = computed(() => this.chatMessage()?.translation);
|
|
1912
|
+
this.isUserMessage = computed(() => this.chatMessage()?.role === ChatRole.User);
|
|
1951
1913
|
// Field initializer for the effect - runs during component creation
|
|
1952
1914
|
this.messageEffect = effect(() => {
|
|
1953
1915
|
const message = this.chatMessage();
|
|
1954
1916
|
if (!message)
|
|
1955
1917
|
return;
|
|
1956
|
-
const settings$ = this.conversationChatSettings();
|
|
1957
|
-
// Skip processing if settings aren't loaded yet
|
|
1958
|
-
if (!settings$)
|
|
1959
|
-
return;
|
|
1960
1918
|
if (message.role === ChatRole.AssistantHelper) {
|
|
1961
1919
|
return;
|
|
1962
1920
|
}
|
|
1963
1921
|
if (message.role === ChatRole.User) {
|
|
1964
|
-
|
|
1965
|
-
this.playMessage(message);
|
|
1966
|
-
}
|
|
1922
|
+
this.audioMessage.set({ ...message });
|
|
1967
1923
|
}
|
|
1968
|
-
else if (
|
|
1969
|
-
if (
|
|
1970
|
-
this.generateAndPlayAllAudios(message.multiMessages);
|
|
1924
|
+
else if (message.role === ChatRole.Assistant) {
|
|
1925
|
+
if (this.multiMessages().length > 0) {
|
|
1971
1926
|
}
|
|
1972
1927
|
else {
|
|
1973
|
-
this.
|
|
1928
|
+
this.audioMessage.set({ ...message });
|
|
1974
1929
|
}
|
|
1975
1930
|
}
|
|
1976
1931
|
});
|
|
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);
|
|
1986
|
-
}
|
|
1987
|
-
async ngOnInit() {
|
|
1988
|
-
// Load settings - the effect will react to this change
|
|
1989
|
-
const settings = await this.agentCardService.getConversationUserChatSettings();
|
|
1990
|
-
this.conversationChatSettings.set(settings);
|
|
1991
|
-
// No need for effect here as it's now a field initializer
|
|
1992
|
-
}
|
|
1993
|
-
async generateAndPlayAudio(message, overwriteText = null) {
|
|
1994
|
-
// this.isLoading.set(true);
|
|
1995
|
-
this.audioMessage.set({ ...message, isLoading: true });
|
|
1996
|
-
try {
|
|
1997
|
-
const text = overwriteText || message.content;
|
|
1998
|
-
const ttsObject = buildObjectTTSRequest({ ...message, text }, this.conversationChatSettings());
|
|
1999
|
-
if (message.ssml) {
|
|
2000
|
-
ttsObject.ssml = message.ssml;
|
|
2001
|
-
}
|
|
2002
|
-
const speechAudio = await this.agentCardService.getTextAudioFile(ttsObject);
|
|
2003
|
-
extractAudioAndTranscription(message, speechAudio);
|
|
2004
|
-
this.audioMessage.set({ ...message, isLoading: false, shouldPlayAudio: true });
|
|
2005
|
-
// this.playMessage(message);
|
|
2006
|
-
}
|
|
2007
|
-
finally {
|
|
2008
|
-
this.isLoading.set(false);
|
|
2009
|
-
}
|
|
2010
|
-
}
|
|
2011
|
-
playMessage(message) {
|
|
2012
|
-
debugger;
|
|
2013
|
-
if (!message.audioUrl) {
|
|
2014
|
-
return null;
|
|
2015
|
-
}
|
|
2016
|
-
const audioElement = this.audioService.playAudio(message.audioUrl);
|
|
2017
|
-
message['audioHtml'] = audioElement;
|
|
2018
|
-
// console.log('Playing message with transcription:', {
|
|
2019
|
-
// hasTranscription: !!message.transcriptionTimestamps,
|
|
2020
|
-
// transcriptionLength: message.transcriptionTimestamps?.length,
|
|
2021
|
-
// audioUrl: message.audioUrl,
|
|
2022
|
-
// audioElement,
|
|
2023
|
-
// });
|
|
2024
|
-
if (message.transcriptionTimestamps) {
|
|
2025
|
-
// Generate a unique ID for this message
|
|
2026
|
-
const messageId = this.generateMessageId(message);
|
|
2027
|
-
console.log('Syncing audio with text:', {
|
|
2028
|
-
messageId,
|
|
2029
|
-
transcriptionTimestamps: message.transcriptionTimestamps,
|
|
2030
|
-
wordCount: message.transcriptionTimestamps.length,
|
|
2031
|
-
});
|
|
2032
|
-
// Use the audio-text sync service instead of direct manipulation
|
|
2033
|
-
// Pass the messageId to ensure each message has its own state
|
|
2034
|
-
// this.audioTextSyncService.syncAudioWithText(audioElement, message.transcriptionTimestamps, messageId);
|
|
2035
|
-
}
|
|
2036
|
-
return audioElement;
|
|
2037
|
-
}
|
|
2038
|
-
/**
|
|
2039
|
-
* Generate a unique ID for a message
|
|
2040
|
-
* Uses message content/text and a timestamp to ensure uniqueness
|
|
2041
|
-
*/
|
|
2042
|
-
generateMessageId(message) {
|
|
2043
|
-
const messageContent = message.content || message.text || '';
|
|
2044
|
-
// Use a safe approach to get an ID-like value since 'id' isn't guaranteed on these types
|
|
2045
|
-
const messageId = message._id || message.id || '';
|
|
2046
|
-
const timestamp = new Date().getTime();
|
|
2047
|
-
return `msg_${messageId}_${messageContent.substring(0, 10).replace(/\s+/g, '_')}_${timestamp}`;
|
|
2048
|
-
}
|
|
2049
|
-
// This method is no longer needed as we're using the AudioTextSyncService
|
|
2050
|
-
// It's kept here as a reference but can be removed
|
|
2051
|
-
/*
|
|
2052
|
-
private setupTranscriptionHighlighting(audioElement: HTMLAudioElement, transcriptionTimestamps: WordTimestamps[]): void {
|
|
2053
|
-
// This functionality has been moved to AudioTextSyncService
|
|
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;
|
|
2071
|
-
}
|
|
2072
|
-
else {
|
|
2073
|
-
const request = buildObjectTTSRequest(currentMessage, this.conversationChatSettings());
|
|
2074
|
-
currentMessage.audioPromise = this.agentCardService.getTextAudioFile(request);
|
|
2075
|
-
const audio = await currentMessage.audioPromise;
|
|
2076
|
-
extractAudioAndTranscription(currentMessage, audio);
|
|
2077
|
-
}
|
|
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;
|
|
2097
|
-
}
|
|
2098
|
-
};
|
|
2099
|
-
// Start the sequential playback
|
|
2100
|
-
playAudioSequentially();
|
|
2101
|
-
}
|
|
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
|
-
}
|
|
2114
|
-
}
|
|
2115
1932
|
}
|
|
2116
1933
|
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 <
|
|
1934
|
+
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 <img src=\"assets/defaults/avatar.jpg\" alt=\"User\" class=\"avatar-image\" />\n </div>\n </div>\n }\n\n <!-- Message content -->\n <div class=\"message-bubble\">\n @if (hasMultiMessages()) {\n <dc-message-orchestrator [messages]=\"multiMessages()\" [messageRole]=\"chatMessage().role\"></dc-message-orchestrator>\n } @else {\n <dc-message-orchestrator [messages]=\"[audioMessage()]\" [messageRole]=\"chatMessage().role\"></dc-message-orchestrator>\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 <img src=\"assets/defaults/avatar.jpg\" alt=\"User\" class=\"avatar-image\" />\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}.user-message .message-bubble{background-color:#0d5878;color:#fff;border-radius:18px 18px 0;margin-left: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;overflow:hidden}.avatar-image{width:100%;height:100%;object-fit:cover}.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: MessageOrchestratorComponent, selector: "dc-message-orchestrator", inputs: ["messages", "messageRole"], outputs: ["playAudio", "audioCompleted"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
2118
1935
|
}
|
|
2119
1936
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: ChatMessageComponent, decorators: [{
|
|
2120
1937
|
type: Component,
|
|
2121
|
-
args: [{ selector: 'dc-chat-message', standalone: true, imports: [CommonModule,
|
|
2122
|
-
}]
|
|
1938
|
+
args: [{ selector: 'dc-chat-message', standalone: true, imports: [CommonModule, MessageOrchestratorComponent], 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 <img src=\"assets/defaults/avatar.jpg\" alt=\"User\" class=\"avatar-image\" />\n </div>\n </div>\n }\n\n <!-- Message content -->\n <div class=\"message-bubble\">\n @if (hasMultiMessages()) {\n <dc-message-orchestrator [messages]=\"multiMessages()\" [messageRole]=\"chatMessage().role\"></dc-message-orchestrator>\n } @else {\n <dc-message-orchestrator [messages]=\"[audioMessage()]\" [messageRole]=\"chatMessage().role\"></dc-message-orchestrator>\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 <img src=\"assets/defaults/avatar.jpg\" alt=\"User\" class=\"avatar-image\" />\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}.user-message .message-bubble{background-color:#0d5878;color:#fff;border-radius:18px 18px 0;margin-left: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;overflow:hidden}.avatar-image{width:100%;height:100%;object-fit:cover}.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"] }]
|
|
1939
|
+
}] });
|
|
2123
1940
|
|
|
2124
1941
|
class ChatMessagesListComponent {
|
|
2125
1942
|
constructor() {
|
|
2126
1943
|
this.chatUserSettings = input.required();
|
|
2127
|
-
this.thinkingImg = input.required();
|
|
2128
1944
|
this.aiIcon = 'assets/default/ai.png';
|
|
2129
1945
|
this.conversationService = inject(ConversationService);
|
|
2130
|
-
|
|
2131
|
-
|
|
1946
|
+
this.inputMessages = input.required();
|
|
1947
|
+
// Inject ElementRef to access the component's host element
|
|
1948
|
+
this.elementRef = inject((ElementRef));
|
|
2132
1949
|
// Get messages and thinking state from the conversation service
|
|
2133
1950
|
this.messages = computed(() => {
|
|
2134
1951
|
// Get the actual array of messages from the signal by calling it as a function
|
|
2135
|
-
const allMessages = this.conversationService.
|
|
2136
|
-
|
|
1952
|
+
const allMessages = this.conversationService.getMessagesSignal()();
|
|
1953
|
+
console.log('Getting messages', allMessages);
|
|
1954
|
+
return allMessages.filter((message) => message.role !== ChatRole.System);
|
|
2137
1955
|
});
|
|
2138
1956
|
this.isThinking = this.conversationService.isThinking();
|
|
1957
|
+
// Use effect to automatically scroll to bottom when messages change
|
|
1958
|
+
effect(() => {
|
|
1959
|
+
// Access the messages to create a dependency
|
|
1960
|
+
const messages = this.messages();
|
|
1961
|
+
// Schedule the scroll after the view has been updated
|
|
1962
|
+
setTimeout(() => this.scrollToBottom(), 0);
|
|
1963
|
+
});
|
|
2139
1964
|
}
|
|
2140
|
-
|
|
2141
|
-
|
|
2142
|
-
this.
|
|
1965
|
+
ngAfterViewInit() {
|
|
1966
|
+
// Initial scroll to bottom when view is initialized
|
|
1967
|
+
this.scrollToBottom();
|
|
1968
|
+
}
|
|
1969
|
+
// Scroll to the bottom of the component itself with smooth animation
|
|
1970
|
+
scrollToBottom() {
|
|
1971
|
+
const element = this.elementRef.nativeElement;
|
|
1972
|
+
element.scrollTo({
|
|
1973
|
+
top: element.scrollHeight,
|
|
1974
|
+
behavior: 'smooth'
|
|
1975
|
+
});
|
|
2143
1976
|
}
|
|
2144
1977
|
// Track messages by their content and role for efficient rendering
|
|
2145
1978
|
trackByMessage(index, message) {
|
|
2146
1979
|
return `${message.role}-${index}-${message.content.substring(0, 20)}`;
|
|
2147
1980
|
}
|
|
2148
1981
|
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 },
|
|
1982
|
+
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 }, inputMessages: { classPropertyName: "inputMessages", publicName: "inputMessages", 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;height:100%}.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 }); }
|
|
2150
1983
|
}
|
|
2151
1984
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: ChatMessagesListComponent, decorators: [{
|
|
2152
1985
|
type: Component,
|
|
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;
|
|
2154
|
-
}] });
|
|
1986
|
+
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;height:100%}.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"] }]
|
|
1987
|
+
}], ctorParameters: () => [] });
|
|
2155
1988
|
|
|
2156
1989
|
const SpeedDescription = {
|
|
2157
1990
|
1: 'Muy Lento',
|
|
@@ -2329,99 +2162,43 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImpor
|
|
|
2329
2162
|
BadgeModule,
|
|
2330
2163
|
SkeletonModule,
|
|
2331
2164
|
TooltipModule,
|
|
2332
|
-
ProviderSelectorComponent
|
|
2165
|
+
ProviderSelectorComponent,
|
|
2333
2166
|
], 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
2167
|
}], ctorParameters: () => [], propDecorators: { tooltipRef: [{
|
|
2335
2168
|
type: ViewChild,
|
|
2336
2169
|
args: ['tooltipRef']
|
|
2337
2170
|
}] } });
|
|
2338
2171
|
|
|
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
|
-
};
|
|
2351
|
-
|
|
2352
2172
|
class DCChatComponent {
|
|
2353
2173
|
constructor() {
|
|
2354
|
-
this.agentCardService = inject(CONVERSATION_AI_TOKEN);
|
|
2355
2174
|
this.conversationBuilder = inject(DCConversationPromptBuilderService);
|
|
2356
2175
|
this.dialogService = inject(DialogService);
|
|
2357
2176
|
this.conversationService = inject(ConversationService);
|
|
2358
|
-
this.
|
|
2359
|
-
this.
|
|
2177
|
+
this.userDataExchange = inject(USER_DATA_EXCHANGE);
|
|
2178
|
+
this.chatUserSettings = this.userDataExchange.getUserChatSettings(); // Default to user data exchange
|
|
2179
|
+
// TODO: those are optional think if i need them,
|
|
2180
|
+
this.evaluatorAgentCard = input();
|
|
2360
2181
|
this.parseDict = input({});
|
|
2361
|
-
this.sendMessage = output();
|
|
2182
|
+
this.sendMessage = output(); // notifies whatever happened inside the chat
|
|
2362
2183
|
this.micSettings = { useWhisper: true, lang: 'en' };
|
|
2363
|
-
this.imageUser = `assets/default/user.svg`;
|
|
2364
|
-
this.thinkingImg = `assets/default/thinking.svg`;
|
|
2365
|
-
this.aiIcon = `assets/default/ai.svg`;
|
|
2366
2184
|
this.isInfoVisible = false;
|
|
2367
|
-
this.isChatSettingsVisible = false;
|
|
2368
|
-
this.isUserTalking = false;
|
|
2369
|
-
this.isGettingTranscription = false;
|
|
2370
2185
|
this.isAdmin = true;
|
|
2371
2186
|
// Get reactive state from services
|
|
2372
|
-
|
|
2373
|
-
this.
|
|
2374
|
-
this.score = this.evaluationService.getScore();
|
|
2187
|
+
// public messages = this.conversationService.getMessages();
|
|
2188
|
+
this.messages = signal([]);
|
|
2375
2189
|
}
|
|
2376
2190
|
async ngOnInit() {
|
|
2377
|
-
|
|
2378
|
-
|
|
2379
|
-
|
|
2191
|
+
if (this.conversationSettings) {
|
|
2192
|
+
await this.conversationService.initConversationWithSettings(this.conversationSettings);
|
|
2193
|
+
}
|
|
2194
|
+
else {
|
|
2195
|
+
await this.conversationService.initConversationWithAgentCard(this.agentCard);
|
|
2380
2196
|
}
|
|
2381
|
-
// Initialize conversation
|
|
2382
|
-
await this.conversationService.initConversation(this.agentCard, this.conversationBuilder, this.parseDict());
|
|
2383
2197
|
}
|
|
2384
2198
|
ngOnDestroy() {
|
|
2385
2199
|
// Mark conversation as destroyed to prevent async operations
|
|
2386
2200
|
this.conversationService.setDestroyed(true);
|
|
2387
2201
|
}
|
|
2388
|
-
// onUserMessage method has been moved to the chat-footer component
|
|
2389
|
-
/**
|
|
2390
|
-
* Handle microphone input finished
|
|
2391
|
-
*/
|
|
2392
|
-
async onMicFinished(eventBlob) {
|
|
2393
|
-
if (!(eventBlob instanceof Blob)) {
|
|
2394
|
-
if (!this.chatUserSettings.superHearing) {
|
|
2395
|
-
this.isUserTalking = false;
|
|
2396
|
-
}
|
|
2397
|
-
return;
|
|
2398
|
-
}
|
|
2399
|
-
// Check if audio is too small
|
|
2400
|
-
if (eventBlob.size < 10000) {
|
|
2401
|
-
return;
|
|
2402
|
-
}
|
|
2403
|
-
this.isUserTalking = false;
|
|
2404
|
-
this.isGettingTranscription = true;
|
|
2405
|
-
try {
|
|
2406
|
-
// Get transcription from audio
|
|
2407
|
-
const transcription = await this.agentCardService.getAudioTranscriptions(eventBlob, {
|
|
2408
|
-
conversationId: this.agentCard._id,
|
|
2409
|
-
});
|
|
2410
|
-
// Create message with transcription
|
|
2411
|
-
const message = {
|
|
2412
|
-
content: transcription.text,
|
|
2413
|
-
role: ChatRole.User,
|
|
2414
|
-
transcriptionTimestamps: transcription.words,
|
|
2415
|
-
};
|
|
2416
|
-
message['audioUrl'] = URL.createObjectURL(eventBlob);
|
|
2417
|
-
// Send message to conversation
|
|
2418
|
-
// The evaluation will happen automatically in the conversation service
|
|
2419
|
-
await this.conversationService.sendUserMessage(message);
|
|
2420
|
-
}
|
|
2421
|
-
finally {
|
|
2422
|
-
this.isGettingTranscription = false;
|
|
2423
|
-
}
|
|
2424
|
-
}
|
|
2425
2202
|
/**
|
|
2426
2203
|
* Open chat settings dialog
|
|
2427
2204
|
*/
|
|
@@ -2435,7 +2212,7 @@ class DCChatComponent {
|
|
|
2435
2212
|
closeOnEscape: true,
|
|
2436
2213
|
})
|
|
2437
2214
|
.onClose.subscribe(async () => {
|
|
2438
|
-
|
|
2215
|
+
// TODO: Que hacer cuando cambie las configuraciones del usario?, creo que si existe en exchange no necesito guardarlo y leer desde el servicio.
|
|
2439
2216
|
});
|
|
2440
2217
|
}
|
|
2441
2218
|
/**
|
|
@@ -2454,12 +2231,14 @@ class DCChatComponent {
|
|
|
2454
2231
|
await this.ngOnInit();
|
|
2455
2232
|
}
|
|
2456
2233
|
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\" [
|
|
2234
|
+
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 }, conversationSettings: { classPropertyName: "conversationSettings", publicName: "conversationSettings", 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\" [inputMessages]=\"messages()\"> </dc-chat-messages-list>\n\n <!-- Chat Footer -->\n <dc-chat-footer [micSettings]=\"micSettings\" [evaluatorAgentCard]=\"evaluatorAgentCard()\"> </dc-chat-footer>\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 | safeJson }}</pre>\n\n <h3>User Settings</h3>\n <pre>{{ chatUserSettings | safeJson }}</pre>\n </div>\n </p-dialog>\n</div>\n", styles: ["::ng-deep .p-drawer-content{padding:0!important}.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: "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"] }, { kind: "component", type: ChatMessagesListComponent, selector: "dc-chat-messages-list", inputs: ["chatUserSettings", "inputMessages"] }, { kind: "ngmodule", type: DialogModule }, { kind: "component", type: i1$3.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: "pipe", type: SafeJsonPipe, name: "safeJson" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
2458
2235
|
}
|
|
2459
2236
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: DCChatComponent, decorators: [{
|
|
2460
2237
|
type: Component,
|
|
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\" [
|
|
2462
|
-
}],
|
|
2238
|
+
args: [{ selector: 'dc-chat', standalone: true, imports: [CommonModule, ChatHeaderComponent, ChatFooterComponent, ChatMessagesListComponent, DialogModule, ProgressBarModule, SafeJsonPipe], 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\" [inputMessages]=\"messages()\"> </dc-chat-messages-list>\n\n <!-- Chat Footer -->\n <dc-chat-footer [micSettings]=\"micSettings\" [evaluatorAgentCard]=\"evaluatorAgentCard()\"> </dc-chat-footer>\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 | safeJson }}</pre>\n\n <h3>User Settings</h3>\n <pre>{{ chatUserSettings | safeJson }}</pre>\n </div>\n </p-dialog>\n</div>\n", styles: ["::ng-deep .p-drawer-content{padding:0!important}.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"] }]
|
|
2239
|
+
}], propDecorators: { chatUserSettings: [{
|
|
2240
|
+
type: Input
|
|
2241
|
+
}], conversationSettings: [{
|
|
2463
2242
|
type: Input
|
|
2464
2243
|
}], agentCard: [{
|
|
2465
2244
|
type: Input
|
|
@@ -2803,7 +2582,7 @@ class AccountPlatformForm {
|
|
|
2803
2582
|
}
|
|
2804
2583
|
}
|
|
2805
2584
|
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$
|
|
2585
|
+
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$4.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 }); }
|
|
2807
2586
|
}
|
|
2808
2587
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: AccountPlatformForm, decorators: [{
|
|
2809
2588
|
type: Component,
|
|
@@ -3283,7 +3062,7 @@ class DCConversationCardUIComponent {
|
|
|
3283
3062
|
this.onCardAction.emit({ event: 'delete', card: this.card() });
|
|
3284
3063
|
}
|
|
3285
3064
|
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$
|
|
3065
|
+
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$5.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$4.Card, selector: "p-card", inputs: ["header", "subheader", "style", "styleClass"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
3287
3066
|
}
|
|
3288
3067
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: DCConversationCardUIComponent, decorators: [{
|
|
3289
3068
|
type: Component,
|
|
@@ -3430,7 +3209,7 @@ class AgentCardListComponent extends PaginationBase {
|
|
|
3430
3209
|
}
|
|
3431
3210
|
}
|
|
3432
3211
|
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$
|
|
3212
|
+
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$4.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"] }] }); }
|
|
3434
3213
|
}
|
|
3435
3214
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: AgentCardListComponent, decorators: [{
|
|
3436
3215
|
type: Component,
|
|
@@ -3511,7 +3290,7 @@ class DcAgentCardDetailsComponent {
|
|
|
3511
3290
|
this.cdr.markForCheck();
|
|
3512
3291
|
}
|
|
3513
3292
|
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$
|
|
3293
|
+
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$4.Card, selector: "p-card", inputs: ["header", "subheader", "style", "styleClass"] }, { kind: "pipe", type: ParseCardPipe, name: "parseCard" }] }); }
|
|
3515
3294
|
}
|
|
3516
3295
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImport: i0, type: DcAgentCardDetailsComponent, decorators: [{
|
|
3517
3296
|
type: Component,
|
|
@@ -3520,6 +3299,19 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImpor
|
|
|
3520
3299
|
type: Input
|
|
3521
3300
|
}] } });
|
|
3522
3301
|
|
|
3302
|
+
const EvalResultStringDefinition = `
|
|
3303
|
+
interface EvalResult {
|
|
3304
|
+
score: number; // Score of the user's response 0 to 3
|
|
3305
|
+
feedback: string; // Feedback of the user's understanding of the conversation
|
|
3306
|
+
}`;
|
|
3307
|
+
const DefaultEvaluatorAgentCard = {
|
|
3308
|
+
task: 'Evaluate the user understanding of the lesson',
|
|
3309
|
+
messages: [],
|
|
3310
|
+
expectedResponseType: EvalResultStringDefinition,
|
|
3311
|
+
model: { id: 'gpt-4o-mini', provider: 'openai' },
|
|
3312
|
+
sources: [],
|
|
3313
|
+
};
|
|
3314
|
+
|
|
3523
3315
|
/*
|
|
3524
3316
|
* Public API Surface of ngx-agent-cards
|
|
3525
3317
|
*/
|
|
@@ -3529,5 +3321,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.4", ngImpor
|
|
|
3529
3321
|
* Generated bundle index. Do not edit.
|
|
3530
3322
|
*/
|
|
3531
3323
|
|
|
3532
|
-
export { AgentCardListComponent, AgentCardsAbstractService, AudioService, AudioSpeed, CONVERSATION_AI_TOKEN, ChatMessage,
|
|
3324
|
+
export { AgentCardListComponent, AgentCardsAbstractService, AudioService, AudioSpeed, CONVERSATION_AI_TOKEN, ChatMessage, ChatRole, ChatUserSettings, ConversationDTO, ConversationMessagesDTO, ConversationType, ConversationTypeOptions, DCAgentCardFormComponent, DCChatComponent, DCConversationCardUIComponent, DCConversationPromptBuilderService, DcAgentCardDetailsComponent, DefaultEvaluatorAgentCard, EAccountsPlatform, LangCodeDescriptionEs, MessageAudio, MessageOrchestratorComponent, ProviderSelectorComponent, TextEngineOptions, TextEngines, TextHighlighterComponent, USER_DATA_EXCHANGE, UserDataExchangeAbstractService, VoiceTTSOption, VoiceTTSOptions, WordTimestamps, buildObjectTTSRequest, characterCardStringDataDefinition, convertToHTML, defaultconvUserSettings, extractAudioAndTranscription, extractJsonFromResponse, markdownBasicToHTML, markdownToHTML$1 as markdownToHTML, markdownToHTML2, markdownToHtml, matchTranscription, provideChatAIService, provideUserDataExchange, removeAllEmojis, removeEmojis };
|
|
3533
3325
|
//# sourceMappingURL=dataclouder-ngx-agent-cards.mjs.map
|