@ekodb/ekodb-client 0.13.0 → 0.14.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.
package/dist/client.js CHANGED
@@ -71,6 +71,7 @@ var MergeStrategy;
71
71
  class EkoDBClient {
72
72
  constructor(config, apiKey) {
73
73
  this.token = null;
74
+ this.tokenExpiry = 0;
74
75
  this.rateLimitInfo = null;
75
76
  // Support both old (baseURL, apiKey) and new (config object) signatures
76
77
  if (typeof config === "string") {
@@ -133,20 +134,68 @@ class EkoDBClient {
133
134
  }
134
135
  const result = (await response.json());
135
136
  this.token = result.token;
137
+ // Extract and cache JWT expiry for proactive refresh
138
+ const expiry = this.extractJWTExpiry(result.token);
139
+ this.tokenExpiry = expiry ?? Math.floor(Date.now() / 1000) + 3600; // fallback: 1 hour
136
140
  }
137
141
  /**
138
- * Get the current authentication token.
139
- * Returns null if not yet authenticated. Call refreshToken() first.
140
- */
141
- getToken() {
142
+ * Get a valid authentication token.
143
+ *
144
+ * Returns a cached token if it has more than 60s of validity remaining.
145
+ * Otherwise fetches a new one via refreshToken(). This means callers
146
+ * never need to handle token refresh themselves — every getToken() call
147
+ * returns a token that's valid for at least 60 more seconds.
148
+ */
149
+ async getToken() {
150
+ if (this.token) {
151
+ const now = Math.floor(Date.now() / 1000);
152
+ if (now + 60 >= this.tokenExpiry) {
153
+ // Token is about to expire or already expired — refresh proactively
154
+ await this.refreshToken();
155
+ }
156
+ }
157
+ else {
158
+ // No token yet — fetch one
159
+ await this.refreshToken();
160
+ }
142
161
  return this.token;
143
162
  }
144
163
  /**
145
- * Clear the cached authentication token.
164
+ * Clear the cached authentication token and expiry.
146
165
  * The next request will trigger a fresh token exchange.
147
166
  */
148
167
  clearTokenCache() {
149
168
  this.token = null;
169
+ this.tokenExpiry = 0;
170
+ }
171
+ /**
172
+ * Extract the `exp` claim from a JWT without verifying the signature.
173
+ * Returns the Unix timestamp (seconds) of expiry, or null if parsing fails.
174
+ */
175
+ extractJWTExpiry(token) {
176
+ try {
177
+ const parts = token.split(".");
178
+ if (parts.length !== 3) {
179
+ return null;
180
+ }
181
+ // Convert base64url to standard base64
182
+ let payload = parts[1];
183
+ payload = payload.replace(/-/g, "+").replace(/_/g, "/");
184
+ // Pad to multiple of 4
185
+ const pad = payload.length % 4;
186
+ if (pad) {
187
+ payload += "=".repeat(4 - pad);
188
+ }
189
+ const decoded = atob(payload);
190
+ const claims = JSON.parse(decoded);
191
+ if (typeof claims.exp === "number") {
192
+ return claims.exp;
193
+ }
194
+ return null;
195
+ }
196
+ catch {
197
+ return null;
198
+ }
150
199
  }
151
200
  /**
152
201
  * Extract rate limit information from response headers
@@ -906,12 +955,198 @@ class EkoDBClient {
906
955
  async rawCompletion(request) {
907
956
  return this.makeRequest("POST", "/api/chat/complete", request, 0, true);
908
957
  }
958
+ /**
959
+ * Stateless raw LLM completion via SSE streaming.
960
+ *
961
+ * Same as rawCompletion() but uses Server-Sent Events to keep the
962
+ * connection alive. Preferred for deployed instances where reverse proxies
963
+ * may kill idle HTTP connections before the LLM responds.
964
+ */
965
+ async rawCompletionStream(request) {
966
+ let token = await this.getToken();
967
+ const url = `${this.baseURL}/api/chat/complete/stream`;
968
+ const response = await fetch(url, {
969
+ method: "POST",
970
+ headers: {
971
+ "Content-Type": "application/json",
972
+ Accept: "text/event-stream",
973
+ Authorization: `Bearer ${token}`,
974
+ },
975
+ body: JSON.stringify(request),
976
+ });
977
+ if (!response.ok) {
978
+ const body = await response.text();
979
+ throw new Error(`SSE raw completion failed (${response.status}): ${body}`);
980
+ }
981
+ const body = await response.text();
982
+ let content = "";
983
+ let lastError = null;
984
+ for (const line of body.split("\n")) {
985
+ if (line.startsWith("data:")) {
986
+ const dataStr = line.slice(5).trim();
987
+ if (!dataStr)
988
+ continue;
989
+ try {
990
+ const eventData = JSON.parse(dataStr);
991
+ if (eventData.token)
992
+ content += eventData.token;
993
+ if (eventData.content)
994
+ content = eventData.content;
995
+ if (eventData.error)
996
+ lastError = eventData.error;
997
+ }
998
+ catch {
999
+ // skip malformed SSE data
1000
+ }
1001
+ }
1002
+ }
1003
+ if (lastError)
1004
+ throw new Error(lastError);
1005
+ return { content };
1006
+ }
1007
+ /**
1008
+ * Stateless raw LLM completion via SSE streaming with token-level progress.
1009
+ *
1010
+ * Same as rawCompletionStream() but invokes `onToken` with each token as it
1011
+ * arrives, allowing callers to show real-time progress.
1012
+ */
1013
+ async rawCompletionStreamWithProgress(request, onToken) {
1014
+ let token = await this.getToken();
1015
+ const url = `${this.baseURL}/api/chat/complete/stream`;
1016
+ const response = await fetch(url, {
1017
+ method: "POST",
1018
+ headers: {
1019
+ "Content-Type": "application/json",
1020
+ Accept: "text/event-stream",
1021
+ Authorization: `Bearer ${token}`,
1022
+ },
1023
+ body: JSON.stringify(request),
1024
+ });
1025
+ if (!response.ok) {
1026
+ const body = await response.text();
1027
+ throw new Error(`SSE raw completion failed (${response.status}): ${body}`);
1028
+ }
1029
+ const body = await response.text();
1030
+ let content = "";
1031
+ let lastError = null;
1032
+ for (const line of body.split("\n")) {
1033
+ if (line.startsWith("data:")) {
1034
+ const dataStr = line.slice(5).trim();
1035
+ if (!dataStr)
1036
+ continue;
1037
+ try {
1038
+ const eventData = JSON.parse(dataStr);
1039
+ if (eventData.token) {
1040
+ content += eventData.token;
1041
+ onToken(eventData.token);
1042
+ }
1043
+ if (eventData.content)
1044
+ content = eventData.content;
1045
+ if (eventData.error)
1046
+ lastError = eventData.error;
1047
+ }
1048
+ catch {
1049
+ // skip malformed SSE data
1050
+ }
1051
+ }
1052
+ }
1053
+ if (lastError)
1054
+ throw new Error(lastError);
1055
+ return { content };
1056
+ }
909
1057
  /**
910
1058
  * Send a message in an existing chat session
911
1059
  */
912
1060
  async chatMessage(sessionId, request) {
913
1061
  return this.makeRequest("POST", `/api/chat/${sessionId}/messages`, request, 0, true);
914
1062
  }
1063
+ /**
1064
+ * Send a message in an existing chat session via SSE streaming.
1065
+ *
1066
+ * Returns an EventStream that emits ChatStreamEvent objects as they arrive:
1067
+ * - `{ type: "chunk", content: "..." }` for each token
1068
+ * - `{ type: "end", messageId, executionTimeMs, tokenUsage?, contextWindow? }` when complete
1069
+ * - `{ type: "error", error: "..." }` on failure
1070
+ *
1071
+ * Preferred over chatMessage() for long-running responses where reverse
1072
+ * proxies may kill idle HTTP connections before the LLM responds.
1073
+ */
1074
+ chatMessageStream(chatId, request) {
1075
+ const stream = new EventStream();
1076
+ (async () => {
1077
+ try {
1078
+ let token = this.getToken();
1079
+ if (!token) {
1080
+ await this.refreshToken();
1081
+ token = this.getToken();
1082
+ }
1083
+ const url = `${this.baseURL}/api/chat/${chatId}/messages/stream`;
1084
+ const response = await fetch(url, {
1085
+ method: "POST",
1086
+ headers: {
1087
+ "Content-Type": "application/json",
1088
+ Accept: "text/event-stream",
1089
+ Authorization: `Bearer ${token}`,
1090
+ },
1091
+ body: JSON.stringify(request),
1092
+ });
1093
+ if (!response.ok) {
1094
+ const body = await response.text();
1095
+ stream.emit("event", {
1096
+ type: "error",
1097
+ error: `SSE chat message stream failed (${response.status}): ${body}`,
1098
+ });
1099
+ stream.close();
1100
+ return;
1101
+ }
1102
+ const body = await response.text();
1103
+ for (const line of body.split("\n")) {
1104
+ if (!line.startsWith("data:"))
1105
+ continue;
1106
+ const dataStr = line.slice(5).trim();
1107
+ if (!dataStr)
1108
+ continue;
1109
+ try {
1110
+ const eventData = JSON.parse(dataStr);
1111
+ if (eventData.error) {
1112
+ stream.emit("event", {
1113
+ type: "error",
1114
+ error: eventData.error,
1115
+ });
1116
+ }
1117
+ else if (eventData.content && eventData.message_id) {
1118
+ // Done event — has full content + message_id
1119
+ stream.emit("event", {
1120
+ type: "end",
1121
+ messageId: eventData.message_id,
1122
+ executionTimeMs: eventData.execution_time_ms ?? 0,
1123
+ tokenUsage: eventData.token_usage,
1124
+ contextWindow: eventData.context_window,
1125
+ });
1126
+ }
1127
+ else if (eventData.token) {
1128
+ stream.emit("event", {
1129
+ type: "chunk",
1130
+ content: eventData.token,
1131
+ });
1132
+ }
1133
+ }
1134
+ catch {
1135
+ // skip malformed SSE data
1136
+ }
1137
+ }
1138
+ stream.close();
1139
+ }
1140
+ catch (err) {
1141
+ stream.emit("event", {
1142
+ type: "error",
1143
+ error: err.message ?? String(err),
1144
+ });
1145
+ stream.close();
1146
+ }
1147
+ })();
1148
+ return stream;
1149
+ }
915
1150
  /**
916
1151
  * Get a chat session by ID
917
1152
  */
@@ -1116,6 +1351,204 @@ class EkoDBClient {
1116
1351
  await this.makeRequest("DELETE", `/api/functions/${encodeURIComponent(label)}`, undefined, 0, true);
1117
1352
  }
1118
1353
  // ========================================================================
1354
+ // GOAL API
1355
+ // ========================================================================
1356
+ /** Create a new goal */
1357
+ async goalCreate(data) {
1358
+ return this.makeRequest("POST", "/api/chat/goals", data, 0, true);
1359
+ }
1360
+ /** List all goals */
1361
+ async goalList() {
1362
+ return this.makeRequest("GET", "/api/chat/goals", undefined, 0, true);
1363
+ }
1364
+ /** Get a goal by ID */
1365
+ async goalGet(id) {
1366
+ return this.makeRequest("GET", `/api/chat/goals/${encodeURIComponent(id)}`, undefined, 0, true);
1367
+ }
1368
+ /** Update a goal by ID */
1369
+ async goalUpdate(id, data) {
1370
+ return this.makeRequest("PUT", `/api/chat/goals/${encodeURIComponent(id)}`, data, 0, true);
1371
+ }
1372
+ /** Delete a goal by ID */
1373
+ async goalDelete(id) {
1374
+ await this.makeRequest("DELETE", `/api/chat/goals/${encodeURIComponent(id)}`, undefined, 0, true);
1375
+ }
1376
+ // ── Goal Templates ──
1377
+ /** Create a new goal template */
1378
+ async goalTemplateCreate(data) {
1379
+ return this.makeRequest("POST", "/api/chat/goal-templates", data, 0, true);
1380
+ }
1381
+ /** List all goal templates */
1382
+ async goalTemplateList() {
1383
+ return this.makeRequest("GET", "/api/chat/goal-templates", undefined, 0, true);
1384
+ }
1385
+ /** Get a goal template by ID */
1386
+ async goalTemplateGet(id) {
1387
+ return this.makeRequest("GET", `/api/chat/goal-templates/${encodeURIComponent(id)}`, undefined, 0, true);
1388
+ }
1389
+ /** Update a goal template by ID */
1390
+ async goalTemplateUpdate(id, data) {
1391
+ return this.makeRequest("PUT", `/api/chat/goal-templates/${encodeURIComponent(id)}`, data, 0, true);
1392
+ }
1393
+ /** Delete a goal template by ID */
1394
+ async goalTemplateDelete(id) {
1395
+ await this.makeRequest("DELETE", `/api/chat/goal-templates/${encodeURIComponent(id)}`, undefined, 0, true);
1396
+ }
1397
+ /** Search goals */
1398
+ async goalSearch(query) {
1399
+ const params = new URLSearchParams({ q: query });
1400
+ return this.makeRequest("GET", `/api/chat/goals/search?${params}`, undefined, 0, true);
1401
+ }
1402
+ /** Mark a goal as complete (status -> pending_review) */
1403
+ async goalComplete(id, data) {
1404
+ return this.makeRequest("POST", `/api/chat/goals/${encodeURIComponent(id)}/complete`, data, 0, true);
1405
+ }
1406
+ /** Approve a goal (status -> in_progress) */
1407
+ async goalApprove(id) {
1408
+ return this.makeRequest("POST", `/api/chat/goals/${encodeURIComponent(id)}/approve`, undefined, 0, true);
1409
+ }
1410
+ /** Reject a goal (status -> failed) */
1411
+ async goalReject(id, data) {
1412
+ return this.makeRequest("POST", `/api/chat/goals/${encodeURIComponent(id)}/reject`, data, 0, true);
1413
+ }
1414
+ /** Start a goal step (status -> in_progress) */
1415
+ async goalStepStart(id, stepIndex) {
1416
+ return this.makeRequest("POST", `/api/chat/goals/${encodeURIComponent(id)}/steps/${stepIndex}/start`, undefined, 0, true);
1417
+ }
1418
+ /** Complete a goal step with result */
1419
+ async goalStepComplete(id, stepIndex, data) {
1420
+ return this.makeRequest("POST", `/api/chat/goals/${encodeURIComponent(id)}/steps/${stepIndex}/complete`, data, 0, true);
1421
+ }
1422
+ /** Fail a goal step with error */
1423
+ async goalStepFail(id, stepIndex, data) {
1424
+ return this.makeRequest("POST", `/api/chat/goals/${encodeURIComponent(id)}/steps/${stepIndex}/fail`, data, 0, true);
1425
+ }
1426
+ // ========================================================================
1427
+ // TASK API
1428
+ // ========================================================================
1429
+ /** Create a new scheduled task */
1430
+ async taskCreate(data) {
1431
+ return this.makeRequest("POST", "/api/chat/tasks", data, 0, true);
1432
+ }
1433
+ /** List all scheduled tasks */
1434
+ async taskList() {
1435
+ return this.makeRequest("GET", "/api/chat/tasks", undefined, 0, true);
1436
+ }
1437
+ /** Get a task by ID */
1438
+ async taskGet(id) {
1439
+ return this.makeRequest("GET", `/api/chat/tasks/${encodeURIComponent(id)}`, undefined, 0, true);
1440
+ }
1441
+ /** Update a task by ID */
1442
+ async taskUpdate(id, data) {
1443
+ return this.makeRequest("PUT", `/api/chat/tasks/${encodeURIComponent(id)}`, data, 0, true);
1444
+ }
1445
+ /** Delete a task by ID */
1446
+ async taskDelete(id) {
1447
+ await this.makeRequest("DELETE", `/api/chat/tasks/${encodeURIComponent(id)}`, undefined, 0, true);
1448
+ }
1449
+ /** Get tasks that are due at the given time */
1450
+ async taskDue(now) {
1451
+ const params = new URLSearchParams({ now });
1452
+ return this.makeRequest("GET", `/api/chat/tasks/due?${params}`, undefined, 0, true);
1453
+ }
1454
+ /** Start a task (status -> running) */
1455
+ async taskStart(id) {
1456
+ return this.makeRequest("POST", `/api/chat/tasks/${encodeURIComponent(id)}/start`, undefined, 0, true);
1457
+ }
1458
+ /** Mark a task as succeeded */
1459
+ async taskSucceed(id, data) {
1460
+ return this.makeRequest("POST", `/api/chat/tasks/${encodeURIComponent(id)}/succeed`, data, 0, true);
1461
+ }
1462
+ /** Mark a task as failed */
1463
+ async taskFail(id, data) {
1464
+ return this.makeRequest("POST", `/api/chat/tasks/${encodeURIComponent(id)}/fail`, data, 0, true);
1465
+ }
1466
+ /** Pause a task */
1467
+ async taskPause(id) {
1468
+ return this.makeRequest("POST", `/api/chat/tasks/${encodeURIComponent(id)}/pause`, undefined, 0, true);
1469
+ }
1470
+ /** Resume a paused task */
1471
+ async taskResume(id, data) {
1472
+ return this.makeRequest("POST", `/api/chat/tasks/${encodeURIComponent(id)}/resume`, data, 0, true);
1473
+ }
1474
+ // ========================================================================
1475
+ // AGENT API
1476
+ // ========================================================================
1477
+ /** Create a new agent */
1478
+ async agentCreate(data) {
1479
+ return this.makeRequest("POST", "/api/chat/agents", data, 0, true);
1480
+ }
1481
+ /** List all agents */
1482
+ async agentList() {
1483
+ return this.makeRequest("GET", "/api/chat/agents", undefined, 0, true);
1484
+ }
1485
+ /** Get an agent by ID */
1486
+ async agentGet(id) {
1487
+ return this.makeRequest("GET", `/api/chat/agents/${encodeURIComponent(id)}`, undefined, 0, true);
1488
+ }
1489
+ /** Get an agent by name */
1490
+ async agentGetByName(name) {
1491
+ return this.makeRequest("GET", `/api/chat/agents/by-name/${encodeURIComponent(name)}`, undefined, 0, true);
1492
+ }
1493
+ /** Update an agent by ID */
1494
+ async agentUpdate(id, data) {
1495
+ return this.makeRequest("PUT", `/api/chat/agents/${encodeURIComponent(id)}`, data, 0, true);
1496
+ }
1497
+ /** Delete an agent by ID */
1498
+ async agentDelete(id) {
1499
+ await this.makeRequest("DELETE", `/api/chat/agents/${encodeURIComponent(id)}`, undefined, 0, true);
1500
+ }
1501
+ /** Get agents by deployment ID */
1502
+ async agentsByDeployment(deploymentId) {
1503
+ return this.makeRequest("GET", `/api/chat/agents/by-deployment/${encodeURIComponent(deploymentId)}`, undefined, 0, true);
1504
+ }
1505
+ // ========================================================================
1506
+ // KV DOCUMENT LINKING
1507
+ // ========================================================================
1508
+ /** Get documents linked to a KV key */
1509
+ async kvGetLinks(key) {
1510
+ return this.makeRequest("GET", `/api/kv/links/${encodeURIComponent(key)}`, undefined, 0, true);
1511
+ }
1512
+ /** Link a document to a KV key */
1513
+ async kvLink(key, collection, documentId) {
1514
+ return this.makeRequest("POST", `/api/kv/link`, { key, collection, document_id: documentId }, 0, true);
1515
+ }
1516
+ /** Unlink a document from a KV key */
1517
+ async kvUnlink(key, collection, documentId) {
1518
+ return this.makeRequest("POST", `/api/kv/unlink`, { key, collection, document_id: documentId }, 0, true);
1519
+ }
1520
+ // ========================================================================
1521
+ // SCHEDULE MANAGEMENT
1522
+ // ========================================================================
1523
+ /** Create a new schedule */
1524
+ async createSchedule(data) {
1525
+ return this.makeRequest("POST", `/api/schedules`, data, 0, true);
1526
+ }
1527
+ /** List all schedules */
1528
+ async listSchedules() {
1529
+ return this.makeRequest("GET", `/api/schedules`, undefined, 0, true);
1530
+ }
1531
+ /** Get a schedule by ID */
1532
+ async getSchedule(id) {
1533
+ return this.makeRequest("GET", `/api/schedules/${encodeURIComponent(id)}`, undefined, 0, true);
1534
+ }
1535
+ /** Update a schedule */
1536
+ async updateSchedule(id, data) {
1537
+ return this.makeRequest("PUT", `/api/schedules/${encodeURIComponent(id)}`, data, 0, true);
1538
+ }
1539
+ /** Delete a schedule */
1540
+ async deleteSchedule(id) {
1541
+ await this.makeRequest("DELETE", `/api/schedules/${encodeURIComponent(id)}`, undefined, 0, true);
1542
+ }
1543
+ /** Pause a schedule */
1544
+ async pauseSchedule(id) {
1545
+ return this.makeRequest("POST", `/api/schedules/${encodeURIComponent(id)}/pause`, undefined, 0, true);
1546
+ }
1547
+ /** Resume a schedule */
1548
+ async resumeSchedule(id) {
1549
+ return this.makeRequest("POST", `/api/schedules/${encodeURIComponent(id)}/resume`, undefined, 0, true);
1550
+ }
1551
+ // ========================================================================
1119
1552
  // COLLECTION UTILITIES
1120
1553
  // ========================================================================
1121
1554
  /**
@@ -1385,7 +1818,12 @@ class WebSocketClient {
1385
1818
  switch (msg.type) {
1386
1819
  case "Success":
1387
1820
  case "Error": {
1388
- const messageId = msg.payload?.message_id || msg.payload?.messageId;
1821
+ // Try messageId from top-level, then from payload
1822
+ const messageId = msg.messageId ||
1823
+ msg.message_id ||
1824
+ msg.payload?.message_id ||
1825
+ msg.payload?.messageId;
1826
+ let matched = false;
1389
1827
  if (messageId && this.pendingRequests.has(messageId)) {
1390
1828
  const pending = this.pendingRequests.get(messageId);
1391
1829
  this.pendingRequests.delete(messageId);
@@ -1395,8 +1833,9 @@ class WebSocketClient {
1395
1833
  else {
1396
1834
  pending.resolve(msg.payload);
1397
1835
  }
1836
+ matched = true;
1398
1837
  }
1399
- else if (this.registerToolsAck) {
1838
+ if (!matched && this.registerToolsAck) {
1400
1839
  const ack = this.registerToolsAck;
1401
1840
  this.registerToolsAck = null;
1402
1841
  if (msg.type === "Error") {
@@ -1405,6 +1844,21 @@ class WebSocketClient {
1405
1844
  else {
1406
1845
  ack.resolve(msg.payload);
1407
1846
  }
1847
+ matched = true;
1848
+ }
1849
+ // Server doesn't echo messageId — if there's exactly one pending
1850
+ // request, deliver the response to it (sequential request/response).
1851
+ if (!matched && this.pendingRequests.size === 1) {
1852
+ const entry = this.pendingRequests.entries().next().value;
1853
+ const key = entry[0];
1854
+ const pending = entry[1];
1855
+ this.pendingRequests.delete(key);
1856
+ if (msg.type === "Error") {
1857
+ pending.reject(new Error(msg.message || "Unknown error"));
1858
+ }
1859
+ else {
1860
+ pending.resolve(msg.payload);
1861
+ }
1408
1862
  }
1409
1863
  break;
1410
1864
  }
@@ -1445,6 +1899,7 @@ class WebSocketClient {
1445
1899
  tokenUsage: msg.payload.token_usage || msg.payload.tokenUsage,
1446
1900
  toolCallHistory: msg.payload.tool_call_history || msg.payload.toolCallHistory,
1447
1901
  executionTimeMs: msg.payload.execution_time_ms || msg.payload.executionTimeMs || 0,
1902
+ contextWindow: msg.payload.context_window || msg.payload.contextWindow,
1448
1903
  });
1449
1904
  this.chatStreams.delete(chatId);
1450
1905
  stream.close();
@@ -1604,6 +2059,30 @@ class WebSocketClient {
1604
2059
  };
1605
2060
  this.ws.send(JSON.stringify(request));
1606
2061
  }
2062
+ /**
2063
+ * Stateless raw LLM completion via WebSocket.
2064
+ *
2065
+ * Sends a RawComplete message and waits for the Success response.
2066
+ * Preferred over HTTP for deployed instances: the persistent WSS
2067
+ * connection is already authenticated and won't be killed by reverse
2068
+ * proxy timeouts.
2069
+ */
2070
+ async rawCompletion(request) {
2071
+ await this.ensureConnected();
2072
+ const messageId = this.genMessageId();
2073
+ const payload = await this.sendRequest({
2074
+ type: "RawComplete",
2075
+ messageId,
2076
+ payload: {
2077
+ system_prompt: request.system_prompt,
2078
+ message: request.message,
2079
+ ...(request.provider && { provider: request.provider }),
2080
+ ...(request.model && { model: request.model }),
2081
+ ...(request.max_tokens != null && { max_tokens: request.max_tokens }),
2082
+ },
2083
+ });
2084
+ return { content: payload?.data?.content || "" };
2085
+ }
1607
2086
  /**
1608
2087
  * Close the WebSocket connection.
1609
2088
  */