@jerome-benoit/sap-ai-provider 3.0.0 → 4.0.0-rc.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/README.md CHANGED
@@ -3,6 +3,7 @@
3
3
  [![npm](https://img.shields.io/npm/v/@mymediset/sap-ai-provider/latest?label=npm&color=blue)](https://www.npmjs.com/package/@mymediset/sap-ai-provider)
4
4
  [![License: Apache-2.0](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0)
5
5
  [![Vercel AI SDK](https://img.shields.io/badge/Vercel%20AI%20SDK-6.0+-black.svg)](https://sdk.vercel.ai/docs)
6
+ [![Language Model](https://img.shields.io/badge/Language%20Model-V3-green.svg)](https://sdk.vercel.ai/docs/ai-sdk-core/provider-interfaces)
6
7
 
7
8
  A community provider for SAP AI Core that integrates seamlessly with the Vercel AI SDK. Built on top of the official **@sap-ai-sdk/orchestration** package, this provider enables you to use SAP's enterprise-grade AI models through the familiar Vercel AI SDK interface.
8
9
 
@@ -12,15 +13,38 @@ A community provider for SAP AI Core that integrates seamlessly with the Vercel
12
13
  - [Quick Start](#quick-start)
13
14
  - [Quick Reference](#quick-reference)
14
15
  - [Installation](#installation)
16
+ - [Provider Creation](#provider-creation)
17
+ - [Option 1: Factory Function (Recommended for Custom Configuration)](#option-1-factory-function-recommended-for-custom-configuration)
18
+ - [Option 2: Default Instance (Quick Start)](#option-2-default-instance-quick-start)
15
19
  - [Authentication](#authentication)
16
20
  - [Basic Usage](#basic-usage)
21
+ - [Text Generation](#text-generation)
22
+ - [Chat Conversations](#chat-conversations)
23
+ - [Streaming Responses](#streaming-responses)
24
+ - [Model Configuration](#model-configuration)
17
25
  - [Supported Models](#supported-models)
18
26
  - [Advanced Features](#advanced-features)
27
+ - [Tool Calling](#tool-calling)
28
+ - [Multi-modal Input (Images)](#multi-modal-input-images)
29
+ - [Data Masking (SAP DPI)](#data-masking-sap-dpi)
30
+ - [Content Filtering](#content-filtering)
19
31
  - [Configuration Options](#configuration-options)
20
32
  - [Error Handling](#error-handling)
33
+ - [Troubleshooting](#troubleshooting)
34
+ - [Performance](#performance)
35
+ - [Security](#security)
36
+ - [Debug Mode](#debug-mode)
21
37
  - [Examples](#examples)
22
38
  - [Migration Guides](#migration-guides)
39
+ - [Upgrading from v3.x to v4.x](#upgrading-from-v3x-to-v4x)
40
+ - [Upgrading from v2.x to v3.x](#upgrading-from-v2x-to-v3x)
41
+ - [Upgrading from v1.x to v2.x](#upgrading-from-v1x-to-v2x)
42
+ - [Important Note](#important-note)
23
43
  - [Contributing](#contributing)
44
+ - [Resources](#resources)
45
+ - [Documentation](#documentation)
46
+ - [Community](#community)
47
+ - [Related Projects](#related-projects)
24
48
  - [License](#license)
25
49
 
26
50
  ## Features
@@ -29,11 +53,12 @@ A community provider for SAP AI Core that integrates seamlessly with the Vercel
29
53
  - 🎯 **Tool Calling Support** - Full tool/function calling capabilities
30
54
  - 🧠 **Reasoning-Safe by Default** - Assistant reasoning parts are not forwarded unless enabled
31
55
  - 🖼️ **Multi-modal Input** - Support for text and image inputs
32
- - 📡 **Streaming Support** - Real-time text generation
56
+ - 📡 **Streaming Support** - Real-time text generation with structured V3 blocks
33
57
  - 🔒 **Data Masking** - Built-in SAP DPI integration for privacy
34
58
  - 🛡️ **Content Filtering** - Azure Content Safety and Llama Guard support
35
59
  - 🔧 **TypeScript Support** - Full type safety and IntelliSense
36
60
  - 🎨 **Multiple Models** - Support for GPT-4, Claude, Gemini, Nova, and more
61
+ - ⚡ **Language Model V3** - Latest Vercel AI SDK specification with enhanced streaming
37
62
 
38
63
  ## Quick Start
39
64
 
@@ -263,13 +288,13 @@ This provider supports all models available through SAP AI Core Orchestration se
263
288
  **Popular models:**
264
289
 
265
290
  - **OpenAI**: gpt-4o, gpt-4o-mini, gpt-4.1, o1, o3 (recommended for multi-tool apps)
266
- - **Anthropic Claude**: claude-3.5-sonnet, claude-4-opus
291
+ - **Anthropic Claude**: anthropic--claude-3.5-sonnet, anthropic--claude-4-opus
267
292
  - **Google Gemini**: gemini-2.5-pro, gemini-2.0-flash
268
293
 
269
294
  ⚠️ **Important:** Google Gemini models have a 1 tool limit per request.
270
295
 
271
- - **Amazon Nova**: nova-pro, nova-lite
272
- - **Open Source**: mistralai-mistral-large, llama3.1-70b
296
+ - **Amazon Nova**: amazon--nova-pro, amazon--nova-lite
297
+ - **Open Source**: mistralai--mistral-large-instruct, meta--llama3.1-70b-instruct
273
298
 
274
299
  > **Note:** Model availability depends on your SAP AI Core tenant configuration, region, and subscription.
275
300
 
@@ -496,6 +521,23 @@ npx tsx examples/example-generate-text.ts
496
521
 
497
522
  ## Migration Guides
498
523
 
524
+ ### Upgrading from v3.x to v4.x
525
+
526
+ Version 4.0 migrates from **LanguageModelV2** to **LanguageModelV3** specification (AI SDK 6.0+). **See the [Migration Guide](./MIGRATION_GUIDE.md#version-3x-to-4x-breaking-changes) for complete upgrade instructions.**
527
+
528
+ **Key changes:**
529
+
530
+ - **Finish Reason**: Changed from string to object (`result.finishReason.unified`)
531
+ - **Usage Structure**: Nested format with detailed token breakdown (`result.usage.inputTokens.total`)
532
+ - **Stream Events**: Structured blocks (`text-start`, `text-delta`, `text-end`) instead of simple deltas
533
+ - **Warning Types**: Updated format with `feature` field for categorization
534
+
535
+ **Impact by user type:**
536
+
537
+ - High-level API users (`generateText`/`streamText`): ✅ Minimal impact (likely no changes)
538
+ - Direct provider users: ⚠️ Update type imports (`LanguageModelV2` → `LanguageModelV3`)
539
+ - Custom stream parsers: ⚠️ Update parsing logic for V3 structure
540
+
499
541
  ### Upgrading from v2.x to v3.x
500
542
 
501
543
  Version 3.0 standardizes error handling to use Vercel AI SDK native error types. **See the [Migration Guide](./MIGRATION_GUIDE.md#v2x--v30) for complete upgrade instructions.**
@@ -526,34 +568,28 @@ Version 2.0 uses the official SAP AI SDK. **See the [Migration Guide](./MIGRATIO
526
568
 
527
569
  We welcome contributions! Please see our [Contributing Guide](./CONTRIBUTING.md) for details.
528
570
 
529
- ## License
530
-
531
- Apache License 2.0 - see [LICENSE](./LICENSE.md) for details.
532
-
533
- ## Support
534
-
535
- - 📖 [Documentation](https://github.com/BITASIA/sap-ai-provider)
536
- - 🐛 [Issue Tracker](https://github.com/BITASIA/sap-ai-provider/issues)
571
+ ## Resources
537
572
 
538
- ## Documentation
539
-
540
- ### Guides
541
-
542
- - [Environment Setup](./ENVIRONMENT_SETUP.md) - Authentication and configuration setup
543
- - [Migration Guide](./MIGRATION_GUIDE.md) - Upgrading from v1.x to v2.x with step-by-step instructions
544
- - [curl API Testing](./CURL_API_TESTING_GUIDE.md) - Direct API testing for debugging
545
-
546
- ### Reference
573
+ ### Documentation
547
574
 
575
+ - [Migration Guide](./MIGRATION_GUIDE.md) - Version upgrade instructions (v1.x → v2.x → v3.x → v4.x)
548
576
  - [API Reference](./API_REFERENCE.md) - Complete API documentation with all types and functions
577
+ - [Environment Setup](./ENVIRONMENT_SETUP.md) - Authentication and configuration setup
578
+ - [Troubleshooting](./TROUBLESHOOTING.md) - Common issues and solutions
549
579
  - [Architecture](./ARCHITECTURE.md) - Internal architecture, design decisions, and request flows
580
+ - [curl API Testing](./CURL_API_TESTING_GUIDE.md) - Direct API testing for debugging
550
581
 
551
- ### Contributing
582
+ ### Community
552
583
 
553
- - [Contributing Guide](./CONTRIBUTING.md) - How to contribute to this project
584
+ - 🐛 [Issue Tracker](https://github.com/BITASIA/sap-ai-provider/issues) - Report bugs and request features
585
+ - 💬 [Discussions](https://github.com/BITASIA/sap-ai-provider/discussions) - Ask questions and share ideas
554
586
 
555
- ## Related
587
+ ### Related Projects
556
588
 
557
589
  - [Vercel AI SDK](https://sdk.vercel.ai/) - The AI SDK this provider extends
558
590
  - [SAP AI SDK](https://sap.github.io/ai-sdk/) - Official SAP Cloud SDK for AI
559
591
  - [SAP AI Core Documentation](https://help.sap.com/docs/ai-core) - Official SAP AI Core docs
592
+
593
+ ## License
594
+
595
+ Apache License 2.0 - see [LICENSE](./LICENSE.md) for details.
package/dist/index.cjs CHANGED
@@ -24766,7 +24766,7 @@ var require_form_data = __commonJS({
24766
24766
  var parseUrl = require("url").parse;
24767
24767
  var fs = require("fs");
24768
24768
  var Stream = require("stream").Stream;
24769
- var crypto = require("crypto");
24769
+ var crypto2 = require("crypto");
24770
24770
  var mime = require_mime_types();
24771
24771
  var asynckit = require_asynckit();
24772
24772
  var setToStringTag = require_es_set_tostringtag();
@@ -24972,7 +24972,7 @@ var require_form_data = __commonJS({
24972
24972
  return Buffer.concat([dataBuffer, Buffer.from(this._lastBoundary())]);
24973
24973
  };
24974
24974
  FormData2.prototype._generateBoundary = function() {
24975
- this._boundary = "--------------------------" + crypto.randomBytes(12).toString("hex");
24975
+ this._boundary = "--------------------------" + crypto2.randomBytes(12).toString("hex");
24976
24976
  };
24977
24977
  FormData2.prototype.getLengthSync = function() {
24978
24978
  var knownLength = this._overheadLength + this._valueLength;
@@ -26202,7 +26202,7 @@ var require_axios = __commonJS({
26202
26202
  "node_modules/axios/dist/node/axios.cjs"(exports2, module2) {
26203
26203
  "use strict";
26204
26204
  var FormData$1 = require_form_data();
26205
- var crypto = require("crypto");
26205
+ var crypto2 = require("crypto");
26206
26206
  var url = require("url");
26207
26207
  var proxyFromEnv = require_proxy_from_env();
26208
26208
  var http = require("http");
@@ -26217,7 +26217,7 @@ var require_axios = __commonJS({
26217
26217
  return e && typeof e === "object" && "default" in e ? e : { "default": e };
26218
26218
  }
26219
26219
  var FormData__default = /* @__PURE__ */ _interopDefaultLegacy(FormData$1);
26220
- var crypto__default = /* @__PURE__ */ _interopDefaultLegacy(crypto);
26220
+ var crypto__default = /* @__PURE__ */ _interopDefaultLegacy(crypto2);
26221
26221
  var url__default = /* @__PURE__ */ _interopDefaultLegacy(url);
26222
26222
  var proxyFromEnv__default = /* @__PURE__ */ _interopDefaultLegacy(proxyFromEnv);
26223
26223
  var http__default = /* @__PURE__ */ _interopDefaultLegacy(http);
@@ -29897,12 +29897,14 @@ function convertToSAPMessages(prompt, options = {}) {
29897
29897
  }
29898
29898
  case "tool": {
29899
29899
  for (const part of message.content) {
29900
- const toolMessage = {
29901
- role: "tool",
29902
- tool_call_id: part.toolCallId,
29903
- content: JSON.stringify(part.output)
29904
- };
29905
- messages.push(toolMessage);
29900
+ if (part.type === "tool-result") {
29901
+ const toolMessage = {
29902
+ role: "tool",
29903
+ tool_call_id: part.toolCallId,
29904
+ content: JSON.stringify(part.output)
29905
+ };
29906
+ messages.push(toolMessage);
29907
+ }
29906
29908
  }
29907
29909
  break;
29908
29910
  }
@@ -30071,6 +30073,16 @@ See: https://help.sap.com/docs/sap-ai-core/sap-ai-core-service-guide/create-serv
30071
30073
  }
30072
30074
 
30073
30075
  // src/sap-ai-chat-language-model.ts
30076
+ var StreamIdGenerator = class {
30077
+ /**
30078
+ * Generates a unique ID for a text block.
30079
+ *
30080
+ * @returns RFC 4122-compliant UUID string
30081
+ */
30082
+ generateTextBlockId() {
30083
+ return crypto.randomUUID();
30084
+ }
30085
+ };
30074
30086
  function validateModelParameters(params, warnings) {
30075
30087
  if (params.temperature !== void 0 && (params.temperature < 0 || params.temperature > 2)) {
30076
30088
  warnings.push({
@@ -30162,7 +30174,7 @@ function buildSAPToolParameters(schema) {
30162
30174
  };
30163
30175
  }
30164
30176
  var SAPAIChatLanguageModel = class {
30165
- specificationVersion = "v2";
30177
+ specificationVersion = "v3";
30166
30178
  modelId;
30167
30179
  config;
30168
30180
  settings;
@@ -30185,10 +30197,14 @@ var SAPAIChatLanguageModel = class {
30185
30197
  * Checks if a URL is supported for file/image uploads.
30186
30198
  *
30187
30199
  * @param url - The URL to check
30188
- * @returns True if the URL protocol is HTTPS or data (content-type rules are enforced via supportedUrls)
30200
+ * @returns True if the URL protocol is HTTPS or data with valid image format
30189
30201
  */
30190
30202
  supportsUrl(url) {
30191
- return url.protocol === "https:" || url.protocol === "data:";
30203
+ if (url.protocol === "https:") return true;
30204
+ if (url.protocol === "data:") {
30205
+ return /^data:image\//i.test(url.href);
30206
+ }
30207
+ return false;
30192
30208
  }
30193
30209
  /**
30194
30210
  * Returns supported URL patterns for different content types.
@@ -30201,9 +30217,45 @@ var SAPAIChatLanguageModel = class {
30201
30217
  };
30202
30218
  }
30203
30219
  /**
30204
- * Gets the provider identifier.
30220
+ * Generates text completion using SAP AI Core's Orchestration API.
30221
+ *
30222
+ * This method implements the `LanguageModelV3.doGenerate` interface,
30223
+ * providing synchronous (non-streaming) text generation with support for:
30224
+ * - Multi-turn conversations with system/user/assistant messages
30225
+ * - Tool calling (function calling) with structured outputs
30226
+ * - Multi-modal inputs (text + images)
30227
+ * - Data masking via SAP DPI
30228
+ * - Content filtering via Azure Content Safety or Llama Guard
30229
+ *
30230
+ * **Return Structure:**
30231
+ * - Finish reason: `{ unified: string, raw?: string }`
30232
+ * - Usage: Nested structure with token breakdown `{ inputTokens: { total, ... }, outputTokens: { total, ... } }`
30233
+ * - Warnings: Array of warnings with `type` and optional `feature` field
30234
+ *
30235
+ * @param options - Generation options including prompt, tools, temperature, etc.
30236
+ * @returns Promise resolving to generation result with content, usage, and metadata
30237
+ *
30238
+ * @throws {InvalidPromptError} If prompt format is invalid
30239
+ * @throws {InvalidArgumentError} If arguments are malformed
30240
+ * @throws {APICallError} If the SAP AI Core API call fails
30241
+ *
30242
+ * @example
30243
+ * ```typescript
30244
+ * const result = await model.doGenerate({
30245
+ * prompt: [
30246
+ * { role: 'user', content: [{ type: 'text', text: 'Hello!' }] }
30247
+ * ],
30248
+ * temperature: 0.7,
30249
+ * maxTokens: 100
30250
+ * });
30251
+ *
30252
+ * console.log(result.content); // Array of V3 content parts
30253
+ * console.log(result.finishReason.unified); // "stop", "length", "tool-calls", etc.
30254
+ * console.log(result.usage.inputTokens.total); // Total input tokens
30255
+ * ```
30205
30256
  *
30206
- * @returns The provider name ('sap-ai')
30257
+ * @since 1.0.0
30258
+ * @since 4.0.0 Updated to LanguageModelV3 interface
30207
30259
  */
30208
30260
  get provider() {
30209
30261
  return this.config.provider;
@@ -30270,11 +30322,11 @@ var SAPAIChatLanguageModel = class {
30270
30322
  const schemaRecord = jsonSchema;
30271
30323
  delete schemaRecord.$schema;
30272
30324
  parameters = buildSAPToolParameters(schemaRecord);
30273
- } catch {
30325
+ } catch (error) {
30274
30326
  warnings.push({
30275
- type: "unsupported-tool",
30276
- tool,
30277
- details: "Failed to convert tool Zod schema to JSON Schema. Falling back to empty object schema."
30327
+ type: "unsupported",
30328
+ feature: `tool schema conversion for ${tool.name}`,
30329
+ details: `Failed to convert tool Zod schema: ${error instanceof Error ? error.message : String(error)}. Falling back to empty object schema.`
30278
30330
  });
30279
30331
  parameters = buildSAPToolParameters({});
30280
30332
  }
@@ -30298,8 +30350,9 @@ var SAPAIChatLanguageModel = class {
30298
30350
  };
30299
30351
  } else {
30300
30352
  warnings.push({
30301
- type: "unsupported-tool",
30302
- tool
30353
+ type: "unsupported",
30354
+ feature: `tool type for ${tool.name}`,
30355
+ details: "Only 'function' tool type is supported."
30303
30356
  });
30304
30357
  return null;
30305
30358
  }
@@ -30351,8 +30404,8 @@ var SAPAIChatLanguageModel = class {
30351
30404
  );
30352
30405
  if (options.toolChoice && options.toolChoice.type !== "auto") {
30353
30406
  warnings.push({
30354
- type: "unsupported-setting",
30355
- setting: "toolChoice",
30407
+ type: "unsupported",
30408
+ feature: "toolChoice",
30356
30409
  details: `SAP AI SDK does not support toolChoice '${options.toolChoice.type}'. Using default 'auto' behavior.`
30357
30410
  });
30358
30411
  }
@@ -30412,7 +30465,7 @@ var SAPAIChatLanguageModel = class {
30412
30465
  /**
30413
30466
  * Generates a single completion (non-streaming).
30414
30467
  *
30415
- * This method implements the `LanguageModelV2.doGenerate` interface,
30468
+ * This method implements the `LanguageModelV3.doGenerate` interface,
30416
30469
  * sending a request to SAP AI Core and returning the complete response.
30417
30470
  *
30418
30471
  * **Features:**
@@ -30420,6 +30473,13 @@ var SAPAIChatLanguageModel = class {
30420
30473
  * - Multi-modal input (text + images)
30421
30474
  * - Data masking (if configured)
30422
30475
  * - Content filtering (if configured)
30476
+ * - Abort signal support (via Promise.race)
30477
+ *
30478
+ * **Note on Abort Signal:**
30479
+ * The abort signal implementation uses Promise.race to reject the promise when
30480
+ * the signal is aborted. However, this does not cancel the underlying HTTP request
30481
+ * to SAP AI Core - the request continues executing on the server. This is a
30482
+ * limitation of the SAP AI SDK's chatCompletion API.
30423
30483
  *
30424
30484
  * @param options - Generation options including prompt, tools, and settings
30425
30485
  * @returns Promise resolving to the generation result with content, usage, and metadata
@@ -30492,7 +30552,7 @@ var SAPAIChatLanguageModel = class {
30492
30552
  Object.entries(responseHeadersRaw).flatMap(([key, value]) => {
30493
30553
  if (typeof value === "string") return [[key, value]];
30494
30554
  if (Array.isArray(value)) {
30495
- const strings = value.filter((item) => typeof item === "string").join(",");
30555
+ const strings = value.filter((item) => typeof item === "string").join("; ");
30496
30556
  return strings.length > 0 ? [[key, strings]] : [];
30497
30557
  }
30498
30558
  if (typeof value === "number" || typeof value === "boolean") {
@@ -30533,9 +30593,17 @@ var SAPAIChatLanguageModel = class {
30533
30593
  content,
30534
30594
  finishReason,
30535
30595
  usage: {
30536
- inputTokens: tokenUsage.prompt_tokens,
30537
- outputTokens: tokenUsage.completion_tokens,
30538
- totalTokens: tokenUsage.total_tokens
30596
+ inputTokens: {
30597
+ total: tokenUsage.prompt_tokens,
30598
+ noCache: tokenUsage.prompt_tokens,
30599
+ cacheRead: void 0,
30600
+ cacheWrite: void 0
30601
+ },
30602
+ outputTokens: {
30603
+ total: tokenUsage.completion_tokens,
30604
+ text: tokenUsage.completion_tokens,
30605
+ reasoning: void 0
30606
+ }
30539
30607
  },
30540
30608
  providerMetadata: {
30541
30609
  "sap-ai": {
@@ -30566,19 +30634,28 @@ var SAPAIChatLanguageModel = class {
30566
30634
  /**
30567
30635
  * Generates a streaming completion.
30568
30636
  *
30569
- * This method implements the `LanguageModelV2.doStream` interface,
30637
+ * This method implements the `LanguageModelV3.doStream` interface,
30570
30638
  * sending a streaming request to SAP AI Core and returning a stream of response parts.
30571
30639
  *
30572
30640
  * **Stream Events:**
30573
- * - `stream-start` - Stream initialization
30641
+ * - `stream-start` - Stream initialization with warnings
30574
30642
  * - `response-metadata` - Response metadata (model, timestamp)
30575
- * - `text-start` - Text generation starts
30576
- * - `text-delta` - Incremental text chunks
30577
- * - `text-end` - Text generation completes
30578
- * - `tool-call` - Tool call detected
30643
+ * - `text-start` - Text block begins (with unique ID)
30644
+ * - `text-delta` - Incremental text chunks (with block ID)
30645
+ * - `text-end` - Text block completes (with accumulated text)
30646
+ * - `tool-input-start` - Tool input begins
30647
+ * - `tool-input-delta` - Tool input chunk
30648
+ * - `tool-input-end` - Tool input completes
30649
+ * - `tool-call` - Complete tool call
30579
30650
  * - `finish` - Stream completes with usage and finish reason
30580
30651
  * - `error` - Error occurred
30581
30652
  *
30653
+ * **Stream Structure:**
30654
+ * - Text blocks have explicit lifecycle with unique IDs
30655
+ * - Finish reason format: `{ unified: string, raw?: string }`
30656
+ * - Usage format: `{ inputTokens: { total, ... }, outputTokens: { total, ... } }`
30657
+ * - Warnings only in `stream-start` event
30658
+ *
30582
30659
  * @param options - Streaming options including prompt, tools, and settings
30583
30660
  * @returns Promise resolving to stream and request metadata
30584
30661
  *
@@ -30594,8 +30671,13 @@ var SAPAIChatLanguageModel = class {
30594
30671
  * if (part.type === 'text-delta') {
30595
30672
  * process.stdout.write(part.delta);
30596
30673
  * }
30674
+ * if (part.type === 'text-end') {
30675
+ * console.log('Block complete:', part.id, part.text);
30676
+ * }
30597
30677
  * }
30598
30678
  * ```
30679
+ *
30680
+ * @since 4.0.0
30599
30681
  */
30600
30682
  async doStream(options) {
30601
30683
  try {
@@ -30623,12 +30705,25 @@ var SAPAIChatLanguageModel = class {
30623
30705
  options.abortSignal,
30624
30706
  { promptTemplating: { include_usage: true } }
30625
30707
  );
30708
+ const idGenerator = new StreamIdGenerator();
30709
+ let textBlockId = null;
30626
30710
  const streamState = {
30627
- finishReason: "unknown",
30711
+ finishReason: {
30712
+ unified: "other",
30713
+ raw: void 0
30714
+ },
30628
30715
  usage: {
30629
- inputTokens: void 0,
30630
- outputTokens: void 0,
30631
- totalTokens: void 0
30716
+ inputTokens: {
30717
+ total: void 0,
30718
+ noCache: void 0,
30719
+ cacheRead: void 0,
30720
+ cacheWrite: void 0
30721
+ },
30722
+ outputTokens: {
30723
+ total: void 0,
30724
+ text: void 0,
30725
+ reasoning: void 0
30726
+ }
30632
30727
  },
30633
30728
  isFirstChunk: true,
30634
30729
  activeText: false
@@ -30656,19 +30751,25 @@ var SAPAIChatLanguageModel = class {
30656
30751
  }
30657
30752
  const deltaToolCalls = chunk.getDeltaToolCalls();
30658
30753
  if (Array.isArray(deltaToolCalls) && deltaToolCalls.length > 0) {
30659
- streamState.finishReason = "tool-calls";
30754
+ streamState.finishReason = {
30755
+ unified: "tool-calls",
30756
+ raw: void 0
30757
+ };
30660
30758
  }
30661
30759
  const deltaContent = chunk.getDeltaContent();
30662
- if (typeof deltaContent === "string" && deltaContent.length > 0 && streamState.finishReason !== "tool-calls") {
30760
+ if (typeof deltaContent === "string" && deltaContent.length > 0 && streamState.finishReason.unified !== "tool-calls") {
30663
30761
  if (!streamState.activeText) {
30664
- controller.enqueue({ type: "text-start", id: "0" });
30762
+ textBlockId = idGenerator.generateTextBlockId();
30763
+ controller.enqueue({ type: "text-start", id: textBlockId });
30665
30764
  streamState.activeText = true;
30666
30765
  }
30667
- controller.enqueue({
30668
- type: "text-delta",
30669
- id: "0",
30670
- delta: deltaContent
30671
- });
30766
+ if (textBlockId) {
30767
+ controller.enqueue({
30768
+ type: "text-delta",
30769
+ id: textBlockId,
30770
+ delta: deltaContent
30771
+ });
30772
+ }
30672
30773
  }
30673
30774
  if (Array.isArray(deltaToolCalls) && deltaToolCalls.length > 0) {
30674
30775
  for (const toolCallChunk of deltaToolCalls) {
@@ -30718,7 +30819,7 @@ var SAPAIChatLanguageModel = class {
30718
30819
  const chunkFinishReason = chunk.getFinishReason();
30719
30820
  if (chunkFinishReason) {
30720
30821
  streamState.finishReason = mapFinishReason(chunkFinishReason);
30721
- if (streamState.finishReason === "tool-calls") {
30822
+ if (streamState.finishReason.unified === "tool-calls") {
30722
30823
  const toolCalls2 = Array.from(toolCallsInProgress.values());
30723
30824
  for (const tc of toolCalls2) {
30724
30825
  if (tc.didEmitCall) {
@@ -30747,8 +30848,8 @@ var SAPAIChatLanguageModel = class {
30747
30848
  input: tc.arguments
30748
30849
  });
30749
30850
  }
30750
- if (streamState.activeText) {
30751
- controller.enqueue({ type: "text-end", id: "0" });
30851
+ if (streamState.activeText && textBlockId) {
30852
+ controller.enqueue({ type: "text-end", id: textBlockId });
30752
30853
  streamState.activeText = false;
30753
30854
  }
30754
30855
  }
@@ -30784,20 +30885,24 @@ var SAPAIChatLanguageModel = class {
30784
30885
  input: tc.arguments
30785
30886
  });
30786
30887
  }
30787
- if (streamState.activeText) {
30788
- controller.enqueue({ type: "text-end", id: "0" });
30888
+ if (streamState.activeText && textBlockId) {
30889
+ controller.enqueue({ type: "text-end", id: textBlockId });
30789
30890
  }
30790
30891
  const finalFinishReason = streamResponse.getFinishReason();
30791
30892
  if (finalFinishReason) {
30792
30893
  streamState.finishReason = mapFinishReason(finalFinishReason);
30793
30894
  } else if (didEmitAnyToolCalls) {
30794
- streamState.finishReason = "tool-calls";
30895
+ streamState.finishReason = {
30896
+ unified: "tool-calls",
30897
+ raw: void 0
30898
+ };
30795
30899
  }
30796
30900
  const finalUsage = streamResponse.getTokenUsage();
30797
30901
  if (finalUsage) {
30798
- streamState.usage.inputTokens = finalUsage.prompt_tokens;
30799
- streamState.usage.outputTokens = finalUsage.completion_tokens;
30800
- streamState.usage.totalTokens = finalUsage.total_tokens;
30902
+ streamState.usage.inputTokens.total = finalUsage.prompt_tokens;
30903
+ streamState.usage.inputTokens.noCache = finalUsage.prompt_tokens;
30904
+ streamState.usage.outputTokens.total = finalUsage.completion_tokens;
30905
+ streamState.usage.outputTokens.text = finalUsage.completion_tokens;
30801
30906
  }
30802
30907
  controller.enqueue({
30803
30908
  type: "finish",
@@ -30828,8 +30933,7 @@ var SAPAIChatLanguageModel = class {
30828
30933
  stream: transformedStream,
30829
30934
  request: {
30830
30935
  body: requestBody
30831
- },
30832
- warnings: warningsOut
30936
+ }
30833
30937
  };
30834
30938
  } catch (error) {
30835
30939
  throw convertToAISDKError(error, {
@@ -30841,27 +30945,28 @@ var SAPAIChatLanguageModel = class {
30841
30945
  }
30842
30946
  };
30843
30947
  function mapFinishReason(reason) {
30844
- if (!reason) return "unknown";
30948
+ const raw = reason;
30949
+ if (!reason) return { unified: "other", raw };
30845
30950
  switch (reason.toLowerCase()) {
30846
30951
  case "stop":
30847
30952
  case "end_turn":
30848
30953
  case "stop_sequence":
30849
30954
  case "eos":
30850
- return "stop";
30955
+ return { unified: "stop", raw };
30851
30956
  case "length":
30852
30957
  case "max_tokens":
30853
30958
  case "max_tokens_reached":
30854
- return "length";
30959
+ return { unified: "length", raw };
30855
30960
  case "tool_calls":
30856
30961
  case "tool_call":
30857
30962
  case "function_call":
30858
- return "tool-calls";
30963
+ return { unified: "tool-calls", raw };
30859
30964
  case "content_filter":
30860
- return "content-filter";
30965
+ return { unified: "content-filter", raw };
30861
30966
  case "error":
30862
- return "error";
30967
+ return { unified: "error", raw };
30863
30968
  default:
30864
- return "other";
30969
+ return { unified: "other", raw };
30865
30970
  }
30866
30971
  }
30867
30972