@vertesia/client 0.56.0 → 0.58.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 (56) hide show
  1. package/lib/cjs/store/FilesApi.js +29 -19
  2. package/lib/cjs/store/FilesApi.js.map +1 -1
  3. package/lib/cjs/store/ObjectsApi.js +22 -20
  4. package/lib/cjs/store/ObjectsApi.js.map +1 -1
  5. package/lib/cjs/store/WorkflowsApi.js +82 -16
  6. package/lib/cjs/store/WorkflowsApi.js.map +1 -1
  7. package/lib/esm/store/FilesApi.js +29 -19
  8. package/lib/esm/store/FilesApi.js.map +1 -1
  9. package/lib/esm/store/ObjectsApi.js +22 -20
  10. package/lib/esm/store/ObjectsApi.js.map +1 -1
  11. package/lib/esm/store/WorkflowsApi.js +82 -16
  12. package/lib/esm/store/WorkflowsApi.js.map +1 -1
  13. package/lib/tsconfig.tsbuildinfo +1 -1
  14. package/lib/types/AccountApi.d.ts +1 -0
  15. package/lib/types/AccountsApi.d.ts +1 -0
  16. package/lib/types/AnalyticsApi.d.ts +1 -0
  17. package/lib/types/ApiKeysApi.d.ts +1 -0
  18. package/lib/types/CommandsApi.d.ts +1 -0
  19. package/lib/types/EnvironmentsApi.d.ts +1 -0
  20. package/lib/types/IamApi.d.ts +1 -0
  21. package/lib/types/InteractionBase.d.ts +1 -0
  22. package/lib/types/InteractionsApi.d.ts +1 -0
  23. package/lib/types/PluginsApi.d.ts +1 -0
  24. package/lib/types/ProjectsApi.d.ts +1 -0
  25. package/lib/types/PromptsApi.d.ts +1 -0
  26. package/lib/types/RefsApi.d.ts +1 -0
  27. package/lib/types/RunsApi.d.ts +1 -0
  28. package/lib/types/StreamSource.d.ts +1 -0
  29. package/lib/types/TrainingApi.d.ts +1 -0
  30. package/lib/types/UsersApi.d.ts +1 -0
  31. package/lib/types/client.d.ts +1 -0
  32. package/lib/types/execute.d.ts +1 -0
  33. package/lib/types/index.d.ts +1 -0
  34. package/lib/types/nodejs/NodeStreamSource.d.ts +1 -0
  35. package/lib/types/nodejs/index.d.ts +1 -0
  36. package/lib/types/store/AgentsApi.d.ts +1 -0
  37. package/lib/types/store/AnalyzeDocApi.d.ts +1 -0
  38. package/lib/types/store/CollectionsApi.d.ts +1 -0
  39. package/lib/types/store/CommandsApi.d.ts +1 -0
  40. package/lib/types/store/EmbeddingsApi.d.ts +1 -0
  41. package/lib/types/store/FilesApi.d.ts +2 -0
  42. package/lib/types/store/FilesApi.d.ts.map +1 -1
  43. package/lib/types/store/ObjectsApi.d.ts +2 -7
  44. package/lib/types/store/ObjectsApi.d.ts.map +1 -1
  45. package/lib/types/store/TypesApi.d.ts +1 -0
  46. package/lib/types/store/WorkflowsApi.d.ts +1 -0
  47. package/lib/types/store/WorkflowsApi.d.ts.map +1 -1
  48. package/lib/types/store/client.d.ts +1 -0
  49. package/lib/types/store/errors.d.ts +1 -0
  50. package/lib/types/store/index.d.ts +1 -0
  51. package/lib/vertesia-client.js +1 -1
  52. package/lib/vertesia-client.js.map +1 -1
  53. package/package.json +4 -4
  54. package/src/store/FilesApi.ts +83 -55
  55. package/src/store/ObjectsApi.ts +326 -313
  56. package/src/store/WorkflowsApi.ts +91 -17
