@contractspec/module.ai-chat 4.1.5 → 4.2.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.
Files changed (30) hide show
  1. package/README.md +29 -3
  2. package/dist/browser/core/index.js +53 -17
  3. package/dist/browser/index.js +982 -426
  4. package/dist/browser/presentation/components/index.js +975 -419
  5. package/dist/browser/presentation/hooks/index.js +8 -1
  6. package/dist/browser/presentation/index.js +983 -427
  7. package/dist/core/create-chat-route.d.ts +14 -3
  8. package/dist/core/index.js +53 -17
  9. package/dist/core/message-types.d.ts +4 -0
  10. package/dist/index.js +982 -426
  11. package/dist/node/core/index.js +53 -17
  12. package/dist/node/index.js +982 -426
  13. package/dist/node/presentation/components/index.js +975 -419
  14. package/dist/node/presentation/hooks/index.js +8 -1
  15. package/dist/node/presentation/index.js +983 -427
  16. package/dist/presentation/components/ChainOfThought.d.ts +30 -0
  17. package/dist/presentation/components/ChatInput.d.ts +3 -4
  18. package/dist/presentation/components/ChatMessage.d.ts +6 -4
  19. package/dist/presentation/components/ChatWithExport.d.ts +14 -1
  20. package/dist/presentation/components/ChatWithSidebar.d.ts +12 -1
  21. package/dist/presentation/components/Reasoning.d.ts +24 -0
  22. package/dist/presentation/components/Sources.d.ts +22 -0
  23. package/dist/presentation/components/Suggestion.d.ts +12 -0
  24. package/dist/presentation/components/ToolResultRenderer.d.ts +20 -3
  25. package/dist/presentation/components/component-types.d.ts +52 -0
  26. package/dist/presentation/components/index.d.ts +6 -1
  27. package/dist/presentation/components/index.js +975 -419
  28. package/dist/presentation/hooks/index.js +8 -1
  29. package/dist/presentation/index.js +983 -427
  30. package/package.json +13 -13
package/README.md CHANGED
@@ -162,7 +162,10 @@ For full AI SDK compatibility including tool approval, use `createChatRoute` wit
162
162
 
163
163
  ```ts
164
164
  // app/api/chat/route.ts (Next.js App Router)
165
- import { createChatRoute } from '@contractspec/module.ai-chat/core';
165
+ import {
166
+ createChatRoute,
167
+ CHAT_ROUTE_MAX_DURATION,
168
+ } from '@contractspec/module.ai-chat/core';
166
169
  import { createProvider } from '@contractspec/lib.ai-providers';
167
170
 
168
171
  const provider = createProvider({
@@ -172,8 +175,11 @@ const provider = createProvider({
172
175
  });
173
176
 
174
177
  export const POST = createChatRoute({ provider });
178
+ export const maxDuration = CHAT_ROUTE_MAX_DURATION;
175
179
  ```
176
180
 
181
+ `maxDuration` is required for long-running streaming on Vercel/serverless. The route sends sources and reasoning by default (AI Elements parity). Override with `sendSources` and `sendReasoning` in options. Use `getModel` to support a model picker from the request body.
182
+
177
183
  ```tsx
178
184
  // Client: use @ai-sdk/react useChat with DefaultChatTransport
179
185
  import { useChat } from '@ai-sdk/react';
@@ -229,9 +235,29 @@ const chatService = new ChatService({
229
235
  // based on thinking level when modelSelector is set
230
236
  ```
231
237
 
