@astack-tech/integrations 0.1.1-beta.6 → 0.2.0-beta.8

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 ADDED
@@ -0,0 +1,50 @@
1
+ # @astack-tech/integrations
2
+
3
+ ## 0.2.0-beta.8
4
+
5
+ ### Minor Changes
6
+
7
+ - 9aab43a: Add `ModelProvider.OpenAICompatible`, a provider for any API that speaks the OpenAI chat completions protocol: OpenAI and the compatible endpoints of DeepSeek, Qwen, Moonshot, Ollama, vLLM and others. Pass `model`, and `baseURL` for providers other than OpenAI; `name` labels errors and logs. `Deepseek` is now this provider with DeepSeek's defaults; its options, methods, ports and exported types are unchanged. The neutral message, tool and usage types are exported next to the `Deepseek*` names.
8
+
9
+ ### Patch Changes
10
+
11
+ - Updated dependencies [1aba97f]
12
+ - @astack-tech/core@0.2.0-beta.3
13
+
14
+ ## 0.2.0-beta.7
15
+
16
+ ### Minor Changes
17
+
18
+ - 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.
19
+
20
+ ### Patch Changes
21
+
22
+ - Updated dependencies [e71dd3b]
23
+ - @astack-tech/core@0.2.0-beta.2
24
+
25
+ ## 0.1.1-beta.6
26
+
27
+ ### Patch Changes
28
+
29
+ - Updated dependencies
30
+ - @astack-tech/core@0.2.0-beta.1
31
+
32
+ ## 0.1.1-beta.2
33
+
34
+ ### Patch Changes
35
+
36
+ - rebuild package
37
+
38
+ ## 0.1.1-beta.1
39
+
40
+ ### Patch Changes
41
+
42
+ - fix agent tool finder
43
+
44
+ ## 0.1.1-beta.0
45
+
46
+ ### Patch Changes
47
+
48
+ - beta release
49
+ - Updated dependencies
50
+ - @astack-tech/core@0.1.1-beta.0
package/README.md CHANGED
@@ -1 +1,96 @@
1
- > @astack-tech/integrations
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
+ [![npm](https://img.shields.io/npm/v/@astack-tech/integrations?color=111&label=npm)](https://www.npmjs.com/package/@astack-tech/integrations) [![license](https://img.shields.io/npm/l/@astack-tech/integrations?color=111)](https://github.com/astack-tech/astack/blob/master/LICENSE) [![node](https://img.shields.io/node/v/@astack-tech/integrations?color=111)](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` and `OpenAICompatible` are also available from the subpath: `import { Deepseek, OpenAICompatible } 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
+ ## OpenAICompatible
60
+
61
+ Calls any API that speaks the OpenAI chat completions protocol: OpenAI and the compatible endpoints of DeepSeek, Qwen, Moonshot, Ollama, vLLM and others. `Deepseek` is this provider with DeepSeek's defaults, so methods, ports and the other options are the same.
62
+
63
+ ```typescript
64
+ import { ModelProvider } from '@astack-tech/integrations';
65
+
66
+ const model = new ModelProvider.OpenAICompatible({
67
+ apiKey: process.env['OPENAI_API_KEY'] ?? '',
68
+ model: 'gpt-4o-mini',
69
+ });
70
+ ```
71
+
72
+ | Option | Default | Description |
73
+ | --------- | --------------------------- | -------------------------------- |
74
+ | `apiKey` | — | API key of the provider |
75
+ | `model` | — | Model name (required) |
76
+ | `baseURL` | `https://api.openai.com/v1` | API base URL |
77
+ | `name` | `The model API` | Provider name in errors and logs |
78
+
79
+ ## Learn more
80
+
81
+ - [API reference](https://astack.tech/docs/api/integrations): every export, generated from the source
82
+ - [Model providers](https://astack.tech/docs/model-providers), including how to write your own
83
+ - Example: [chat](https://astack.tech/examples/chat)
84
+
85
+ ## The AStack packages
86
+
87
+ | Package | Purpose |
88
+ | -------------------------------------------------------------------------------------- | ------------------------------------------ |
89
+ | [`@astack-tech/core`](https://www.npmjs.com/package/@astack-tech/core) | `Component`, `Pipeline` and ports |
90
+ | [`@astack-tech/components`](https://www.npmjs.com/package/@astack-tech/components) | Agents, text splitting and tool invocation |
91
+ | [`@astack-tech/tools`](https://www.npmjs.com/package/@astack-tech/tools) | Tool definitions for agents |
92
+ | [`@astack-tech/integrations`](https://www.npmjs.com/package/@astack-tech/integrations) | Model providers |
93
+
94
+ ## License
95
+
96
+ [MIT](./LICENSE). AStack is in public beta: APIs can change between beta releases. See [CHANGELOG.md](./CHANGELOG.md).
package/dist/index.cjs CHANGED
@@ -37,310 +37,265 @@ module.exports = __toCommonJS(src_exports);
37
37
  // src/model-provider/index.ts
38
38
  var model_provider_exports = {};
39
39
  __export(model_provider_exports, {
40
- Deepseek: () => deepseek_default
40
+ Deepseek: () => deepseek_default,
41
+ OpenAICompatible: () => openai_compatible_default
41
42
  });
42
43
 
43
- // src/model-provider/deepseek/index.ts
44
+ // src/model-provider/openai-compatible/index.ts
44
45
  var import_core = require("@astack-tech/core");
45
46
  var import_openai = __toESM(require("openai"), 1);
46
- var Deepseek = class extends import_core.Component {
47
+ var DEFAULT_BASE_URL = "https://api.openai.com/v1";
48
+ var DEFAULT_NAME = "The model API";
49
+ var DEFAULT_TEMPERATURE = 0.7;
50
+ var FALLBACK_TEXT = "API call failed";
51
+ function isRecord(value) {
52
+ return typeof value === "object" && value !== null && !Array.isArray(value);
53
+ }
54
+ function toApiTool(tool) {
55
+ if (isRecord(tool) && tool["type"] === "function") {
56
+ return tool;
57
+ }
58
+ const raw = isRecord(tool) ? tool : {};
59
+ const { name, description, parameters } = raw;
60
+ return {
61
+ type: "function",
62
+ function: {
63
+ name: typeof name === "string" ? name : "",
64
+ description: typeof description === "string" ? description : "",
65
+ parameters: isRecord(parameters) ? parameters : {}
66
+ }
67
+ };
68
+ }
69
+ function parseArguments(raw) {
70
+ if (raw.trim().length === 0) {
71
+ return {};
72
+ }
73
+ try {
74
+ const parsed = JSON.parse(raw);
75
+ return isRecord(parsed) ? parsed : {};
76
+ } catch {
77
+ return {};
78
+ }
79
+ }
80
+ function mapUsage(usage) {
81
+ return {
82
+ completion_tokens: usage.completion_tokens ?? 0,
83
+ prompt_tokens: usage.prompt_tokens ?? 0,
84
+ total_tokens: usage.total_tokens ?? 0,
85
+ ...usage.prompt_cache_hit_tokens === void 0 ? {} : { prompt_cache_hit_tokens: usage.prompt_cache_hit_tokens },
86
+ ...usage.prompt_cache_miss_tokens === void 0 ? {} : { prompt_cache_miss_tokens: usage.prompt_cache_miss_tokens }
87
+ };
88
+ }
89
+ var OpenAICompatible = class extends import_core.Component {
90
+ client;
91
+ model;
92
+ temperature;
93
+ maxTokens;
94
+ topP;
95
+ systemPrompt;
96
+ tools;
97
+ verbose;
98
+ name;
47
99
  constructor(config) {
48
100
  super({});
49
101
  this.client = new import_openai.default({
50
102
  apiKey: config.apiKey,
51
- baseURL: config.baseURL || "https://api.deepseek.com/v1",
103
+ baseURL: config.baseURL ?? DEFAULT_BASE_URL,
52
104
  dangerouslyAllowBrowser: config.dangerouslyAllowBrowser ?? false
53
105
  });
54
- this.model = config.model || "deepseek-chat";
55
- this.temperature = config.temperature ?? 0.7;
106
+ this.model = config.model;
107
+ this.name = config.name ?? DEFAULT_NAME;
108
+ this.temperature = config.temperature ?? DEFAULT_TEMPERATURE;
56
109
  this.maxTokens = config.maxTokens;
57
110
  this.topP = config.topP;
58
111
  this.systemPrompt = config.systemPrompt;
59
- if (config.rawTools && config.rawTools.length > 0) {
60
- this.tools = config.rawTools.map((tool) => ({
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
- }
112
+ this.verbose = config.verbose ?? false;
113
+ this.tools = config.rawTools !== void 0 && config.rawTools.length > 0 ? config.rawTools.map(toApiTool) : config.tools;
71
114
  import_core.Component.Port.I("prompt").attach(this);
72
115
  import_core.Component.Port.I("messages").attach(this);
73
116
  import_core.Component.Port.O("completion").attach(this);
74
117
  import_core.Component.Port.O("message").attach(this);
75
118
  }
76
119
  /**
77
- * 生成文本完成
78
- * @param prompt 提示文本
79
- * @returns 生成的文本
120
+ * Generates a completion for a single prompt.
121
+ *
122
+ * @returns The text answer, or the JSON-encoded assistant message when it contains tool calls.
80
123
  */
81
124
  async generateCompletion(prompt) {
82
- const messages = [];
83
- if (this.systemPrompt) {
84
- messages.push({
85
- role: "system",
86
- content: this.systemPrompt
87
- });
88
- }
89
- messages.push({
90
- role: "user",
91
- content: prompt
125
+ const response = await this.client.chat.completions.create({
126
+ ...this.baseRequest(),
127
+ messages: [...this.systemMessages(), { role: "user", content: prompt }],
128
+ ...this.toolsParam(this.tools)
92
129
  });
93
- const requestParams = {
94
- model: this.model,
95
- messages,
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;
130
+ const message = this.firstMessage(response);
131
+ if (message.tool_calls !== void 0 && message.tool_calls.length > 0) {
132
+ return JSON.stringify(message);
102
133
  }
103
- const response = await this.client.chat.completions.create(requestParams);
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 || "";
134
+ return message.content ?? "";
108
135
  }
109
136
  /**
110
- * 流式处理对话消息
111
- * @param messages 对话消息数组
112
- * @param options 可选的调用选项,包含临时工具列表
113
- * @returns 生成的流式响应消息异步生成器
137
+ * Sends a conversation and returns the assistant reply.
114
138
  */
115
- async *streamChatCompletion(messages, options) {
116
- const formattedMessages = [];
117
- if (this.systemPrompt) {
118
- formattedMessages.push({
119
- role: "system",
120
- content: this.systemPrompt
121
- });
139
+ async chatCompletion(messages, options) {
140
+ const response = await this.client.chat.completions.create({
141
+ ...this.baseRequest(),
142
+ messages: this.formatMessages(messages),
143
+ ...this.toolsParam(options?.temporaryTools ?? this.tools)
144
+ });
145
+ const message = this.firstMessage(response);
146
+ const result = { role: "assistant", content: message.content ?? "" };
147
+ if (response.usage !== void 0) {
148
+ result.usage = mapUsage(response.usage);
122
149
  }
123
- formattedMessages.push(
124
- ...messages.map((msg) => {
125
- const formattedMsg = {
126
- role: msg.role,
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;
150
+ if (message.tool_calls !== void 0 && message.tool_calls.length > 0) {
151
+ result.tool_calls = message.tool_calls.map((toolCall) => {
152
+ if (toolCall.type !== "function") {
153
+ throw new Error(`Unsupported tool call type: ${toolCall.type}`);
150
154
  }
155
+ const rawArguments = toolCall.function.arguments;
151
156
  return {
157
+ id: toolCall.id,
152
158
  type: "function",
153
159
  function: {
154
- name: typedTool.name,
155
- description: typedTool.description,
156
- parameters: typedTool.parameters || {}
157
- }
160
+ name: toolCall.function.name,
161
+ arguments: rawArguments.length > 0 ? rawArguments : "{}"
162
+ },
163
+ tool_name: toolCall.function.name,
164
+ arguments: parseArguments(rawArguments)
158
165
  };
159
166
  });
160
- requestParams.tools = formattedTools;
161
167
  }
162
- const stream = await this.client.chat.completions.create(requestParams);
168
+ return result;
169
+ }
170
+ /**
171
+ * Streams the assistant reply. Each chunk holds a text delta, tool-call deltas, or usage.
172
+ */
173
+ async *streamChatCompletion(messages, options) {
174
+ const stream = await this.client.chat.completions.create({
175
+ ...this.baseRequest(),
176
+ messages: this.formatMessages(messages),
177
+ ...this.toolsParam(options?.temporaryTools ?? this.tools),
178
+ stream: true
179
+ });
163
180
  for await (const chunk of stream) {
164
181
  const delta = chunk.choices[0]?.delta;
165
182
  if (delta?.content) {
166
- yield {
167
- role: "assistant",
168
- content: delta.content
169
- };
183
+ yield { role: "assistant", content: delta.content };
170
184
  }
171
185
  if (delta?.tool_calls) {
172
186
  yield {
173
187
  role: "assistant",
174
188
  content: "",
175
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
176
- tool_calls: delta.tool_calls.map((toolCall) => ({
177
- id: toolCall.id || "",
178
- function: {
179
- name: toolCall.function?.name || "",
180
- arguments: toolCall.function?.arguments || ""
181
- },
182
- type: "function",
183
- tool_name: toolCall.function?.name || "",
184
- arguments: toolCall.function?.arguments || ""
185
- }))
189
+ tool_calls: delta.tool_calls.map((toolCall) => {
190
+ const name = toolCall.function?.name ?? "";
191
+ const args = toolCall.function?.arguments ?? "";
192
+ return {
193
+ id: toolCall.id ?? "",
194
+ type: "function",
195
+ function: { name, arguments: args },
196
+ tool_name: name,
197
+ arguments: args
198
+ };
199
+ })
186
200
  };
187
201
  }
188
202
  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
- };
203
+ yield { role: "assistant", content: "", usage: mapUsage(chunk.usage) };
200
204
  }
201
205
  }
202
206
  }
203
207
  /**
204
- * 处理对话消息
205
- * @param messages 对话消息数组
206
- * @param options 可选的调用选项,包含临时工具列表
207
- * @returns 生成的响应消息
208
+ * Runs the model outside a pipeline.
209
+ *
210
+ * @param input - A prompt string or a list of messages.
208
211
  */
209
- async chatCompletion(messages, options) {
210
- const formattedMessages = [];
211
- if (this.systemPrompt) {
212
- formattedMessages.push({
213
- role: "system",
214
- content: this.systemPrompt
215
- });
216
- }
217
- formattedMessages.push(
218
- ...messages.map((msg) => {
219
- const formattedMsg = {
220
- role: msg.role,
221
- content: msg.content
222
- };
223
- if (msg.role === "tool" && msg.tool_call_id) {
224
- formattedMsg.tool_call_id = msg.tool_call_id;
212
+ run(input) {
213
+ return typeof input === "string" ? this.generateCompletion(input) : this.chatCompletion(input);
214
+ }
215
+ /**
216
+ * Connects the `prompt` and `messages` input ports to the model.
217
+ * API errors produce a fallback value so the pipeline keeps flowing.
218
+ */
219
+ _transform($i, $o) {
220
+ $i("prompt").receive((prompt) => {
221
+ this.generateCompletion(prompt).then(
222
+ (completion) => {
223
+ $o("completion").send(completion);
224
+ },
225
+ (error) => {
226
+ this.logError(error);
227
+ $o("completion").send(FALLBACK_TEXT);
225
228
  }
226
- return formattedMsg;
227
- })
228
- );
229
- const requestParams = {
229
+ );
230
+ });
231
+ $i("messages").receive((messages) => {
232
+ this.chatCompletion(messages).then(
233
+ (message) => {
234
+ $o("message").send(message);
235
+ },
236
+ (error) => {
237
+ this.logError(error);
238
+ $o("message").send({ role: "assistant", content: FALLBACK_TEXT });
239
+ }
240
+ );
241
+ });
242
+ }
243
+ baseRequest() {
244
+ return {
230
245
  model: this.model,
231
- messages: formattedMessages,
232
246
  temperature: this.temperature,
233
- max_tokens: this.maxTokens,
234
- top_p: this.topP
247
+ ...this.maxTokens === void 0 ? {} : { max_tokens: this.maxTokens },
248
+ ...this.topP === void 0 ? {} : { top_p: this.topP }
235
249
  };
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 || ""
259
- };
260
- if (response.usage) {
261
- const usage = response.usage;
262
- result.usage = {
263
- completion_tokens: usage.completion_tokens || 0,
264
- prompt_tokens: usage.prompt_tokens || 0,
265
- prompt_cache_hit_tokens: usage.prompt_cache_hit_tokens,
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;
250
+ }
251
+ toolsParam(tools) {
252
+ return tools !== void 0 && tools.length > 0 ? { tools: tools.map(toApiTool) } : {};
253
+ }
254
+ systemMessages() {
255
+ return this.systemPrompt ? [{ role: "system", content: this.systemPrompt }] : [];
304
256
  }
305
257
  /**
306
- * 在独立模式下运行组件
307
- * @param input 输入参数,可以是提示文本或对话消息数组
308
- * @returns 生成的文本或响应消息
258
+ * Converts input messages to API messages. Assistant `tool_calls` are not forwarded:
259
+ * agents send tool results back as user messages, and the API rejects assistant tool
260
+ * calls that are not followed by tool messages.
309
261
  */
310
- async run(input) {
311
- if (typeof input === "string") {
312
- return this.generateCompletion(input);
313
- } else {
314
- return this.chatCompletion(input);
262
+ formatMessages(messages) {
263
+ return [
264
+ ...this.systemMessages(),
265
+ ...messages.map(
266
+ (message) => ({
267
+ role: message.role,
268
+ content: message.content,
269
+ ...message.role === "tool" && message.tool_call_id ? { tool_call_id: message.tool_call_id } : {}
270
+ })
271
+ )
272
+ ];
273
+ }
274
+ firstMessage(response) {
275
+ const choice = response.choices[0];
276
+ if (choice === void 0) {
277
+ throw new Error(`${this.name} returned no choices.`);
315
278
  }
279
+ return choice.message;
316
280
  }
317
- /**
318
- * 在流水线中运行组件
319
- * @param $i 输入端口
320
- * @param $o 输出端口
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
- }
281
+ logError(error) {
282
+ if (this.verbose) {
283
+ console.error(`[${this.name}] API call failed:`, error);
284
+ }
285
+ }
286
+ };
287
+ var openai_compatible_default = OpenAICompatible;
288
+
289
+ // src/model-provider/deepseek/index.ts
290
+ var DEFAULT_BASE_URL2 = "https://api.deepseek.com/v1";
291
+ var DEFAULT_MODEL = "deepseek-chat";
292
+ var Deepseek = class extends openai_compatible_default {
293
+ constructor(config) {
294
+ super({
295
+ ...config,
296
+ model: config.model ?? DEFAULT_MODEL,
297
+ baseURL: config.baseURL ?? DEFAULT_BASE_URL2,
298
+ name: "DeepSeek"
344
299
  });
345
300
  }
346
301
  };