@@ -73,14 +73,45 @@ export class WorkflowsApi extends ApiTopic {
73
73
 
74
74
  async streamMessages(runId: string, onMessage?: (message: AgentMessage) => void, since?: number): Promise<void> {
75
75
  return new Promise((resolve, reject) => {
76
- const setupStream = async () => {
76
+ let reconnectAttempts = 0;
77
+ let lastMessageTimestamp = since || 0;
78
+ let isClosed = false;
79
+ let currentSse: EventSource | null = null;
80
+ let interval: NodeJS.Timeout | null = null;
81
+
82
+ const maxReconnectAttempts = 10;
83
+ const baseDelay = 1000; // 1 second base delay
84
+ const maxDelay = 30000; // 30 seconds max delay
85
+
86
+ const calculateBackoffDelay = (attempts: number): number => {
87
+ const exponentialDelay = Math.min(baseDelay * Math.pow(2, attempts), maxDelay);
88
+ // Add jitter to prevent thundering herd
89
+ const jitter = Math.random() * 0.1 * exponentialDelay;
90
+ return exponentialDelay + jitter;
91
+ };
92
+
93
+ const cleanup = () => {
94
+ if (interval) {
95
+ clearInterval(interval);
96
+ interval = null;
97
+ }
98
+ if (currentSse) {
99
+ currentSse.close();
100
+ currentSse = null;
101
+ }
102
+ };
103
+
104
+ const setupStream = async (isReconnect: boolean = false) => {
105
+ if (isClosed) return;
106
+
77
107
  try {
78
108
  const EventSourceImpl = await EventSourceProvider();
79
109
  const client = this.client as VertesiaClient;
80
110
  const streamUrl = new URL(client.workflows.baseUrl + "/runs/" + runId + "/stream");
81
111
 
82
- if (since) {
83
- streamUrl.searchParams.set("since", since.toString());
112
+ // Use the timestamp of the last received message for reconnection
113
+ if (lastMessageTimestamp > 0) {
114
+ streamUrl.searchParams.set("since", lastMessageTimestamp.toString());
84
115
  }
85
116
 
86
117
  const bearerToken = client._auth ? await client._auth() : undefined;
@@ -91,18 +122,25 @@ export class WorkflowsApi extends ApiTopic {
91
122
 
92
123
  const token = bearerToken.split(" ")[1];
93
124
  streamUrl.searchParams.set("access_token", token);
125
+
126
+ if (isReconnect) {
127
+ console.log(`Reconnecting to SSE stream for run ${runId} (attempt ${reconnectAttempts + 1}/${maxReconnectAttempts})`);
128
+ }
94
129
 
95
130
  const sse = new EventSourceImpl(streamUrl.href);
96
- let isClosed = false;
131
+ currentSse = sse;
97
132
 
98
133
  // Prevent Node from exiting prematurely
99
- const interval = setInterval(() => {}, 1000);
134
+ interval = setInterval(() => {}, 1000);
100
135
 
101
- // Cleanup when stream resolves
102
- const cleanup = () => {
103
- clearInterval(interval);
136
+ sse.onopen = () => {
137
+ if (isReconnect) {
138
+ console.log(`Successfully reconnected to SSE stream for run ${runId}`);
139
+ }
140
+ // Reset reconnect attempts on successful connection
141
+ reconnectAttempts = 0;
104
142
  };
105
-
143
+
106
144
  sse.onmessage = (ev: MessageEvent) => {
107
145
  if (!ev.data || ev.data.startsWith(":")) {
108
146
  console.log("Received comment or heartbeat; ignoring it.: ", ev.data);
@@ -111,6 +149,12 @@ export class WorkflowsApi extends ApiTopic {
111
149
 
112
150
  try {
113
151
  const message = JSON.parse(ev.data) as AgentMessage;
152
+
153
+ // Update last message timestamp for reconnection
154
+ if (message.timestamp) {
155
+ lastMessageTimestamp = Math.max(lastMessageTimestamp, message.timestamp);
156
+ }
157
+
114
158
  if (onMessage) onMessage(message);
115
159
 
116
160
  // Only close the stream when the main workstream completes
@@ -118,7 +162,6 @@ export class WorkflowsApi extends ApiTopic {
118
162
  console.log("Closing stream due to COMPLETE message from main workstream");
119
163
  if (!isClosed) {
120
164
  isClosed = true;
121
- sse.close();
122
165
  cleanup();
123
166
  resolve();
124
167
  }
@@ -131,21 +174,52 @@ export class WorkflowsApi extends ApiTopic {
131
174
  };
132
175
 
133
176
  sse.onerror = (err: any) => {
134
- if (!isClosed) {
135
- console.error("SSE stream error:", err);
177
+ if (isClosed) return;
178
+
179
+ console.warn(`SSE stream error for run ${runId}:`, err);
180
+ cleanup();
181
+
182
+ // Check if we should attempt reconnection
183
+ if (reconnectAttempts < maxReconnectAttempts) {
184
+ const delay = calculateBackoffDelay(reconnectAttempts);
185
+ console.log(`Attempting to reconnect in ${delay}ms (attempt ${reconnectAttempts + 1}/${maxReconnectAttempts})`);
186
+
187
+ reconnectAttempts++;
188
+ setTimeout(() => {
189
+ if (!isClosed) {
190
+ setupStream(true);
191
+ }
192
+ }, delay);
193
+ } else {
194
+ console.error(`Failed to reconnect to SSE stream for run ${runId} after ${maxReconnectAttempts} attempts`);
136
195
  isClosed = true;
137
- sse.close();
138
- cleanup();
139
- reject(err);
196
+ reject(new Error(`SSE connection failed after ${maxReconnectAttempts} reconnection attempts`));
140
197
  }
141
198
  };
142
199
  } catch (err) {
143
- reject(err);
200
+ console.error("Error setting up SSE stream:", err);
201
+ if (reconnectAttempts < maxReconnectAttempts) {
202
+ const delay = calculateBackoffDelay(reconnectAttempts);
203
+ reconnectAttempts++;
204
+ setTimeout(() => {
205
+ if (!isClosed) {
206
+ setupStream(true);
207
+ }
208
+ }, delay);
209
+ } else {
210
+ reject(err);
211
+ }
144
212
  }
145
213
  };
146
214
 
147
215
  // Start the async setup process
148
- setupStream();
216
+ setupStream(false);
217
+
218
+ // Return cleanup function for external cancellation
219
+ return () => {
220
+ isClosed = true;
221
+ cleanup();
222
+ };
149
223
  });
150
224
  }
151
225