@memori.ai/memori-react 8.38.8 → 8.40.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. package/CHANGELOG.md +20 -0
  2. package/dist/components/MemoriWidget/MemoriWidget.js +401 -87
  3. package/dist/components/MemoriWidget/MemoriWidget.js.map +1 -1
  4. package/dist/helpers/credits.d.ts +5 -2
  5. package/dist/helpers/credits.js +5 -1
  6. package/dist/helpers/credits.js.map +1 -1
  7. package/dist/helpers/nats/getNatsConfig.d.ts +5 -0
  8. package/dist/helpers/nats/getNatsConfig.js +29 -0
  9. package/dist/helpers/nats/getNatsConfig.js.map +1 -0
  10. package/dist/helpers/nats/useNats.d.ts +12 -0
  11. package/dist/helpers/nats/useNats.js +72 -0
  12. package/dist/helpers/nats/useNats.js.map +1 -0
  13. package/dist/helpers/nats/useNatsSession.d.ts +27 -0
  14. package/dist/helpers/nats/useNatsSession.js +108 -0
  15. package/dist/helpers/nats/useNatsSession.js.map +1 -0
  16. package/dist/version.d.ts +1 -1
  17. package/dist/version.js +1 -1
  18. package/esm/components/MemoriWidget/MemoriWidget.js +401 -87
  19. package/esm/components/MemoriWidget/MemoriWidget.js.map +1 -1
  20. package/esm/helpers/credits.d.ts +5 -2
  21. package/esm/helpers/credits.js +5 -1
  22. package/esm/helpers/credits.js.map +1 -1
  23. package/esm/helpers/nats/getNatsConfig.d.ts +5 -0
  24. package/esm/helpers/nats/getNatsConfig.js +25 -0
  25. package/esm/helpers/nats/getNatsConfig.js.map +1 -0
  26. package/esm/helpers/nats/useNats.d.ts +12 -0
  27. package/esm/helpers/nats/useNats.js +68 -0
  28. package/esm/helpers/nats/useNats.js.map +1 -0
  29. package/esm/helpers/nats/useNatsSession.d.ts +27 -0
  30. package/esm/helpers/nats/useNatsSession.js +103 -0
  31. package/esm/helpers/nats/useNatsSession.js.map +1 -0
  32. package/esm/version.d.ts +1 -1
  33. package/esm/version.js +1 -1
  34. package/package.json +3 -2
  35. package/src/components/MemoriWidget/MemoriWidget.tsx +546 -140
  36. package/src/components/StartPanel/StartPanel.stories.tsx +21 -0
  37. package/src/components/StartPanel/StartPanel.test.tsx +66 -1
  38. package/src/components/StartPanel/__snapshots__/StartPanel.test.tsx.snap +156 -0
  39. package/src/components/layouts/layouts.stories.tsx +28 -34
  40. package/src/helpers/credits.ts +16 -1
  41. package/src/helpers/nats/getNatsConfig.ts +69 -0
  42. package/src/helpers/nats/useNats.ts +122 -0
  43. package/src/helpers/nats/useNatsSession.ts +210 -0
  44. package/src/index.stories.tsx +19 -3
  45. package/src/version.ts +1 -1
