@astack-tech/integrations 0.1.1-beta.6 → 0.2.0-beta.7
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 +39 -0
- package/README.md +75 -1
- package/dist/index.cjs +180 -243
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +180 -243
- package/dist/index.js.map +1 -1
- package/dist/model-provider/index.cjs +180 -243
- package/dist/model-provider/index.cjs.map +1 -1
- package/dist/model-provider/index.d.cts +146 -145
- package/dist/model-provider/index.d.ts +146 -145
- package/dist/model-provider/index.js +180 -243
- package/dist/model-provider/index.js.map +1 -1
- package/package.json +45 -26
- package/src/index.ts +1 -5
- package/src/model-provider/deepseek/index.ts +326 -481
- package/src/model-provider/index.ts +9 -2
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# @astack-tech/integrations
|
|
2
|
+
|
|
3
|
+
## 0.2.0-beta.7
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- e71dd3b: Deepseek satisfies the `ModelProvider` interface of `@astack-tech/components` without a cast. Unset request options are omitted, an empty `choices` array fails with a clear error, non-object tool arguments become `{}`, and the pipeline fallback text is `API call failed`. API errors are logged only when `verbose` is set. Node.js 20 or later is required.
|
|
8
|
+
|
|
9
|
+
### Patch Changes
|
|
10
|
+
|
|
11
|
+
- Updated dependencies [e71dd3b]
|
|
12
|
+
- @astack-tech/core@0.2.0-beta.2
|
|
13
|
+
|
|
14
|
+
## 0.1.1-beta.6
|
|
15
|
+
|
|
16
|
+
### Patch Changes
|
|
17
|
+
|
|
18
|
+
- Updated dependencies
|
|
19
|
+
- @astack-tech/core@0.2.0-beta.1
|
|
20
|
+
|
|
21
|
+
## 0.1.1-beta.2
|
|
22
|
+
|
|
23
|
+
### Patch Changes
|
|
24
|
+
|
|
25
|
+
- rebuild package
|
|
26
|
+
|
|
27
|
+
## 0.1.1-beta.1
|
|
28
|
+
|
|
29
|
+
### Patch Changes
|
|
30
|
+
|
|
31
|
+
- fix agent tool finder
|
|
32
|
+
|
|
33
|
+
## 0.1.1-beta.0
|
|
34
|
+
|
|
35
|
+
### Patch Changes
|
|
36
|
+
|
|
37
|
+
- beta release
|
|
38
|
+
- Updated dependencies
|
|
39
|
+
- @astack-tech/core@0.1.1-beta.0
|
package/README.md
CHANGED
|
@@ -1 +1,75 @@
|
|
|
1
|
-
|
|
1
|
+
# @astack-tech/integrations
|
|
2
|
+
|
|
3
|
+
<img src="https://raw.githubusercontent.com/astack-tech/astack/master/assets/brand/readme-banner.png" alt="AStack" width="100%">
|
|
4
|
+
|
|
5
|
+
[](https://www.npmjs.com/package/@astack-tech/integrations) [](https://github.com/astack-tech/astack/blob/master/LICENSE) [](https://nodejs.org)
|
|
6
|
+
|
|
7
|
+
Model providers for AStack. Each provider is a component: call it directly, use it in a pipeline, or pass it to an agent.
|
|
8
|
+
|
|
9
|
+
[Website](https://astack.tech) · [Docs](https://astack.tech/docs) · [Examples](https://astack.tech/examples) · [GitHub](https://github.com/astack-tech/astack)
|
|
10
|
+
|
|
11
|
+
## Install
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
npm install @astack-tech/integrations @astack-tech/core
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## Quick start
|
|
18
|
+
|
|
19
|
+
```typescript
|
|
20
|
+
import { ModelProvider } from '@astack-tech/integrations';
|
|
21
|
+
|
|
22
|
+
const model = new ModelProvider.Deepseek({ apiKey: process.env['DEEPSEEK_API_KEY'] ?? '' });
|
|
23
|
+
|
|
24
|
+
const text = await model.run('What is flow-based programming?');
|
|
25
|
+
const reply = await model.chatCompletion([{ role: 'user', content: 'Hello' }]);
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
`Deepseek` is also available from the subpath: `import { Deepseek } from '@astack-tech/integrations/model-provider'`.
|
|
29
|
+
|
|
30
|
+
## Deepseek
|
|
31
|
+
|
|
32
|
+
Uses the OpenAI-compatible DeepSeek API through the official `openai` client.
|
|
33
|
+
|
|
34
|
+
| Option | Default | Description |
|
|
35
|
+
| -------------------- | ----------------------------- | -------------------------------------------------------------------- |
|
|
36
|
+
| `apiKey` | — | DeepSeek API key |
|
|
37
|
+
| `model` | `deepseek-chat` | Model name |
|
|
38
|
+
| `baseURL` | `https://api.deepseek.com/v1` | API base URL |
|
|
39
|
+
| `temperature` | `0.7` | Sampling temperature |
|
|
40
|
+
| `maxTokens`, `topP` | not sent | Optional sampling limits |
|
|
41
|
+
| `systemPrompt` | none | Added before every request |
|
|
42
|
+
| `rawTools` / `tools` | none | Tools as `{ name, description, parameters }` or in the OpenAI format |
|
|
43
|
+
| `verbose` | `false` | Log API errors to the console |
|
|
44
|
+
|
|
45
|
+
| Method | Returns |
|
|
46
|
+
| --------------------------------------------------------------- | -------------------------------------------------------------- |
|
|
47
|
+
| `run(prompt)` | Text |
|
|
48
|
+
| `run(messages)`, `chatCompletion(messages, { temporaryTools })` | Assistant message with `content`, `tool_calls` and `usage` |
|
|
49
|
+
| `streamChatCompletion(messages, options)` | Async generator of text, tool-call and usage chunks |
|
|
50
|
+
| `generateCompletion(prompt)` | Text, or the JSON assistant message when the model calls tools |
|
|
51
|
+
|
|
52
|
+
| Pipeline input port | Output port |
|
|
53
|
+
| ------------------- | ------------ |
|
|
54
|
+
| `prompt` | `completion` |
|
|
55
|
+
| `messages` | `message` |
|
|
56
|
+
|
|
57
|
+
`Deepseek` satisfies the `ModelProvider` interface of `@astack-tech/components`, so it can drive `Agent` and `StreamingAgent` directly.
|
|
58
|
+
|
|
59
|
+
## Learn more
|
|
60
|
+
|
|
61
|
+
- [Model providers](https://astack.tech/docs/model-providers), including how to write your own
|
|
62
|
+
- Example: [chat](https://astack.tech/examples/chat)
|
|
63
|
+
|
|
64
|
+
## The AStack packages
|
|
65
|
+
|
|
66
|
+
| Package | Purpose |
|
|
67
|
+
| -------------------------------------------------------------------------------------- | ------------------------------------------ |
|
|
68
|
+
| [`@astack-tech/core`](https://www.npmjs.com/package/@astack-tech/core) | `Component`, `Pipeline` and ports |
|
|
69
|
+
| [`@astack-tech/components`](https://www.npmjs.com/package/@astack-tech/components) | Agents, text splitting and tool invocation |
|
|
70
|
+
| [`@astack-tech/tools`](https://www.npmjs.com/package/@astack-tech/tools) | Tool definitions for agents |
|
|
71
|
+
| [`@astack-tech/integrations`](https://www.npmjs.com/package/@astack-tech/integrations) | Model providers |
|
|
72
|
+
|
|
73
|
+
## License
|
|
74
|
+
|
|
75
|
+
[MIT](./LICENSE). AStack is in public beta: APIs can change between beta releases. See [CHANGELOG.md](./CHANGELOG.md).
|
package/dist/index.cjs
CHANGED
|
@@ -43,305 +43,242 @@ __export(model_provider_exports, {
|
|
|
43
43
|
// src/model-provider/deepseek/index.ts
|
|
44
44
|
var import_core = require("@astack-tech/core");
|
|
45
45
|
var import_openai = __toESM(require("openai"), 1);
|
|
46
|
+
var DEFAULT_BASE_URL = "https://api.deepseek.com/v1";
|
|
47
|
+
var DEFAULT_MODEL = "deepseek-chat";
|
|
48
|
+
var DEFAULT_TEMPERATURE = 0.7;
|
|
49
|
+
var FALLBACK_TEXT = "API call failed";
|
|
50
|
+
function isRecord(value) {
|
|
51
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
52
|
+
}
|
|
53
|
+
function toApiTool(tool) {
|
|
54
|
+
if (isRecord(tool) && tool["type"] === "function") {
|
|
55
|
+
return tool;
|
|
56
|
+
}
|
|
57
|
+
const raw = isRecord(tool) ? tool : {};
|
|
58
|
+
const { name, description, parameters } = raw;
|
|
59
|
+
return {
|
|
60
|
+
type: "function",
|
|
61
|
+
function: {
|
|
62
|
+
name: typeof name === "string" ? name : "",
|
|
63
|
+
description: typeof description === "string" ? description : "",
|
|
64
|
+
parameters: isRecord(parameters) ? parameters : {}
|
|
65
|
+
}
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
function parseArguments(raw) {
|
|
69
|
+
if (raw.trim().length === 0) {
|
|
70
|
+
return {};
|
|
71
|
+
}
|
|
72
|
+
try {
|
|
73
|
+
const parsed = JSON.parse(raw);
|
|
74
|
+
return isRecord(parsed) ? parsed : {};
|
|
75
|
+
} catch {
|
|
76
|
+
return {};
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
function mapUsage(usage) {
|
|
80
|
+
return {
|
|
81
|
+
completion_tokens: usage.completion_tokens ?? 0,
|
|
82
|
+
prompt_tokens: usage.prompt_tokens ?? 0,
|
|
83
|
+
total_tokens: usage.total_tokens ?? 0,
|
|
84
|
+
...usage.prompt_cache_hit_tokens === void 0 ? {} : { prompt_cache_hit_tokens: usage.prompt_cache_hit_tokens },
|
|
85
|
+
...usage.prompt_cache_miss_tokens === void 0 ? {} : { prompt_cache_miss_tokens: usage.prompt_cache_miss_tokens }
|
|
86
|
+
};
|
|
87
|
+
}
|
|
46
88
|
var Deepseek = class extends import_core.Component {
|
|
89
|
+
client;
|
|
90
|
+
model;
|
|
91
|
+
temperature;
|
|
92
|
+
maxTokens;
|
|
93
|
+
topP;
|
|
94
|
+
systemPrompt;
|
|
95
|
+
tools;
|
|
96
|
+
verbose;
|
|
47
97
|
constructor(config) {
|
|
48
98
|
super({});
|
|
49
99
|
this.client = new import_openai.default({
|
|
50
100
|
apiKey: config.apiKey,
|
|
51
|
-
baseURL: config.baseURL
|
|
101
|
+
baseURL: config.baseURL ?? DEFAULT_BASE_URL,
|
|
52
102
|
dangerouslyAllowBrowser: config.dangerouslyAllowBrowser ?? false
|
|
53
103
|
});
|
|
54
|
-
this.model = config.model
|
|
55
|
-
this.temperature = config.temperature ??
|
|
104
|
+
this.model = config.model ?? DEFAULT_MODEL;
|
|
105
|
+
this.temperature = config.temperature ?? DEFAULT_TEMPERATURE;
|
|
56
106
|
this.maxTokens = config.maxTokens;
|
|
57
107
|
this.topP = config.topP;
|
|
58
108
|
this.systemPrompt = config.systemPrompt;
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
type: "function",
|
|
62
|
-
function: {
|
|
63
|
-
name: tool.name,
|
|
64
|
-
description: tool.description,
|
|
65
|
-
parameters: tool.parameters || {}
|
|
66
|
-
}
|
|
67
|
-
}));
|
|
68
|
-
} else {
|
|
69
|
-
this.tools = config.tools;
|
|
70
|
-
}
|
|
109
|
+
this.verbose = config.verbose ?? false;
|
|
110
|
+
this.tools = config.rawTools !== void 0 && config.rawTools.length > 0 ? config.rawTools.map(toApiTool) : config.tools;
|
|
71
111
|
import_core.Component.Port.I("prompt").attach(this);
|
|
72
112
|
import_core.Component.Port.I("messages").attach(this);
|
|
73
113
|
import_core.Component.Port.O("completion").attach(this);
|
|
74
114
|
import_core.Component.Port.O("message").attach(this);
|
|
75
115
|
}
|
|
76
116
|
/**
|
|
77
|
-
*
|
|
78
|
-
*
|
|
79
|
-
* @returns
|
|
117
|
+
* Generates a completion for a single prompt.
|
|
118
|
+
*
|
|
119
|
+
* @returns The text answer, or the JSON-encoded assistant message when it contains tool calls.
|
|
80
120
|
*/
|
|
81
121
|
async generateCompletion(prompt) {
|
|
82
|
-
const
|
|
83
|
-
|
|
84
|
-
messages.
|
|
85
|
-
|
|
86
|
-
content: this.systemPrompt
|
|
87
|
-
});
|
|
88
|
-
}
|
|
89
|
-
messages.push({
|
|
90
|
-
role: "user",
|
|
91
|
-
content: prompt
|
|
122
|
+
const response = await this.client.chat.completions.create({
|
|
123
|
+
...this.baseRequest(),
|
|
124
|
+
messages: [...this.systemMessages(), { role: "user", content: prompt }],
|
|
125
|
+
...this.toolsParam(this.tools)
|
|
92
126
|
});
|
|
93
|
-
const
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
temperature: this.temperature,
|
|
97
|
-
max_tokens: this.maxTokens,
|
|
98
|
-
top_p: this.topP
|
|
99
|
-
};
|
|
100
|
-
if (this.tools && this.tools.length > 0) {
|
|
101
|
-
requestParams.tools = this.tools;
|
|
127
|
+
const message = this.firstMessage(response);
|
|
128
|
+
if (message.tool_calls !== void 0 && message.tool_calls.length > 0) {
|
|
129
|
+
return JSON.stringify(message);
|
|
102
130
|
}
|
|
103
|
-
|
|
104
|
-
if (response.choices[0].message.tool_calls && response.choices[0].message.tool_calls.length > 0) {
|
|
105
|
-
return JSON.stringify(response.choices[0].message);
|
|
106
|
-
}
|
|
107
|
-
return response.choices[0].message.content || "";
|
|
131
|
+
return message.content ?? "";
|
|
108
132
|
}
|
|
109
133
|
/**
|
|
110
|
-
*
|
|
111
|
-
* @param messages 对话消息数组
|
|
112
|
-
* @param options 可选的调用选项,包含临时工具列表
|
|
113
|
-
* @returns 生成的流式响应消息异步生成器
|
|
134
|
+
* Sends a conversation and returns the assistant reply.
|
|
114
135
|
*/
|
|
115
|
-
async
|
|
116
|
-
const
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
136
|
+
async chatCompletion(messages, options) {
|
|
137
|
+
const response = await this.client.chat.completions.create({
|
|
138
|
+
...this.baseRequest(),
|
|
139
|
+
messages: this.formatMessages(messages),
|
|
140
|
+
...this.toolsParam(options?.temporaryTools ?? this.tools)
|
|
141
|
+
});
|
|
142
|
+
const message = this.firstMessage(response);
|
|
143
|
+
const result = { role: "assistant", content: message.content ?? "" };
|
|
144
|
+
if (response.usage !== void 0) {
|
|
145
|
+
result.usage = mapUsage(response.usage);
|
|
122
146
|
}
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
content: msg.content
|
|
128
|
-
};
|
|
129
|
-
if (msg.role === "tool" && msg.tool_call_id) {
|
|
130
|
-
formattedMsg.tool_call_id = msg.tool_call_id;
|
|
131
|
-
}
|
|
132
|
-
return formattedMsg;
|
|
133
|
-
})
|
|
134
|
-
);
|
|
135
|
-
const requestParams = {
|
|
136
|
-
model: this.model,
|
|
137
|
-
messages: formattedMessages,
|
|
138
|
-
temperature: this.temperature,
|
|
139
|
-
max_tokens: this.maxTokens,
|
|
140
|
-
top_p: this.topP,
|
|
141
|
-
stream: true
|
|
142
|
-
// 开启流式模式
|
|
143
|
-
};
|
|
144
|
-
const toolsToUse = options?.temporaryTools || this.tools;
|
|
145
|
-
if (toolsToUse && toolsToUse.length > 0) {
|
|
146
|
-
const formattedTools = toolsToUse.map((tool) => {
|
|
147
|
-
const typedTool = tool;
|
|
148
|
-
if (typedTool.type === "function") {
|
|
149
|
-
return tool;
|
|
147
|
+
if (message.tool_calls !== void 0 && message.tool_calls.length > 0) {
|
|
148
|
+
result.tool_calls = message.tool_calls.map((toolCall) => {
|
|
149
|
+
if (toolCall.type !== "function") {
|
|
150
|
+
throw new Error(`Unsupported tool call type: ${toolCall.type}`);
|
|
150
151
|
}
|
|
152
|
+
const rawArguments = toolCall.function.arguments;
|
|
151
153
|
return {
|
|
154
|
+
id: toolCall.id,
|
|
152
155
|
type: "function",
|
|
153
156
|
function: {
|
|
154
|
-
name:
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
157
|
+
name: toolCall.function.name,
|
|
158
|
+
arguments: rawArguments.length > 0 ? rawArguments : "{}"
|
|
159
|
+
},
|
|
160
|
+
tool_name: toolCall.function.name,
|
|
161
|
+
arguments: parseArguments(rawArguments)
|
|
158
162
|
};
|
|
159
163
|
});
|
|
160
|
-
requestParams.tools = formattedTools;
|
|
161
164
|
}
|
|
162
|
-
|
|
165
|
+
return result;
|
|
166
|
+
}
|
|
167
|
+
/**
|
|
168
|
+
* Streams the assistant reply. Each chunk holds a text delta, tool-call deltas, or usage.
|
|
169
|
+
*/
|
|
170
|
+
async *streamChatCompletion(messages, options) {
|
|
171
|
+
const stream = await this.client.chat.completions.create({
|
|
172
|
+
...this.baseRequest(),
|
|
173
|
+
messages: this.formatMessages(messages),
|
|
174
|
+
...this.toolsParam(options?.temporaryTools ?? this.tools),
|
|
175
|
+
stream: true
|
|
176
|
+
});
|
|
163
177
|
for await (const chunk of stream) {
|
|
164
178
|
const delta = chunk.choices[0]?.delta;
|
|
165
179
|
if (delta?.content) {
|
|
166
|
-
yield {
|
|
167
|
-
role: "assistant",
|
|
168
|
-
content: delta.content
|
|
169
|
-
};
|
|
180
|
+
yield { role: "assistant", content: delta.content };
|
|
170
181
|
}
|
|
171
182
|
if (delta?.tool_calls) {
|
|
172
183
|
yield {
|
|
173
184
|
role: "assistant",
|
|
174
185
|
content: "",
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
})
|
|
186
|
+
tool_calls: delta.tool_calls.map((toolCall) => {
|
|
187
|
+
const name = toolCall.function?.name ?? "";
|
|
188
|
+
const args = toolCall.function?.arguments ?? "";
|
|
189
|
+
return {
|
|
190
|
+
id: toolCall.id ?? "",
|
|
191
|
+
type: "function",
|
|
192
|
+
function: { name, arguments: args },
|
|
193
|
+
tool_name: name,
|
|
194
|
+
arguments: args
|
|
195
|
+
};
|
|
196
|
+
})
|
|
186
197
|
};
|
|
187
198
|
}
|
|
188
199
|
if (chunk.usage) {
|
|
189
|
-
yield {
|
|
190
|
-
role: "assistant",
|
|
191
|
-
content: "",
|
|
192
|
-
usage: {
|
|
193
|
-
completion_tokens: chunk.usage.completion_tokens || 0,
|
|
194
|
-
prompt_tokens: chunk.usage.prompt_tokens || 0,
|
|
195
|
-
prompt_cache_hit_tokens: chunk.usage.prompt_cache_hit_tokens,
|
|
196
|
-
prompt_cache_miss_tokens: chunk.usage.prompt_cache_miss_tokens,
|
|
197
|
-
total_tokens: chunk.usage.total_tokens || 0
|
|
198
|
-
}
|
|
199
|
-
};
|
|
200
|
+
yield { role: "assistant", content: "", usage: mapUsage(chunk.usage) };
|
|
200
201
|
}
|
|
201
202
|
}
|
|
202
203
|
}
|
|
203
204
|
/**
|
|
204
|
-
*
|
|
205
|
-
*
|
|
206
|
-
* @param
|
|
207
|
-
* @returns 生成的响应消息
|
|
205
|
+
* Runs the model outside a pipeline.
|
|
206
|
+
*
|
|
207
|
+
* @param input A prompt string or a list of messages.
|
|
208
208
|
*/
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
209
|
+
run(input) {
|
|
210
|
+
return typeof input === "string" ? this.generateCompletion(input) : this.chatCompletion(input);
|
|
211
|
+
}
|
|
212
|
+
/**
|
|
213
|
+
* Connects the `prompt` and `messages` input ports to the model.
|
|
214
|
+
* API errors produce a fallback value so the pipeline keeps flowing.
|
|
215
|
+
*/
|
|
216
|
+
_transform($i, $o) {
|
|
217
|
+
$i("prompt").receive((prompt) => {
|
|
218
|
+
this.generateCompletion(prompt).then(
|
|
219
|
+
(completion) => {
|
|
220
|
+
$o("completion").send(completion);
|
|
221
|
+
},
|
|
222
|
+
(error) => {
|
|
223
|
+
this.logError(error);
|
|
224
|
+
$o("completion").send(FALLBACK_TEXT);
|
|
225
225
|
}
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
)
|
|
229
|
-
|
|
226
|
+
);
|
|
227
|
+
});
|
|
228
|
+
$i("messages").receive((messages) => {
|
|
229
|
+
this.chatCompletion(messages).then(
|
|
230
|
+
(message) => {
|
|
231
|
+
$o("message").send(message);
|
|
232
|
+
},
|
|
233
|
+
(error) => {
|
|
234
|
+
this.logError(error);
|
|
235
|
+
$o("message").send({ role: "assistant", content: FALLBACK_TEXT });
|
|
236
|
+
}
|
|
237
|
+
);
|
|
238
|
+
});
|
|
239
|
+
}
|
|
240
|
+
baseRequest() {
|
|
241
|
+
return {
|
|
230
242
|
model: this.model,
|
|
231
|
-
messages: formattedMessages,
|
|
232
243
|
temperature: this.temperature,
|
|
233
|
-
max_tokens: this.maxTokens,
|
|
234
|
-
top_p: this.topP
|
|
235
|
-
};
|
|
236
|
-
const toolsToUse = options?.temporaryTools || this.tools;
|
|
237
|
-
if (toolsToUse && toolsToUse.length > 0) {
|
|
238
|
-
const formattedTools = toolsToUse.map((tool) => {
|
|
239
|
-
const typedTool = tool;
|
|
240
|
-
if (typedTool.type === "function") {
|
|
241
|
-
return tool;
|
|
242
|
-
}
|
|
243
|
-
return {
|
|
244
|
-
type: "function",
|
|
245
|
-
function: {
|
|
246
|
-
name: typedTool.name,
|
|
247
|
-
description: typedTool.description,
|
|
248
|
-
parameters: typedTool.parameters || {}
|
|
249
|
-
}
|
|
250
|
-
};
|
|
251
|
-
});
|
|
252
|
-
requestParams.tools = formattedTools;
|
|
253
|
-
}
|
|
254
|
-
const response = await this.client.chat.completions.create(requestParams);
|
|
255
|
-
const responseMessage = response.choices[0].message;
|
|
256
|
-
const result = {
|
|
257
|
-
role: "assistant",
|
|
258
|
-
content: responseMessage.content || ""
|
|
244
|
+
...this.maxTokens === void 0 ? {} : { max_tokens: this.maxTokens },
|
|
245
|
+
...this.topP === void 0 ? {} : { top_p: this.topP }
|
|
259
246
|
};
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
prompt_cache_miss_tokens: usage.prompt_cache_miss_tokens,
|
|
267
|
-
total_tokens: usage.total_tokens || 0
|
|
268
|
-
};
|
|
269
|
-
}
|
|
270
|
-
if (responseMessage.tool_calls && responseMessage.tool_calls.length > 0) {
|
|
271
|
-
result.tool_calls = responseMessage.tool_calls.map((toolCall) => {
|
|
272
|
-
if (toolCall.type !== "function" || !("function" in toolCall)) {
|
|
273
|
-
throw new Error(`Unsupported tool call type: ${toolCall.type}`);
|
|
274
|
-
}
|
|
275
|
-
let args;
|
|
276
|
-
const rawArgs = toolCall.function.arguments;
|
|
277
|
-
if (typeof rawArgs === "string") {
|
|
278
|
-
const raw = rawArgs;
|
|
279
|
-
try {
|
|
280
|
-
args = raw && raw.trim().length > 0 ? JSON.parse(raw) : {};
|
|
281
|
-
} catch {
|
|
282
|
-
args = {};
|
|
283
|
-
}
|
|
284
|
-
} else if (rawArgs && typeof rawArgs === "object") {
|
|
285
|
-
args = rawArgs;
|
|
286
|
-
} else {
|
|
287
|
-
args = {};
|
|
288
|
-
}
|
|
289
|
-
return {
|
|
290
|
-
id: toolCall.id,
|
|
291
|
-
// 满足 Agent 组件期望的格式
|
|
292
|
-
function: {
|
|
293
|
-
name: toolCall.function.name,
|
|
294
|
-
arguments: toolCall.function.arguments || "{}"
|
|
295
|
-
},
|
|
296
|
-
type: "function",
|
|
297
|
-
// 满足内部 ToolCall 接口
|
|
298
|
-
tool_name: toolCall.function.name,
|
|
299
|
-
arguments: args
|
|
300
|
-
};
|
|
301
|
-
});
|
|
302
|
-
}
|
|
303
|
-
return result;
|
|
247
|
+
}
|
|
248
|
+
toolsParam(tools) {
|
|
249
|
+
return tools !== void 0 && tools.length > 0 ? { tools: tools.map(toApiTool) } : {};
|
|
250
|
+
}
|
|
251
|
+
systemMessages() {
|
|
252
|
+
return this.systemPrompt ? [{ role: "system", content: this.systemPrompt }] : [];
|
|
304
253
|
}
|
|
305
254
|
/**
|
|
306
|
-
*
|
|
307
|
-
*
|
|
308
|
-
*
|
|
255
|
+
* Converts input messages to API messages. Assistant `tool_calls` are not forwarded:
|
|
256
|
+
* agents send tool results back as user messages, and the API rejects assistant tool
|
|
257
|
+
* calls that are not followed by tool messages.
|
|
309
258
|
*/
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
259
|
+
formatMessages(messages) {
|
|
260
|
+
return [
|
|
261
|
+
...this.systemMessages(),
|
|
262
|
+
...messages.map(
|
|
263
|
+
(message) => ({
|
|
264
|
+
role: message.role,
|
|
265
|
+
content: message.content,
|
|
266
|
+
...message.role === "tool" && message.tool_call_id ? { tool_call_id: message.tool_call_id } : {}
|
|
267
|
+
})
|
|
268
|
+
)
|
|
269
|
+
];
|
|
270
|
+
}
|
|
271
|
+
firstMessage(response) {
|
|
272
|
+
const choice = response.choices[0];
|
|
273
|
+
if (choice === void 0) {
|
|
274
|
+
throw new Error("DeepSeek returned no choices.");
|
|
315
275
|
}
|
|
276
|
+
return choice.message;
|
|
316
277
|
}
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
*/
|
|
322
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
323
|
-
_transform($i, $o) {
|
|
324
|
-
$i("prompt").receive(async (prompt) => {
|
|
325
|
-
try {
|
|
326
|
-
const completion = await this.generateCompletion(prompt);
|
|
327
|
-
$o("completion").send(completion);
|
|
328
|
-
} catch (error) {
|
|
329
|
-
console.error("Deepseek API \u8C03\u7528\u9519\u8BEF:", error);
|
|
330
|
-
$o("completion").send("API \u8C03\u7528\u9519\u8BEF");
|
|
331
|
-
}
|
|
332
|
-
});
|
|
333
|
-
$i("messages").receive(async (messages) => {
|
|
334
|
-
try {
|
|
335
|
-
const responseMessage = await this.chatCompletion(messages);
|
|
336
|
-
$o("message").send(responseMessage);
|
|
337
|
-
} catch (error) {
|
|
338
|
-
console.error("Deepseek API \u8C03\u7528\u9519\u8BEF:", error);
|
|
339
|
-
$o("message").send({
|
|
340
|
-
role: "assistant",
|
|
341
|
-
content: "API \u8C03\u7528\u9519\u8BEF"
|
|
342
|
-
});
|
|
343
|
-
}
|
|
344
|
-
});
|
|
278
|
+
logError(error) {
|
|
279
|
+
if (this.verbose) {
|
|
280
|
+
console.error("[Deepseek] API call failed:", error);
|
|
281
|
+
}
|
|
345
282
|
}
|
|
346
283
|
};
|
|
347
284
|
var deepseek_default = Deepseek;
|