@providerprotocol/ai 0.0.34 → 0.0.36

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 (136) hide show
  1. package/README.md +546 -3
  2. package/dist/anthropic/index.d.ts +2 -1
  3. package/dist/anthropic/index.js +151 -145
  4. package/dist/anthropic/index.js.map +1 -1
  5. package/dist/cerebras/index.d.ts +392 -0
  6. package/dist/cerebras/index.js +648 -0
  7. package/dist/cerebras/index.js.map +1 -0
  8. package/dist/chunk-2YXFLRQ6.js +49 -0
  9. package/dist/chunk-2YXFLRQ6.js.map +1 -0
  10. package/dist/chunk-4OGB7JZA.js +157 -0
  11. package/dist/chunk-4OGB7JZA.js.map +1 -0
  12. package/dist/chunk-4RX4VQCB.js +31 -0
  13. package/dist/chunk-4RX4VQCB.js.map +1 -0
  14. package/dist/chunk-5IWHCXKN.js +30 -0
  15. package/dist/chunk-5IWHCXKN.js.map +1 -0
  16. package/dist/{chunk-3C7O2RNO.js → chunk-A2IM7PGT.js} +6 -4
  17. package/dist/{chunk-3C7O2RNO.js.map → chunk-A2IM7PGT.js.map} +1 -1
  18. package/dist/{chunk-3D6XGGVG.js → chunk-ARVM24K2.js} +2 -2
  19. package/dist/{chunk-4J6OFUKX.js → chunk-AY55T37A.js} +70 -162
  20. package/dist/chunk-AY55T37A.js.map +1 -0
  21. package/dist/{chunk-ILR2D5PN.js → chunk-BRP5XJ6Q.js} +2 -86
  22. package/dist/chunk-BRP5XJ6Q.js.map +1 -0
  23. package/dist/chunk-C4JP64VW.js +298 -0
  24. package/dist/chunk-C4JP64VW.js.map +1 -0
  25. package/dist/chunk-COS4ON4G.js +111 -0
  26. package/dist/chunk-COS4ON4G.js.map +1 -0
  27. package/dist/chunk-CRP6Y7NF.js +31 -0
  28. package/dist/chunk-CRP6Y7NF.js.map +1 -0
  29. package/dist/chunk-EPB3GQNL.js +118 -0
  30. package/dist/chunk-EPB3GQNL.js.map +1 -0
  31. package/dist/chunk-ETBFOLQN.js +34 -0
  32. package/dist/chunk-ETBFOLQN.js.map +1 -0
  33. package/dist/chunk-I53CI6ZZ.js +142 -0
  34. package/dist/chunk-I53CI6ZZ.js.map +1 -0
  35. package/dist/chunk-IDZOVWP3.js +29 -0
  36. package/dist/chunk-IDZOVWP3.js.map +1 -0
  37. package/dist/chunk-JA3UZALR.js +88 -0
  38. package/dist/chunk-JA3UZALR.js.map +1 -0
  39. package/dist/{chunk-WAKD3OO5.js → chunk-N5DX5JW3.js} +31 -31
  40. package/dist/chunk-N5DX5JW3.js.map +1 -0
  41. package/dist/chunk-OIEWDFQU.js +97 -0
  42. package/dist/chunk-OIEWDFQU.js.map +1 -0
  43. package/dist/{chunk-TOJCZMVU.js → chunk-PMK5LZ5Z.js} +40 -40
  44. package/dist/chunk-PMK5LZ5Z.js.map +1 -0
  45. package/dist/{chunk-6S222DHN.js → chunk-RJGTRQ47.js} +20 -1
  46. package/dist/chunk-RJGTRQ47.js.map +1 -0
  47. package/dist/chunk-UFFJDYCE.js +94 -0
  48. package/dist/chunk-UFFJDYCE.js.map +1 -0
  49. package/dist/chunk-VGKZIGVI.js +222 -0
  50. package/dist/chunk-VGKZIGVI.js.map +1 -0
  51. package/dist/{chunk-KUPF5KHT.js → chunk-Y5H7C5J4.js} +2 -2
  52. package/dist/{embedding-D2BYIehX.d.ts → embedding-BXA72PlJ.d.ts} +1 -1
  53. package/dist/google/index.d.ts +2 -1
  54. package/dist/google/index.js +202 -199
  55. package/dist/google/index.js.map +1 -1
  56. package/dist/groq/index.d.ts +410 -0
  57. package/dist/groq/index.js +649 -0
  58. package/dist/groq/index.js.map +1 -0
  59. package/dist/http/index.d.ts +3 -2
  60. package/dist/http/index.js +5 -4
  61. package/dist/image-stream-CCgwB7ve.d.ts +11 -0
  62. package/dist/index.d.ts +8 -118
  63. package/dist/index.js +520 -769
  64. package/dist/index.js.map +1 -1
  65. package/dist/{llm-BQJZj3cD.d.ts → llm-ByUFPcFH.d.ts} +12 -1632
  66. package/dist/middleware/logging/index.d.ts +76 -0
  67. package/dist/middleware/logging/index.js +74 -0
  68. package/dist/middleware/logging/index.js.map +1 -0
  69. package/dist/middleware/parsed-object/index.d.ts +45 -0
  70. package/dist/middleware/parsed-object/index.js +73 -0
  71. package/dist/middleware/parsed-object/index.js.map +1 -0
  72. package/dist/middleware/pubsub/index.d.ts +97 -0
  73. package/dist/middleware/pubsub/index.js +160 -0
  74. package/dist/middleware/pubsub/index.js.map +1 -0
  75. package/dist/middleware/pubsub/server/express/index.d.ts +66 -0
  76. package/dist/middleware/pubsub/server/express/index.js +11 -0
  77. package/dist/middleware/pubsub/server/express/index.js.map +1 -0
  78. package/dist/middleware/pubsub/server/fastify/index.d.ts +67 -0
  79. package/dist/middleware/pubsub/server/fastify/index.js +11 -0
  80. package/dist/middleware/pubsub/server/fastify/index.js.map +1 -0
  81. package/dist/middleware/pubsub/server/h3/index.d.ts +70 -0
  82. package/dist/middleware/pubsub/server/h3/index.js +11 -0
  83. package/dist/middleware/pubsub/server/h3/index.js.map +1 -0
  84. package/dist/middleware/pubsub/server/index.d.ts +78 -0
  85. package/dist/middleware/pubsub/server/index.js +34 -0
  86. package/dist/middleware/pubsub/server/index.js.map +1 -0
  87. package/dist/middleware/pubsub/server/webapi/index.d.ts +63 -0
  88. package/dist/middleware/pubsub/server/webapi/index.js +11 -0
  89. package/dist/middleware/pubsub/server/webapi/index.js.map +1 -0
  90. package/dist/ollama/index.d.ts +2 -1
  91. package/dist/ollama/index.js +48 -45
  92. package/dist/ollama/index.js.map +1 -1
  93. package/dist/openai/index.d.ts +2 -1
  94. package/dist/openai/index.js +319 -313
  95. package/dist/openai/index.js.map +1 -1
  96. package/dist/openrouter/index.d.ts +2 -1
  97. package/dist/openrouter/index.js +381 -385
  98. package/dist/openrouter/index.js.map +1 -1
  99. package/dist/proxy/index.d.ts +10 -914
  100. package/dist/proxy/index.js +275 -1007
  101. package/dist/proxy/index.js.map +1 -1
  102. package/dist/proxy/server/express/index.d.ts +161 -0
  103. package/dist/proxy/server/express/index.js +24 -0
  104. package/dist/proxy/server/express/index.js.map +1 -0
  105. package/dist/proxy/server/fastify/index.d.ts +162 -0
  106. package/dist/proxy/server/fastify/index.js +24 -0
  107. package/dist/proxy/server/fastify/index.js.map +1 -0
  108. package/dist/proxy/server/h3/index.d.ts +189 -0
  109. package/dist/proxy/server/h3/index.js +28 -0
  110. package/dist/proxy/server/h3/index.js.map +1 -0
  111. package/dist/proxy/server/index.d.ts +151 -0
  112. package/dist/proxy/server/index.js +48 -0
  113. package/dist/proxy/server/index.js.map +1 -0
  114. package/dist/proxy/server/webapi/index.d.ts +278 -0
  115. package/dist/proxy/server/webapi/index.js +32 -0
  116. package/dist/proxy/server/webapi/index.js.map +1 -0
  117. package/dist/responses/index.d.ts +650 -0
  118. package/dist/responses/index.js +930 -0
  119. package/dist/responses/index.js.map +1 -0
  120. package/dist/{retry-8Ch-WWgX.d.ts → retry-BDMo4AVu.d.ts} +1 -1
  121. package/dist/stream-S7nwQRqM.d.ts +1643 -0
  122. package/dist/types-CE4B7pno.d.ts +96 -0
  123. package/dist/utils/index.d.ts +53 -0
  124. package/dist/utils/index.js +7 -0
  125. package/dist/utils/index.js.map +1 -0
  126. package/dist/xai/index.d.ts +2 -1
  127. package/dist/xai/index.js +310 -310
  128. package/dist/xai/index.js.map +1 -1
  129. package/package.json +82 -4
  130. package/dist/chunk-4J6OFUKX.js.map +0 -1
  131. package/dist/chunk-6S222DHN.js.map +0 -1
  132. package/dist/chunk-ILR2D5PN.js.map +0 -1
  133. package/dist/chunk-TOJCZMVU.js.map +0 -1
  134. package/dist/chunk-WAKD3OO5.js.map +0 -1
  135. /package/dist/{chunk-3D6XGGVG.js.map → chunk-ARVM24K2.js.map} +0 -0
  136. /package/dist/{chunk-KUPF5KHT.js.map → chunk-Y5H7C5J4.js.map} +0 -0