@@ -0,0 +1,210 @@
1
+ // helpers/nats/useNatsSession.ts - Subscribe to a session's NATS channel.
2
+ //
3
+ // Opens a W3C WebSocket connection (via `wsconnect`), subscribes to the
4
+ // session subject (`sessions.<sessionId>`) and forwards every decoded event to
5
+ // `onMessage`. The cleanup (`nc.close()`) is essential: without it, every
6
+ // `sessionId` change would leave orphan connections / iterators.
7
+ import { useEffect, useRef } from 'react';
8
+ import { wsconnect, NatsConnection } from '@nats-io/nats-core';
9
+
10
+ /**
11
+ * `progress` event: job advancement updates.
12
+ */
13
+ export interface NatsProgressEvent {
14
+ eventType: 'progress';
15
+ jobId?: string;
16
+ currentStep?: number;
17
+ finalStep?: number;
18
+ message?: string;
19
+ startingTime?: string;
20
+ correlationID?: string;
21
+ }
22
+
23
+ /**
24
+ * `dialog.text_entered_response` event: same body returned by
25
+ * `POST /memori/v2/EnterTextAsync/{sessionId}` (via NATS).
26
+ */
27
+ export interface NatsDialogResponseEvent {
28
+ eventType: 'dialog_text_entered_response';
29
+ requestID?: string;
30
+ resultCode?: number;
31
+ resultMessage?: string;
32
+ currentState?: any;
33
+ correlationID?: string;
34
+ }
35
+
36
+ /**
37
+ * `error` event.
38
+ */
39
+ export interface NatsErrorEvent {
40
+ eventType: 'error';
41
+ errorCode?: string | number;
42
+ errorMessage?: string;
43
+ backtrace?: string;
44
+ correlationID?: string;
45
+ }
46
+
47
+ /**
48
+ * Discriminated union of all events received on the session subject.
49
+ */
50
+ export type NatsSessionEvent =
51
+ | NatsProgressEvent
52
+ | NatsDialogResponseEvent
53
+ | NatsErrorEvent;
54
+
55
+ function readString(
56
+ raw: Record<string, unknown>,
57
+ camelKey: string,
58
+ snakeKey: string
59
+ ): string | undefined {
60
+ const value = raw[camelKey] ?? raw[snakeKey];
61
+ return typeof value === 'string' ? value : undefined;
62
+ }
63
+
64
+ function readNumber(
65
+ raw: Record<string, unknown>,
66
+ camelKey: string,
67
+ snakeKey: string
68
+ ): number | undefined {
69
+ const value = raw[camelKey] ?? raw[snakeKey];
70
+ return typeof value === 'number' ? value : undefined;
71
+ }
72
+
73
+ /**
74
+ * Normalizes raw NATS payloads (snake_case or camelCase) into typed events.
75
+ */
76
+ export function normalizeNatsEvent(raw: Record<string, unknown>): NatsSessionEvent {
77
+ const eventType = (readString(raw, 'eventType', 'event_type') ??
78
+ 'unknown') as NatsSessionEvent['eventType'];
79
+
80
+ const correlationID =
81
+ readString(raw, 'correlationID', 'correlation_id') ??
82
+ readString(raw, 'correlationId', 'correlation_id');
83
+
84
+ if (eventType === 'progress') {
85
+ return {
86
+ eventType: 'progress',
87
+ jobId: readString(raw, 'jobId', 'job_id'),
88
+ currentStep: readNumber(raw, 'currentStep', 'current_step'),
89
+ finalStep: readNumber(raw, 'finalStep', 'final_step'),
90
+ message: typeof raw.message === 'string' ? raw.message : undefined,
91
+ startingTime: readString(raw, 'startingTime', 'starting_time'),
92
+ correlationID,
93
+ };
94
+ }
95
+
96
+ if (eventType === 'dialog_text_entered_response') {
97
+ return {
98
+ eventType: 'dialog_text_entered_response',
99
+ requestID:
100
+ typeof raw.requestID === 'string' ? raw.requestID : undefined,
101
+ resultCode:
102
+ typeof raw.resultCode === 'number' ? raw.resultCode : undefined,
103
+ resultMessage:
104
+ typeof raw.resultMessage === 'string' ? raw.resultMessage : undefined,
105
+ currentState: raw.currentState,
106
+ correlationID,
107
+ };
108
+ }
109
+
110
+ if (eventType === 'error') {
111
+ return {
112
+ eventType: 'error',
113
+ errorCode: (raw.errorCode ?? raw.error_code) as string | number | undefined,
114
+ errorMessage: readString(raw, 'errorMessage', 'error_message'),
115
+ backtrace: typeof raw.backtrace === 'string' ? raw.backtrace : undefined,
116
+ correlationID,
117
+ };
118
+ }
119
+
120
+ return {
121
+ eventType,
122
+ correlationID,
123
+ } as NatsSessionEvent;
124
+ }
125
+
126
+ /**
127
+ * Open a NATS WebSocket connection, subscribe to the session subject and
128
+ * forward decoded events to `onMessage`.
129
+ *
130
+ * `onMessage` is kept in a ref so callers do not need to memoize it: the
131
+ * connection is only (re)opened when `sessionId`, `natsWsUrl` or `natsToken`
132
+ * change.
133
+ *
134
+ * @param sessionId Current session UUID (subscription is skipped when falsy).
135
+ * @param natsWsUrl WebSocket URL of the NATS server.
136
+ * @param natsToken Bearer token for authentication.
137
+ * @param onMessage Callback invoked for each decoded event.
138
+ */
139
+ export function useNatsSession(
140
+ sessionId: string | undefined,
141
+ natsWsUrl: string | undefined,
142
+ natsToken: string | undefined,
143
+ onMessage: (event: NatsSessionEvent) => void
144
+ ) {
145
+ const connRef = useRef<NatsConnection | null>(null);
146
+ const onMessageRef = useRef(onMessage);
147
+
148
+ // Keep the latest callback without retriggering the connection effect.
149
+ useEffect(() => {
150
+ onMessageRef.current = onMessage;
151
+ }, [onMessage]);
152
+
153
+ useEffect(() => {
154
+ if (!sessionId || !natsWsUrl || !natsToken) {
155
+ console.debug(
156
+ '[NATS] subscription skipped (missing sessionId/url/token)',
157
+ { sessionId, hasUrl: !!natsWsUrl, hasToken: !!natsToken }
158
+ );
159
+ return;
160
+ }
161
+ let closed = false;
162
+ const subject = sessionId;
163
+
164
+ (async () => {
165
+ console.info('[NATS] connecting to', natsWsUrl, 'for session', sessionId);
166
+ const nc = await wsconnect({
167
+ servers: [natsWsUrl],
168
+ token: natsToken,
169
+ });
170
+ if (closed) {
171
+ console.debug(
172
+ '[NATS] connection established after cleanup, closing immediately'
173
+ );
174
+ await nc.close();
175
+ return;
176
+ }
177
+ connRef.current = nc;
178
+ console.info('[NATS] connected to', nc.getServer());
179
+
180
+ const sub = nc.subscribe(subject);
181
+ console.info('[NATS] subscribed to subject', subject);
182
+ let received = 0;
183
+ for await (const msg of sub) {
184
+ received += 1;
185
+ try {
186
+ const payload = normalizeNatsEvent(
187
+ msg.json<Record<string, unknown>>()
188
+ );
189
+ console.debug(
190
+ `[NATS] message #${received} on ${subject} (eventType=${payload.eventType})`,
191
+ payload
192
+ );
193
+ onMessageRef.current(payload);
194
+ } catch (err) {
195
+ console.error('[NATS] message decode error', err);
196
+ }
197
+ }
198
+ console.info(
199
+ `[NATS] subscription on ${subject} closed (received ${received} messages)`
200
+ );
201
+ })().catch(err => console.error('[NATS] connection error', err));
202
+
203
+ return () => {
204
+ console.info('[NATS] cleanup: closing connection for subject', subject);
205
+ closed = true;
206
+ connRef.current?.close();
207
+ connRef.current = null;
208
+ };
209
+ }, [sessionId, natsWsUrl, natsToken]);
210
+ }
@@ -22,7 +22,6 @@ export default meta;
22
22
 
