@omnikit-ai/sdk 2.2.1 → 2.2.2

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/index.mjs CHANGED
@@ -1548,6 +1548,18 @@ Example: await ${collectionName}.list({ limit: 100, sort: '-created_at' })`,
1548
1548
  if (params?.model) {
1549
1549
  params = { ...params, model: mapLLMModel(params.model) };
1550
1550
  }
1551
+ if (params?.saveResult) {
1552
+ params = {
1553
+ ...params,
1554
+ save_result: {
1555
+ collection: params.saveResult.collection,
1556
+ document_id: params.saveResult.documentId,
1557
+ field: params.saveResult.field,
1558
+ additional_fields: params.saveResult.additionalFields
1559
+ }
1560
+ };
1561
+ delete params.saveResult;
1562
+ }
1551
1563
  }
1552
1564
  const integrationPackage = client._integrations[packageName];
1553
1565
  if (!integrationPackage) {
@@ -1662,7 +1674,7 @@ Example: await ${collectionName}.list({ limit: 100, sort: '-created_at' })`,
1662
1674
  );
1663
1675
  return response;
1664
1676
  },
1665
- // List all records with MongoDB/Mongoose-style filtering
1677
+ // List all records with MongoDB/Mongoose-style filtering and cursor pagination
1666
1678
  async list(...args) {
1667
1679
  const queryParams = new URLSearchParams();
1668
1680
  let filter;
@@ -1707,11 +1719,25 @@ Example: await ${collectionName}.list({ limit: 100, sort: '-created_at' })`,
1707
1719
  if (options.offset !== void 0) {
1708
1720
  queryParams.append("offset", String(options.offset));
1709
1721
  }
1722
+ if (options.after !== void 0) {
1723
+ queryParams.append("after", options.after);
1724
+ }
1710
1725
  }
1711
1726
  }
1712
1727
  const url = queryParams.toString() ? `${client.baseUrl}/apps/${client.appId}/collections/${collectionName}?${queryParams}` : `${client.baseUrl}/apps/${client.appId}/collections/${collectionName}`;
1713
1728
  const response = await client.makeRequest(url, "GET");
1714
- return makeArrayForgiving(Array.isArray(response) ? response : []);
1729
+ if (response && typeof response === "object" && "data" in response && "hasMore" in response) {
1730
+ return {
1731
+ data: makeArrayForgiving(response.data),
1732
+ hasMore: response.hasMore,
1733
+ nextCursor: response.nextCursor
1734
+ };
1735
+ }
1736
+ return {
1737
+ data: makeArrayForgiving(Array.isArray(response) ? response : []),
1738
+ hasMore: false,
1739
+ nextCursor: null
1740
+ };
1715
1741
  },
1716
1742
  // Filter records by query (alias for list)
1717
1743
  async filter(...args) {
@@ -1720,21 +1746,21 @@ Example: await ${collectionName}.list({ limit: 100, sort: '-created_at' })`,
1720
1746
  // Find single record matching query
1721
1747
  async findOne(...args) {
1722
1748
  if (args.length === 0) {
1723
- const results = await this.list({}, { limit: 1 });
1724
- return results[0] || null;
1749
+ const result = await this.list({}, { limit: 1 });
1750
+ return result.data[0] || null;
1725
1751
  } else if (args.length === 1) {
1726
1752
  const arg = args[0];
1727
1753
  if (arg && (arg.q !== void 0 || arg._count !== void 0)) {
1728
- const results = await this.list({ ...arg, limit: 1 });
1729
- return results[0] || null;
1754
+ const result = await this.list({ ...arg, limit: 1 });
1755
+ return result.data[0] || null;
1730
1756
  } else {
1731
- const results = await this.list(arg, { limit: 1 });
1732
- return results[0] || null;
1757
+ const result = await this.list(arg, { limit: 1 });
1758
+ return result.data[0] || null;
1733
1759
  }
1734
1760
  } else {
1735
1761
  const [filter, options] = args;
1736
- const results = await this.list(filter, { ...options, limit: 1 });
1737
- return results[0] || null;
1762
+ const result = await this.list(filter, { ...options, limit: 1 });
1763
+ return result.data[0] || null;
1738
1764
  }
1739
1765
  },
1740
1766
  // Create new record
@@ -2360,6 +2386,266 @@ Example: await ${collectionName}.list({ limit: 100, sort: '-created_at' })`,
2360
2386
  config
2361
2387
  );
2362
2388
  }
2389
+ /**
2390
+ * Run an assistant with streaming response.
2391
+ *
2392
+ * Assistants are server-side AI agents with custom instructions and built-in actions.
2393
+ * They can create/update/delete records, send emails, make HTTP requests, etc.
2394
+ *
2395
+ * @example
2396
+ * ```typescript
2397
+ * const result = await omnikit.runAssistant('customer_support', {
2398
+ * messages: [{ role: 'user', content: 'Help me track my order #12345' }],
2399
+ * onToken: (token) => setResponse(prev => prev + token),
2400
+ * onActionStart: (action) => console.log(`Starting: ${action.name}`),
2401
+ * onActionComplete: (action) => console.log(`Completed: ${action.name}`),
2402
+ * });
2403
+ *
2404
+ * // Continue the conversation
2405
+ * await omnikit.runAssistant('customer_support', {
2406
+ * messages: [{ role: 'user', content: 'When will it arrive?' }],
2407
+ * threadId: result.threadId,
2408
+ * onToken: (token) => setResponse(prev => prev + token),
2409
+ * });
2410
+ * ```
2411
+ *
2412
+ * @param assistantName - Name of the assistant to run
2413
+ * @param params - Run parameters including messages and callbacks
2414
+ * @returns Promise resolving to the run result with threadId
2415
+ */
2416
+ async runAssistant(assistantName, params) {
2417
+ await this.ensureInitialized();
2418
+ const headers = {
2419
+ "Content-Type": "application/json"
2420
+ };
2421
+ const token = this.getAuthToken();
2422
+ if (token) {
2423
+ headers["Authorization"] = `Bearer ${token}`;
2424
+ }
2425
+ if (this._serviceToken) {
2426
+ headers["X-Service-Token"] = this._serviceToken;
2427
+ }
2428
+ if (this._apiKey) {
2429
+ headers["X-API-Key"] = this._apiKey;
2430
+ }
2431
+ const requestBody = {
2432
+ messages: params.messages,
2433
+ thread_id: params.threadId,
2434
+ stream: true
2435
+ };
2436
+ const response = await fetch(
2437
+ `${this.baseUrl}/apps/${this.appId}/assistants/${assistantName}/runs`,
2438
+ {
2439
+ method: "POST",
2440
+ headers,
2441
+ body: JSON.stringify(requestBody)
2442
+ }
2443
+ );
2444
+ if (!response.ok) {
2445
+ const errorText = await response.text();
2446
+ let errorMessage = `Assistant run failed: ${response.statusText}`;
2447
+ try {
2448
+ const errorJson = JSON.parse(errorText);
2449
+ errorMessage = errorJson.detail || errorJson.message || errorMessage;
2450
+ } catch {
2451
+ errorMessage = errorText || errorMessage;
2452
+ }
2453
+ const err = new OmnikitError(errorMessage, response.status, "ASSISTANT_ERROR");
2454
+ params.onError?.(err);
2455
+ throw err;
2456
+ }
2457
+ const reader = response.body?.getReader();
2458
+ if (!reader) {
2459
+ const err = new OmnikitError("No response body", 500, "NO_BODY");
2460
+ params.onError?.(err);
2461
+ throw err;
2462
+ }
2463
+ const decoder = new TextDecoder();
2464
+ let fullResponse = "";
2465
+ let buffer = "";
2466
+ let threadId = params.threadId || "";
2467
+ let messageCount = 0;
2468
+ try {
2469
+ while (true) {
2470
+ const { done, value } = await reader.read();
2471
+ if (done) break;
2472
+ buffer += decoder.decode(value, { stream: true });
2473
+ const lines = buffer.split("\n");
2474
+ buffer = lines.pop() || "";
2475
+ for (const line of lines) {
2476
+ if (line.startsWith("data: ")) {
2477
+ try {
2478
+ const event = JSON.parse(line.slice(6));
2479
+ if (event.type === "token" && event.content) {
2480
+ fullResponse += event.content;
2481
+ params.onToken?.(event.content);
2482
+ } else if (event.type === "action_start") {
2483
+ params.onActionStart?.({
2484
+ id: event.id || "",
2485
+ name: event.name || "",
2486
+ arguments: event.arguments || {}
2487
+ });
2488
+ } else if (event.type === "action_complete") {
2489
+ params.onActionComplete?.({
2490
+ id: event.id || "",
2491
+ name: event.name || "",
2492
+ output: event.output
2493
+ });
2494
+ } else if (event.type === "action_failed") {
2495
+ params.onActionFailed?.({
2496
+ id: event.id || "",
2497
+ name: event.name || "",
2498
+ error: event.error || "Action failed"
2499
+ });
2500
+ } else if (event.type === "done") {
2501
+ threadId = event.thread_id || threadId;
2502
+ messageCount = event.message_count || 0;
2503
+ const result = {
2504
+ threadId,
2505
+ messageCount,
2506
+ response: fullResponse
2507
+ };
2508
+ params.onComplete?.(result);
2509
+ } else if (event.type === "error") {
2510
+ const err = new OmnikitError(
2511
+ event.message || "Assistant error",
2512
+ 500,
2513
+ "ASSISTANT_ERROR"
2514
+ );
2515
+ params.onError?.(err);
2516
+ throw err;
2517
+ }
2518
+ } catch (parseError) {
2519
+ if (parseError instanceof OmnikitError) {
2520
+ throw parseError;
2521
+ }
2522
+ }
2523
+ }
2524
+ }
2525
+ }
2526
+ } finally {
2527
+ reader.releaseLock();
2528
+ }
2529
+ return {
2530
+ threadId,
2531
+ messageCount,
2532
+ response: fullResponse
2533
+ };
2534
+ }
2535
+ /**
2536
+ * Subscribe to a thread via WebSocket for real-time updates.
2537
+ *
2538
+ * Use this to watch a thread without triggering a run. Useful for:
2539
+ * - Showing live updates when another client runs the assistant
2540
+ * - Reconnecting after page navigation
2541
+ * - Observing thread activity in real-time
2542
+ *
2543
+ * @example
2544
+ * ```typescript
2545
+ * // Subscribe to a thread
2546
+ * const unsubscribe = omnikit.subscribeToThread(threadId, {
2547
+ * onConnected: (info) => console.log(`Connected to ${info.assistantName}`),
2548
+ * onToken: (token) => setResponse(prev => prev + token),
2549
+ * onActionStart: (action) => console.log(`Action: ${action.name}`),
2550
+ * onActionComplete: (action) => console.log(`Result: ${JSON.stringify(action.output)}`),
2551
+ * onRunComplete: (result) => console.log(`Run complete, ${result.messageCount} messages`),
2552
+ * onError: (error) => console.error(error),
2553
+ * });
2554
+ *
2555
+ * // Later: disconnect
2556
+ * unsubscribe();
2557
+ * ```
2558
+ *
2559
+ * @param threadId - Thread ID to subscribe to
2560
+ * @param callbacks - Event callbacks for real-time updates
2561
+ * @returns Unsubscribe function to close the WebSocket
2562
+ */
2563
+ subscribeToThread(threadId, callbacks) {
2564
+ const wsProtocol = this.baseUrl.startsWith("https") ? "wss" : "ws";
2565
+ const wsBaseUrl = this.baseUrl.replace(/^https?:\/\//, "");
2566
+ let wsUrl = `${wsProtocol}://${wsBaseUrl}/apps/${this.appId}/threads/${threadId}/subscribe`;
2567
+ const token = this.getAuthToken();
2568
+ if (token) {
2569
+ wsUrl += `?token=${encodeURIComponent(token)}`;
2570
+ }
2571
+ const ws = new WebSocket(wsUrl);
2572
+ let pingInterval = null;
2573
+ ws.onopen = () => {
2574
+ pingInterval = setInterval(() => {
2575
+ if (ws.readyState === WebSocket.OPEN) {
2576
+ ws.send(JSON.stringify({ type: "ping" }));
2577
+ }
2578
+ }, 3e4);
2579
+ };
2580
+ ws.onmessage = (event) => {
2581
+ try {
2582
+ const data = JSON.parse(event.data);
2583
+ switch (data.type) {
2584
+ case "connected":
2585
+ callbacks.onConnected?.({
2586
+ threadId: data.thread_id,
2587
+ assistantName: data.assistant_name,
2588
+ messageCount: data.message_count
2589
+ });
2590
+ break;
2591
+ case "token":
2592
+ callbacks.onToken?.(data.content || "");
2593
+ break;
2594
+ case "action_start":
2595
+ callbacks.onActionStart?.({
2596
+ id: data.id || "",
2597
+ name: data.name || "",
2598
+ arguments: data.arguments || {}
2599
+ });
2600
+ break;
2601
+ case "action_complete":
2602
+ callbacks.onActionComplete?.({
2603
+ id: data.id || "",
2604
+ name: data.name || "",
2605
+ output: data.output
2606
+ });
2607
+ break;
2608
+ case "action_failed":
2609
+ callbacks.onActionFailed?.({
2610
+ id: data.id || "",
2611
+ name: data.name || "",
2612
+ error: data.error || "Action failed"
2613
+ });
2614
+ break;
2615
+ case "run_complete":
2616
+ case "done":
2617
+ callbacks.onRunComplete?.({
2618
+ threadId: data.thread_id || threadId,
2619
+ messageCount: data.message_count || 0
2620
+ });
2621
+ break;
2622
+ case "error":
2623
+ callbacks.onError?.(new Error(data.message || "WebSocket error"));
2624
+ break;
2625
+ case "pong":
2626
+ break;
2627
+ }
2628
+ } catch (parseError) {
2629
+ }
2630
+ };
2631
+ ws.onerror = (event) => {
2632
+ callbacks.onError?.(new Error("WebSocket connection error"));
2633
+ };
2634
+ ws.onclose = () => {
2635
+ if (pingInterval) {
2636
+ clearInterval(pingInterval);
2637
+ }
2638
+ callbacks.onDisconnect?.();
2639
+ };
2640
+ return () => {
2641
+ if (pingInterval) {
2642
+ clearInterval(pingInterval);
2643
+ }
2644
+ if (ws.readyState === WebSocket.OPEN || ws.readyState === WebSocket.CONNECTING) {
2645
+ ws.close();
2646
+ }
2647
+ };
2648
+ }
2363
2649
  /**
2364
2650
  * Invoke a backend function by name.
2365
2651
  *