ai-sdk-ollama 3.8.0 → 3.8.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,17 @@
1
1
  # Changelog
2
2
 
3
+ ## 3.8.2
4
+
5
+ ### Patch Changes
6
+
7
+ - 92ca938: Fix TypeScript 6.0 build failure caused by deprecated baseUrl option in tsup DTS generation
8
+
9
+ ## 3.8.1
10
+
11
+ ### Patch Changes
12
+
13
+ - aeafdd6: - Update `generateText` to detect and use the stable `output` option instead of the deprecated `experimental_output` when `enableToolsWithStructuredOutput` is enabled, including a two-phase tools + structured output path and an integration test to guard this behavior.
14
+
3
15
  ## 3.8.0
4
16
 
5
17
  ### Minor Changes
@@ -17066,6 +17066,17 @@ var ollama = createOllama();
17066
17066
 
17067
17067
  // src/functions/generate-text.ts
17068
17068
  var import_ai3 = require("ai");
17069
+ function resultWithOverrides(base, overrides) {
17070
+ const descriptors = {};
17071
+ for (const key of Object.keys(overrides)) {
17072
+ descriptors[key] = {
17073
+ value: overrides[key],
17074
+ enumerable: true,
17075
+ configurable: true
17076
+ };
17077
+ }
17078
+ return Object.create(base, descriptors);
17079
+ }
17069
17080
  async function generateText(options) {
17070
17081
  const { enhancedOptions = {}, ...generateTextOptions } = options;
17071
17082
  const {
@@ -17076,33 +17087,42 @@ async function generateText(options) {
17076
17087
  enableToolsWithStructuredOutput = false
17077
17088
  } = enhancedOptions;
17078
17089
  const hasTools = generateTextOptions.tools && Object.keys(generateTextOptions.tools).length > 0;
17079
- const hasExperimentalOutput = "experimental_output" in generateTextOptions;
17090
+ const hasOutput = "output" in generateTextOptions;
17080
17091
  const requiresTools = generateTextOptions.toolChoice === "required" || typeof generateTextOptions.toolChoice === "object" && "type" in generateTextOptions.toolChoice && generateTextOptions.toolChoice.type === "tool";
17081
- if (enableToolsWithStructuredOutput && hasExperimentalOutput && requiresTools && hasTools) {
17082
- const toolResult = await (0, import_ai3.generateText)({
17083
- ...generateTextOptions,
17084
- experimental_output: void 0
17085
- });
17092
+ if (enableToolsWithStructuredOutput && hasOutput && requiresTools && hasTools) {
17093
+ const phase1Options = Object.fromEntries(
17094
+ Object.entries(generateTextOptions).filter(([key]) => key !== "output")
17095
+ );
17096
+ const toolResult = await (0, import_ai3.generateText)(
17097
+ phase1Options
17098
+ );
17086
17099
  if (toolResult.toolCalls && toolResult.toolCalls.length > 0) {
17087
17100
  const toolContext = toolResult.toolResults?.map(
17088
- (tr, i) => `${toolResult.toolCalls?.[i]?.toolName}: ${JSON.stringify(tr.output || tr)}`
17101
+ (tr, i) => `${toolResult.toolCalls?.[i]?.toolName}: ${JSON.stringify(tr.output ?? tr)}`
17089
17102
  ).join("\n");
17090
- const contextualPrompt = typeof generateTextOptions.prompt === "string" ? `${generateTextOptions.prompt}
17103
+ const toolResultsSuffix = `
17091
17104
 
17092
17105
  Tool Results:
17093
17106
  ${toolContext}
17094
17107
 
17095
- Please provide a structured response based on these tool results.` : generateTextOptions.prompt;
17096
- const _generateTextOptions = {
17097
- ...generateTextOptions,
17098
- prompt: contextualPrompt,
17099
- tools: void 0,
17100
- toolChoice: void 0
17101
- };
17108
+ Please provide a structured response based on these tool results.`;
17109
+ const phase2Base = Object.fromEntries(
17110
+ Object.entries(generateTextOptions).filter(
17111
+ ([key]) => key !== "tools" && key !== "toolChoice"
17112
+ )
17113
+ );
17114
+ const phase2OptionsWithPrompt = typeof phase2Base.prompt === "string" ? { ...phase2Base, prompt: phase2Base.prompt + toolResultsSuffix } : phase2Base.messages ? {
17115
+ ...phase2Base,
17116
+ prompt: void 0,
17117
+ messages: [
17118
+ ...phase2Base.messages,
17119
+ { role: "user", content: toolResultsSuffix.trim() }
17120
+ ]
17121
+ } : phase2Base;
17102
17122
  const structuredResult = await (0, import_ai3.generateText)(
17103
- _generateTextOptions
17123
+ phase2OptionsWithPrompt
17104
17124
  );
17105
- const enhancedResult = Object.assign(structuredResult, {
17125
+ const enhancedResult = resultWithOverrides(structuredResult, {
17106
17126
  toolCalls: toolResult.toolCalls,
17107
17127
  toolResults: toolResult.toolResults,
17108
17128
  staticToolCalls: toolResult.staticToolCalls,
@@ -17110,9 +17130,9 @@ Please provide a structured response based on these tool results.` : generateTex
17110
17130
  staticToolResults: toolResult.staticToolResults,
17111
17131
  dynamicToolResults: toolResult.dynamicToolResults,
17112
17132
  usage: {
17113
- inputTokens: (toolResult.usage.inputTokens || 0) + (structuredResult.usage.inputTokens || 0),
17114
- outputTokens: (toolResult.usage.outputTokens || 0) + (structuredResult.usage.outputTokens || 0),
17115
- totalTokens: (toolResult.usage.totalTokens || 0) + (structuredResult.usage.totalTokens || 0)
17133
+ inputTokens: (toolResult.usage.inputTokens ?? 0) + (structuredResult.usage.inputTokens ?? 0),
17134
+ outputTokens: (toolResult.usage.outputTokens ?? 0) + (structuredResult.usage.outputTokens ?? 0),
17135
+ totalTokens: (toolResult.usage.totalTokens ?? 0) + (structuredResult.usage.totalTokens ?? 0)
17116
17136
  }
17117
17137
  });
17118
17138
  return enhancedResult;
@@ -17123,7 +17143,9 @@ Please provide a structured response based on these tool results.` : generateTex
17123
17143
  // Only set stopWhen default if user didn't provide one and tools are enabled
17124
17144
  stopWhen: generateTextOptions.stopWhen ?? (hasTools ? (0, import_ai3.stepCountIs)(5) : void 0)
17125
17145
  });
17126
- const toolsWereCalled = result.steps?.some((step) => step.toolCalls && step.toolCalls.length > 0) ?? false;
17146
+ const toolsWereCalled = result.steps?.some(
17147
+ (step) => step.toolCalls && step.toolCalls.length > 0
17148
+ ) ?? false;
17127
17149
  const hasMinimalText = !result.text || result.text.trim().length < minResponseLength;
17128
17150
  if (!hasTools || !toolsWereCalled || !hasMinimalText || !enableSynthesis) {
17129
17151
  return result;
@@ -17155,11 +17177,16 @@ Tool results:
17155
17177
  ${toolContext}
17156
17178
 
17157
17179
  ${synthesisPrompt}`;
17158
- const { tools, prompt, messages, ...baseOptions } = generateTextOptions;
17159
- const synthesisOptions = messages ? {
17180
+ const generateOptions = generateTextOptions;
17181
+ const baseOptions = Object.fromEntries(
17182
+ Object.entries(generateOptions).filter(
17183
+ ([key]) => key !== "tools" && key !== "prompt" && key !== "messages"
17184
+ )
17185
+ );
17186
+ const synthesisOptions = generateOptions.messages ? {
17160
17187
  ...baseOptions,
17161
17188
  messages: [
17162
- ...messages || [],
17189
+ ...generateOptions.messages || [],
17163
17190
  { role: "user", content: fullSynthesisPrompt }
17164
17191
  ]
17165
17192
  } : {
@@ -17170,12 +17197,12 @@ ${synthesisPrompt}`;
17170
17197
  synthesisOptions
17171
17198
  );
17172
17199
  if (synthesisResult.text && synthesisResult.text.trim().length >= minResponseLength) {
17173
- const enhancedResult = Object.assign(result, {
17200
+ const enhancedResult = resultWithOverrides(result, {
17174
17201
  text: synthesisResult.text,
17175
17202
  usage: {
17176
- inputTokens: (result.usage.inputTokens || 0) + (synthesisResult.usage.inputTokens || 0),
17177
- outputTokens: (result.usage.outputTokens || 0) + (synthesisResult.usage.outputTokens || 0),
17178
- totalTokens: (result.usage.totalTokens || 0) + (synthesisResult.usage.totalTokens || 0)
17203
+ inputTokens: (result.usage.inputTokens ?? 0) + (synthesisResult.usage.inputTokens ?? 0),
17204
+ outputTokens: (result.usage.outputTokens ?? 0) + (synthesisResult.usage.outputTokens ?? 0),
17205
+ totalTokens: (result.usage.totalTokens ?? 0) + (synthesisResult.usage.totalTokens ?? 0)
17179
17206
  }
17180
17207
  });
17181
17208
  return enhancedResult;
@@ -17201,7 +17228,6 @@ async function streamText(options) {
17201
17228
  }
17202
17229
  const streamResult = await (0, import_ai4.streamText)({
17203
17230
  ...streamTextOptions,
17204
- // Only set stopWhen default if user didn't provide one and tools are enabled
17205
17231
  stopWhen: streamTextOptions.stopWhen ?? (hasTools ? (0, import_ai4.stepCountIs)(5) : void 0)
17206
17232
  });
17207
17233
  const state = {
@@ -17213,11 +17239,13 @@ async function streamText(options) {
17213
17239
  };
17214
17240
  let synthesisApplied = false;
17215
17241
  let synthesisInProgress = false;
17216
- const generateSynthesis = async () => {
17242
+ const generateSynthesis = async (toolCallsOverride, toolResultsOverride) => {
17217
17243
  if (synthesisApplied || synthesisInProgress) {
17218
17244
  return "";
17219
17245
  }
17220
- if (state.toolCalls.length === 0) {
17246
+ const toolCalls = toolCallsOverride ?? state.toolCalls;
17247
+ const toolResults = toolResultsOverride ?? state.toolResults;
17248
+ if (toolCalls.length === 0) {
17221
17249
  return "";
17222
17250
  }
17223
17251
  if (state.textContent.length >= minStreamLength) {
@@ -17225,7 +17253,7 @@ async function streamText(options) {
17225
17253
  }
17226
17254
  synthesisInProgress = true;
17227
17255
  try {
17228
- const toolContext = state.toolResults.map((tr) => {
17256
+ const toolContext = toolResults.map((tr) => {
17229
17257
  return `${tr.toolName}: ${JSON.stringify(tr.output)}`;
17230
17258
  }).join("\n") || "";
17231
17259
  const originalPromptText = typeof options.prompt === "string" ? options.prompt : options.messages?.at(-1)?.content || "the user question";
@@ -17290,20 +17318,55 @@ Based on the tool results above, please provide a comprehensive response to the
17290
17318
  }
17291
17319
  return false;
17292
17320
  };
17293
- const applySynthesis = async () => {
17321
+ const applySynthesisFromSteps = async () => {
17294
17322
  if (synthesisApplied || controllerClosed) return;
17295
- const synthesisText = await generateSynthesis();
17296
- if (synthesisText && !controllerClosed) {
17297
- for (const char of synthesisText) {
17298
- if (!safeEnqueue(char)) break;
17323
+ try {
17324
+ const steps = await streamResult.steps;
17325
+ const allToolCalls = [];
17326
+ const allToolResults = [];
17327
+ if (steps) {
17328
+ for (const step of steps) {
17329
+ if (step.toolCalls) {
17330
+ for (const tc of step.toolCalls) {
17331
+ allToolCalls.push({
17332
+ toolCallId: tc.toolCallId,
17333
+ toolName: tc.toolName,
17334
+ input: tc.input
17335
+ });
17336
+ }
17337
+ }
17338
+ if (step.toolResults) {
17339
+ for (const tr of step.toolResults) {
17340
+ allToolResults.push({
17341
+ toolCallId: tr.toolCallId,
17342
+ toolName: tr.toolName,
17343
+ output: tr.output
17344
+ });
17345
+ }
17346
+ }
17347
+ }
17348
+ }
17349
+ if (allToolCalls.length > 0 && state.textContent.length < minStreamLength) {
17350
+ const synthesisText = await generateSynthesis(
17351
+ allToolCalls,
17352
+ allToolResults
17353
+ );
17354
+ if (synthesisText && !controllerClosed) {
17355
+ const chunkSize = 20;
17356
+ for (let i = 0; i < synthesisText.length; i += chunkSize) {
17357
+ if (!safeEnqueue(synthesisText.slice(i, i + chunkSize)))
17358
+ break;
17359
+ }
17360
+ }
17299
17361
  }
17362
+ } catch {
17300
17363
  }
17301
17364
  };
17302
17365
  const resetTimeout = () => {
17303
17366
  if (streamTimeout) clearTimeout(streamTimeout);
17304
17367
  streamTimeout = setTimeout(async () => {
17305
17368
  if (!streamComplete && !synthesisApplied && !controllerClosed) {
17306
- await applySynthesis();
17369
+ await applySynthesisFromSteps();
17307
17370
  }
17308
17371
  safeClose();
17309
17372
  }, synthesisTimeout);
@@ -17318,7 +17381,7 @@ Based on the tool results above, please provide a comprehensive response to the
17318
17381
  if (streamTimeout) clearTimeout(streamTimeout);
17319
17382
  state.hasFinished = true;
17320
17383
  if (!synthesisApplied && !controllerClosed) {
17321
- await applySynthesis();
17384
+ await applySynthesisFromSteps();
17322
17385
  }
17323
17386
  safeClose();
17324
17387
  break;
@@ -17342,7 +17405,7 @@ Based on the tool results above, please provide a comprehensive response to the
17342
17405
  return new ReadableStream({
17343
17406
  async start(controller) {
17344
17407
  let controllerClosed = false;
17345
- let hasSeenText = false;
17408
+ let hasSeenMeaningfulText = false;
17346
17409
  let currentTextId = null;
17347
17410
  const safeClose = () => {
17348
17411
  if (!controllerClosed) {
@@ -17392,7 +17455,7 @@ Based on the tool results above, please provide a comprehensive response to the
17392
17455
  output: tr.output
17393
17456
  }));
17394
17457
  }
17395
- if (state.toolCalls.length > 0 && state.textContent.length < minStreamLength && !hasSeenText) {
17458
+ if (state.toolCalls.length > 0 && state.textContent.length < minStreamLength && !hasSeenMeaningfulText) {
17396
17459
  const synthesisText = await generateSynthesis();
17397
17460
  if (synthesisText && synthesisText.length > 0) {
17398
17461
  currentTextId = crypto.randomUUID();
@@ -17428,12 +17491,12 @@ Based on the tool results above, please provide a comprehensive response to the
17428
17491
  switch (part.type) {
17429
17492
  case "text-delta":
17430
17493
  case "text-delta-text": {
17431
- hasSeenText = true;
17432
17494
  const delta = typeof part.delta === "string" ? part.delta : "";
17433
17495
  const text = typeof part.text === "string" ? part.text : "";
17434
17496
  const textDelta = delta || text;
17435
17497
  if (textDelta) {
17436
17498
  state.textContent += textDelta;
17499
+ hasSeenMeaningfulText = true;
17437
17500
  }
17438
17501
  if (!currentTextId && typeof part.id === "string") {
17439
17502
  currentTextId = part.id;
@@ -17441,7 +17504,6 @@ Based on the tool results above, please provide a comprehensive response to the
17441
17504
  break;
17442
17505
  }
17443
17506
  case "text-start": {
17444
- hasSeenText = true;
17445
17507
  if (typeof part.id === "string") {
17446
17508
  currentTextId = part.id;
17447
17509
  }