@@ -0,0 +1,76 @@
1
+ import { M as Middleware } from '../../llm-ByUFPcFH.js';
2
+ import '../../stream-S7nwQRqM.js';
3
+
4
+ /**
5
+ * @fileoverview Logging middleware for request/response visibility.
6
+ *
7
+ * Provides configurable logging for LLM, embedding, and image operations,
8
+ * including timing, error tracking, and optional event logging.
9
+ *
10
+ * @module middleware/logging
11
+ */
12
+
13
+ /**
14
+ * Log levels for filtering output.
15
+ */
16
+ type LogLevel = 'debug' | 'info' | 'warn' | 'error';
17
+ /**
18
+ * Options for logging middleware.
19
+ */
20
+ interface LoggingOptions {
21
+ /**
22
+ * Minimum log level to output.
23
+ * @default 'info'
24
+ */
25
+ level?: LogLevel;
26
+ /**
27
+ * Log individual stream events.
28
+ * @default false
29
+ */
30
+ logStreamEvents?: boolean;
31
+ /**
32
+ * Log tool calls and results.
33
+ * @default true
34
+ */
35
+ logToolCalls?: boolean;
36
+ /**
37
+ * Custom logger function. If not provided, uses console.log.
38
+ * @param level - The log level
39
+ * @param message - The log message
40
+ * @param data - Optional additional data
41
+ */
42
+ logger?(level: LogLevel, message: string, data?: Record<string, unknown>): void;
43
+ /**
44
+ * Prefix for all log messages.
45
+ * @default '[PP]'
46
+ */
47
+ prefix?: string;
48
+ }
49
+ /**
50
+ * Creates a logging middleware for visibility into request lifecycle.
51
+ *
52
+ * This middleware logs the start, end, and errors of requests,
53
+ * with optional logging of stream events and tool calls.
54
+ *
55
+ * @param options - Configuration options
56
+ * @returns A middleware that logs request lifecycle events
57
+ *
58
+ * @example
59
+ * ```typescript
60
+ * import { llm } from '@providerprotocol/ai';
61
+ * import { loggingMiddleware } from '@providerprotocol/ai/middleware/logging';
62
+ * import { anthropic } from '@providerprotocol/ai/anthropic';
63
+ *
64
+ * const model = llm({
65
+ * model: anthropic('claude-sonnet-4-20250514'),
66
+ * middleware: [loggingMiddleware({ level: 'debug' })],
67
+ * });
68
+ *
69
+ * // Logs: [PP] [anthropic] Starting llm request (streaming)
70
+ * // Logs: [PP] [anthropic] Completed in 1234ms
71
+ * const result = await model.generate('Hello');
72
+ * ```
73
+ */
74
+ declare function loggingMiddleware(options?: LoggingOptions): Middleware;
75
+
76
+ export { type LogLevel, type LoggingOptions, loggingMiddleware };
@@ -0,0 +1,74 @@
1
+ // src/middleware/logging.ts
2
+ var LOG_LEVELS = {
3
+ debug: 0,
4
+ info: 1,
5
+ warn: 2,
6
+ error: 3
7
+ };
8
+ function loggingMiddleware(options = {}) {
9
+ const {
10
+ level = "info",
11
+ logStreamEvents = false,
12
+ logToolCalls = true,
13
+ logger,
14
+ prefix = "[PP]"
15
+ } = options;
16
+ const minLevel = LOG_LEVELS[level];
17
+ const log = (logLevel, message, data) => {
18
+ if (LOG_LEVELS[logLevel] < minLevel) {
19
+ return;
20
+ }
21
+ const fullMessage = `${prefix} ${message}`;
22
+ if (logger) {
23
+ logger(logLevel, fullMessage, data);
24
+ } else {
25
+ const consoleMethod = logLevel === "error" ? console.error : logLevel === "warn" ? console.warn : console.log;
26
+ if (data) {
27
+ consoleMethod(fullMessage, data);
28
+ } else {
29
+ consoleMethod(fullMessage);
30
+ }
31
+ }
32
+ };
33
+ return {
34
+ name: "logging",
35
+ onStart(ctx) {
36
+ const streamingLabel = ctx.streaming ? "(streaming)" : "";
37
+ log("info", `[${ctx.provider}] Starting ${ctx.modality} request ${streamingLabel}`.trim());
38
+ log("debug", `[${ctx.provider}] Model: ${ctx.modelId}`);
39
+ },
40
+ onEnd(ctx) {
41
+ const duration = ctx.endTime ? ctx.endTime - ctx.startTime : 0;
42
+ log("info", `[${ctx.provider}] Completed in ${duration}ms`);
43
+ },
44
+ onError(error, ctx) {
45
+ const duration = Date.now() - ctx.startTime;
46
+ log("error", `[${ctx.provider}] Error after ${duration}ms: ${error.message}`);
47
+ },
48
+ onAbort(error, ctx) {
49
+ const duration = Date.now() - ctx.startTime;
50
+ log("warn", `[${ctx.provider}] Aborted after ${duration}ms: ${error.message}`);
51
+ },
52
+ onStreamEvent(event, ctx) {
53
+ if (logStreamEvents) {
54
+ log("debug", `Stream event: ${event.type}`, { index: event.index });
55
+ }
56
+ return event;
57
+ },
58
+ onToolCall(tool, params, ctx) {
59
+ if (logToolCalls) {
60
+ log("info", `[${ctx.provider}] Tool call: ${tool.name}`);
61
+ log("debug", `[${ctx.provider}] Tool params:`, { params });
62
+ }
63
+ },
64
+ onToolResult(tool, result, ctx) {
65
+ if (logToolCalls) {
66
+ log("debug", `[${ctx.provider}] Tool result: ${tool.name}`, { result });
67
+ }
68
+ }
69
+ };
70
+ }
71
+ export {
72
+ loggingMiddleware
73
+ };
74
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/middleware/logging.ts"],"sourcesContent":["/**\n * @fileoverview Logging middleware for request/response visibility.\n *\n * Provides configurable logging for LLM, embedding, and image operations,\n * including timing, error tracking, and optional event logging.\n *\n * @module middleware/logging\n */\n\nimport type { Middleware, MiddlewareContext, StreamContext } from '../types/middleware.ts';\nimport type { StreamEvent } from '../types/stream.ts';\n\n/**\n * Log levels for filtering output.\n */\nexport type LogLevel = 'debug' | 'info' | 'warn' | 'error';\n\n/**\n * Options for logging middleware.\n */\nexport interface LoggingOptions {\n /**\n * Minimum log level to output.\n * @default 'info'\n */\n level?: LogLevel;\n\n /**\n * Log individual stream events.\n * @default false\n */\n logStreamEvents?: boolean;\n\n /**\n * Log tool calls and results.\n * @default true\n */\n logToolCalls?: boolean;\n\n /**\n * Custom logger function. If not provided, uses console.log.\n * @param level - The log level\n * @param message - The log message\n * @param data - Optional additional data\n */\n logger?(level: LogLevel, message: string, data?: Record<string, unknown>): void;\n\n /**\n * Prefix for all log messages.\n * @default '[PP]'\n */\n prefix?: string;\n}\n\nconst LOG_LEVELS: Record<LogLevel, number> = {\n debug: 0,\n info: 1,\n warn: 2,\n error: 3,\n};\n\n/**\n * Creates a logging middleware for visibility into request lifecycle.\n *\n * This middleware logs the start, end, and errors of requests,\n * with optional logging of stream events and tool calls.\n *\n * @param options - Configuration options\n * @returns A middleware that logs request lifecycle events\n *\n * @example\n * ```typescript\n * import { llm } from '@providerprotocol/ai';\n * import { loggingMiddleware } from '@providerprotocol/ai/middleware/logging';\n * import { anthropic } from '@providerprotocol/ai/anthropic';\n *\n * const model = llm({\n * model: anthropic('claude-sonnet-4-20250514'),\n * middleware: [loggingMiddleware({ level: 'debug' })],\n * });\n *\n * // Logs: [PP] [anthropic] Starting llm request (streaming)\n * // Logs: [PP] [anthropic] Completed in 1234ms\n * const result = await model.generate('Hello');\n * ```\n */\nexport function loggingMiddleware(options: LoggingOptions = {}): Middleware {\n const {\n level = 'info',\n logStreamEvents = false,\n logToolCalls = true,\n logger,\n prefix = '[PP]',\n } = options;\n\n const minLevel = LOG_LEVELS[level];\n\n const log = (logLevel: LogLevel, message: string, data?: Record<string, unknown>) => {\n if (LOG_LEVELS[logLevel] < minLevel) {\n return;\n }\n\n const fullMessage = `${prefix} ${message}`;\n\n if (logger) {\n logger(logLevel, fullMessage, data);\n } else {\n const consoleMethod = logLevel === 'error' ? console.error : logLevel === 'warn' ? console.warn : console.log;\n if (data) {\n consoleMethod(fullMessage, data);\n } else {\n consoleMethod(fullMessage);\n }\n }\n };\n\n return {\n name: 'logging',\n\n onStart(ctx: MiddlewareContext): void {\n const streamingLabel = ctx.streaming ? '(streaming)' : '';\n log('info', `[${ctx.provider}] Starting ${ctx.modality} request ${streamingLabel}`.trim());\n log('debug', `[${ctx.provider}] Model: ${ctx.modelId}`);\n },\n\n onEnd(ctx: MiddlewareContext): void {\n const duration = ctx.endTime ? ctx.endTime - ctx.startTime : 0;\n log('info', `[${ctx.provider}] Completed in ${duration}ms`);\n },\n\n onError(error: Error, ctx: MiddlewareContext): void {\n const duration = Date.now() - ctx.startTime;\n log('error', `[${ctx.provider}] Error after ${duration}ms: ${error.message}`);\n },\n\n onAbort(error: Error, ctx: MiddlewareContext): void {\n const duration = Date.now() - ctx.startTime;\n log('warn', `[${ctx.provider}] Aborted after ${duration}ms: ${error.message}`);\n },\n\n onStreamEvent(event: StreamEvent, ctx: StreamContext): StreamEvent {\n if (logStreamEvents) {\n log('debug', `Stream event: ${event.type}`, { index: event.index });\n }\n return event;\n },\n\n onToolCall(tool, params, ctx: MiddlewareContext): void {\n if (logToolCalls) {\n log('info', `[${ctx.provider}] Tool call: ${tool.name}`);\n log('debug', `[${ctx.provider}] Tool params:`, { params });\n }\n },\n\n onToolResult(tool, result, ctx: MiddlewareContext): void {\n if (logToolCalls) {\n log('debug', `[${ctx.provider}] Tool result: ${tool.name}`, { result });\n }\n },\n };\n}\n"],"mappings":";AAsDA,IAAM,aAAuC;AAAA,EAC3C,OAAO;AAAA,EACP,MAAM;AAAA,EACN,MAAM;AAAA,EACN,OAAO;AACT;AA2BO,SAAS,kBAAkB,UAA0B,CAAC,GAAe;AAC1E,QAAM;AAAA,IACJ,QAAQ;AAAA,IACR,kBAAkB;AAAA,IAClB,eAAe;AAAA,IACf;AAAA,IACA,SAAS;AAAA,EACX,IAAI;AAEJ,QAAM,WAAW,WAAW,KAAK;AAEjC,QAAM,MAAM,CAAC,UAAoB,SAAiB,SAAmC;AACnF,QAAI,WAAW,QAAQ,IAAI,UAAU;AACnC;AAAA,IACF;AAEA,UAAM,cAAc,GAAG,MAAM,IAAI,OAAO;AAExC,QAAI,QAAQ;AACV,aAAO,UAAU,aAAa,IAAI;AAAA,IACpC,OAAO;AACL,YAAM,gBAAgB,aAAa,UAAU,QAAQ,QAAQ,aAAa,SAAS,QAAQ,OAAO,QAAQ;AAC1G,UAAI,MAAM;AACR,sBAAc,aAAa,IAAI;AAAA,MACjC,OAAO;AACL,sBAAc,WAAW;AAAA,MAC3B;AAAA,IACF;AAAA,EACF;AAEA,SAAO;AAAA,IACL,MAAM;AAAA,IAEN,QAAQ,KAA8B;AACpC,YAAM,iBAAiB,IAAI,YAAY,gBAAgB;AACvD,UAAI,QAAQ,IAAI,IAAI,QAAQ,cAAc,IAAI,QAAQ,YAAY,cAAc,GAAG,KAAK,CAAC;AACzF,UAAI,SAAS,IAAI,IAAI,QAAQ,YAAY,IAAI,OAAO,EAAE;AAAA,IACxD;AAAA,IAEA,MAAM,KAA8B;AAClC,YAAM,WAAW,IAAI,UAAU,IAAI,UAAU,IAAI,YAAY;AAC7D,UAAI,QAAQ,IAAI,IAAI,QAAQ,kBAAkB,QAAQ,IAAI;AAAA,IAC5D;AAAA,IAEA,QAAQ,OAAc,KAA8B;AAClD,YAAM,WAAW,KAAK,IAAI,IAAI,IAAI;AAClC,UAAI,SAAS,IAAI,IAAI,QAAQ,iBAAiB,QAAQ,OAAO,MAAM,OAAO,EAAE;AAAA,IAC9E;AAAA,IAEA,QAAQ,OAAc,KAA8B;AAClD,YAAM,WAAW,KAAK,IAAI,IAAI,IAAI;AAClC,UAAI,QAAQ,IAAI,IAAI,QAAQ,mBAAmB,QAAQ,OAAO,MAAM,OAAO,EAAE;AAAA,IAC/E;AAAA,IAEA,cAAc,OAAoB,KAAiC;AACjE,UAAI,iBAAiB;AACnB,YAAI,SAAS,iBAAiB,MAAM,IAAI,IAAI,EAAE,OAAO,MAAM,MAAM,CAAC;AAAA,MACpE;AACA,aAAO;AAAA,IACT;AAAA,IAEA,WAAW,MAAM,QAAQ,KAA8B;AACrD,UAAI,cAAc;AAChB,YAAI,QAAQ,IAAI,IAAI,QAAQ,gBAAgB,KAAK,IAAI,EAAE;AACvD,YAAI,SAAS,IAAI,IAAI,QAAQ,kBAAkB,EAAE,OAAO,CAAC;AAAA,MAC3D;AAAA,IACF;AAAA,IAEA,aAAa,MAAM,QAAQ,KAA8B;AACvD,UAAI,cAAc;AAChB,YAAI,SAAS,IAAI,IAAI,QAAQ,kBAAkB,KAAK,IAAI,IAAI,EAAE,OAAO,CAAC;AAAA,MACxE;AAAA,IACF;AAAA,EACF;AACF;","names":[]}
@@ -0,0 +1,45 @@
1
+ import { M as Middleware } from '../../llm-ByUFPcFH.js';
2
+ import { E as EventDelta, S as StreamEvent } from '../../stream-S7nwQRqM.js';
3
+
4
+ /**
5
+ * @fileoverview Parsed object middleware for incremental JSON parsing.
6
+ *
7
+ * This middleware parses partial JSON from ObjectDelta and ToolCallDelta
8
+ * stream events, providing incremental structured data during streaming.
9
+ *
10
+ * @module middleware/parsed-object
11
+ */
12
+
13
+ /**
14
+ * Event delta with parsed JSON data.
15
+ * Extended by parsedObjectMiddleware when parsing is enabled.
16
+ */
17
+ interface ParsedEventDelta extends EventDelta {
18
+ /** Incrementally parsed JSON value */
19
+ parsed?: unknown;
20
+ }
21
+ /**
22
+ * Stream event with parsed JSON data.
23
+ * Returned by parsedObjectMiddleware for ObjectDelta and ToolCallDelta events.
24
+ */
25
+ interface ParsedStreamEvent extends Omit<StreamEvent, 'delta'> {
26
+ delta: ParsedEventDelta;
27
+ }
28
+ /**
29
+ * Options for parsed object middleware.
30
+ */
31
+ interface ParsedObjectOptions {
32
+ /**
33
+ * Parse ObjectDelta events (structured output responses).
34
+ * @default true
35
+ */
36
+ parseObjects?: boolean;
37
+ /**
38
+ * Parse ToolCallDelta events (tool call arguments).
39
+ * @default true
40
+ */
41
+ parseToolCalls?: boolean;
42
+ }
43
+ declare function parsedObjectMiddleware(options?: ParsedObjectOptions): Middleware;
44
+
45
+ export { type ParsedEventDelta, type ParsedObjectOptions, type ParsedStreamEvent, parsedObjectMiddleware };
@@ -0,0 +1,73 @@
1
+ import {
2
+ parsePartialJson
3
+ } from "../../chunk-I53CI6ZZ.js";
4
+ import {
5
+ StreamEventType
6
+ } from "../../chunk-RJGTRQ47.js";
7
+
8
+ // src/middleware/parsed-object.ts
9
+ var ACCUMULATED_TEXT_KEY = "parsedObject:text";
10
+ var ACCUMULATED_ARGS_KEY = "parsedObject:args";
11
+ function getAccumulatedText(state) {
12
+ let map = state.get(ACCUMULATED_TEXT_KEY);
13
+ if (!map) {
14
+ map = /* @__PURE__ */ new Map();
15
+ state.set(ACCUMULATED_TEXT_KEY, map);
16
+ }
17
+ return map;
18
+ }
19
+ function getAccumulatedArgs(state) {
20
+ let map = state.get(ACCUMULATED_ARGS_KEY);
21
+ if (!map) {
22
+ map = /* @__PURE__ */ new Map();
23
+ state.set(ACCUMULATED_ARGS_KEY, map);
24
+ }
25
+ return map;
26
+ }
27
+ function parsedObjectMiddleware(options = {}) {
28
+ const { parseObjects = true, parseToolCalls = true } = options;
29
+ return {
30
+ name: "parsed-object",
31
+ onStreamEvent(event, ctx) {
32
+ if (parseObjects && event.type === StreamEventType.ObjectDelta) {
33
+ const accumulatedText = getAccumulatedText(ctx.state);
34
+ const current = accumulatedText.get(event.index) ?? "";
35
+ const newText = current + (event.delta.text ?? "");
36
+ accumulatedText.set(event.index, newText);
37
+ const parseResult = parsePartialJson(newText);
38
+ const parsedEvent = {
39
+ ...event,
40
+ delta: {
41
+ ...event.delta,
42
+ parsed: parseResult.value
43
+ }
44
+ };
45
+ return parsedEvent;
46
+ }
47
+ if (parseToolCalls && event.type === StreamEventType.ToolCallDelta) {
48
+ const accumulatedArgs = getAccumulatedArgs(ctx.state);
49
+ const current = accumulatedArgs.get(event.index) ?? "";
50
+ const newJson = current + (event.delta.argumentsJson ?? "");
51
+ accumulatedArgs.set(event.index, newJson);
52
+ const parseResult = parsePartialJson(newJson);
53
+ const parsedEvent = {
54
+ ...event,
55
+ delta: {
56
+ ...event.delta,
57
+ parsed: parseResult.value
58
+ }
59
+ };
60
+ return parsedEvent;
61
+ }
62
+ return event;
63
+ },
64
+ onStreamEnd(ctx) {
65
+ ctx.state.delete(ACCUMULATED_TEXT_KEY);
66
+ ctx.state.delete(ACCUMULATED_ARGS_KEY);
67
+ }
68
+ };
69
+ }
70
+ export {
71
+ parsedObjectMiddleware
72
+ };
73
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/middleware/parsed-object.ts"],"sourcesContent":["/**\n * @fileoverview Parsed object middleware for incremental JSON parsing.\n *\n * This middleware parses partial JSON from ObjectDelta and ToolCallDelta\n * stream events, providing incremental structured data during streaming.\n *\n * @module middleware/parsed-object\n */\n\nimport type { Middleware, StreamContext } from '../types/middleware.ts';\nimport type { EventDelta, StreamEvent } from '../types/stream.ts';\nimport { StreamEventType } from '../types/stream.ts';\nimport { parsePartialJson } from '../utils/partial-json.ts';\n\n/**\n * Event delta with parsed JSON data.\n * Extended by parsedObjectMiddleware when parsing is enabled.\n */\nexport interface ParsedEventDelta extends EventDelta {\n /** Incrementally parsed JSON value */\n parsed?: unknown;\n}\n\n/**\n * Stream event with parsed JSON data.\n * Returned by parsedObjectMiddleware for ObjectDelta and ToolCallDelta events.\n */\nexport interface ParsedStreamEvent extends Omit<StreamEvent, 'delta'> {\n delta: ParsedEventDelta;\n}\n\n/**\n * Options for parsed object middleware.\n */\nexport interface ParsedObjectOptions {\n /**\n * Parse ObjectDelta events (structured output responses).\n * @default true\n */\n parseObjects?: boolean;\n\n /**\n * Parse ToolCallDelta events (tool call arguments).\n * @default true\n */\n parseToolCalls?: boolean;\n}\n\n/**\n * Creates a middleware that parses partial JSON from stream events.\n *\n * This middleware accumulates text from ObjectDelta events and tool\n * argument JSON from ToolCallDelta events, then parses them incrementally\n * using partial JSON parsing. The parsed result is added to the event's\n * `parsed` field.\n *\n * @param options - Configuration options\n * @returns A middleware that adds parsed JSON to stream events\n *\n * @example\n * ```typescript\n * import { llm } from '@providerprotocol/ai';\n * import { parsedObjectMiddleware } from '@providerprotocol/ai/middleware/parsed-object';\n * import { anthropic } from '@providerprotocol/ai/anthropic';\n *\n * const model = llm({\n * model: anthropic('claude-sonnet-4-20250514'),\n * structure: mySchema,\n * middleware: [parsedObjectMiddleware()],\n * });\n *\n * for await (const event of model.stream('Extract data from this text')) {\n * if (event.type === 'object_delta') {\n * // event.delta.parsed contains incrementally parsed object\n * console.log(event.delta.parsed);\n * }\n * }\n * ```\n */\n/** State key for accumulated object text */\nconst ACCUMULATED_TEXT_KEY = 'parsedObject:text';\n/** State key for accumulated tool arguments */\nconst ACCUMULATED_ARGS_KEY = 'parsedObject:args';\n\n/**\n * Gets or creates the accumulated text map from state.\n */\nfunction getAccumulatedText(state: Map<string, unknown>): Map<number, string> {\n let map = state.get(ACCUMULATED_TEXT_KEY) as Map<number, string> | undefined;\n if (!map) {\n map = new Map();\n state.set(ACCUMULATED_TEXT_KEY, map);\n }\n return map;\n}\n\n/**\n * Gets or creates the accumulated args map from state.\n */\nfunction getAccumulatedArgs(state: Map<string, unknown>): Map<number, string> {\n let map = state.get(ACCUMULATED_ARGS_KEY) as Map<number, string> | undefined;\n if (!map) {\n map = new Map();\n state.set(ACCUMULATED_ARGS_KEY, map);\n }\n return map;\n}\n\nexport function parsedObjectMiddleware(options: ParsedObjectOptions = {}): Middleware {\n const { parseObjects = true, parseToolCalls = true } = options;\n\n return {\n name: 'parsed-object',\n\n onStreamEvent(event: StreamEvent, ctx: StreamContext): StreamEvent | StreamEvent[] | null {\n if (parseObjects && event.type === StreamEventType.ObjectDelta) {\n const accumulatedText = getAccumulatedText(ctx.state);\n const current = accumulatedText.get(event.index) ?? '';\n const newText = current + (event.delta.text ?? '');\n accumulatedText.set(event.index, newText);\n\n const parseResult = parsePartialJson(newText);\n\n const parsedEvent: ParsedStreamEvent = {\n ...event,\n delta: {\n ...event.delta,\n parsed: parseResult.value,\n },\n };\n return parsedEvent as StreamEvent;\n }\n\n if (parseToolCalls && event.type === StreamEventType.ToolCallDelta) {\n const accumulatedArgs = getAccumulatedArgs(ctx.state);\n const current = accumulatedArgs.get(event.index) ?? '';\n const newJson = current + (event.delta.argumentsJson ?? '');\n accumulatedArgs.set(event.index, newJson);\n\n const parseResult = parsePartialJson(newJson);\n\n const parsedEvent: ParsedStreamEvent = {\n ...event,\n delta: {\n ...event.delta,\n parsed: parseResult.value,\n },\n };\n return parsedEvent as StreamEvent;\n }\n\n return event;\n },\n\n onStreamEnd(ctx: StreamContext): void {\n // Clean up accumulated state to prevent memory buildup in long sessions\n ctx.state.delete(ACCUMULATED_TEXT_KEY);\n ctx.state.delete(ACCUMULATED_ARGS_KEY);\n },\n };\n}\n"],"mappings":";;;;;;;;AAgFA,IAAM,uBAAuB;AAE7B,IAAM,uBAAuB;AAK7B,SAAS,mBAAmB,OAAkD;AAC5E,MAAI,MAAM,MAAM,IAAI,oBAAoB;AACxC,MAAI,CAAC,KAAK;AACR,UAAM,oBAAI,IAAI;AACd,UAAM,IAAI,sBAAsB,GAAG;AAAA,EACrC;AACA,SAAO;AACT;AAKA,SAAS,mBAAmB,OAAkD;AAC5E,MAAI,MAAM,MAAM,IAAI,oBAAoB;AACxC,MAAI,CAAC,KAAK;AACR,UAAM,oBAAI,IAAI;AACd,UAAM,IAAI,sBAAsB,GAAG;AAAA,EACrC;AACA,SAAO;AACT;AAEO,SAAS,uBAAuB,UAA+B,CAAC,GAAe;AACpF,QAAM,EAAE,eAAe,MAAM,iBAAiB,KAAK,IAAI;AAEvD,SAAO;AAAA,IACL,MAAM;AAAA,IAEN,cAAc,OAAoB,KAAwD;AACxF,UAAI,gBAAgB,MAAM,SAAS,gBAAgB,aAAa;AAC9D,cAAM,kBAAkB,mBAAmB,IAAI,KAAK;AACpD,cAAM,UAAU,gBAAgB,IAAI,MAAM,KAAK,KAAK;AACpD,cAAM,UAAU,WAAW,MAAM,MAAM,QAAQ;AAC/C,wBAAgB,IAAI,MAAM,OAAO,OAAO;AAExC,cAAM,cAAc,iBAAiB,OAAO;AAE5C,cAAM,cAAiC;AAAA,UACrC,GAAG;AAAA,UACH,OAAO;AAAA,YACL,GAAG,MAAM;AAAA,YACT,QAAQ,YAAY;AAAA,UACtB;AAAA,QACF;AACA,eAAO;AAAA,MACT;AAEA,UAAI,kBAAkB,MAAM,SAAS,gBAAgB,eAAe;AAClE,cAAM,kBAAkB,mBAAmB,IAAI,KAAK;AACpD,cAAM,UAAU,gBAAgB,IAAI,MAAM,KAAK,KAAK;AACpD,cAAM,UAAU,WAAW,MAAM,MAAM,iBAAiB;AACxD,wBAAgB,IAAI,MAAM,OAAO,OAAO;AAExC,cAAM,cAAc,iBAAiB,OAAO;AAE5C,cAAM,cAAiC;AAAA,UACrC,GAAG;AAAA,UACH,OAAO;AAAA,YACL,GAAG,MAAM;AAAA,YACT,QAAQ,YAAY;AAAA,UACtB;AAAA,QACF;AACA,eAAO;AAAA,MACT;AAEA,aAAO;AAAA,IACT;AAAA,IAEA,YAAY,KAA0B;AAEpC,UAAI,MAAM,OAAO,oBAAoB;AACrC,UAAI,MAAM,OAAO,oBAAoB;AAAA,IACvC;AAAA,EACF;AACF;","names":[]}
@@ -0,0 +1,97 @@
1
+ import { M as Middleware } from '../../llm-ByUFPcFH.js';
2
+ import { M as MemoryAdapterOptions, P as PubSubAdapter, a as PubSubOptions } from '../../types-CE4B7pno.js';
3
+ export { C as CompletionCallback, S as StoredStream, b as SubscriptionCallback, U as Unsubscribe } from '../../types-CE4B7pno.js';
4
+ import '../../stream-S7nwQRqM.js';
5
+
6
+ /**
7
+ * @fileoverview In-memory storage adapter for pub-sub middleware.
8
+ *
9
+ * Provides a simple Map-based implementation for temporary stream
10
+ * storage during active generation.
11
+ *
12
+ * @module middleware/pubsub/memory-adapter
13
+ */
14
+
15
+ /**
16
+ * Creates an in-memory storage adapter for pub-sub middleware.
17
+ *
18
+ * Stores streams in a Map. Throws when maxStreams is exceeded.
19
+ * Streams are created lazily on first append or subscribe.
20
+ *
21
+ * @param options - Adapter configuration
22
+ * @returns A PubSubAdapter instance
23
+ *
24
+ * @example
25
+ * ```typescript
26
+ * import { pubsubMiddleware, memoryAdapter } from '@providerprotocol/ai/middleware/pubsub';
27
+ *
28
+ * const adapter = memoryAdapter({ maxStreams: 500 });
29
+ * ```
30
+ */
31
+ declare function memoryAdapter(options?: MemoryAdapterOptions): PubSubAdapter;
32
+
33
+ /**
34
+ * @fileoverview Pub-sub middleware for stream resumption.
35
+ *
36
+ * Enables reconnecting clients to catch up on missed events during
37
+ * active generation. The middleware buffers events and publishes them
38
+ * to subscribers. Server routes handle reconnection logic using the
39
+ * exported `createSubscriberStream` utility.
40
+ *
41
+ * @module middleware/pubsub
42
+ */
43
+
44
+ /**
45
+ * Gets the stream ID from middleware state.
46
+ *
47
+ * @param state - Middleware state map
48
+ * @returns Stream ID or undefined if not set
49
+ */
50
+ declare function getStreamId(state: Map<string, unknown>): string | undefined;
51
+ /**
52
+ * Gets the adapter from middleware state.
53
+ *
54
+ * @param state - Middleware state map
55
+ * @returns Adapter or undefined if not set
56
+ */
57
+ declare function getAdapter(state: Map<string, unknown>): PubSubAdapter | undefined;
58
+ /**
59
+ * Creates pub-sub middleware for stream buffering and publishing.
60
+ *
61
+ * The middleware:
62
+ * - Creates stream entries for new requests
63
+ * - Buffers all stream events
64
+ * - Publishes events to subscribers
65
+ * - On stream end: notifies subscribers, then removes from adapter
66
+ *
67
+ * Server routes handle reconnection logic using `streamSubscriber`.
68
+ *
69
+ * @param options - Middleware configuration
70
+ * @returns Middleware instance
71
+ *
72
+ * @example
73
+ * ```typescript
74
+ * import { llm } from '@providerprotocol/ai';
75
+ * import { anthropic } from '@providerprotocol/ai/anthropic';
76
+ * import { pubsubMiddleware, memoryAdapter } from '@providerprotocol/ai/middleware/pubsub';
77
+ * import { h3 } from '@providerprotocol/ai/middleware/pubsub/server';
78
+ *
79
+ * const adapter = memoryAdapter();
80
+ *
81
+ * export default defineEventHandler(async (event) => {
82
+ * const { input, conversationId } = await readBody(event);
83
+ *
84
+ * // Fire and forget - subscriber connects immediately, events flow when ready
85
+ * const model = llm({
86
+ * model: anthropic('claude-sonnet-4-20250514'),
87
+ * middleware: [pubsubMiddleware({ adapter, streamId: conversationId })],
88
+ * });
89
+ * model.stream(input).then(turn => saveToDatabase(turn));
90
+ *
91
+ * return h3.streamSubscriber(conversationId, adapter, event);
92
+ * });
93
+ * ```
94
+ */
95
+ declare function pubsubMiddleware(options?: PubSubOptions): Middleware;
96
+
97
+ export { MemoryAdapterOptions, PubSubAdapter, PubSubOptions, getAdapter, getStreamId, memoryAdapter, pubsubMiddleware };
@@ -0,0 +1,160 @@
1
+ // src/middleware/pubsub/memory-adapter.ts
2
+ function memoryAdapter(options = {}) {
3
+ const { maxStreams = 1e3 } = options;
4
+ const streams = /* @__PURE__ */ new Map();
5
+ const eventCursors = /* @__PURE__ */ new WeakMap();
6
+ const scheduleCallback = (callback) => {
7
+ queueMicrotask(() => {
8
+ try {
9
+ callback();
10
+ } catch {
11
+ }
12
+ });
13
+ };
14
+ const getOrCreate = (streamId) => {
15
+ let entry = streams.get(streamId);
16
+ if (!entry) {
17
+ if (streams.size >= maxStreams) {
18
+ throw new Error(`Maximum concurrent streams (${maxStreams}) exceeded`);
19
+ }
20
+ entry = {
21
+ stream: {
22
+ streamId,
23
+ createdAt: Date.now(),
24
+ events: []
25
+ },
26
+ subscribers: /* @__PURE__ */ new Set()
27
+ };
28
+ streams.set(streamId, entry);
29
+ }
30
+ return entry;
31
+ };
32
+ return {
33
+ async exists(streamId) {
34
+ return streams.has(streamId);
35
+ },
36
+ async append(streamId, event) {
37
+ const entry = getOrCreate(streamId);
38
+ entry.stream.events.push(event);
39
+ eventCursors.set(event, entry.stream.events.length - 1);
40
+ },
41
+ async getEvents(streamId) {
42
+ const entry = streams.get(streamId);
43
+ return entry ? [...entry.stream.events] : [];
44
+ },
45
+ subscribe(streamId, onEvent, onComplete) {
46
+ const entry = getOrCreate(streamId);
47
+ const subscriber = { onEvent, onComplete };
48
+ entry.subscribers.add(subscriber);
49
+ return () => {
50
+ entry.subscribers.delete(subscriber);
51
+ };
52
+ },
53
+ publish(streamId, event) {
54
+ const entry = streams.get(streamId);
55
+ if (!entry) {
56
+ return;
57
+ }
58
+ const cursor = eventCursors.get(event) ?? entry.stream.events.length - 1;
59
+ for (const subscriber of entry.subscribers) {
60
+ scheduleCallback(() => {
61
+ subscriber.onEvent(event, cursor);
62
+ });
63
+ }
64
+ },
65
+ async remove(streamId) {
66
+ const entry = streams.get(streamId);
67
+ if (entry) {
68
+ for (const subscriber of entry.subscribers) {
69
+ scheduleCallback(subscriber.onComplete);
70
+ }
71
+ streams.delete(streamId);
72
+ }
73
+ }
74
+ };
75
+ }
76
+
77
+ // src/middleware/pubsub/index.ts
78
+ var STATE_KEY_STREAM_ID = "pubsub:streamId";
79
+ var STATE_KEY_ADAPTER = "pubsub:adapter";
80
+ function getStreamId(state) {
81
+ return state.get(STATE_KEY_STREAM_ID);
82
+ }
83
+ function getAdapter(state) {
84
+ return state.get(STATE_KEY_ADAPTER);
85
+ }
86
+ function pubsubMiddleware(options = {}) {
87
+ const {
88
+ adapter = memoryAdapter(),
89
+ streamId
90
+ } = options;
91
+ const appendChains = /* @__PURE__ */ new Map();
92
+ const enqueueAppend = (id, event) => {
93
+ const state = appendChains.get(id) ?? { chain: Promise.resolve() };
94
+ const task = state.chain.catch(() => {
95
+ }).then(async () => {
96
+ await adapter.append(id, event);
97
+ adapter.publish(id, event);
98
+ });
99
+ state.chain = task.catch(() => {
100
+ });
101
+ appendChains.set(id, state);
102
+ };
103
+ const waitForAppends = async (id) => {
104
+ const state = appendChains.get(id);
105
+ if (!state) {
106
+ return;
107
+ }
108
+ await state.chain.catch(() => {
109
+ });
110
+ };
111
+ const clearAppendState = (id) => {
112
+ appendChains.delete(id);
113
+ };
114
+ const finalizeStreamByState = async (state) => {
115
+ const id = state.get(STATE_KEY_STREAM_ID);
116
+ if (!id) {
117
+ return;
118
+ }
119
+ await waitForAppends(id);
120
+ clearAppendState(id);
121
+ await adapter.remove(id).catch(() => {
122
+ });
123
+ };
124
+ return {
125
+ name: "pubsub",
126
+ onStart(ctx) {
127
+ ctx.state.set(STATE_KEY_ADAPTER, adapter);
128
+ if (streamId) {
129
+ ctx.state.set(STATE_KEY_STREAM_ID, streamId);
130
+ adapter.subscribe(streamId, () => {
131
+ }, () => {
132
+ })();
133
+ }
134
+ },
135
+ onStreamEvent(event, ctx) {
136
+ const id = ctx.state.get(STATE_KEY_STREAM_ID);
137
+ if (!id) {
138
+ return event;
139
+ }
140
+ enqueueAppend(id, event);
141
+ return event;
142
+ },
143
+ async onStreamEnd(ctx) {
144
+ await finalizeStreamByState(ctx.state);
145
+ },
146
+ async onError(_error, ctx) {
147
+ await finalizeStreamByState(ctx.state);
148
+ },
149
+ async onAbort(_error, ctx) {
150
+ await finalizeStreamByState(ctx.state);
151
+ }
152
+ };
153
+ }
154
+ export {
155
+ getAdapter,
156
+ getStreamId,
157
+ memoryAdapter,
158
+ pubsubMiddleware
159
+ };
160
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/middleware/pubsub/memory-adapter.ts","../../../src/middleware/pubsub/index.ts"],"sourcesContent":["/**\n * @fileoverview In-memory storage adapter for pub-sub middleware.\n *\n * Provides a simple Map-based implementation for temporary stream\n * storage during active generation.\n *\n * @module middleware/pubsub/memory-adapter\n */\n\nimport type { StreamEvent } from '../../types/stream.ts';\nimport type {\n PubSubAdapter,\n StoredStream,\n SubscriptionCallback,\n CompletionCallback,\n Unsubscribe,\n MemoryAdapterOptions,\n} from './types.ts';\n\ninterface MutableStoredStream {\n streamId: string;\n createdAt: number;\n events: StreamEvent[];\n}\n\ninterface Subscriber {\n onEvent: SubscriptionCallback;\n onComplete: CompletionCallback;\n}\n\ninterface StreamEntry {\n stream: MutableStoredStream;\n subscribers: Set<Subscriber>;\n}\n\n/**\n * Creates an in-memory storage adapter for pub-sub middleware.\n *\n * Stores streams in a Map. Throws when maxStreams is exceeded.\n * Streams are created lazily on first append or subscribe.\n *\n * @param options - Adapter configuration\n * @returns A PubSubAdapter instance\n *\n * @example\n * ```typescript\n * import { pubsubMiddleware, memoryAdapter } from '@providerprotocol/ai/middleware/pubsub';\n *\n * const adapter = memoryAdapter({ maxStreams: 500 });\n * ```\n */\nexport function memoryAdapter(options: MemoryAdapterOptions = {}): PubSubAdapter {\n const { maxStreams = 1000 } = options;\n\n const streams = new Map<string, StreamEntry>();\n const eventCursors = new WeakMap<StreamEvent, number>();\n\n const scheduleCallback = (callback: () => void): void => {\n queueMicrotask(() => {\n try {\n callback();\n } catch {\n // Subscriber errors should not affect other subscribers\n }\n });\n };\n\n const getOrCreate = (streamId: string): StreamEntry => {\n let entry = streams.get(streamId);\n if (!entry) {\n if (streams.size >= maxStreams) {\n throw new Error(`Maximum concurrent streams (${maxStreams}) exceeded`);\n }\n entry = {\n stream: {\n streamId,\n createdAt: Date.now(),\n events: [],\n },\n subscribers: new Set(),\n };\n streams.set(streamId, entry);\n }\n return entry;\n };\n\n return {\n async exists(streamId): Promise<boolean> {\n return streams.has(streamId);\n },\n\n async append(streamId, event): Promise<void> {\n const entry = getOrCreate(streamId);\n entry.stream.events.push(event);\n eventCursors.set(event, entry.stream.events.length - 1);\n },\n\n async getEvents(streamId): Promise<StreamEvent[]> {\n const entry = streams.get(streamId);\n return entry ? [...entry.stream.events] : [];\n },\n\n subscribe(streamId, onEvent, onComplete): Unsubscribe {\n const entry = getOrCreate(streamId);\n const subscriber: Subscriber = { onEvent, onComplete };\n entry.subscribers.add(subscriber);\n\n return () => {\n entry.subscribers.delete(subscriber);\n };\n },\n\n publish(streamId, event): void {\n const entry = streams.get(streamId);\n if (!entry) {\n return;\n }\n\n const cursor = eventCursors.get(event) ?? entry.stream.events.length - 1;\n for (const subscriber of entry.subscribers) {\n scheduleCallback(() => {\n subscriber.onEvent(event, cursor);\n });\n }\n },\n\n async remove(streamId): Promise<void> {\n const entry = streams.get(streamId);\n if (entry) {\n for (const subscriber of entry.subscribers) {\n scheduleCallback(subscriber.onComplete);\n }\n streams.delete(streamId);\n }\n },\n };\n}\n","/**\n * @fileoverview Pub-sub middleware for stream resumption.\n *\n * Enables reconnecting clients to catch up on missed events during\n * active generation. The middleware buffers events and publishes them\n * to subscribers. Server routes handle reconnection logic using the\n * exported `createSubscriberStream` utility.\n *\n * @module middleware/pubsub\n */\n\nimport type {\n Middleware,\n MiddlewareContext,\n StreamContext,\n} from '../../types/middleware.ts';\nimport type { StreamEvent } from '../../types/stream.ts';\nimport type { PubSubAdapter, PubSubOptions } from './types.ts';\nimport { memoryAdapter } from './memory-adapter.ts';\n\nexport type {\n PubSubAdapter,\n PubSubOptions,\n StoredStream,\n SubscriptionCallback,\n CompletionCallback,\n Unsubscribe,\n MemoryAdapterOptions,\n} from './types.ts';\nexport { memoryAdapter } from './memory-adapter.ts';\n\nconst STATE_KEY_STREAM_ID = 'pubsub:streamId';\nconst STATE_KEY_ADAPTER = 'pubsub:adapter';\n\ninterface AppendChainState {\n chain: Promise<void>;\n}\n\n/**\n * Gets the stream ID from middleware state.\n *\n * @param state - Middleware state map\n * @returns Stream ID or undefined if not set\n */\nexport function getStreamId(state: Map<string, unknown>): string | undefined {\n return state.get(STATE_KEY_STREAM_ID) as string | undefined;\n}\n\n/**\n * Gets the adapter from middleware state.\n *\n * @param state - Middleware state map\n * @returns Adapter or undefined if not set\n */\nexport function getAdapter(state: Map<string, unknown>): PubSubAdapter | undefined {\n return state.get(STATE_KEY_ADAPTER) as PubSubAdapter | undefined;\n}\n\n/**\n * Creates pub-sub middleware for stream buffering and publishing.\n *\n * The middleware:\n * - Creates stream entries for new requests\n * - Buffers all stream events\n * - Publishes events to subscribers\n * - On stream end: notifies subscribers, then removes from adapter\n *\n * Server routes handle reconnection logic using `streamSubscriber`.\n *\n * @param options - Middleware configuration\n * @returns Middleware instance\n *\n * @example\n * ```typescript\n * import { llm } from '@providerprotocol/ai';\n * import { anthropic } from '@providerprotocol/ai/anthropic';\n * import { pubsubMiddleware, memoryAdapter } from '@providerprotocol/ai/middleware/pubsub';\n * import { h3 } from '@providerprotocol/ai/middleware/pubsub/server';\n *\n * const adapter = memoryAdapter();\n *\n * export default defineEventHandler(async (event) => {\n * const { input, conversationId } = await readBody(event);\n *\n * // Fire and forget - subscriber connects immediately, events flow when ready\n * const model = llm({\n * model: anthropic('claude-sonnet-4-20250514'),\n * middleware: [pubsubMiddleware({ adapter, streamId: conversationId })],\n * });\n * model.stream(input).then(turn => saveToDatabase(turn));\n *\n * return h3.streamSubscriber(conversationId, adapter, event);\n * });\n * ```\n */\nexport function pubsubMiddleware(options: PubSubOptions = {}): Middleware {\n const {\n adapter = memoryAdapter(),\n streamId,\n } = options;\n\n const appendChains = new Map<string, AppendChainState>();\n\n const enqueueAppend = (id: string, event: StreamEvent): void => {\n const state = appendChains.get(id) ?? { chain: Promise.resolve() };\n\n const task = state.chain\n .catch(() => {})\n .then(async () => {\n await adapter.append(id, event);\n adapter.publish(id, event);\n });\n\n state.chain = task.catch(() => {});\n appendChains.set(id, state);\n };\n\n const waitForAppends = async (id: string): Promise<void> => {\n const state = appendChains.get(id);\n if (!state) {\n return;\n }\n\n await state.chain.catch(() => {});\n };\n\n const clearAppendState = (id: string): void => {\n appendChains.delete(id);\n };\n\n /**\n * Finalizes a stream by marking completion and removing from adapter.\n *\n * Called on any terminal state (complete, error, abort). After finalization,\n * the stream is removed from the adapter. Apps should use `.then()` to persist\n * completed conversations and serve them from their own storage on reconnect.\n */\n const finalizeStreamByState = async (state: Map<string, unknown>): Promise<void> => {\n const id = state.get(STATE_KEY_STREAM_ID) as string | undefined;\n if (!id) {\n return;\n }\n\n await waitForAppends(id);\n clearAppendState(id);\n\n // Remove from adapter (notifies subscribers) - apps persist via .then()\n await adapter.remove(id).catch(() => {});\n };\n\n return {\n name: 'pubsub',\n\n onStart(ctx: MiddlewareContext): void {\n ctx.state.set(STATE_KEY_ADAPTER, adapter);\n\n if (streamId) {\n ctx.state.set(STATE_KEY_STREAM_ID, streamId);\n // Ensure stream exists immediately so exists() returns true\n // before first token arrives (prevents duplicate generations)\n adapter.subscribe(streamId, () => {}, () => {})();\n }\n },\n\n onStreamEvent(event: StreamEvent, ctx: StreamContext): StreamEvent {\n const id = ctx.state.get(STATE_KEY_STREAM_ID) as string | undefined;\n if (!id) {\n return event;\n }\n\n enqueueAppend(id, event);\n\n return event;\n },\n\n async onStreamEnd(ctx: StreamContext): Promise<void> {\n await finalizeStreamByState(ctx.state);\n },\n\n async onError(_error: Error, ctx: MiddlewareContext): Promise<void> {\n await finalizeStreamByState(ctx.state);\n },\n\n async onAbort(_error: Error, ctx: MiddlewareContext): Promise<void> {\n await finalizeStreamByState(ctx.state);\n },\n };\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,66 @@
1
+ import { P as PubSubAdapter } from '../../../../types-CE4B7pno.js';
2
+ import '../../../../stream-S7nwQRqM.js';
3
+
4
+ /**
5
+ * @fileoverview Express/Connect adapter for pub-sub stream resumption.
6
+ *
7
+ * Provides utilities for Express.js or Connect-based servers
8
+ * to handle stream reconnections.
9
+ *
10
+ * @module middleware/pubsub/server/express
11
+ */
12
+
13
+ /**
14
+ * Express Response interface (minimal type to avoid dependency).
15
+ */
16
+ interface ExpressResponse {
17
+ setHeader(name: string, value: string): void;
18
+ write(chunk: string): boolean;
19
+ end(): void;
20
+ on(event: 'close', listener: () => void): void;
21
+ }
22
+ /**
23
+ * Stream buffered and live events to an Express response.
24
+ *
25
+ * Handles reconnection for Express routes:
26
+ * 1. Replays buffered events from the adapter
27
+ * 2. Subscribes to live events until completion signal
28
+ * 3. Ends when stream completes or client disconnects
29
+ *
30
+ * @param streamId - The stream ID to subscribe to
31
+ * @param adapter - The pub-sub adapter instance
32
+ * @param res - Express response object
33
+ *
34
+ * @example
35
+ * ```typescript
36
+ * import { llm } from '@providerprotocol/ai';
37
+ * import { anthropic } from '@providerprotocol/ai/anthropic';
38
+ * import { pubsubMiddleware, memoryAdapter } from '@providerprotocol/ai/middleware/pubsub';
39
+ * import { express } from '@providerprotocol/ai/middleware/pubsub/server';
40
+ *
41
+ * const adapter = memoryAdapter();
42
+ *
43
+ * app.post('/api/chat', async (req, res) => {
44
+ * const { input, conversationId } = req.body;
45
+ *
46
+ * if (!await adapter.exists(conversationId)) {
47
+ * const model = llm({
48
+ * model: anthropic('claude-sonnet-4-20250514'),
49
+ * middleware: [pubsubMiddleware({ adapter, streamId: conversationId })],
50
+ * });
51
+ * model.stream(input).then(turn => saveToDatabase(conversationId, turn));
52
+ * }
53
+ *
54
+ * return express.streamSubscriber(conversationId, adapter, res);
55
+ * });
56
+ * ```
57
+ */
58
+ declare function streamSubscriber(streamId: string, adapter: PubSubAdapter, res: ExpressResponse): Promise<void>;
59
+ /**
60
+ * Express adapter namespace for pub-sub server utilities.
61
+ */
62
+ declare const express: {
63
+ streamSubscriber: typeof streamSubscriber;
64
+ };
65
+
66
+ export { express, streamSubscriber };
@@ -0,0 +1,11 @@
1
+ import {
2
+ express,
3
+ streamSubscriber
4
+ } from "../../../../chunk-5IWHCXKN.js";
5
+ import "../../../../chunk-EPB3GQNL.js";
6
+ import "../../../../chunk-ETBFOLQN.js";
7
+ export {
8
+ express,
9
+ streamSubscriber
10
+ };
11
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}