@ai-sdk/deepseek 3.0.0-beta.5 → 3.0.0-beta.55
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 +410 -4
- package/README.md +2 -0
- package/dist/index.d.ts +12 -4
- package/dist/index.js +169 -225
- package/dist/index.js.map +1 -1
- package/dist/internal/index.d.ts +57 -0
- package/dist/{index.mjs → internal/index.js} +83 -182
- package/dist/internal/index.js.map +1 -0
- package/docs/30-deepseek.mdx +31 -19
- package/internal.d.ts +1 -0
- package/package.json +21 -15
- package/src/chat/convert-to-deepseek-chat-messages.ts +11 -5
- package/src/chat/convert-to-deepseek-usage.ts +1 -1
- package/src/chat/deepseek-chat-language-model-options.ts +34 -0
- package/src/chat/deepseek-chat-language-model.ts +77 -154
- package/src/chat/deepseek-prepare-tools.ts +5 -2
- package/src/chat/map-deepseek-finish-reason.ts +1 -1
- package/src/deepseek-provider.ts +5 -5
- package/src/index.ts +12 -5
- package/src/internal/index.ts +2 -0
- package/dist/index.d.mts +0 -68
- package/dist/index.mjs.map +0 -1
- package/src/chat/deepseek-chat-options.ts +0 -22
package/docs/30-deepseek.mdx
CHANGED
|
@@ -30,10 +30,10 @@ The DeepSeek provider is available via the `@ai-sdk/deepseek` module. You can in
|
|
|
30
30
|
|
|
31
31
|
## Provider Instance
|
|
32
32
|
|
|
33
|
-
You can import the default provider instance `
|
|
33
|
+
You can import the default provider instance `deepSeek` from `@ai-sdk/deepseek`:
|
|
34
34
|
|
|
35
35
|
```ts
|
|
36
|
-
import {
|
|
36
|
+
import { deepSeek } from '@ai-sdk/deepseek';
|
|
37
37
|
```
|
|
38
38
|
|
|
39
39
|
For custom configuration, you can import `createDeepSeek` and create a provider instance with your settings:
|
|
@@ -41,7 +41,7 @@ For custom configuration, you can import `createDeepSeek` and create a provider
|
|
|
41
41
|
```ts
|
|
42
42
|
import { createDeepSeek } from '@ai-sdk/deepseek';
|
|
43
43
|
|
|
44
|
-
const
|
|
44
|
+
const deepSeek = createDeepSeek({
|
|
45
45
|
apiKey: process.env.DEEPSEEK_API_KEY ?? '',
|
|
46
46
|
});
|
|
47
47
|
```
|
|
@@ -71,11 +71,11 @@ You can use the following optional settings to customize the DeepSeek provider i
|
|
|
71
71
|
You can create language models using a provider instance:
|
|
72
72
|
|
|
73
73
|
```ts
|
|
74
|
-
import {
|
|
74
|
+
import { deepSeek } from '@ai-sdk/deepseek';
|
|
75
75
|
import { generateText } from 'ai';
|
|
76
76
|
|
|
77
77
|
const { text } = await generateText({
|
|
78
|
-
model:
|
|
78
|
+
model: deepSeek('deepseek-chat'),
|
|
79
79
|
prompt: 'Write a vegetarian lasagna recipe for 4 people.',
|
|
80
80
|
});
|
|
81
81
|
```
|
|
@@ -83,9 +83,9 @@ const { text } = await generateText({
|
|
|
83
83
|
You can also use the `.chat()` or `.languageModel()` factory methods:
|
|
84
84
|
|
|
85
85
|
```ts
|
|
86
|
-
const model =
|
|
86
|
+
const model = deepSeek.chat('deepseek-chat');
|
|
87
87
|
// or
|
|
88
|
-
const model =
|
|
88
|
+
const model = deepSeek.languageModel('deepseek-chat');
|
|
89
89
|
```
|
|
90
90
|
|
|
91
91
|
DeepSeek language models can be used in the `streamText` function
|
|
@@ -96,20 +96,32 @@ The following optional provider options are available for DeepSeek models:
|
|
|
96
96
|
- `thinking` _object_
|
|
97
97
|
|
|
98
98
|
Optional. Controls thinking mode (chain-of-thought reasoning). You can enable thinking mode either by using the `deepseek-reasoner` model or by setting this option.
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
99
|
+
- `type`: `'adaptive' | 'enabled' | 'disabled'` - Enable, disable, or let the model decide (`adaptive`) when to think. See [DeepSeek's thinking mode docs](https://api-docs.deepseek.com/guides/thinking_mode).
|
|
100
|
+
|
|
101
|
+
- `reasoningEffort` _'low' | 'medium' | 'high' | 'xhigh' | 'max'_
|
|
102
|
+
|
|
103
|
+
Optional. Controls thinking strength for DeepSeek V4 reasoning models. Per
|
|
104
|
+
DeepSeek's docs, `low` and `medium` are mapped to `high`, and `xhigh` is
|
|
105
|
+
mapped to `max` server-side for compatibility with other providers. When
|
|
106
|
+
using the top-level `reasoning` setting, `minimal` is sent as `low`, and
|
|
107
|
+
`low`, `medium`, `high`, and `xhigh` pass through to DeepSeek's native
|
|
108
|
+
effort values.
|
|
109
|
+
|
|
110
|
+
```ts highlight="7-12"
|
|
111
|
+
import {
|
|
112
|
+
deepSeek,
|
|
113
|
+
type DeepSeekLanguageModelChatOptions,
|
|
114
|
+
} from '@ai-sdk/deepseek';
|
|
104
115
|
import { generateText } from 'ai';
|
|
105
116
|
|
|
106
117
|
const { text, reasoning } = await generateText({
|
|
107
|
-
model:
|
|
118
|
+
model: deepSeek('deepseek-chat'),
|
|
108
119
|
prompt: 'How many "r"s are in the word "strawberry"?',
|
|
109
120
|
providerOptions: {
|
|
110
121
|
deepseek: {
|
|
111
122
|
thinking: { type: 'enabled' },
|
|
112
|
-
|
|
123
|
+
reasoningEffort: 'high',
|
|
124
|
+
} satisfies DeepSeekLanguageModelChatOptions,
|
|
113
125
|
},
|
|
114
126
|
});
|
|
115
127
|
```
|
|
@@ -119,15 +131,15 @@ const { text, reasoning } = await generateText({
|
|
|
119
131
|
DeepSeek has reasoning support for the `deepseek-reasoner` model. The reasoning is exposed through streaming:
|
|
120
132
|
|
|
121
133
|
```ts
|
|
122
|
-
import {
|
|
134
|
+
import { deepSeek } from '@ai-sdk/deepseek';
|
|
123
135
|
import { streamText } from 'ai';
|
|
124
136
|
|
|
125
137
|
const result = streamText({
|
|
126
|
-
model:
|
|
138
|
+
model: deepSeek('deepseek-reasoner'),
|
|
127
139
|
prompt: 'How many "r"s are in the word "strawberry"?',
|
|
128
140
|
});
|
|
129
141
|
|
|
130
|
-
for await (const part of result.
|
|
142
|
+
for await (const part of result.stream) {
|
|
131
143
|
if (part.type === 'reasoning') {
|
|
132
144
|
// This is the reasoning text
|
|
133
145
|
console.log('Reasoning:', part.text);
|
|
@@ -146,11 +158,11 @@ on how to integrate reasoning into your chatbot.
|
|
|
146
158
|
DeepSeek provides context caching on disk technology that can significantly reduce token costs for repeated content. You can access the cache hit/miss metrics through the `providerMetadata` property in the response:
|
|
147
159
|
|
|
148
160
|
```ts
|
|
149
|
-
import {
|
|
161
|
+
import { deepSeek } from '@ai-sdk/deepseek';
|
|
150
162
|
import { generateText } from 'ai';
|
|
151
163
|
|
|
152
164
|
const result = await generateText({
|
|
153
|
-
model:
|
|
165
|
+
model: deepSeek('deepseek-chat'),
|
|
154
166
|
prompt: 'Your prompt here',
|
|
155
167
|
});
|
|
156
168
|
|
package/internal.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './dist/internal';
|
package/package.json
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ai-sdk/deepseek",
|
|
3
|
-
"version": "3.0.0-beta.
|
|
3
|
+
"version": "3.0.0-beta.55",
|
|
4
|
+
"type": "module",
|
|
4
5
|
"license": "Apache-2.0",
|
|
5
6
|
"sideEffects": false,
|
|
6
7
|
"main": "./dist/index.js",
|
|
7
|
-
"module": "./dist/index.mjs",
|
|
8
8
|
"types": "./dist/index.d.ts",
|
|
9
9
|
"files": [
|
|
10
10
|
"dist/**/*",
|
|
@@ -15,7 +15,8 @@
|
|
|
15
15
|
"!src/**/__snapshots__",
|
|
16
16
|
"!src/**/__fixtures__",
|
|
17
17
|
"CHANGELOG.md",
|
|
18
|
-
"README.md"
|
|
18
|
+
"README.md",
|
|
19
|
+
"internal.d.ts"
|
|
19
20
|
],
|
|
20
21
|
"directories": {
|
|
21
22
|
"doc": "./docs"
|
|
@@ -24,35 +25,42 @@
|
|
|
24
25
|
"./package.json": "./package.json",
|
|
25
26
|
".": {
|
|
26
27
|
"types": "./dist/index.d.ts",
|
|
27
|
-
"import": "./dist/index.
|
|
28
|
-
"
|
|
28
|
+
"import": "./dist/index.js",
|
|
29
|
+
"default": "./dist/index.js"
|
|
30
|
+
},
|
|
31
|
+
"./internal": {
|
|
32
|
+
"types": "./dist/internal/index.d.ts",
|
|
33
|
+
"import": "./dist/internal/index.js",
|
|
34
|
+
"default": "./dist/internal/index.js"
|
|
29
35
|
}
|
|
30
36
|
},
|
|
31
37
|
"dependencies": {
|
|
32
|
-
"@ai-sdk/provider": "4.0.0-beta.
|
|
33
|
-
"@ai-sdk/provider-utils": "5.0.0-beta.
|
|
38
|
+
"@ai-sdk/provider": "4.0.0-beta.19",
|
|
39
|
+
"@ai-sdk/provider-utils": "5.0.0-beta.49"
|
|
34
40
|
},
|
|
35
41
|
"devDependencies": {
|
|
36
|
-
"@types/node": "
|
|
37
|
-
"tsup": "^8",
|
|
42
|
+
"@types/node": "22.19.19",
|
|
43
|
+
"tsup": "^8.5.1",
|
|
38
44
|
"typescript": "5.8.3",
|
|
39
45
|
"zod": "3.25.76",
|
|
40
|
-
"@ai-sdk/test-server": "2.0.0-beta.
|
|
46
|
+
"@ai-sdk/test-server": "2.0.0-beta.7",
|
|
41
47
|
"@vercel/ai-tsconfig": "0.0.0"
|
|
42
48
|
},
|
|
43
49
|
"peerDependencies": {
|
|
44
50
|
"zod": "^3.25.76 || ^4.1.8"
|
|
45
51
|
},
|
|
46
52
|
"engines": {
|
|
47
|
-
"node": ">=
|
|
53
|
+
"node": ">=22"
|
|
48
54
|
},
|
|
49
55
|
"publishConfig": {
|
|
50
|
-
"access": "public"
|
|
56
|
+
"access": "public",
|
|
57
|
+
"provenance": true
|
|
51
58
|
},
|
|
52
59
|
"homepage": "https://ai-sdk.dev/docs",
|
|
53
60
|
"repository": {
|
|
54
61
|
"type": "git",
|
|
55
|
-
"url": "
|
|
62
|
+
"url": "https://github.com/vercel/ai",
|
|
63
|
+
"directory": "packages/deepseek"
|
|
56
64
|
},
|
|
57
65
|
"bugs": {
|
|
58
66
|
"url": "https://github.com/vercel/ai/issues"
|
|
@@ -64,9 +72,7 @@
|
|
|
64
72
|
"build": "pnpm clean && tsup --tsconfig tsconfig.build.json",
|
|
65
73
|
"build:watch": "pnpm clean && tsup --watch",
|
|
66
74
|
"clean": "del-cli dist docs *.tsbuildinfo",
|
|
67
|
-
"lint": "eslint \"./**/*.ts*\"",
|
|
68
75
|
"type-check": "tsc --build",
|
|
69
|
-
"prettier-check": "prettier --check \"./**/*.ts*\"",
|
|
70
76
|
"test": "pnpm test:node && pnpm test:edge",
|
|
71
77
|
"test:update": "pnpm test:node -u",
|
|
72
78
|
"test:watch": "vitest --config vitest.node.config.js",
|
|
@@ -1,20 +1,23 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import type {
|
|
2
2
|
LanguageModelV4CallOptions,
|
|
3
3
|
LanguageModelV4Prompt,
|
|
4
4
|
SharedV4Warning,
|
|
5
5
|
} from '@ai-sdk/provider';
|
|
6
|
-
import { DeepSeekChatPrompt } from './deepseek-chat-api-types';
|
|
6
|
+
import type { DeepSeekChatPrompt } from './deepseek-chat-api-types';
|
|
7
7
|
|
|
8
8
|
export function convertToDeepSeekChatMessages({
|
|
9
9
|
prompt,
|
|
10
10
|
responseFormat,
|
|
11
|
+
modelId,
|
|
11
12
|
}: {
|
|
12
13
|
prompt: LanguageModelV4Prompt;
|
|
13
14
|
responseFormat: LanguageModelV4CallOptions['responseFormat'];
|
|
15
|
+
modelId: string;
|
|
14
16
|
}): {
|
|
15
17
|
messages: DeepSeekChatPrompt;
|
|
16
18
|
warnings: Array<SharedV4Warning>;
|
|
17
19
|
} {
|
|
20
|
+
const isDeepSeekV4 = modelId.includes('deepseek-v4');
|
|
18
21
|
const messages: DeepSeekChatPrompt = [];
|
|
19
22
|
const warnings: Array<SharedV4Warning> = [];
|
|
20
23
|
|
|
@@ -96,7 +99,8 @@ export function convertToDeepSeekChatMessages({
|
|
|
96
99
|
break;
|
|
97
100
|
}
|
|
98
101
|
case 'reasoning': {
|
|
99
|
-
|
|
102
|
+
// R1 must not receive prior reasoning; V4 requires it.
|
|
103
|
+
if (index <= lastUserMessageIndex && !isDeepSeekV4) {
|
|
100
104
|
break;
|
|
101
105
|
}
|
|
102
106
|
|
|
@@ -121,10 +125,12 @@ export function convertToDeepSeekChatMessages({
|
|
|
121
125
|
}
|
|
122
126
|
}
|
|
123
127
|
|
|
128
|
+
// V4 demands the field on every assistant turn — back-fill an empty
|
|
129
|
+
// string when the source message had no reasoning part at all.
|
|
124
130
|
messages.push({
|
|
125
131
|
role: 'assistant',
|
|
126
132
|
content: text,
|
|
127
|
-
reasoning_content: reasoning,
|
|
133
|
+
reasoning_content: reasoning ?? (isDeepSeekV4 ? '' : undefined),
|
|
128
134
|
tool_calls: toolCalls.length > 0 ? toolCalls : undefined,
|
|
129
135
|
});
|
|
130
136
|
|
|
@@ -145,7 +151,7 @@ export function convertToDeepSeekChatMessages({
|
|
|
145
151
|
contentValue = output.value;
|
|
146
152
|
break;
|
|
147
153
|
case 'execution-denied':
|
|
148
|
-
contentValue = output.reason ?? 'Tool execution denied.';
|
|
154
|
+
contentValue = output.reason ?? 'Tool call execution denied.';
|
|
149
155
|
break;
|
|
150
156
|
case 'content':
|
|
151
157
|
case 'json':
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { z } from 'zod/v4';
|
|
2
|
+
|
|
3
|
+
// https://api-docs.deepseek.com/quick_start/pricing
|
|
4
|
+
export type DeepSeekChatModelId =
|
|
5
|
+
| 'deepseek-chat'
|
|
6
|
+
| 'deepseek-reasoner'
|
|
7
|
+
| (string & {});
|
|
8
|
+
|
|
9
|
+
export const deepseekLanguageModelChatOptions = z.object({
|
|
10
|
+
/**
|
|
11
|
+
* Type of thinking to use. Defaults to `enabled`.
|
|
12
|
+
*
|
|
13
|
+
* See https://api-docs.deepseek.com/guides/thinking_mode for the
|
|
14
|
+
* `adaptive` option, which lets the model decide when to think.
|
|
15
|
+
*/
|
|
16
|
+
thinking: z
|
|
17
|
+
.object({
|
|
18
|
+
type: z.enum(['adaptive', 'enabled', 'disabled']).optional(),
|
|
19
|
+
})
|
|
20
|
+
.optional(),
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Controls the thinking strength for DeepSeek V4 reasoning models.
|
|
24
|
+
*
|
|
25
|
+
* DeepSeek's API accepts `low`, `medium`, `high`, `xhigh`, and `max`.
|
|
26
|
+
* Per their docs, `low` and `medium` are mapped to `high`, and `xhigh`
|
|
27
|
+
* is mapped to `max` server-side for compatibility with other providers.
|
|
28
|
+
*/
|
|
29
|
+
reasoningEffort: z.enum(['low', 'medium', 'high', 'xhigh', 'max']).optional(),
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
export type DeepSeekLanguageModelChatOptions = z.infer<
|
|
33
|
+
typeof deepseekLanguageModelChatOptions
|
|
34
|
+
>;
|
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import type {
|
|
2
2
|
APICallError,
|
|
3
|
-
InvalidResponseDataError,
|
|
4
3
|
LanguageModelV4,
|
|
5
4
|
LanguageModelV4CallOptions,
|
|
6
5
|
LanguageModelV4Content,
|
|
@@ -8,42 +7,49 @@ import {
|
|
|
8
7
|
LanguageModelV4GenerateResult,
|
|
9
8
|
LanguageModelV4StreamPart,
|
|
10
9
|
LanguageModelV4StreamResult,
|
|
10
|
+
SharedV4Warning,
|
|
11
11
|
} from '@ai-sdk/provider';
|
|
12
12
|
import {
|
|
13
13
|
combineHeaders,
|
|
14
14
|
createEventSourceResponseHandler,
|
|
15
15
|
createJsonErrorResponseHandler,
|
|
16
16
|
createJsonResponseHandler,
|
|
17
|
-
FetchFunction,
|
|
18
17
|
generateId,
|
|
19
|
-
|
|
20
|
-
|
|
18
|
+
isCustomReasoning,
|
|
19
|
+
mapReasoningToProviderEffort,
|
|
21
20
|
parseProviderOptions,
|
|
22
|
-
ParseResult,
|
|
23
21
|
postJsonToApi,
|
|
24
|
-
|
|
22
|
+
serializeModelOptions,
|
|
23
|
+
StreamingToolCallTracker,
|
|
24
|
+
WORKFLOW_SERIALIZE,
|
|
25
|
+
WORKFLOW_DESERIALIZE,
|
|
26
|
+
type FetchFunction,
|
|
27
|
+
type InferSchema,
|
|
28
|
+
type ParseResult,
|
|
29
|
+
type ResponseHandler,
|
|
25
30
|
} from '@ai-sdk/provider-utils';
|
|
26
31
|
import { convertToDeepSeekChatMessages } from './convert-to-deepseek-chat-messages';
|
|
27
32
|
import { convertDeepSeekUsage } from './convert-to-deepseek-usage';
|
|
28
33
|
import {
|
|
29
34
|
deepseekChatChunkSchema,
|
|
30
35
|
deepseekChatResponseSchema,
|
|
31
|
-
DeepSeekChatTokenUsage,
|
|
32
36
|
deepSeekErrorSchema,
|
|
37
|
+
type DeepSeekChatTokenUsage,
|
|
33
38
|
} from './deepseek-chat-api-types';
|
|
34
39
|
import {
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
} from './deepseek-chat-options';
|
|
40
|
+
deepseekLanguageModelChatOptions,
|
|
41
|
+
type DeepSeekChatModelId,
|
|
42
|
+
} from './deepseek-chat-language-model-options';
|
|
38
43
|
import { prepareTools } from './deepseek-prepare-tools';
|
|
39
44
|
import { getResponseMetadata } from './get-response-metadata';
|
|
40
45
|
import { mapDeepSeekFinishReason } from './map-deepseek-finish-reason';
|
|
41
46
|
|
|
42
47
|
export type DeepSeekChatConfig = {
|
|
43
48
|
provider: string;
|
|
44
|
-
headers
|
|
49
|
+
headers?: () => Record<string, string | undefined>;
|
|
45
50
|
url: (options: { modelId: string; path: string }) => string;
|
|
46
51
|
fetch?: FetchFunction;
|
|
52
|
+
supportsThinking?: boolean;
|
|
47
53
|
};
|
|
48
54
|
|
|
49
55
|
export class DeepSeekChatLanguageModel implements LanguageModelV4 {
|
|
@@ -55,6 +61,20 @@ export class DeepSeekChatLanguageModel implements LanguageModelV4 {
|
|
|
55
61
|
private readonly config: DeepSeekChatConfig;
|
|
56
62
|
private readonly failedResponseHandler: ResponseHandler<APICallError>;
|
|
57
63
|
|
|
64
|
+
static [WORKFLOW_SERIALIZE](model: DeepSeekChatLanguageModel) {
|
|
65
|
+
return serializeModelOptions({
|
|
66
|
+
modelId: model.modelId,
|
|
67
|
+
config: model.config,
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
static [WORKFLOW_DESERIALIZE](options: {
|
|
72
|
+
modelId: DeepSeekChatModelId;
|
|
73
|
+
config: DeepSeekChatConfig;
|
|
74
|
+
}) {
|
|
75
|
+
return new DeepSeekChatLanguageModel(options.modelId, options.config);
|
|
76
|
+
}
|
|
77
|
+
|
|
58
78
|
constructor(modelId: DeepSeekChatModelId, config: DeepSeekChatConfig) {
|
|
59
79
|
this.modelId = modelId;
|
|
60
80
|
this.config = config;
|
|
@@ -82,6 +102,7 @@ export class DeepSeekChatLanguageModel implements LanguageModelV4 {
|
|
|
82
102
|
topK,
|
|
83
103
|
frequencyPenalty,
|
|
84
104
|
presencePenalty,
|
|
105
|
+
reasoning,
|
|
85
106
|
providerOptions,
|
|
86
107
|
stopSequences,
|
|
87
108
|
responseFormat,
|
|
@@ -93,20 +114,22 @@ export class DeepSeekChatLanguageModel implements LanguageModelV4 {
|
|
|
93
114
|
(await parseProviderOptions({
|
|
94
115
|
provider: this.providerOptionsName,
|
|
95
116
|
providerOptions,
|
|
96
|
-
schema:
|
|
117
|
+
schema: deepseekLanguageModelChatOptions,
|
|
97
118
|
})) ?? {};
|
|
98
119
|
|
|
99
120
|
const { messages, warnings } = convertToDeepSeekChatMessages({
|
|
100
121
|
prompt,
|
|
101
122
|
responseFormat,
|
|
123
|
+
modelId: this.modelId,
|
|
102
124
|
});
|
|
125
|
+
const allWarnings: SharedV4Warning[] = [...warnings];
|
|
103
126
|
|
|
104
127
|
if (topK != null) {
|
|
105
|
-
|
|
128
|
+
allWarnings.push({ type: 'unsupported', feature: 'topK' });
|
|
106
129
|
}
|
|
107
130
|
|
|
108
131
|
if (seed != null) {
|
|
109
|
-
|
|
132
|
+
allWarnings.push({ type: 'unsupported', feature: 'seed' });
|
|
110
133
|
}
|
|
111
134
|
|
|
112
135
|
const {
|
|
@@ -118,6 +141,31 @@ export class DeepSeekChatLanguageModel implements LanguageModelV4 {
|
|
|
118
141
|
toolChoice,
|
|
119
142
|
});
|
|
120
143
|
|
|
144
|
+
const thinking =
|
|
145
|
+
this.config.supportsThinking === false
|
|
146
|
+
? undefined
|
|
147
|
+
: deepseekOptions.thinking?.type != null
|
|
148
|
+
? { type: deepseekOptions.thinking.type }
|
|
149
|
+
: isCustomReasoning(reasoning)
|
|
150
|
+
? { type: reasoning === 'none' ? 'disabled' : 'enabled' }
|
|
151
|
+
: undefined;
|
|
152
|
+
|
|
153
|
+
const reasoningEffort =
|
|
154
|
+
deepseekOptions.reasoningEffort ??
|
|
155
|
+
(isCustomReasoning(reasoning) && reasoning !== 'none'
|
|
156
|
+
? mapReasoningToProviderEffort({
|
|
157
|
+
reasoning,
|
|
158
|
+
effortMap: {
|
|
159
|
+
minimal: 'low',
|
|
160
|
+
low: 'low',
|
|
161
|
+
medium: 'medium',
|
|
162
|
+
high: 'high',
|
|
163
|
+
xhigh: 'max',
|
|
164
|
+
},
|
|
165
|
+
warnings: allWarnings,
|
|
166
|
+
})
|
|
167
|
+
: undefined);
|
|
168
|
+
|
|
121
169
|
return {
|
|
122
170
|
args: {
|
|
123
171
|
model: this.modelId,
|
|
@@ -132,12 +180,13 @@ export class DeepSeekChatLanguageModel implements LanguageModelV4 {
|
|
|
132
180
|
messages,
|
|
133
181
|
tools: deepseekTools,
|
|
134
182
|
tool_choice: deepseekToolChoices,
|
|
135
|
-
thinking
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
:
|
|
183
|
+
thinking,
|
|
184
|
+
...(thinking?.type !== 'disabled' &&
|
|
185
|
+
reasoningEffort != null && {
|
|
186
|
+
reasoning_effort: reasoningEffort,
|
|
187
|
+
}),
|
|
139
188
|
},
|
|
140
|
-
warnings: [...
|
|
189
|
+
warnings: [...allWarnings, ...toolWarnings],
|
|
141
190
|
};
|
|
142
191
|
}
|
|
143
192
|
|
|
@@ -155,7 +204,7 @@ export class DeepSeekChatLanguageModel implements LanguageModelV4 {
|
|
|
155
204
|
path: '/chat/completions',
|
|
156
205
|
modelId: this.modelId,
|
|
157
206
|
}),
|
|
158
|
-
headers: combineHeaders(this.config.headers(), options.headers),
|
|
207
|
+
headers: combineHeaders(this.config.headers?.(), options.headers),
|
|
159
208
|
body: args,
|
|
160
209
|
failedResponseHandler: this.failedResponseHandler,
|
|
161
210
|
successfulResponseHandler: createJsonResponseHandler(
|
|
@@ -234,7 +283,7 @@ export class DeepSeekChatLanguageModel implements LanguageModelV4 {
|
|
|
234
283
|
path: '/chat/completions',
|
|
235
284
|
modelId: this.modelId,
|
|
236
285
|
}),
|
|
237
|
-
headers: combineHeaders(this.config.headers(), options.headers),
|
|
286
|
+
headers: combineHeaders(this.config.headers?.(), options.headers),
|
|
238
287
|
body,
|
|
239
288
|
failedResponseHandler: this.failedResponseHandler,
|
|
240
289
|
successfulResponseHandler: createEventSourceResponseHandler(
|
|
@@ -244,15 +293,7 @@ export class DeepSeekChatLanguageModel implements LanguageModelV4 {
|
|
|
244
293
|
fetch: this.config.fetch,
|
|
245
294
|
});
|
|
246
295
|
|
|
247
|
-
|
|
248
|
-
id: string;
|
|
249
|
-
type: 'function';
|
|
250
|
-
function: {
|
|
251
|
-
name: string;
|
|
252
|
-
arguments: string;
|
|
253
|
-
};
|
|
254
|
-
hasFinished: boolean;
|
|
255
|
-
}> = [];
|
|
296
|
+
let toolCallTracker: StreamingToolCallTracker;
|
|
256
297
|
|
|
257
298
|
let finishReason: LanguageModelV4FinishReason = {
|
|
258
299
|
unified: 'other',
|
|
@@ -271,6 +312,9 @@ export class DeepSeekChatLanguageModel implements LanguageModelV4 {
|
|
|
271
312
|
LanguageModelV4StreamPart
|
|
272
313
|
>({
|
|
273
314
|
start(controller) {
|
|
315
|
+
toolCallTracker = new StreamingToolCallTracker(controller, {
|
|
316
|
+
generateId,
|
|
317
|
+
});
|
|
274
318
|
controller.enqueue({ type: 'stream-start', warnings });
|
|
275
319
|
},
|
|
276
320
|
|
|
@@ -374,113 +418,7 @@ export class DeepSeekChatLanguageModel implements LanguageModelV4 {
|
|
|
374
418
|
}
|
|
375
419
|
|
|
376
420
|
for (const toolCallDelta of delta.tool_calls) {
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
if (toolCalls[index] == null) {
|
|
380
|
-
if (toolCallDelta.id == null) {
|
|
381
|
-
throw new InvalidResponseDataError({
|
|
382
|
-
data: toolCallDelta,
|
|
383
|
-
message: `Expected 'id' to be a string.`,
|
|
384
|
-
});
|
|
385
|
-
}
|
|
386
|
-
|
|
387
|
-
if (toolCallDelta.function?.name == null) {
|
|
388
|
-
throw new InvalidResponseDataError({
|
|
389
|
-
data: toolCallDelta,
|
|
390
|
-
message: `Expected 'function.name' to be a string.`,
|
|
391
|
-
});
|
|
392
|
-
}
|
|
393
|
-
|
|
394
|
-
controller.enqueue({
|
|
395
|
-
type: 'tool-input-start',
|
|
396
|
-
id: toolCallDelta.id,
|
|
397
|
-
toolName: toolCallDelta.function.name,
|
|
398
|
-
});
|
|
399
|
-
|
|
400
|
-
toolCalls[index] = {
|
|
401
|
-
id: toolCallDelta.id,
|
|
402
|
-
type: 'function',
|
|
403
|
-
function: {
|
|
404
|
-
name: toolCallDelta.function.name,
|
|
405
|
-
arguments: toolCallDelta.function.arguments ?? '',
|
|
406
|
-
},
|
|
407
|
-
hasFinished: false,
|
|
408
|
-
};
|
|
409
|
-
|
|
410
|
-
const toolCall = toolCalls[index];
|
|
411
|
-
|
|
412
|
-
if (
|
|
413
|
-
toolCall.function?.name != null &&
|
|
414
|
-
toolCall.function?.arguments != null
|
|
415
|
-
) {
|
|
416
|
-
// send delta if the argument text has already started:
|
|
417
|
-
if (toolCall.function.arguments.length > 0) {
|
|
418
|
-
controller.enqueue({
|
|
419
|
-
type: 'tool-input-delta',
|
|
420
|
-
id: toolCall.id,
|
|
421
|
-
delta: toolCall.function.arguments,
|
|
422
|
-
});
|
|
423
|
-
}
|
|
424
|
-
|
|
425
|
-
// check if tool call is complete
|
|
426
|
-
// (some providers send the full tool call in one chunk):
|
|
427
|
-
if (isParsableJson(toolCall.function.arguments)) {
|
|
428
|
-
controller.enqueue({
|
|
429
|
-
type: 'tool-input-end',
|
|
430
|
-
id: toolCall.id,
|
|
431
|
-
});
|
|
432
|
-
|
|
433
|
-
controller.enqueue({
|
|
434
|
-
type: 'tool-call',
|
|
435
|
-
toolCallId: toolCall.id ?? generateId(),
|
|
436
|
-
toolName: toolCall.function.name,
|
|
437
|
-
input: toolCall.function.arguments,
|
|
438
|
-
});
|
|
439
|
-
toolCall.hasFinished = true;
|
|
440
|
-
}
|
|
441
|
-
}
|
|
442
|
-
|
|
443
|
-
continue;
|
|
444
|
-
}
|
|
445
|
-
|
|
446
|
-
// existing tool call, merge if not finished
|
|
447
|
-
const toolCall = toolCalls[index];
|
|
448
|
-
|
|
449
|
-
if (toolCall.hasFinished) {
|
|
450
|
-
continue;
|
|
451
|
-
}
|
|
452
|
-
|
|
453
|
-
if (toolCallDelta.function?.arguments != null) {
|
|
454
|
-
toolCall.function!.arguments +=
|
|
455
|
-
toolCallDelta.function?.arguments ?? '';
|
|
456
|
-
}
|
|
457
|
-
|
|
458
|
-
// send delta
|
|
459
|
-
controller.enqueue({
|
|
460
|
-
type: 'tool-input-delta',
|
|
461
|
-
id: toolCall.id,
|
|
462
|
-
delta: toolCallDelta.function.arguments ?? '',
|
|
463
|
-
});
|
|
464
|
-
|
|
465
|
-
// check if tool call is complete
|
|
466
|
-
if (
|
|
467
|
-
toolCall.function?.name != null &&
|
|
468
|
-
toolCall.function?.arguments != null &&
|
|
469
|
-
isParsableJson(toolCall.function.arguments)
|
|
470
|
-
) {
|
|
471
|
-
controller.enqueue({
|
|
472
|
-
type: 'tool-input-end',
|
|
473
|
-
id: toolCall.id,
|
|
474
|
-
});
|
|
475
|
-
|
|
476
|
-
controller.enqueue({
|
|
477
|
-
type: 'tool-call',
|
|
478
|
-
toolCallId: toolCall.id ?? generateId(),
|
|
479
|
-
toolName: toolCall.function.name,
|
|
480
|
-
input: toolCall.function.arguments,
|
|
481
|
-
});
|
|
482
|
-
toolCall.hasFinished = true;
|
|
483
|
-
}
|
|
421
|
+
toolCallTracker.processDelta(toolCallDelta);
|
|
484
422
|
}
|
|
485
423
|
}
|
|
486
424
|
},
|
|
@@ -494,22 +432,7 @@ export class DeepSeekChatLanguageModel implements LanguageModelV4 {
|
|
|
494
432
|
controller.enqueue({ type: 'text-end', id: 'txt-0' });
|
|
495
433
|
}
|
|
496
434
|
|
|
497
|
-
|
|
498
|
-
for (const toolCall of toolCalls.filter(
|
|
499
|
-
toolCall => !toolCall.hasFinished,
|
|
500
|
-
)) {
|
|
501
|
-
controller.enqueue({
|
|
502
|
-
type: 'tool-input-end',
|
|
503
|
-
id: toolCall.id,
|
|
504
|
-
});
|
|
505
|
-
|
|
506
|
-
controller.enqueue({
|
|
507
|
-
type: 'tool-call',
|
|
508
|
-
toolCallId: toolCall.id ?? generateId(),
|
|
509
|
-
toolName: toolCall.function.name,
|
|
510
|
-
input: toolCall.function.arguments,
|
|
511
|
-
});
|
|
512
|
-
}
|
|
435
|
+
toolCallTracker.flush();
|
|
513
436
|
|
|
514
437
|
controller.enqueue({
|
|
515
438
|
type: 'finish',
|
|
@@ -1,5 +1,8 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
1
|
+
import type {
|
|
2
|
+
LanguageModelV4CallOptions,
|
|
3
|
+
SharedV4Warning,
|
|
4
|
+
} from '@ai-sdk/provider';
|
|
5
|
+
import type {
|
|
3
6
|
DeepSeekFunctionTool,
|
|
4
7
|
DeepSeekToolChoice,
|
|
5
8
|
} from './deepseek-chat-api-types';
|