@yourgpt/llm-sdk 1.3.0 → 1.4.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/index.mjs CHANGED
@@ -1361,6 +1361,12 @@ function createSSEHeaders() {
1361
1361
  "X-Accel-Buffering": "no"
1362
1362
  };
1363
1363
  }
1364
+ function createTextStreamHeaders() {
1365
+ return {
1366
+ "Content-Type": "text/plain; charset=utf-8",
1367
+ "Cache-Control": "no-cache"
1368
+ };
1369
+ }
1364
1370
  function formatSSEData(event) {
1365
1371
  return `data: ${JSON.stringify(event)}
1366
1372
 
@@ -1388,11 +1394,407 @@ function createEventStream(generator) {
1388
1394
  }
1389
1395
  });
1390
1396
  }
1391
- function createSSEResponse(generator) {
1397
+ function createSSEResponse(generator, options) {
1392
1398
  return new Response(createEventStream(generator), {
1393
- headers: createSSEHeaders()
1399
+ headers: {
1400
+ ...createSSEHeaders(),
1401
+ ...options?.headers
1402
+ }
1394
1403
  });
1395
1404
  }
1405
+ function createTextStreamResponse(generator, options) {
1406
+ const encoder = new TextEncoder();
1407
+ const stream = new ReadableStream({
1408
+ async start(controller) {
1409
+ try {
1410
+ for await (const event of generator) {
1411
+ if (event.type === "message:delta") {
1412
+ controller.enqueue(encoder.encode(event.content));
1413
+ }
1414
+ }
1415
+ } catch (error) {
1416
+ console.error("[Streaming] Text stream error:", error);
1417
+ } finally {
1418
+ controller.close();
1419
+ }
1420
+ }
1421
+ });
1422
+ return new Response(stream, {
1423
+ headers: {
1424
+ ...createTextStreamHeaders(),
1425
+ ...options?.headers
1426
+ }
1427
+ });
1428
+ }
1429
+ async function pipeSSEToResponse(generator, res, options) {
1430
+ res.setHeader("Content-Type", "text/event-stream");
1431
+ res.setHeader("Cache-Control", "no-cache, no-transform");
1432
+ res.setHeader("Connection", "keep-alive");
1433
+ res.setHeader("X-Accel-Buffering", "no");
1434
+ if (options?.headers) {
1435
+ for (const [key, value] of Object.entries(options.headers)) {
1436
+ res.setHeader(key, value);
1437
+ }
1438
+ }
1439
+ try {
1440
+ for await (const event of generator) {
1441
+ res.write(formatSSEData(event));
1442
+ }
1443
+ } catch (error) {
1444
+ const errorEvent = {
1445
+ type: "error",
1446
+ message: error instanceof Error ? error.message : "Unknown error"
1447
+ };
1448
+ res.write(formatSSEData(errorEvent));
1449
+ } finally {
1450
+ res.write("data: [DONE]\n\n");
1451
+ res.end();
1452
+ }
1453
+ }
1454
+ async function pipeTextToResponse(generator, res, options) {
1455
+ res.setHeader("Content-Type", "text/plain; charset=utf-8");
1456
+ res.setHeader("Cache-Control", "no-cache");
1457
+ if (options?.headers) {
1458
+ for (const [key, value] of Object.entries(options.headers)) {
1459
+ res.setHeader(key, value);
1460
+ }
1461
+ }
1462
+ try {
1463
+ for await (const event of generator) {
1464
+ if (event.type === "message:delta") {
1465
+ res.write(event.content);
1466
+ }
1467
+ }
1468
+ } finally {
1469
+ res.end();
1470
+ }
1471
+ }
1472
+
1473
+ // src/server/stream-result.ts
1474
+ var StreamResult = class {
1475
+ constructor(generator) {
1476
+ this.consumed = false;
1477
+ this.eventHandlers = /* @__PURE__ */ new Map();
1478
+ this.generator = generator;
1479
+ }
1480
+ // ============================================
1481
+ // Async Iteration (base pattern)
1482
+ // ============================================
1483
+ /**
1484
+ * Iterate over stream events
1485
+ *
1486
+ * @example
1487
+ * ```typescript
1488
+ * const result = runtime.stream(body);
1489
+ * for await (const event of result) {
1490
+ * if (event.type === 'message:delta') {
1491
+ * console.log(event.content);
1492
+ * }
1493
+ * }
1494
+ * ```
1495
+ */
1496
+ [Symbol.asyncIterator]() {
1497
+ this.ensureNotConsumed();
1498
+ return this.generator;
1499
+ }
1500
+ // ============================================
1501
+ // Web API Response Methods (Next.js, Cloudflare, Deno)
1502
+ // ============================================
1503
+ /**
1504
+ * Returns SSE Response for Web API frameworks
1505
+ *
1506
+ * @example
1507
+ * ```typescript
1508
+ * // Next.js App Router
1509
+ * export async function POST(req: Request) {
1510
+ * const body = await req.json();
1511
+ * return runtime.stream(body).toResponse();
1512
+ * }
1513
+ * ```
1514
+ */
1515
+ toResponse(options) {
1516
+ this.ensureNotConsumed();
1517
+ const headers = {
1518
+ ...createSSEHeaders(),
1519
+ ...options?.headers
1520
+ };
1521
+ return new Response(createEventStream(this.generator), { headers });
1522
+ }
1523
+ /**
1524
+ * Alias for toResponse() - returns SSE Response
1525
+ */
1526
+ toSSEResponse(options) {
1527
+ return this.toResponse(options);
1528
+ }
1529
+ /**
1530
+ * Returns text-only Response (no SSE events, just text content)
1531
+ *
1532
+ * @example
1533
+ * ```typescript
1534
+ * // Simple text streaming
1535
+ * return runtime.stream(body).toTextResponse();
1536
+ * ```
1537
+ */
1538
+ toTextResponse(options) {
1539
+ this.ensureNotConsumed();
1540
+ const encoder = new TextEncoder();
1541
+ const generator = this.generator;
1542
+ const stream = new ReadableStream({
1543
+ async start(controller) {
1544
+ try {
1545
+ for await (const event of generator) {
1546
+ if (event.type === "message:delta") {
1547
+ controller.enqueue(encoder.encode(event.content));
1548
+ }
1549
+ }
1550
+ } catch (error) {
1551
+ console.error("[StreamResult] Text stream error:", error);
1552
+ } finally {
1553
+ controller.close();
1554
+ }
1555
+ }
1556
+ });
1557
+ return new Response(stream, {
1558
+ headers: {
1559
+ "Content-Type": "text/plain; charset=utf-8",
1560
+ "Cache-Control": "no-cache",
1561
+ ...options?.headers
1562
+ }
1563
+ });
1564
+ }
1565
+ /**
1566
+ * Returns the underlying ReadableStream
1567
+ *
1568
+ * @example
1569
+ * ```typescript
1570
+ * const stream = runtime.stream(body).toReadableStream();
1571
+ * // Use with custom handling
1572
+ * ```
1573
+ */
1574
+ toReadableStream() {
1575
+ this.ensureNotConsumed();
1576
+ return createEventStream(this.generator);
1577
+ }
1578
+ // ============================================
1579
+ // Node.js/Express Response Methods
1580
+ // ============================================
1581
+ /**
1582
+ * Pipe SSE stream to Node.js ServerResponse
1583
+ *
1584
+ * @example
1585
+ * ```typescript
1586
+ * // Express - one-liner
1587
+ * app.post('/chat', async (req, res) => {
1588
+ * await runtime.stream(req.body).pipeToResponse(res);
1589
+ * });
1590
+ * ```
1591
+ */
1592
+ async pipeToResponse(res, options) {
1593
+ this.ensureNotConsumed();
1594
+ res.setHeader("Content-Type", "text/event-stream");
1595
+ res.setHeader("Cache-Control", "no-cache, no-transform");
1596
+ res.setHeader("Connection", "keep-alive");
1597
+ res.setHeader("X-Accel-Buffering", "no");
1598
+ if (options?.headers) {
1599
+ for (const [key, value] of Object.entries(options.headers)) {
1600
+ res.setHeader(key, value);
1601
+ }
1602
+ }
1603
+ const collected = this.createCollector();
1604
+ try {
1605
+ for await (const event of this.generator) {
1606
+ this.collectEvent(event, collected);
1607
+ this.callEventHandlers(event, collected);
1608
+ res.write(formatSSEData(event));
1609
+ }
1610
+ } catch (error) {
1611
+ const errorEvent = {
1612
+ type: "error",
1613
+ message: error instanceof Error ? error.message : "Unknown error"
1614
+ };
1615
+ res.write(formatSSEData(errorEvent));
1616
+ const errorHandler = this.eventHandlers.get("error");
1617
+ if (errorHandler) {
1618
+ errorHandler(error instanceof Error ? error : new Error(String(error)));
1619
+ }
1620
+ } finally {
1621
+ res.write("data: [DONE]\n\n");
1622
+ res.end();
1623
+ }
1624
+ const doneHandler = this.eventHandlers.get("done");
1625
+ if (doneHandler) {
1626
+ doneHandler(collected);
1627
+ }
1628
+ return collected;
1629
+ }
1630
+ /**
1631
+ * Pipe text-only stream to Node.js ServerResponse
1632
+ *
1633
+ * @example
1634
+ * ```typescript
1635
+ * // Express - text-only streaming
1636
+ * app.post('/chat', async (req, res) => {
1637
+ * await runtime.stream(req.body).pipeTextToResponse(res);
1638
+ * });
1639
+ * ```
1640
+ */
1641
+ async pipeTextToResponse(res, options) {
1642
+ this.ensureNotConsumed();
1643
+ res.setHeader("Content-Type", "text/plain; charset=utf-8");
1644
+ res.setHeader("Cache-Control", "no-cache");
1645
+ if (options?.headers) {
1646
+ for (const [key, value] of Object.entries(options.headers)) {
1647
+ res.setHeader(key, value);
1648
+ }
1649
+ }
1650
+ const collected = this.createCollector();
1651
+ try {
1652
+ for await (const event of this.generator) {
1653
+ this.collectEvent(event, collected);
1654
+ this.callEventHandlers(event, collected);
1655
+ if (event.type === "message:delta") {
1656
+ res.write(event.content);
1657
+ }
1658
+ }
1659
+ } catch (error) {
1660
+ const errorHandler = this.eventHandlers.get("error");
1661
+ if (errorHandler) {
1662
+ errorHandler(error instanceof Error ? error : new Error(String(error)));
1663
+ }
1664
+ } finally {
1665
+ res.end();
1666
+ }
1667
+ const doneHandler = this.eventHandlers.get("done");
1668
+ if (doneHandler) {
1669
+ doneHandler(collected);
1670
+ }
1671
+ return collected;
1672
+ }
1673
+ // ============================================
1674
+ // Collection Methods
1675
+ // ============================================
1676
+ /**
1677
+ * Collect all events and return final result
1678
+ *
1679
+ * @example
1680
+ * ```typescript
1681
+ * const { text, messages, toolCalls } = await runtime.stream(body).collect();
1682
+ * console.log('Response:', text);
1683
+ * ```
1684
+ */
1685
+ async collect() {
1686
+ this.ensureNotConsumed();
1687
+ const collected = this.createCollector();
1688
+ for await (const event of this.generator) {
1689
+ this.collectEvent(event, collected);
1690
+ this.callEventHandlers(event, collected);
1691
+ }
1692
+ const doneHandler = this.eventHandlers.get("done");
1693
+ if (doneHandler) {
1694
+ doneHandler(collected);
1695
+ }
1696
+ return collected;
1697
+ }
1698
+ /**
1699
+ * Get final text (convenience method)
1700
+ *
1701
+ * @example
1702
+ * ```typescript
1703
+ * const text = await runtime.stream(body).text();
1704
+ * ```
1705
+ */
1706
+ async text() {
1707
+ const result = await this.collect();
1708
+ return result.text;
1709
+ }
1710
+ on(event, handler) {
1711
+ this.eventHandlers.set(event, handler);
1712
+ return this;
1713
+ }
1714
+ // ============================================
1715
+ // Internal Methods
1716
+ // ============================================
1717
+ /**
1718
+ * Ensure stream hasn't been consumed
1719
+ */
1720
+ ensureNotConsumed() {
1721
+ if (this.consumed) {
1722
+ throw new Error(
1723
+ "StreamResult has already been consumed. Each StreamResult can only be consumed once."
1724
+ );
1725
+ }
1726
+ this.consumed = true;
1727
+ }
1728
+ /**
1729
+ * Create empty collector object
1730
+ */
1731
+ createCollector() {
1732
+ return {
1733
+ text: "",
1734
+ messages: [],
1735
+ toolCalls: [],
1736
+ requiresAction: false,
1737
+ events: []
1738
+ };
1739
+ }
1740
+ /**
1741
+ * Collect event into result
1742
+ */
1743
+ collectEvent(event, collected) {
1744
+ collected.events.push(event);
1745
+ switch (event.type) {
1746
+ case "message:delta":
1747
+ collected.text += event.content;
1748
+ break;
1749
+ case "tool_calls":
1750
+ collected.toolCalls.push(...event.toolCalls);
1751
+ break;
1752
+ case "done":
1753
+ if (event.messages) {
1754
+ collected.messages.push(...event.messages);
1755
+ }
1756
+ if (event.requiresAction) {
1757
+ collected.requiresAction = true;
1758
+ }
1759
+ break;
1760
+ }
1761
+ }
1762
+ /**
1763
+ * Call registered event handlers
1764
+ */
1765
+ callEventHandlers(event, collected) {
1766
+ switch (event.type) {
1767
+ case "message:delta": {
1768
+ const textHandler = this.eventHandlers.get("text");
1769
+ if (textHandler) {
1770
+ textHandler(event.content);
1771
+ }
1772
+ break;
1773
+ }
1774
+ case "tool_calls": {
1775
+ const toolCallHandler = this.eventHandlers.get(
1776
+ "toolCall"
1777
+ );
1778
+ if (toolCallHandler) {
1779
+ for (const toolCall of event.toolCalls) {
1780
+ toolCallHandler(toolCall);
1781
+ }
1782
+ }
1783
+ break;
1784
+ }
1785
+ case "error": {
1786
+ const errorHandler = this.eventHandlers.get("error");
1787
+ if (errorHandler) {
1788
+ errorHandler(new Error(event.message));
1789
+ }
1790
+ break;
1791
+ }
1792
+ }
1793
+ }
1794
+ };
1795
+ function createStreamResult(generator) {
1796
+ return new StreamResult(generator);
1797
+ }
1396
1798
 
1397
1799
  // src/server/runtime.ts
1398
1800
  function buildToolResultForAI(tool2, result, args) {
@@ -2462,6 +2864,89 @@ var Runtime = class {
2462
2864
  }
2463
2865
  return parameters;
2464
2866
  }
2867
+ // ============================================
2868
+ // StreamResult API (Industry Standard Pattern)
2869
+ // ============================================
2870
+ /**
2871
+ * Stream chat and return StreamResult with helper methods
2872
+ *
2873
+ * This is the recommended API for new projects. It returns a StreamResult
2874
+ * object with multiple ways to consume the response:
2875
+ * - `pipeToResponse(res)` for Express/Node.js
2876
+ * - `toResponse()` for Next.js/Web API
2877
+ * - `collect()` for non-streaming use cases
2878
+ *
2879
+ * @example
2880
+ * ```typescript
2881
+ * // Express - one-liner
2882
+ * app.post('/chat', async (req, res) => {
2883
+ * await runtime.stream(req.body).pipeToResponse(res);
2884
+ * });
2885
+ *
2886
+ * // Next.js App Router
2887
+ * export async function POST(req: Request) {
2888
+ * const body = await req.json();
2889
+ * return runtime.stream(body).toResponse();
2890
+ * }
2891
+ *
2892
+ * // With event handlers
2893
+ * const result = runtime.stream(body)
2894
+ * .on('text', (text) => console.log(text))
2895
+ * .on('done', (result) => console.log('Done:', result.text));
2896
+ * await result.pipeToResponse(res);
2897
+ * ```
2898
+ */
2899
+ stream(request, options) {
2900
+ const generator = this.processChatWithLoop(request, options?.signal);
2901
+ return new StreamResult(generator);
2902
+ }
2903
+ /**
2904
+ * Chat and collect the full response (non-streaming)
2905
+ *
2906
+ * Convenience method that calls stream().collect() for you.
2907
+ * Use this when you need the complete response before responding.
2908
+ *
2909
+ * @example
2910
+ * ```typescript
2911
+ * const { text, messages, toolCalls } = await runtime.chat(body);
2912
+ * console.log('Response:', text);
2913
+ * res.json({ response: text });
2914
+ * ```
2915
+ */
2916
+ async chat(request, options) {
2917
+ return this.stream(request, options).collect();
2918
+ }
2919
+ /**
2920
+ * Create Express-compatible handler middleware
2921
+ *
2922
+ * Returns a function that can be used directly as Express middleware.
2923
+ *
2924
+ * @example
2925
+ * ```typescript
2926
+ * // Simple usage
2927
+ * app.post('/chat', runtime.expressHandler());
2928
+ *
2929
+ * // With options
2930
+ * app.post('/chat', runtime.expressHandler({ format: 'text' }));
2931
+ * ```
2932
+ */
2933
+ expressHandler(options) {
2934
+ return async (req, res) => {
2935
+ try {
2936
+ const result = this.stream(req.body);
2937
+ if (options?.format === "text") {
2938
+ await result.pipeTextToResponse(res, { headers: options?.headers });
2939
+ } else {
2940
+ await result.pipeToResponse(res, { headers: options?.headers });
2941
+ }
2942
+ } catch (error) {
2943
+ console.error("[Runtime] Express handler error:", error);
2944
+ res.status(500).json({
2945
+ error: error instanceof Error ? error.message : "Unknown error"
2946
+ });
2947
+ }
2948
+ };
2949
+ }
2465
2950
  };
2466
2951
  function createRuntime(config) {
2467
2952
  return new Runtime(config);
@@ -2549,38 +3034,28 @@ function createNextHandler(config) {
2549
3034
  return runtime.handleRequest(request);
2550
3035
  };
2551
3036
  }
2552
- function createExpressMiddleware(config) {
3037
+ function createExpressMiddleware(config, options) {
2553
3038
  const runtime = createRuntime(config);
2554
- createHonoApp(runtime);
2555
3039
  return async (req, res) => {
2556
3040
  try {
2557
- const url = new URL(req.url, "http://localhost");
2558
- const request = new Request(url, {
2559
- method: req.method,
2560
- headers: req.headers,
2561
- body: req.method !== "GET" ? JSON.stringify(req.body) : void 0
2562
- });
2563
- const response = await runtime.handleRequest(request);
2564
- response.headers.forEach((value, key) => {
2565
- res.setHeader(key, value);
2566
- });
2567
- if (response.body) {
2568
- const reader = response.body.getReader();
2569
- const decoder = new TextDecoder();
2570
- while (true) {
2571
- const { done, value } = await reader.read();
2572
- if (done) break;
2573
- res.write(decoder.decode(value));
2574
- }
3041
+ const result = runtime.stream(req.body);
3042
+ const nodeRes = res;
3043
+ if (options?.format === "text") {
3044
+ await result.pipeTextToResponse(nodeRes, { headers: options?.headers });
3045
+ } else {
3046
+ await result.pipeToResponse(nodeRes, { headers: options?.headers });
2575
3047
  }
2576
- res.end();
2577
3048
  } catch (error) {
3049
+ console.error("[Express Middleware] Error:", error);
2578
3050
  res.status(500).json({
2579
3051
  error: error instanceof Error ? error.message : "Unknown error"
2580
3052
  });
2581
3053
  }
2582
3054
  };
2583
3055
  }
3056
+ function createExpressHandler(runtime, options) {
3057
+ return runtime.expressHandler(options);
3058
+ }
2584
3059
  function createNodeHandler(config) {
2585
3060
  const runtime = createRuntime(config);
2586
3061
  const app = createHonoApp(runtime);
@@ -3144,6 +3619,6 @@ async function executeToolCalls(toolCalls, tools, executeServerTool, waitForClie
3144
3619
  return results;
3145
3620
  }
3146
3621
 
3147
- export { DEFAULT_CAPABILITIES, DEFAULT_MAX_ITERATIONS, Runtime, createEventStream, createExpressMiddleware, createHonoApp, createNextHandler, createNodeHandler, createRuntime, createSSEHeaders, createSSEResponse, formatSSEData, formatToolsForAnthropic, formatToolsForGoogle, formatToolsForOpenAI, generateText, runAgentLoop, streamText, tool };
3622
+ export { DEFAULT_CAPABILITIES, DEFAULT_MAX_ITERATIONS, Runtime, StreamResult, createEventStream, createExpressHandler, createExpressMiddleware, createHonoApp, createNextHandler, createNodeHandler, createRuntime, createSSEHeaders, createSSEResponse, createStreamResult, createTextStreamHeaders, createTextStreamResponse, formatSSEData, formatToolsForAnthropic, formatToolsForGoogle, formatToolsForOpenAI, generateText, pipeSSEToResponse, pipeTextToResponse, runAgentLoop, streamText, tool };
3148
3623
  //# sourceMappingURL=index.mjs.map
3149
3624
  //# sourceMappingURL=index.mjs.map