232
- ### Optional AI Elements
238
+ ### AI Elements-Style Components
239
+
240
+ The module includes native implementations of [Reasoning](https://elements.ai-sdk.dev/components/reasoning), [Sources](https://elements.ai-sdk.dev/components/sources), [Suggestion](https://elements.ai-sdk.dev/components/suggestion), and [Chain of Thought](https://elements.ai-sdk.dev/components/chain-of-thought) patterns. Pass `components` to `ChatMessage` or `ChatWithExport` to override with ai-elements when installed:
241
+
242
+ ```tsx
243
+ import { ChatWithSidebar, type ChatMessageComponents } from '@contractspec/module.ai-chat';
244
+ // When using ai-elements: import { Reasoning, Sources, ... } from '@/components/ai-elements/...';
245
+
246
+ <ChatWithSidebar
247
+ components={{
248
+ Reasoning: AiElementsReasoning,
249
+ Sources: AiElementsSources,
250
+ SourcesTrigger: AiElementsSourcesTrigger,
251
+ Source: AiElementsSource,
252
+ ChainOfThought: AiElementsChainOfThought,
253
+ ChainOfThoughtStep: AiElementsChainOfThoughtStep,
254
+ }}
255
+ suggestions={['Explain how X works', 'What is Y?']}
256
+ showSuggestionsWhenEmpty
257
+ />
258
+ ```
233
259
 
234
- Apps can optionally use [AI Elements](https://elements.ai-sdk.dev) for UI. This module does not depend on ai-elements; provide an adapter from `ChatMessage` to `UIMessage` when integrating.
260
+ Use `suggestions` and `onSuggestionClick` for clickable prompt chips. Apps can optionally use [AI Elements](https://elements.ai-sdk.dev) for enhanced UI; provide an adapter from `ChatMessage` to `UIMessage` when integrating.
235
261
 
236
262
  ### useCompletion (Non-Chat Completion)
237
263
 
@@ -1089,39 +1089,74 @@ import {
1089
1089
  convertToModelMessages,
1090
1090
  streamText as streamText2
1091
1091
  } from "ai";
1092
+ import { z as z4 } from "zod";
1093
+ var CHAT_ROUTE_MAX_DURATION = 30;
1094
+ var REQUEST_BODY_SCHEMA = z4.object({
1095
+ messages: z4.array(z4.unknown()).min(1, "messages array required"),
1096
+ thinkingLevel: z4.enum(["instant", "thinking", "extra_thinking", "max"]).optional(),
1097
+ model: z4.string().optional()
1098
+ });
1092
1099
  var DEFAULT_SYSTEM_PROMPT2 = `You are a helpful AI assistant.`;
1100
+ function defaultSendReasoning(thinkingLevel) {
1101
+ return Boolean(thinkingLevel && thinkingLevel !== "instant");
1102
+ }
1103
+ function defaultSendSources() {
1104
+ return true;
1105
+ }
1093
1106
  function createChatRoute(options) {
1094
1107
  const {
1095
1108
  provider,
1096
1109
  systemPrompt = DEFAULT_SYSTEM_PROMPT2,
1097
1110
  tools,
1098
- thinkingLevel: defaultThinkingLevel
1111
+ thinkingLevel: defaultThinkingLevel,
1112
+ sendSources = defaultSendSources(),
1113
+ sendReasoning = defaultSendReasoning(defaultThinkingLevel),
1114
+ getModel
1099
1115
  } = options;
1100
1116
  return async (req) => {
1101
1117
  if (req.method !== "POST") {
1102
1118
  return new Response("Method not allowed", { status: 405 });
1103
1119
  }
1104
- let body;
1120
+ let rawBody;
1105
1121
  try {
1106
- body = await req.json();
1122
+ rawBody = await req.json();
1107
1123
  } catch {
1108
- return new Response("Invalid JSON body", { status: 400 });
1124
+ return new Response(JSON.stringify({ error: "Invalid JSON body" }), {
1125
+ status: 400,
1126
+ headers: { "Content-Type": "application/json" }
1127
+ });
1109
1128
  }
1110
- const messages = body.messages ?? [];
1111
- if (!Array.isArray(messages) || messages.length === 0) {
1112
- return new Response("messages array required", { status: 400 });
1129
+ const parsed = REQUEST_BODY_SCHEMA.safeParse(rawBody);
1130
+ if (!parsed.success) {
1131
+ const message = parsed.error.issues[0]?.message ?? "Invalid request body";
1132
+ return new Response(JSON.stringify({ error: message }), {
1133
+ status: 400,
1134
+ headers: { "Content-Type": "application/json" }
1135
+ });
1113
1136
  }
1137
+ const body = parsed.data;
1114
1138
  const thinkingLevel = body.thinkingLevel ?? defaultThinkingLevel;
1115
1139
  const providerOptions = getProviderOptions(thinkingLevel, provider.name);
1116
- const model = provider.getModel();
1117
- const result = streamText2({
1118
- model,
1119
- messages: await convertToModelMessages(messages),
1120
- system: systemPrompt,
1121
- tools,
1122
- providerOptions: Object.keys(providerOptions).length > 0 ? providerOptions : undefined
1123
- });
1124
- return result.toUIMessageStreamResponse();
1140
+ const model = getModel ? getModel(body) : provider.getModel();
1141
+ try {
1142
+ const result = streamText2({
1143
+ model,
1144
+ messages: await convertToModelMessages(body.messages),
1145
+ system: systemPrompt,
1146
+ tools,
1147
+ providerOptions: Object.keys(providerOptions).length > 0 ? providerOptions : undefined
1148
+ });
1149
+ return result.toUIMessageStreamResponse({
1150
+ sendSources,
1151
+ sendReasoning
1152
+ });
1153
+ } catch (error) {
1154
+ const message = error instanceof Error ? error.message : "An error occurred";
1155
+ return new Response(JSON.stringify({ error: message }), {
1156
+ status: 500,
1157
+ headers: { "Content-Type": "application/json" }
1158
+ });
1159
+ }
1125
1160
  };
1126
1161
  }
1127
1162
  // src/core/create-completion-route.ts
@@ -1614,5 +1649,6 @@ export {
1614
1649
  THINKING_LEVEL_DESCRIPTIONS,
1615
1650
  LocalStorageConversationStore,
1616
1651
  InMemoryConversationStore,
1617
- ChatService
1652
+ ChatService,
1653
+ CHAT_ROUTE_MAX_DURATION
1618
1654
  };