rio-assist-widget 0.1.10 → 0.1.11
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/dist/rio-assist.js +71 -42
- package/package.json +1 -1
- package/src/components/conversations-panel/conversations-panel.styles.ts +22 -1
- package/src/components/conversations-panel/conversations-panel.template.ts +36 -21
- package/src/components/fullscreen/fullscreen.template.ts +3 -1
- package/src/components/rio-assist/rio-assist.ts +435 -55
- package/src/services/rioWebsocket.ts +5 -6
|
@@ -44,11 +44,13 @@ export class RioAssistWidget extends LitElement {
|
|
|
44
44
|
showConversations: { type: Boolean, state: true },
|
|
45
45
|
conversationSearch: { type: String, state: true },
|
|
46
46
|
conversationMenuId: { state: true },
|
|
47
|
-
conversationMenuPlacement: { state: true },
|
|
48
|
-
isFullscreen: { type: Boolean, state: true },
|
|
49
|
-
conversationScrollbar: { state: true },
|
|
47
|
+
conversationMenuPlacement: { state: true },
|
|
48
|
+
isFullscreen: { type: Boolean, state: true },
|
|
49
|
+
conversationScrollbar: { state: true },
|
|
50
50
|
showNewConversationShortcut: { type: Boolean, state: true },
|
|
51
51
|
conversations: { state: true },
|
|
52
|
+
conversationHistoryLoading: { type: Boolean, state: true },
|
|
53
|
+
activeConversationTitle: { state: true },
|
|
52
54
|
};
|
|
53
55
|
|
|
54
56
|
open = false;
|
|
@@ -83,25 +85,84 @@ export class RioAssistWidget extends LitElement {
|
|
|
83
85
|
|
|
84
86
|
conversationMenuPlacement: 'above' | 'below' = 'below';
|
|
85
87
|
|
|
86
|
-
isFullscreen = false;
|
|
87
|
-
|
|
88
|
-
showNewConversationShortcut = false;
|
|
89
|
-
|
|
90
|
-
conversationScrollbar = {
|
|
91
|
-
height: 0,
|
|
92
|
-
top: 0,
|
|
93
|
-
visible: false,
|
|
94
|
-
};
|
|
88
|
+
isFullscreen = false;
|
|
89
|
+
|
|
90
|
+
showNewConversationShortcut = false;
|
|
91
|
+
|
|
92
|
+
conversationScrollbar = {
|
|
93
|
+
height: 0,
|
|
94
|
+
top: 0,
|
|
95
|
+
visible: false,
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
conversationHistoryLoading = false;
|
|
99
|
+
|
|
100
|
+
private refreshConversationsAfterResponse = false;
|
|
101
|
+
|
|
102
|
+
activeConversationTitle: string | null = null;
|
|
103
|
+
|
|
104
|
+
private generateConversationId() {
|
|
105
|
+
if (!this.conversationUserId) {
|
|
106
|
+
this.conversationUserId = this.inferUserIdFromToken();
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
const userSegment = this.conversationUserId ?? 'user';
|
|
110
|
+
const id = `default-${userSegment}-${this.randomId(8)}`;
|
|
111
|
+
console.info('[RioAssist][conversation] gerando conversationId', id);
|
|
112
|
+
return id;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
private inferUserIdFromToken(): string | null {
|
|
116
|
+
const token = this.rioToken.trim();
|
|
117
|
+
if (!token || !token.includes('.')) {
|
|
118
|
+
return null;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
const [, payload] = token.split('.');
|
|
122
|
+
try {
|
|
123
|
+
const decoded = JSON.parse(atob(payload.replace(/-/g, '+').replace(/_/g, '/')));
|
|
124
|
+
const candidate =
|
|
125
|
+
decoded?.userId ??
|
|
126
|
+
decoded?.user_id ??
|
|
127
|
+
decoded?.sub ??
|
|
128
|
+
decoded?.id ??
|
|
129
|
+
decoded?.email ??
|
|
130
|
+
decoded?.username;
|
|
131
|
+
|
|
132
|
+
if (candidate && typeof candidate === 'string') {
|
|
133
|
+
return candidate.replace(/[^a-zA-Z0-9_-]/g, '');
|
|
134
|
+
}
|
|
135
|
+
} catch {
|
|
136
|
+
return null;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
return null;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
private randomId(length: number) {
|
|
143
|
+
const chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
|
|
144
|
+
let result = '';
|
|
145
|
+
for (let i = 0; i < length; i += 1) {
|
|
146
|
+
result += chars.charAt(Math.floor(Math.random() * chars.length));
|
|
147
|
+
}
|
|
148
|
+
return result;
|
|
149
|
+
}
|
|
95
150
|
|
|
96
151
|
private conversationScrollbarRaf: number | null = null;
|
|
97
152
|
|
|
98
|
-
private rioClient: RioWebsocketClient | null = null;
|
|
99
|
-
|
|
100
|
-
private rioUnsubscribe: (() => void) | null = null;
|
|
101
|
-
|
|
102
|
-
private loadingTimer: number | null = null;
|
|
103
|
-
|
|
104
|
-
private
|
|
153
|
+
private rioClient: RioWebsocketClient | null = null;
|
|
154
|
+
|
|
155
|
+
private rioUnsubscribe: (() => void) | null = null;
|
|
156
|
+
|
|
157
|
+
private loadingTimer: number | null = null;
|
|
158
|
+
|
|
159
|
+
private currentConversationId: string | null = null;
|
|
160
|
+
|
|
161
|
+
private conversationCounter = 0;
|
|
162
|
+
|
|
163
|
+
private conversationUserId: string | null = null;
|
|
164
|
+
|
|
165
|
+
private conversationScrollbarDraggingId: number | null = null;
|
|
105
166
|
|
|
106
167
|
private conversationScrollbarDragState: {
|
|
107
168
|
startY: number;
|
|
@@ -224,13 +285,28 @@ export class RioAssistWidget extends LitElement {
|
|
|
224
285
|
this.requestConversationHistory();
|
|
225
286
|
}
|
|
226
287
|
|
|
227
|
-
toggleNewConversationShortcut() {
|
|
228
|
-
this.showNewConversationShortcut = !this.showNewConversationShortcut;
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
288
|
+
toggleNewConversationShortcut() {
|
|
289
|
+
this.showNewConversationShortcut = !this.showNewConversationShortcut;
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
handleConversationSelect(conversationId: string) {
|
|
293
|
+
if (!conversationId) {
|
|
294
|
+
return;
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
this.showConversations = false;
|
|
298
|
+
this.conversationMenuId = null;
|
|
299
|
+
this.errorMessage = '';
|
|
300
|
+
this.currentConversationId = conversationId;
|
|
301
|
+
this.activeConversationTitle = this.lookupConversationTitle(conversationId);
|
|
302
|
+
|
|
303
|
+
console.info('[RioAssist][history] carregando conversa', conversationId);
|
|
304
|
+
this.requestConversationHistory(conversationId);
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
handleConversationSearch(event: InputEvent) {
|
|
308
|
+
this.conversationSearch = (event.target as HTMLInputElement).value;
|
|
309
|
+
}
|
|
234
310
|
|
|
235
311
|
handleConversationMenuToggle(event: Event, id: string) {
|
|
236
312
|
event.stopPropagation();
|
|
@@ -316,23 +392,25 @@ export class RioAssistWidget extends LitElement {
|
|
|
316
392
|
}
|
|
317
393
|
}
|
|
318
394
|
|
|
319
|
-
handleCreateConversation() {
|
|
320
|
-
if (!this.hasActiveConversation) {
|
|
321
|
-
return;
|
|
322
|
-
}
|
|
323
|
-
|
|
324
|
-
this.clearLoadingGuard();
|
|
325
|
-
this.isLoading = false;
|
|
326
|
-
this.messages = [];
|
|
327
|
-
this.message = '';
|
|
328
|
-
this.errorMessage = '';
|
|
329
|
-
this.showConversations = false;
|
|
330
|
-
this.teardownRioClient();
|
|
331
|
-
this.
|
|
332
|
-
this.
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
395
|
+
handleCreateConversation() {
|
|
396
|
+
if (!this.hasActiveConversation) {
|
|
397
|
+
return;
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
this.clearLoadingGuard();
|
|
401
|
+
this.isLoading = false;
|
|
402
|
+
this.messages = [];
|
|
403
|
+
this.message = '';
|
|
404
|
+
this.errorMessage = '';
|
|
405
|
+
this.showConversations = false;
|
|
406
|
+
this.teardownRioClient();
|
|
407
|
+
this.currentConversationId = this.generateConversationId();
|
|
408
|
+
this.activeConversationTitle = null;
|
|
409
|
+
this.showNewConversationShortcut = false;
|
|
410
|
+
this.dispatchEvent(
|
|
411
|
+
new CustomEvent('rioassist:new-conversation', {
|
|
412
|
+
bubbles: true,
|
|
413
|
+
composed: true,
|
|
336
414
|
}),
|
|
337
415
|
);
|
|
338
416
|
}
|
|
@@ -494,13 +572,18 @@ export class RioAssistWidget extends LitElement {
|
|
|
494
572
|
timestamp: Date.now(),
|
|
495
573
|
};
|
|
496
574
|
}
|
|
497
|
-
|
|
575
|
+
|
|
498
576
|
private async processMessage(rawValue: string) {
|
|
499
577
|
const content = rawValue.trim();
|
|
500
578
|
if (!content || this.isLoading) {
|
|
501
579
|
return;
|
|
502
580
|
}
|
|
503
581
|
|
|
582
|
+
if (!this.currentConversationId) {
|
|
583
|
+
this.currentConversationId = this.generateConversationId();
|
|
584
|
+
this.activeConversationTitle = null;
|
|
585
|
+
}
|
|
586
|
+
|
|
504
587
|
const wasEmptyConversation = this.messages.length === 0;
|
|
505
588
|
|
|
506
589
|
this.dispatchEvent(
|
|
@@ -519,20 +602,21 @@ export class RioAssistWidget extends LitElement {
|
|
|
519
602
|
this.messages = [...this.messages, userMessage];
|
|
520
603
|
if (wasEmptyConversation) {
|
|
521
604
|
this.showNewConversationShortcut = true;
|
|
605
|
+
this.refreshConversationsAfterResponse = true;
|
|
522
606
|
}
|
|
523
607
|
this.message = '';
|
|
524
608
|
this.errorMessage = '';
|
|
525
609
|
this.isLoading = true;
|
|
526
610
|
this.startLoadingGuard();
|
|
527
611
|
|
|
528
|
-
try {
|
|
529
|
-
const client = this.ensureRioClient();
|
|
530
|
-
await client.sendMessage(content);
|
|
531
|
-
} catch (error) {
|
|
532
|
-
this.clearLoadingGuard();
|
|
533
|
-
this.isLoading = false;
|
|
534
|
-
this.errorMessage = error instanceof Error
|
|
535
|
-
? error.message
|
|
612
|
+
try {
|
|
613
|
+
const client = this.ensureRioClient();
|
|
614
|
+
await client.sendMessage(content, this.currentConversationId);
|
|
615
|
+
} catch (error) {
|
|
616
|
+
this.clearLoadingGuard();
|
|
617
|
+
this.isLoading = false;
|
|
618
|
+
this.errorMessage = error instanceof Error
|
|
619
|
+
? error.message
|
|
536
620
|
: 'Nao foi possivel enviar a mensagem para o agente.';
|
|
537
621
|
}
|
|
538
622
|
}
|
|
@@ -559,14 +643,26 @@ export class RioAssistWidget extends LitElement {
|
|
|
559
643
|
private handleIncomingMessage(message: RioIncomingMessage) {
|
|
560
644
|
if (this.isHistoryPayload(message)) {
|
|
561
645
|
this.logHistoryPayload(message);
|
|
562
|
-
this.
|
|
646
|
+
this.handleHistoryPayload(message.data);
|
|
563
647
|
return;
|
|
564
648
|
}
|
|
565
649
|
|
|
650
|
+
console.info('[RioAssist][ws] resposta de mensagem recebida', {
|
|
651
|
+
action: message.action ?? 'message',
|
|
652
|
+
text: message.text,
|
|
653
|
+
raw: message.raw,
|
|
654
|
+
data: message.data,
|
|
655
|
+
});
|
|
656
|
+
|
|
566
657
|
const assistantMessage = this.createMessage('assistant', message.text);
|
|
567
658
|
this.messages = [...this.messages, assistantMessage];
|
|
568
659
|
this.clearLoadingGuard();
|
|
569
660
|
this.isLoading = false;
|
|
661
|
+
|
|
662
|
+
if (this.refreshConversationsAfterResponse) {
|
|
663
|
+
this.refreshConversationsAfterResponse = false;
|
|
664
|
+
this.requestConversationHistory();
|
|
665
|
+
}
|
|
570
666
|
}
|
|
571
667
|
|
|
572
668
|
private teardownRioClient() {
|
|
@@ -591,9 +687,32 @@ export class RioAssistWidget extends LitElement {
|
|
|
591
687
|
limit,
|
|
592
688
|
});
|
|
593
689
|
|
|
690
|
+
this.conversationHistoryLoading = true;
|
|
594
691
|
await client.requestHistory({ conversationId, limit });
|
|
595
692
|
} catch (error) {
|
|
596
693
|
console.error('[RioAssist][history] erro ao solicitar historico', error);
|
|
694
|
+
this.conversationHistoryLoading = false;
|
|
695
|
+
}
|
|
696
|
+
}
|
|
697
|
+
|
|
698
|
+
private handleHistoryPayload(payload: unknown) {
|
|
699
|
+
const entries = this.extractHistoryEntries(payload);
|
|
700
|
+
const conversationId = this.extractConversationId(payload);
|
|
701
|
+
|
|
702
|
+
if (conversationId !== null && conversationId !== undefined) {
|
|
703
|
+
this.applyMessageHistory(entries, conversationId);
|
|
704
|
+
return;
|
|
705
|
+
}
|
|
706
|
+
|
|
707
|
+
if (this.isMessageHistoryEntries(entries)) {
|
|
708
|
+
this.applyMessageHistory(entries);
|
|
709
|
+
return;
|
|
710
|
+
}
|
|
711
|
+
|
|
712
|
+
this.applyConversationHistoryFromEntries(entries);
|
|
713
|
+
|
|
714
|
+
if (this.refreshConversationsAfterResponse) {
|
|
715
|
+
this.refreshConversationsAfterResponse = false;
|
|
597
716
|
}
|
|
598
717
|
}
|
|
599
718
|
|
|
@@ -630,11 +749,11 @@ export class RioAssistWidget extends LitElement {
|
|
|
630
749
|
console.info(label, message.raw);
|
|
631
750
|
}
|
|
632
751
|
|
|
633
|
-
private
|
|
634
|
-
const entries = this.extractHistoryEntries(payload);
|
|
752
|
+
private applyConversationHistoryFromEntries(entries: unknown[]) {
|
|
635
753
|
if (entries.length === 0) {
|
|
636
754
|
console.info('[RioAssist][history] payload sem itens para montar lista de conversas');
|
|
637
755
|
this.conversations = [];
|
|
756
|
+
this.conversationHistoryLoading = false;
|
|
638
757
|
return;
|
|
639
758
|
}
|
|
640
759
|
|
|
@@ -674,9 +793,44 @@ export class RioAssistWidget extends LitElement {
|
|
|
674
793
|
});
|
|
675
794
|
|
|
676
795
|
this.conversations = conversations;
|
|
796
|
+
this.conversationHistoryLoading = false;
|
|
797
|
+
this.syncActiveConversationTitle();
|
|
677
798
|
console.info('[RioAssist][history] conversas normalizadas', conversations);
|
|
678
799
|
}
|
|
679
800
|
|
|
801
|
+
private applyMessageHistory(entries: unknown[], conversationId?: string | null) {
|
|
802
|
+
if (entries.length === 0) {
|
|
803
|
+
console.info('[RioAssist][history] lista de mensagens vazia', { conversationId });
|
|
804
|
+
this.messages = [];
|
|
805
|
+
this.showConversations = false;
|
|
806
|
+
this.clearLoadingGuard();
|
|
807
|
+
this.isLoading = false;
|
|
808
|
+
this.conversationHistoryLoading = false;
|
|
809
|
+
return;
|
|
810
|
+
}
|
|
811
|
+
|
|
812
|
+
const normalized = entries.flatMap((entry, index) =>
|
|
813
|
+
this.normalizeHistoryMessages(entry as Record<string, unknown>, index),
|
|
814
|
+
);
|
|
815
|
+
|
|
816
|
+
if (conversationId) {
|
|
817
|
+
this.currentConversationId = conversationId;
|
|
818
|
+
}
|
|
819
|
+
|
|
820
|
+
this.messages = normalized;
|
|
821
|
+
this.showConversations = false;
|
|
822
|
+
this.clearLoadingGuard();
|
|
823
|
+
this.isLoading = false;
|
|
824
|
+
this.showNewConversationShortcut = normalized.length > 0;
|
|
825
|
+
this.conversationHistoryLoading = false;
|
|
826
|
+
this.refreshConversationsAfterResponse = false;
|
|
827
|
+
|
|
828
|
+
console.info('[RioAssist][history] mensagens carregadas', {
|
|
829
|
+
conversationId: conversationId ?? null,
|
|
830
|
+
total: normalized.length,
|
|
831
|
+
});
|
|
832
|
+
}
|
|
833
|
+
|
|
680
834
|
private extractHistoryEntries(payload: unknown): unknown[] {
|
|
681
835
|
if (Array.isArray(payload)) {
|
|
682
836
|
return payload;
|
|
@@ -709,6 +863,62 @@ export class RioAssistWidget extends LitElement {
|
|
|
709
863
|
return [];
|
|
710
864
|
}
|
|
711
865
|
|
|
866
|
+
private extractConversationId(payload: unknown): string | null | undefined {
|
|
867
|
+
if (payload && typeof payload === 'object') {
|
|
868
|
+
const record = payload as Record<string, unknown>;
|
|
869
|
+
const candidates = [
|
|
870
|
+
record.conversationId,
|
|
871
|
+
record.conversationUUID,
|
|
872
|
+
record.conversationUuid,
|
|
873
|
+
record.uuid,
|
|
874
|
+
record.id,
|
|
875
|
+
];
|
|
876
|
+
|
|
877
|
+
for (const candidate of candidates) {
|
|
878
|
+
if (candidate === null) {
|
|
879
|
+
return null;
|
|
880
|
+
}
|
|
881
|
+
|
|
882
|
+
if (candidate !== undefined) {
|
|
883
|
+
return String(candidate);
|
|
884
|
+
}
|
|
885
|
+
}
|
|
886
|
+
}
|
|
887
|
+
|
|
888
|
+
return undefined;
|
|
889
|
+
}
|
|
890
|
+
|
|
891
|
+
private isMessageHistoryEntries(entries: unknown[]) {
|
|
892
|
+
return entries.some((entry) => this.looksLikeMessageHistoryEntry(entry));
|
|
893
|
+
}
|
|
894
|
+
|
|
895
|
+
private looksLikeMessageHistoryEntry(entry: unknown) {
|
|
896
|
+
if (!entry || typeof entry !== 'object') {
|
|
897
|
+
return false;
|
|
898
|
+
}
|
|
899
|
+
|
|
900
|
+
const item = entry as Record<string, unknown>;
|
|
901
|
+
const role = item.role ?? item.sender ?? item.from ?? item.author ?? item.type;
|
|
902
|
+
if (typeof role === 'string' && role.trim().length > 0) {
|
|
903
|
+
return true;
|
|
904
|
+
}
|
|
905
|
+
|
|
906
|
+
if (
|
|
907
|
+
typeof item.content === 'string' ||
|
|
908
|
+
typeof item.message === 'string' ||
|
|
909
|
+
typeof item.text === 'string' ||
|
|
910
|
+
typeof item.response === 'string'
|
|
911
|
+
) {
|
|
912
|
+
return true;
|
|
913
|
+
}
|
|
914
|
+
|
|
915
|
+
if (Array.isArray(item.parts) && item.parts.length > 0) {
|
|
916
|
+
return true;
|
|
917
|
+
}
|
|
918
|
+
|
|
919
|
+
return false;
|
|
920
|
+
}
|
|
921
|
+
|
|
712
922
|
private normalizeConversationItem(
|
|
713
923
|
value: Record<string, unknown>,
|
|
714
924
|
index: number,
|
|
@@ -751,6 +961,176 @@ export class RioAssistWidget extends LitElement {
|
|
|
751
961
|
return { id, title, updatedAt };
|
|
752
962
|
}
|
|
753
963
|
|
|
964
|
+
private normalizeHistoryMessages(
|
|
965
|
+
value: Record<string, unknown>,
|
|
966
|
+
index: number,
|
|
967
|
+
): ChatMessage[] {
|
|
968
|
+
const messages: ChatMessage[] = [];
|
|
969
|
+
|
|
970
|
+
const rawUserText = value.message ?? value.question ?? value.query ?? value.text ?? value.content;
|
|
971
|
+
const userText = typeof rawUserText === 'string' ? rawUserText.trim() : '';
|
|
972
|
+
|
|
973
|
+
const rawResponseText =
|
|
974
|
+
value.response ?? value.answer ?? value.reply ?? value.completion ?? value.body ?? value.preview;
|
|
975
|
+
const responseText = typeof rawResponseText === 'string' ? rawResponseText.trim() : '';
|
|
976
|
+
|
|
977
|
+
const rawId = value.id ?? value.messageId ?? value.uuid ?? value.conversationMessageId;
|
|
978
|
+
const baseId = rawId !== undefined && rawId !== null
|
|
979
|
+
? String(rawId)
|
|
980
|
+
: `history-${index + 1}`;
|
|
981
|
+
|
|
982
|
+
const userTimestampValue =
|
|
983
|
+
value.timestamp ??
|
|
984
|
+
value.createdAt ??
|
|
985
|
+
value.created_at ??
|
|
986
|
+
value.date ??
|
|
987
|
+
value.time;
|
|
988
|
+
const assistantTimestampValue =
|
|
989
|
+
value.responseTimestamp ??
|
|
990
|
+
value.responseTime ??
|
|
991
|
+
value.responseDate ??
|
|
992
|
+
value.response_at ??
|
|
993
|
+
value.updatedAt ??
|
|
994
|
+
value.updated_at;
|
|
995
|
+
|
|
996
|
+
const userTimestamp = this.parseTimestamp(userTimestampValue);
|
|
997
|
+
const assistantTimestamp = this.parseTimestamp(
|
|
998
|
+
assistantTimestampValue,
|
|
999
|
+
userTimestamp + 1,
|
|
1000
|
+
);
|
|
1001
|
+
|
|
1002
|
+
if (responseText) {
|
|
1003
|
+
if (userText) {
|
|
1004
|
+
messages.push({
|
|
1005
|
+
id: `${baseId}-user`,
|
|
1006
|
+
role: 'user',
|
|
1007
|
+
text: userText,
|
|
1008
|
+
html: this.renderMarkdown(userText),
|
|
1009
|
+
timestamp: userTimestamp,
|
|
1010
|
+
});
|
|
1011
|
+
}
|
|
1012
|
+
|
|
1013
|
+
messages.push({
|
|
1014
|
+
id: `${baseId}-assistant`,
|
|
1015
|
+
role: 'assistant',
|
|
1016
|
+
text: responseText,
|
|
1017
|
+
html: this.renderMarkdown(responseText),
|
|
1018
|
+
timestamp: assistantTimestamp,
|
|
1019
|
+
});
|
|
1020
|
+
} else if (userText) {
|
|
1021
|
+
// Se n�o tiver resposta, n�o exibimos a mensagem do usuario isolada.
|
|
1022
|
+
return [];
|
|
1023
|
+
}
|
|
1024
|
+
|
|
1025
|
+
if (messages.length > 0) {
|
|
1026
|
+
return messages;
|
|
1027
|
+
}
|
|
1028
|
+
|
|
1029
|
+
const fallback = this.normalizeSingleHistoryMessage(value, index);
|
|
1030
|
+
return fallback ? [fallback] : [];
|
|
1031
|
+
}
|
|
1032
|
+
|
|
1033
|
+
private normalizeSingleHistoryMessage(
|
|
1034
|
+
value: Record<string, unknown>,
|
|
1035
|
+
index: number,
|
|
1036
|
+
): ChatMessage | null {
|
|
1037
|
+
const rawText =
|
|
1038
|
+
value.text ??
|
|
1039
|
+
value.message ??
|
|
1040
|
+
value.content ??
|
|
1041
|
+
value.response ??
|
|
1042
|
+
value.body ??
|
|
1043
|
+
value.preview;
|
|
1044
|
+
|
|
1045
|
+
const text = typeof rawText === 'string' && rawText.trim().length > 0
|
|
1046
|
+
? rawText
|
|
1047
|
+
: '';
|
|
1048
|
+
|
|
1049
|
+
if (!text) {
|
|
1050
|
+
return null;
|
|
1051
|
+
}
|
|
1052
|
+
|
|
1053
|
+
const role = this.normalizeRole(
|
|
1054
|
+
value.role ??
|
|
1055
|
+
value.sender ??
|
|
1056
|
+
value.from ??
|
|
1057
|
+
value.author ??
|
|
1058
|
+
value.type ??
|
|
1059
|
+
value.direction,
|
|
1060
|
+
);
|
|
1061
|
+
|
|
1062
|
+
const rawId = value.id ?? value.messageId ?? value.uuid ?? value.conversationMessageId;
|
|
1063
|
+
const id = rawId !== undefined && rawId !== null
|
|
1064
|
+
? String(rawId)
|
|
1065
|
+
: `history-message-${index + 1}`;
|
|
1066
|
+
|
|
1067
|
+
const timestampValue =
|
|
1068
|
+
value.timestamp ??
|
|
1069
|
+
value.createdAt ??
|
|
1070
|
+
value.created_at ??
|
|
1071
|
+
value.updatedAt ??
|
|
1072
|
+
value.updated_at ??
|
|
1073
|
+
value.date ??
|
|
1074
|
+
value.time;
|
|
1075
|
+
|
|
1076
|
+
const timestamp = this.parseTimestamp(timestampValue);
|
|
1077
|
+
|
|
1078
|
+
return {
|
|
1079
|
+
id,
|
|
1080
|
+
role,
|
|
1081
|
+
text,
|
|
1082
|
+
html: this.renderMarkdown(text),
|
|
1083
|
+
timestamp,
|
|
1084
|
+
};
|
|
1085
|
+
}
|
|
1086
|
+
|
|
1087
|
+
private normalizeRole(value: unknown): ChatRole {
|
|
1088
|
+
if (typeof value === 'string') {
|
|
1089
|
+
const normalized = value.toLowerCase();
|
|
1090
|
+
if (normalized.includes('user') || normalized.includes('client')) {
|
|
1091
|
+
return 'user';
|
|
1092
|
+
}
|
|
1093
|
+
if (normalized.includes('assistant') || normalized.includes('agent') || normalized.includes('bot')) {
|
|
1094
|
+
return 'assistant';
|
|
1095
|
+
}
|
|
1096
|
+
}
|
|
1097
|
+
|
|
1098
|
+
return 'assistant';
|
|
1099
|
+
}
|
|
1100
|
+
|
|
1101
|
+
private parseTimestamp(value: unknown, fallback?: number) {
|
|
1102
|
+
const parsed = Date.parse(this.toIsoString(value));
|
|
1103
|
+
if (Number.isFinite(parsed)) {
|
|
1104
|
+
return parsed;
|
|
1105
|
+
}
|
|
1106
|
+
|
|
1107
|
+
if (Number.isFinite(fallback ?? NaN)) {
|
|
1108
|
+
return fallback as number;
|
|
1109
|
+
}
|
|
1110
|
+
|
|
1111
|
+
return Date.now();
|
|
1112
|
+
}
|
|
1113
|
+
|
|
1114
|
+
private lookupConversationTitle(conversationId: string | null) {
|
|
1115
|
+
if (!conversationId) {
|
|
1116
|
+
return null;
|
|
1117
|
+
}
|
|
1118
|
+
|
|
1119
|
+
const found = this.conversations.find((item) => item.id === conversationId);
|
|
1120
|
+
return found ? found.title : null;
|
|
1121
|
+
}
|
|
1122
|
+
|
|
1123
|
+
private syncActiveConversationTitle() {
|
|
1124
|
+
if (!this.currentConversationId) {
|
|
1125
|
+
return;
|
|
1126
|
+
}
|
|
1127
|
+
|
|
1128
|
+
const title = this.lookupConversationTitle(this.currentConversationId);
|
|
1129
|
+
if (title) {
|
|
1130
|
+
this.activeConversationTitle = title;
|
|
1131
|
+
}
|
|
1132
|
+
}
|
|
1133
|
+
|
|
754
1134
|
private toIsoString(value: unknown) {
|
|
755
1135
|
if (typeof value === 'string' || typeof value === 'number') {
|
|
756
1136
|
const date = new Date(value);
|
|
@@ -27,28 +27,27 @@ export class RioWebsocketClient {
|
|
|
27
27
|
return this.token === value;
|
|
28
28
|
}
|
|
29
29
|
|
|
30
|
-
async sendMessage(message: string) {
|
|
30
|
+
async sendMessage(message: string, conversationId?: string | null) {
|
|
31
31
|
const socket = await this.ensureConnection();
|
|
32
32
|
|
|
33
33
|
const payload = {
|
|
34
34
|
action: 'sendMessage',
|
|
35
35
|
message,
|
|
36
|
+
conversationId: conversationId ?? null,
|
|
36
37
|
};
|
|
37
38
|
|
|
39
|
+
console.info('[RioAssist][ws] enviando payload de mensagem', payload);
|
|
38
40
|
socket.send(JSON.stringify(payload));
|
|
39
41
|
}
|
|
40
42
|
|
|
41
|
-
async requestHistory(options: { conversationId?: string; limit?: number } = {}) {
|
|
43
|
+
async requestHistory(options: { conversationId?: string | null; limit?: number } = {}) {
|
|
42
44
|
const socket = await this.ensureConnection();
|
|
43
45
|
const payload: Record<string, unknown> = {
|
|
44
46
|
action: 'getHistory',
|
|
45
47
|
limit: options.limit ?? 50,
|
|
48
|
+
conversationId: options.conversationId ?? null,
|
|
46
49
|
};
|
|
47
50
|
|
|
48
|
-
if (options.conversationId) {
|
|
49
|
-
payload.conversationId = options.conversationId;
|
|
50
|
-
}
|
|
51
|
-
|
|
52
51
|
socket.send(JSON.stringify(payload));
|
|
53
52
|
}
|
|
54
53
|
|