23
23
  const Template: Story<Props> = args => <Memori {...args} />;
24
24
 
25
-
26
25
  export const WithInitialContextAndQuestion = Template.bind({});
27
26
  WithInitialContextAndQuestion.args = {
28
27
  ownerUserName: 'nzambello',
@@ -150,7 +149,8 @@ const piiDetectionConfig = {
150
149
  {
151
150
  id: 'iban',
152
151
  label: { it: 'IBAN', en: 'IBAN' },
153
- pattern: '\\b[A-Z]{2}\\d{2}(?:[ ]?[A-Z0-9]{4}){3,7}(?:[ ]?[A-Z0-9]{1,4})?\\b',
152
+ pattern:
153
+ '\\b[A-Z]{2}\\d{2}(?:[ ]?[A-Z0-9]{4}){3,7}(?:[ ]?[A-Z0-9]{1,4})?\\b',
154
154
  message: {
155
155
  it: 'Il messaggio contiene un codice IBAN.',
156
156
  en: 'The message contains an IBAN code.',
@@ -185,4 +185,20 @@ WithPiiDetection.args = {
185
185
  lang: 'it',
186
186
  }),
187
187
  },
188
- };
188
+ };
189
+
190
+ export const WithLocalNats = Template.bind({});
191
+ WithLocalNats.args = {
192
+ memoriName: 'test324',
193
+ ownerUserName: 'andrea.patini',
194
+ memoriID: 'd661a9ca-e907-4396-a986-5095ccd582d6',
195
+ ownerUserID: '69fcc557-9cb6-4e5e-b8ab-140cff975492',
196
+ tenantID: 'localhost:3000',
197
+ engineURL: 'http://localhost:7778/memori/v2',
198
+ apiURL: 'http://localhost:7778/api/v2',
199
+ baseURL: 'http://localhost:3000',
200
+ layout: 'FULLPAGE',
201
+ uiLang: 'IT',
202
+ spokenLang: 'IT',
203
+ integrationID: 'ee1c3d98-7819-4506-ba28-818e79ba86cb',
204
+ };
package/src/version.ts CHANGED
@@ -1,2 +1,2 @@
1
1
  // This file is auto-generated. Do not edit manually.
2
- export const version = '8.38.8';
2
+ export const version = '8.40.0';