@silkweave/ai 3.2.0 → 3.2.1

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/build/index.mjs CHANGED
@@ -87,44 +87,50 @@ function createChatAction(options) {
87
87
  function silkweaveTransport(subscribe) {
88
88
  return {
89
89
  async sendMessages({ messages, trigger, chatId, messageId, abortSignal, metadata, body, headers }) {
90
- return new ReadableStream({ start(controller) {
91
- let closed = false;
92
- const close = () => {
93
- if (closed) return;
94
- closed = true;
95
- try {
96
- controller.close();
97
- } catch {}
98
- };
99
- const subscription = subscribe({
100
- messages,
101
- trigger,
102
- chatId,
103
- messageId,
104
- metadata,
105
- body,
106
- headers
107
- }, {
108
- onData: (chunk) => {
109
- if (closed) return;
110
- controller.enqueue(chunk);
111
- },
112
- onError: (error) => {
90
+ let subscription;
91
+ return new ReadableStream({
92
+ start(controller) {
93
+ let closed = false;
94
+ const close = () => {
113
95
  if (closed) return;
114
96
  closed = true;
115
- controller.error(error);
116
- },
117
- onComplete: close
118
- });
119
- if (abortSignal) {
120
- const onAbort = () => {
121
- subscription.unsubscribe();
122
- close();
97
+ try {
98
+ controller.close();
99
+ } catch {}
123
100
  };
124
- if (abortSignal.aborted) onAbort();
125
- else abortSignal.addEventListener("abort", onAbort, { once: true });
101
+ subscription = subscribe({
102
+ messages,
103
+ trigger,
104
+ chatId,
105
+ messageId,
106
+ metadata,
107
+ body,
108
+ headers
109
+ }, {
110
+ onData: (chunk) => {
111
+ if (closed) return;
112
+ controller.enqueue(chunk);
113
+ },
114
+ onError: (error) => {
115
+ if (closed) return;
116
+ closed = true;
117
+ controller.error(error);
118
+ },
119
+ onComplete: close
120
+ });
121
+ if (abortSignal) {
122
+ const onAbort = () => {
123
+ subscription?.unsubscribe();
124
+ close();
125
+ };
126
+ if (abortSignal.aborted) onAbort();
127
+ else abortSignal.addEventListener("abort", onAbort, { once: true });
128
+ }
129
+ },
130
+ cancel() {
131
+ subscription?.unsubscribe();
126
132
  }
127
- } });
133
+ });
128
134
  },
129
135
  reconnectToStream() {
130
136
  return Promise.resolve(null);
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":[],"sources":["../src/chatAction.ts","../src/transport.ts"],"sourcesContent":["import { createAction } from '@silkweave/core'\nimport { convertToModelMessages, streamText, type LanguageModel, type ToolSet, type UIMessage, type UIMessageChunk } from 'ai'\nimport z from 'zod/v4'\n\nexport interface CreateChatActionOptions {\n /** Action name. Defaults to `'chat'`. */\n name?: string\n /** Action description (shown to MCP clients, tRPC docs, etc). */\n description?: string\n /** The language model to drive (e.g. `anthropic('claude-sonnet-4-6')`). */\n model: LanguageModel\n /** System prompt prepended to every conversation. */\n system?: string\n /** Tools registered with `streamText`. */\n tools?: ToolSet\n /** Max model output tokens. */\n maxOutputTokens?: number\n /** Model temperature. */\n temperature?: number\n}\n\n/**\n * Build a Silkweave streaming action that wraps Vercel AI SDK's `streamText`.\n *\n * The action accepts the shape `useChat` sends through a custom\n * `ChatTransport` (`messages`, `trigger`, `chatId`, `messageId`) and yields\n * the same `UIMessageChunk`s that AI SDK's `result.toUIMessageStream()`\n * produces - so a paired `silkweaveTransport()` on the client can feed them\n * straight into `useChat` with no wire-format translation.\n *\n * @example\n * ```ts\n * import { silkweave } from '@silkweave/core'\n * import { trpc } from '@silkweave/trpc'\n * import { createChatAction } from '@silkweave/ai'\n * import { anthropic } from '@ai-sdk/anthropic'\n *\n * const ChatAction = createChatAction({\n * model: anthropic('claude-sonnet-4-6'),\n * system: 'You are a helpful assistant.'\n * })\n *\n * await silkweave({ name: 'chat-server', version: '1.0.0' })\n * .adapter(trpc({ port: 8080 }))\n * .action(ChatAction)\n * .start()\n * ```\n */\nexport function createChatAction(options: CreateChatActionOptions) {\n return createAction({\n name: options.name ?? 'chat',\n description: options.description ?? 'Chat with the assistant',\n input: z.object({\n messages: z.array(z.any()),\n trigger: z.enum(['submit-message', 'regenerate-message']).optional(),\n chatId: z.string().optional(),\n messageId: z.string().optional(),\n metadata: z.any().optional(),\n body: z.record(z.string(), z.any()).optional(),\n headers: z.record(z.string(), z.string()).optional()\n }),\n chunk: z.custom<UIMessageChunk>(),\n run: async function* ({ messages }) {\n const uiMessages = messages as UIMessage[]\n const result = streamText({\n model: options.model,\n system: options.system,\n messages: convertToModelMessages(uiMessages),\n tools: options.tools,\n maxOutputTokens: options.maxOutputTokens,\n temperature: options.temperature\n })\n for await (const chunk of result.toUIMessageStream()) {\n yield chunk\n }\n }\n })\n}\n","/* eslint-disable @typescript-eslint/no-unsafe-assignment */\n/* eslint-disable @typescript-eslint/no-explicit-any */\nimport type { ChatTransport, UIMessage, UIMessageChunk } from 'ai'\n\n/**\n * Input passed to the underlying `subscribe` function. Mirrors the relevant\n * fields of `ChatTransport.sendMessages`'s options object - what the chat\n * action needs to drive the model - minus the `AbortSignal` (which is wired\n * separately via `unsubscribe()`).\n */\nexport interface SilkweaveTransportInput<M extends UIMessage = UIMessage> {\n messages: M[]\n trigger: 'submit-message' | 'regenerate-message'\n chatId: string\n messageId: string | undefined\n metadata?: unknown\n body?: Record<string, unknown>\n headers?: Record<string, string>\n}\n\n/**\n * Callback shape passed to the subscribe function. `onData` is typed as\n * `unknown` at the transport boundary (rather than the precise\n * `UIMessageChunk` union) because tRPC v11's `subscribe()` expects callbacks\n * compatible with the *inferred* subscription output, which can differ\n * subtly from `UIMessageChunk` due to how Zod erases discriminated-union\n * variance (e.g. `input?: unknown` vs `input: unknown`). At runtime the\n * server's `createChatAction` only yields valid `UIMessageChunk`s, so the\n * cast is safe.\n */\nexport interface SilkweaveTransportCallbacks {\n onData: (chunk: unknown) => void\n onError: (error: unknown) => void\n onComplete: () => void\n}\n\nexport interface SilkweaveTransportSubscription {\n unsubscribe: () => void\n}\n\n/**\n * The subscribe function. The shape is compatible with tRPC v11's\n * `client.<procedure>.subscribe(input, callbacks)` callsite - wrap your tRPC\n * subscription procedure in this and pass it to `silkweaveTransport()`.\n */\nexport type SilkweaveSubscribe<M extends UIMessage = UIMessage> = (\n input: SilkweaveTransportInput<M>,\n callbacks: SilkweaveTransportCallbacks\n) => SilkweaveTransportSubscription\n\n/**\n * Build a custom `ChatTransport` for Vercel AI SDK's `useChat` that delivers\n * `UIMessageChunk`s over a `subscribe` function (typically a tRPC subscription\n * procedure wired to a Silkweave streaming action).\n *\n * Skips AI SDK's Data Stream Protocol entirely - chunks are JS objects\n * delivered via the consumer-supplied transport, not parsed from prefix-coded\n * HTTP bodies. This means the wire format is whatever your subscribe function\n * uses (tRPC over SSE or WebSocket, in the typical case).\n *\n * @example\n * ```ts\n * import { silkweaveTransport } from '@silkweave/ai'\n * import { trpc } from './trpc-client'\n *\n * const transport = silkweaveTransport((input, callbacks) =>\n * trpc.chat.subscribe(input, callbacks)\n * )\n *\n * const { messages, sendMessage } = useChat({ transport })\n * ```\n *\n * `reconnectToStream()` returns `null` - stream resume after disconnect is not\n * supported. If a connection drops mid-stream the consumer must resend.\n */\nexport function silkweaveTransport<M extends UIMessage = UIMessage>(\n subscribe: SilkweaveSubscribe<M>\n): ChatTransport<M> {\n return {\n async sendMessages({ messages, trigger, chatId, messageId, abortSignal, metadata, body, headers }: any) {\n return new ReadableStream<UIMessageChunk>({\n start(controller) {\n let closed = false\n const close = () => {\n if (closed) { return }\n closed = true\n try { controller.close() } catch { /* already closed */ }\n }\n const subscription = subscribe(\n { messages: messages as M[], trigger, chatId, messageId, metadata, body, headers },\n {\n onData: (chunk) => {\n if (closed) { return }\n controller.enqueue(chunk as UIMessageChunk)\n },\n onError: (error) => {\n if (closed) { return }\n closed = true\n controller.error(error)\n },\n onComplete: close\n }\n )\n if (abortSignal) {\n const onAbort = () => {\n subscription.unsubscribe()\n close()\n }\n if (abortSignal.aborted) {\n onAbort()\n } else {\n abortSignal.addEventListener('abort', onAbort, { once: true })\n }\n }\n }\n })\n },\n reconnectToStream() {\n return Promise.resolve(null)\n }\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAgDA,SAAgB,iBAAiB,SAAkC;AACjE,QAAO,aAAa;EAClB,MAAM,QAAQ,QAAQ;EACtB,aAAa,QAAQ,eAAe;EACpC,OAAO,EAAE,OAAO;GACd,UAAU,EAAE,MAAM,EAAE,KAAK,CAAC;GAC1B,SAAS,EAAE,KAAK,CAAC,kBAAkB,qBAAqB,CAAC,CAAC,UAAU;GACpE,QAAQ,EAAE,QAAQ,CAAC,UAAU;GAC7B,WAAW,EAAE,QAAQ,CAAC,UAAU;GAChC,UAAU,EAAE,KAAK,CAAC,UAAU;GAC5B,MAAM,EAAE,OAAO,EAAE,QAAQ,EAAE,EAAE,KAAK,CAAC,CAAC,UAAU;GAC9C,SAAS,EAAE,OAAO,EAAE,QAAQ,EAAE,EAAE,QAAQ,CAAC,CAAC,UAAU;GACrD,CAAC;EACF,OAAO,EAAE,QAAwB;EACjC,KAAK,iBAAiB,EAAE,YAAY;GAClC,MAAM,aAAa;GACnB,MAAM,SAAS,WAAW;IACxB,OAAO,QAAQ;IACf,QAAQ,QAAQ;IAChB,UAAU,uBAAuB,WAAW;IAC5C,OAAO,QAAQ;IACf,iBAAiB,QAAQ;IACzB,aAAa,QAAQ;IACtB,CAAC;AACF,cAAW,MAAM,SAAS,OAAO,mBAAmB,CAClD,OAAM;;EAGX,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ACDJ,SAAgB,mBACd,WACkB;AAClB,QAAO;EACL,MAAM,aAAa,EAAE,UAAU,SAAS,QAAQ,WAAW,aAAa,UAAU,MAAM,WAAgB;AACtG,UAAO,IAAI,eAA+B,EACxC,MAAM,YAAY;IAChB,IAAI,SAAS;IACb,MAAM,cAAc;AAClB,SAAI,OAAU;AACd,cAAS;AACT,SAAI;AAAE,iBAAW,OAAO;aAAS;;IAEnC,MAAM,eAAe,UACnB;KAAY;KAAiB;KAAS;KAAQ;KAAW;KAAU;KAAM;KAAS,EAClF;KACE,SAAS,UAAU;AACjB,UAAI,OAAU;AACd,iBAAW,QAAQ,MAAwB;;KAE7C,UAAU,UAAU;AAClB,UAAI,OAAU;AACd,eAAS;AACT,iBAAW,MAAM,MAAM;;KAEzB,YAAY;KACb,CACF;AACD,QAAI,aAAa;KACf,MAAM,gBAAgB;AACpB,mBAAa,aAAa;AAC1B,aAAO;;AAET,SAAI,YAAY,QACd,UAAS;SAET,aAAY,iBAAiB,SAAS,SAAS,EAAE,MAAM,MAAM,CAAC;;MAIrE,CAAC;;EAEJ,oBAAoB;AAClB,UAAO,QAAQ,QAAQ,KAAK;;EAE/B"}
1
+ {"version":3,"file":"index.mjs","names":[],"sources":["../src/chatAction.ts","../src/transport.ts"],"sourcesContent":["import { createAction } from '@silkweave/core'\nimport { convertToModelMessages, streamText, type LanguageModel, type ToolSet, type UIMessage, type UIMessageChunk } from 'ai'\nimport z from 'zod/v4'\n\nexport interface CreateChatActionOptions {\n /** Action name. Defaults to `'chat'`. */\n name?: string\n /** Action description (shown to MCP clients, tRPC docs, etc). */\n description?: string\n /** The language model to drive (e.g. `anthropic('claude-sonnet-4-6')`). */\n model: LanguageModel\n /** System prompt prepended to every conversation. */\n system?: string\n /** Tools registered with `streamText`. */\n tools?: ToolSet\n /** Max model output tokens. */\n maxOutputTokens?: number\n /** Model temperature. */\n temperature?: number\n}\n\n/**\n * Build a Silkweave streaming action that wraps Vercel AI SDK's `streamText`.\n *\n * The action accepts the shape `useChat` sends through a custom\n * `ChatTransport` (`messages`, `trigger`, `chatId`, `messageId`) and yields\n * the same `UIMessageChunk`s that AI SDK's `result.toUIMessageStream()`\n * produces - so a paired `silkweaveTransport()` on the client can feed them\n * straight into `useChat` with no wire-format translation.\n *\n * @example\n * ```ts\n * import { silkweave } from '@silkweave/core'\n * import { trpc } from '@silkweave/trpc'\n * import { createChatAction } from '@silkweave/ai'\n * import { anthropic } from '@ai-sdk/anthropic'\n *\n * const ChatAction = createChatAction({\n * model: anthropic('claude-sonnet-4-6'),\n * system: 'You are a helpful assistant.'\n * })\n *\n * await silkweave({ name: 'chat-server', version: '1.0.0' })\n * .adapter(trpc({ port: 8080 }))\n * .action(ChatAction)\n * .start()\n * ```\n */\nexport function createChatAction(options: CreateChatActionOptions) {\n return createAction({\n name: options.name ?? 'chat',\n description: options.description ?? 'Chat with the assistant',\n input: z.object({\n messages: z.array(z.any()),\n trigger: z.enum(['submit-message', 'regenerate-message']).optional(),\n chatId: z.string().optional(),\n messageId: z.string().optional(),\n metadata: z.any().optional(),\n body: z.record(z.string(), z.any()).optional(),\n headers: z.record(z.string(), z.string()).optional()\n }),\n chunk: z.custom<UIMessageChunk>(),\n run: async function* ({ messages }) {\n const uiMessages = messages as UIMessage[]\n const result = streamText({\n model: options.model,\n system: options.system,\n messages: convertToModelMessages(uiMessages),\n tools: options.tools,\n maxOutputTokens: options.maxOutputTokens,\n temperature: options.temperature\n })\n for await (const chunk of result.toUIMessageStream()) {\n yield chunk\n }\n }\n })\n}\n","/* eslint-disable @typescript-eslint/no-unsafe-assignment */\n/* eslint-disable @typescript-eslint/no-explicit-any */\nimport type { ChatTransport, UIMessage, UIMessageChunk } from 'ai'\n\n/**\n * Input passed to the underlying `subscribe` function. Mirrors the relevant\n * fields of `ChatTransport.sendMessages`'s options object - what the chat\n * action needs to drive the model - minus the `AbortSignal` (which is wired\n * separately via `unsubscribe()`).\n */\nexport interface SilkweaveTransportInput<M extends UIMessage = UIMessage> {\n messages: M[]\n trigger: 'submit-message' | 'regenerate-message'\n chatId: string\n messageId: string | undefined\n metadata?: unknown\n body?: Record<string, unknown>\n headers?: Record<string, string>\n}\n\n/**\n * Callback shape passed to the subscribe function. `onData` is typed as\n * `unknown` at the transport boundary (rather than the precise\n * `UIMessageChunk` union) because tRPC v11's `subscribe()` expects callbacks\n * compatible with the *inferred* subscription output, which can differ\n * subtly from `UIMessageChunk` due to how Zod erases discriminated-union\n * variance (e.g. `input?: unknown` vs `input: unknown`). At runtime the\n * server's `createChatAction` only yields valid `UIMessageChunk`s, so the\n * cast is safe.\n */\nexport interface SilkweaveTransportCallbacks {\n onData: (chunk: unknown) => void\n onError: (error: unknown) => void\n onComplete: () => void\n}\n\nexport interface SilkweaveTransportSubscription {\n unsubscribe: () => void\n}\n\n/**\n * The subscribe function. The shape is compatible with tRPC v11's\n * `client.<procedure>.subscribe(input, callbacks)` callsite - wrap your tRPC\n * subscription procedure in this and pass it to `silkweaveTransport()`.\n */\nexport type SilkweaveSubscribe<M extends UIMessage = UIMessage> = (\n input: SilkweaveTransportInput<M>,\n callbacks: SilkweaveTransportCallbacks\n) => SilkweaveTransportSubscription\n\n/**\n * Build a custom `ChatTransport` for Vercel AI SDK's `useChat` that delivers\n * `UIMessageChunk`s over a `subscribe` function (typically a tRPC subscription\n * procedure wired to a Silkweave streaming action).\n *\n * Skips AI SDK's Data Stream Protocol entirely - chunks are JS objects\n * delivered via the consumer-supplied transport, not parsed from prefix-coded\n * HTTP bodies. This means the wire format is whatever your subscribe function\n * uses (tRPC over SSE or WebSocket, in the typical case).\n *\n * @example\n * ```ts\n * import { silkweaveTransport } from '@silkweave/ai'\n * import { trpc } from './trpc-client'\n *\n * const transport = silkweaveTransport((input, callbacks) =>\n * trpc.chat.subscribe(input, callbacks)\n * )\n *\n * const { messages, sendMessage } = useChat({ transport })\n * ```\n *\n * `reconnectToStream()` returns `null` - stream resume after disconnect is not\n * supported. If a connection drops mid-stream the consumer must resend.\n */\nexport function silkweaveTransport<M extends UIMessage = UIMessage>(\n subscribe: SilkweaveSubscribe<M>\n): ChatTransport<M> {\n return {\n async sendMessages({ messages, trigger, chatId, messageId, abortSignal, metadata, body, headers }: any) {\n let subscription: SilkweaveTransportSubscription | undefined\n return new ReadableStream<UIMessageChunk>({\n start(controller) {\n let closed = false\n const close = () => {\n if (closed) { return }\n closed = true\n try { controller.close() } catch { /* already closed */ }\n }\n subscription = subscribe(\n { messages: messages as M[], trigger, chatId, messageId, metadata, body, headers },\n {\n onData: (chunk) => {\n if (closed) { return }\n controller.enqueue(chunk as UIMessageChunk)\n },\n onError: (error) => {\n if (closed) { return }\n closed = true\n controller.error(error)\n },\n onComplete: close\n }\n )\n if (abortSignal) {\n const onAbort = () => {\n subscription?.unsubscribe()\n close()\n }\n if (abortSignal.aborted) {\n onAbort()\n } else {\n abortSignal.addEventListener('abort', onAbort, { once: true })\n }\n }\n },\n // Cancelling the stream directly (without firing abortSignal) must still\n // tear down the tRPC subscription, else it leaks server-side.\n cancel() {\n subscription?.unsubscribe()\n }\n })\n },\n reconnectToStream() {\n return Promise.resolve(null)\n }\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAgDA,SAAgB,iBAAiB,SAAkC;AACjE,QAAO,aAAa;EAClB,MAAM,QAAQ,QAAQ;EACtB,aAAa,QAAQ,eAAe;EACpC,OAAO,EAAE,OAAO;GACd,UAAU,EAAE,MAAM,EAAE,KAAK,CAAC;GAC1B,SAAS,EAAE,KAAK,CAAC,kBAAkB,qBAAqB,CAAC,CAAC,UAAU;GACpE,QAAQ,EAAE,QAAQ,CAAC,UAAU;GAC7B,WAAW,EAAE,QAAQ,CAAC,UAAU;GAChC,UAAU,EAAE,KAAK,CAAC,UAAU;GAC5B,MAAM,EAAE,OAAO,EAAE,QAAQ,EAAE,EAAE,KAAK,CAAC,CAAC,UAAU;GAC9C,SAAS,EAAE,OAAO,EAAE,QAAQ,EAAE,EAAE,QAAQ,CAAC,CAAC,UAAU;GACrD,CAAC;EACF,OAAO,EAAE,QAAwB;EACjC,KAAK,iBAAiB,EAAE,YAAY;GAClC,MAAM,aAAa;GACnB,MAAM,SAAS,WAAW;IACxB,OAAO,QAAQ;IACf,QAAQ,QAAQ;IAChB,UAAU,uBAAuB,WAAW;IAC5C,OAAO,QAAQ;IACf,iBAAiB,QAAQ;IACzB,aAAa,QAAQ;IACtB,CAAC;AACF,cAAW,MAAM,SAAS,OAAO,mBAAmB,CAClD,OAAM;;EAGX,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ACDJ,SAAgB,mBACd,WACkB;AAClB,QAAO;EACL,MAAM,aAAa,EAAE,UAAU,SAAS,QAAQ,WAAW,aAAa,UAAU,MAAM,WAAgB;GACtG,IAAI;AACJ,UAAO,IAAI,eAA+B;IACxC,MAAM,YAAY;KAChB,IAAI,SAAS;KACb,MAAM,cAAc;AAClB,UAAI,OAAU;AACd,eAAS;AACT,UAAI;AAAE,kBAAW,OAAO;cAAS;;AAEnC,oBAAe,UACb;MAAY;MAAiB;MAAS;MAAQ;MAAW;MAAU;MAAM;MAAS,EAClF;MACE,SAAS,UAAU;AACjB,WAAI,OAAU;AACd,kBAAW,QAAQ,MAAwB;;MAE7C,UAAU,UAAU;AAClB,WAAI,OAAU;AACd,gBAAS;AACT,kBAAW,MAAM,MAAM;;MAEzB,YAAY;MACb,CACF;AACD,SAAI,aAAa;MACf,MAAM,gBAAgB;AACpB,qBAAc,aAAa;AAC3B,cAAO;;AAET,UAAI,YAAY,QACd,UAAS;UAET,aAAY,iBAAiB,SAAS,SAAS,EAAE,MAAM,MAAM,CAAC;;;IAMpE,SAAS;AACP,mBAAc,aAAa;;IAE9B,CAAC;;EAEJ,oBAAoB;AAClB,UAAO,QAAQ,QAAQ,KAAK;;EAE/B"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@silkweave/ai",
3
- "version": "3.2.0",
3
+ "version": "3.2.1",
4
4
  "description": "Silkweave Vercel AI SDK integration - custom ChatTransport bridging useChat to tRPC subscriptions, plus a chat action helper that wraps streamText into a streaming Silkweave action",
5
5
  "license": "MIT",
6
6
  "homepage": "https://www.silkweave.dev",
@@ -27,7 +27,7 @@
27
27
  },
28
28
  "dependencies": {
29
29
  "zod": "^3.25.0",
30
- "@silkweave/core": "3.2.0"
30
+ "@silkweave/core": "3.2.1"
31
31
  },
32
32
  "peerDependencies": {
33
33
  "ai": "^5.0.0"