@keo-ai/axiom 0.2.7 → 0.2.9
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
|
@@ -177,14 +177,17 @@ for await (const chunk of LLM.streamPredict({ model: 'qwen-max', prompt: '讲个
|
|
|
177
177
|
if (chunk.type === 'reasoning') {
|
|
178
178
|
process.stdout.write(chunk.delta); // 推理过程
|
|
179
179
|
}
|
|
180
|
-
if (chunk.type === 'finish'
|
|
180
|
+
if (chunk.type === 'finish') {
|
|
181
|
+
console.log('Finish:', chunk.finishReason, chunk.sawDone);
|
|
181
182
|
console.log('Token usage:', chunk.usage);
|
|
182
|
-
// { promptTokens, completionTokens, totalTokens, cachedPromptTokens? }
|
|
183
183
|
}
|
|
184
184
|
}
|
|
185
185
|
```
|
|
186
186
|
|
|
187
|
-
> 💡 流式调用自动启用 `stream_options: { include_usage: true }
|
|
187
|
+
> 💡 流式调用自动启用 `stream_options: { include_usage: true }`。`finish` 事件会返回 `finishReason`、
|
|
188
|
+
> `sawDone`、`sawFinishReason` 和 token 消耗统计。usage 可能与 `finish_reason` 在同一个 SSE chunk,也可能在独立的
|
|
189
|
+
> chunk(`choices: []`)中返回,两种情况均已兼容。若连接结束前既没有 `[DONE]` 也没有非空
|
|
190
|
+
> `finish_reason`,流会抛出异常,避免把提前 EOF 当作成功响应。
|
|
188
191
|
|
|
189
192
|
### 模型列表
|
|
190
193
|
|
|
@@ -194,16 +197,21 @@ for await (const chunk of LLM.streamPredict({ model: 'qwen-max', prompt: '讲个
|
|
|
194
197
|
|------|----------|-----------|------|
|
|
195
198
|
| `qwen-max` | ❌ 不支持 | ✅ | **默认模型** |
|
|
196
199
|
| `qwen3.7-max` | ✅ 映射支持 | ✅ | `low`/`medium`/`high` 映射为 `enable_thinking` |
|
|
200
|
+
| `qwen3.7-flash` | ✅ 映射支持 | ✅ | 同上 |
|
|
197
201
|
| `qwen-plus` | ✅ 映射支持 | ✅ | 同上 |
|
|
198
202
|
| `qwen-turbo` | ✅ 映射支持 | ✅ | 同上 |
|
|
199
|
-
| `deepseek-v4-pro` | ✅
|
|
200
|
-
| `deepseek-v4-flash` | ✅
|
|
201
|
-
| `kimi-k2.6` | ✅
|
|
203
|
+
| `deepseek-v4-pro` | ✅ 映射支持 | ✅ | 同上(百炼走「混合思考模式」,由 `enable_thinking` 控制) |
|
|
204
|
+
| `deepseek-v4-flash` | ✅ 映射支持 | ✅ | 同上 |
|
|
205
|
+
| `kimi-k2.6` | ✅ 映射支持 | ✅ | `low`/`medium`/`high` 映射为 `extra_body.thinking.type` |
|
|
202
206
|
| `qwq-plus` | ❌ 不支持 | ✅ | 固定推理行为,不可调节 |
|
|
203
207
|
| `glm-5.1` | ❌ 不支持 | ❌ | 不支持推理参数,不支持 json 模式 |
|
|
204
208
|
| `qwen-vl-plus` | ❌ 不支持 | ✅ | 视觉模型,不支持推理参数 |
|
|
205
209
|
|
|
206
|
-
> 推理深度的支持方式由 Provider 内部维护。百炼 Provider
|
|
210
|
+
> 推理深度的支持方式由 Provider 内部维护。百炼 Provider 中:
|
|
211
|
+
> - Qwen 系列与 DeepSeek v4 系列均通过 `extra_body.enable_thinking` 映射实现(`low` 关闭,`medium`/`high` 开启);
|
|
212
|
+
> - Kimi 则通过 `extra_body.thinking.type` 映射(`low` 关闭,`medium`/`high` 开启)。
|
|
213
|
+
>
|
|
214
|
+
> 两种模式在百炼兼容接口下都退化为布尔开关,`medium` / `high` 之间的差异无法体现。
|
|
207
215
|
>
|
|
208
216
|
> 目前所有模型均路由到百炼 Provider。后续接入其他厂商时,通过 `MODEL_REGISTRY` 扩展映射即可。
|
|
209
217
|
|
|
@@ -56,10 +56,11 @@ class BailianProvider {
|
|
|
56
56
|
if (((_b = body.response_format) === null || _b === void 0 ? void 0 : _b.type) === 'json_object' && !caps.jsonMode) {
|
|
57
57
|
throw new Error(`[adapt] Model "${body.model}" does not support response_format json_object`);
|
|
58
58
|
}
|
|
59
|
-
// 百炼平台:Qwen
|
|
60
|
-
//
|
|
59
|
+
// 百炼平台:Qwen 系列与 DeepSeek v4 系列均为「混合思考模式」,
|
|
60
|
+
// 通过 extra_body.enable_thinking 控制思考开关(low 关闭,medium/high 开启);
|
|
61
|
+
// Kimi(Moonshot)则通过 extra_body.thinking.type 实现(默认开启)。
|
|
61
62
|
if (body.reasoning_effort !== undefined) {
|
|
62
|
-
if (['qwen-plus', 'qwen-turbo', 'qwen3.7-max'].includes(body.model)) {
|
|
63
|
+
if (['qwen-plus', 'qwen-turbo', 'qwen3.8-flash', 'qwen3.7-max', 'qwen3.7-flash', 'deepseek-v4-pro', 'deepseek-v4-flash'].includes(body.model)) {
|
|
63
64
|
const { reasoning_effort } = body, rest = __rest(body, ["reasoning_effort"]);
|
|
64
65
|
return Object.assign(Object.assign({}, rest), { extra_body: Object.assign(Object.assign({}, ((_c = rest.extra_body) !== null && _c !== void 0 ? _c : {})), { enable_thinking: reasoning_effort !== 'low' }) });
|
|
65
66
|
}
|
|
@@ -69,7 +70,7 @@ class BailianProvider {
|
|
|
69
70
|
// 需要显式传 thinking.type:low 禁用,medium/high 显式开启
|
|
70
71
|
return Object.assign(Object.assign({}, rest), { extra_body: Object.assign(Object.assign({}, ((_d = rest.extra_body) !== null && _d !== void 0 ? _d : {})), { thinking: { type: reasoning_effort === 'low' ? 'disabled' : 'enabled' } }) });
|
|
71
72
|
}
|
|
72
|
-
//
|
|
73
|
+
// 其它不支持 reasoning_effort 的模型会由前面的能力校验抛异常
|
|
73
74
|
}
|
|
74
75
|
return body;
|
|
75
76
|
}
|
|
@@ -108,7 +109,7 @@ class BailianProvider {
|
|
|
108
109
|
*/
|
|
109
110
|
stream(request) {
|
|
110
111
|
return __asyncGenerator(this, arguments, function* stream_1() {
|
|
111
|
-
var _a
|
|
112
|
+
var _a;
|
|
112
113
|
const url = `${this.config.baseUrl}/chat/completions`;
|
|
113
114
|
const body = this.adaptRequest(Object.assign(Object.assign({}, this.buildRequestBody(request)), { stream: true, stream_options: { include_usage: true } }));
|
|
114
115
|
let response;
|
|
@@ -139,6 +140,53 @@ class BailianProvider {
|
|
|
139
140
|
// usage 可能与 finish_reason 在同一个 chunk,也可能在独立的 chunk(choices: [])中返回。
|
|
140
141
|
// 统一在此收集,流结束后一次性 yield finish 事件。
|
|
141
142
|
let streamUsage;
|
|
143
|
+
let finishReason;
|
|
144
|
+
let sawDone = false;
|
|
145
|
+
let sawFinishReason = false;
|
|
146
|
+
function* processSseLine(line) {
|
|
147
|
+
var _a, _b;
|
|
148
|
+
const trimmed = line.trim();
|
|
149
|
+
if (!trimmed || !trimmed.startsWith('data:'))
|
|
150
|
+
return;
|
|
151
|
+
const data = trimmed.slice(5).trimStart();
|
|
152
|
+
if (!data)
|
|
153
|
+
return;
|
|
154
|
+
if (data === '[DONE]') {
|
|
155
|
+
sawDone = true;
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
158
|
+
let parsed;
|
|
159
|
+
try {
|
|
160
|
+
parsed = JSON.parse(data);
|
|
161
|
+
}
|
|
162
|
+
catch (cause) {
|
|
163
|
+
const message = cause instanceof Error ? cause.message : String(cause);
|
|
164
|
+
throw new Error(`[bailian] Malformed SSE JSON: ${message}`);
|
|
165
|
+
}
|
|
166
|
+
// 从任意 chunk 中收集 usage(包括 choices 为空的独立 usage chunk)
|
|
167
|
+
if (parsed.usage) {
|
|
168
|
+
streamUsage = {
|
|
169
|
+
promptTokens: parsed.usage.prompt_tokens,
|
|
170
|
+
completionTokens: parsed.usage.completion_tokens,
|
|
171
|
+
totalTokens: parsed.usage.total_tokens,
|
|
172
|
+
cachedPromptTokens: (_a = parsed.usage.prompt_tokens_details) === null || _a === void 0 ? void 0 : _a.cached_tokens,
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
const choice = (_b = parsed.choices) === null || _b === void 0 ? void 0 : _b[0];
|
|
176
|
+
if (!choice)
|
|
177
|
+
return;
|
|
178
|
+
const delta = choice.delta;
|
|
179
|
+
if (delta.reasoning_content) {
|
|
180
|
+
yield { type: 'reasoning', delta: delta.reasoning_content };
|
|
181
|
+
}
|
|
182
|
+
if (delta.content) {
|
|
183
|
+
yield { type: 'content', delta: delta.content };
|
|
184
|
+
}
|
|
185
|
+
if (choice.finish_reason) {
|
|
186
|
+
finishReason = choice.finish_reason;
|
|
187
|
+
sawFinishReason = true;
|
|
188
|
+
}
|
|
189
|
+
}
|
|
142
190
|
try {
|
|
143
191
|
while (true) {
|
|
144
192
|
const { done, value } = yield __await(reader.read());
|
|
@@ -148,41 +196,31 @@ class BailianProvider {
|
|
|
148
196
|
const lines = buffer.split('\n');
|
|
149
197
|
buffer = (_a = lines.pop()) !== null && _a !== void 0 ? _a : '';
|
|
150
198
|
for (const line of lines) {
|
|
151
|
-
const
|
|
152
|
-
|
|
153
|
-
continue;
|
|
154
|
-
const data = trimmed.slice(6);
|
|
155
|
-
if (data === '[DONE]')
|
|
156
|
-
continue;
|
|
157
|
-
let parsed;
|
|
158
|
-
try {
|
|
159
|
-
parsed = JSON.parse(data);
|
|
160
|
-
}
|
|
161
|
-
catch (_d) {
|
|
162
|
-
continue;
|
|
163
|
-
}
|
|
164
|
-
// 从任意 chunk 中收集 usage(包括 choices 为空的独立 usage chunk)
|
|
165
|
-
if (parsed.usage) {
|
|
166
|
-
streamUsage = {
|
|
167
|
-
promptTokens: parsed.usage.prompt_tokens,
|
|
168
|
-
completionTokens: parsed.usage.completion_tokens,
|
|
169
|
-
totalTokens: parsed.usage.total_tokens,
|
|
170
|
-
cachedPromptTokens: (_b = parsed.usage.prompt_tokens_details) === null || _b === void 0 ? void 0 : _b.cached_tokens,
|
|
171
|
-
};
|
|
172
|
-
}
|
|
173
|
-
const choice = (_c = parsed.choices) === null || _c === void 0 ? void 0 : _c[0];
|
|
174
|
-
if (!choice)
|
|
175
|
-
continue;
|
|
176
|
-
const delta = choice.delta;
|
|
177
|
-
if (delta.reasoning_content) {
|
|
178
|
-
yield yield __await({ type: 'reasoning', delta: delta.reasoning_content });
|
|
199
|
+
for (const chunk of processSseLine(line)) {
|
|
200
|
+
yield yield __await(chunk);
|
|
179
201
|
}
|
|
180
|
-
|
|
181
|
-
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
// TextDecoder 的流式模式可能仍持有不完整的多字节字符;EOF 时必须 flush。
|
|
205
|
+
buffer += decoder.decode();
|
|
206
|
+
if (buffer) {
|
|
207
|
+
const finalLines = buffer.split('\n');
|
|
208
|
+
for (const line of finalLines) {
|
|
209
|
+
for (const chunk of processSseLine(line)) {
|
|
210
|
+
yield yield __await(chunk);
|
|
182
211
|
}
|
|
183
212
|
}
|
|
184
213
|
}
|
|
185
|
-
|
|
214
|
+
if (!sawDone && !sawFinishReason) {
|
|
215
|
+
throw new Error(`[${this.name}] SSE stream ended before [DONE] or finish_reason`);
|
|
216
|
+
}
|
|
217
|
+
yield yield __await({
|
|
218
|
+
type: 'finish',
|
|
219
|
+
finishReason,
|
|
220
|
+
sawDone,
|
|
221
|
+
sawFinishReason,
|
|
222
|
+
usage: streamUsage,
|
|
223
|
+
});
|
|
186
224
|
}
|
|
187
225
|
finally {
|
|
188
226
|
reader.releaseLock();
|
|
@@ -211,7 +249,9 @@ class BailianProvider {
|
|
|
211
249
|
}
|
|
212
250
|
exports.BailianProvider = BailianProvider;
|
|
213
251
|
BailianProvider.SUPPORTED_MODELS = new Set([
|
|
252
|
+
'qwen3.8-flash',
|
|
214
253
|
'qwen3.7-max',
|
|
254
|
+
'qwen3.7-flash',
|
|
215
255
|
'qwen-plus',
|
|
216
256
|
'qwen-turbo',
|
|
217
257
|
'qwq-plus',
|
|
@@ -223,10 +263,14 @@ BailianProvider.SUPPORTED_MODELS = new Set([
|
|
|
223
263
|
]);
|
|
224
264
|
BailianProvider.MODEL_CAPABILITIES = {
|
|
225
265
|
'qwen-max': { jsonMode: true, reasoningEffort: false },
|
|
266
|
+
'qwen3.8-flash': { jsonMode: true, reasoningEffort: true },
|
|
226
267
|
'qwen3.7-max': { jsonMode: true, reasoningEffort: true },
|
|
268
|
+
'qwen3.7-flash': { jsonMode: true, reasoningEffort: true },
|
|
227
269
|
'qwen-plus': { jsonMode: true, reasoningEffort: true },
|
|
228
270
|
'qwen-turbo': { jsonMode: true, reasoningEffort: true },
|
|
229
271
|
'qwq-plus': { jsonMode: true, reasoningEffort: false },
|
|
272
|
+
// DeepSeek v4 系列在百炼走「混合思考模式」:由 enable_thinking 控制开关,
|
|
273
|
+
// 通过 adaptRequest 映射为 extra_body.enable_thinking(与 Qwen 同一分支)。
|
|
230
274
|
'deepseek-v4-pro': { jsonMode: true, reasoningEffort: true },
|
|
231
275
|
'deepseek-v4-flash': { jsonMode: true, reasoningEffort: true },
|
|
232
276
|
'kimi-k2.6': { jsonMode: true, reasoningEffort: true },
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* 支持的模型枚举。项目层通过此枚举选择模型,Axiom 内部路由到对应 Provider。
|
|
3
3
|
*/
|
|
4
|
-
export type Model = 'qwen3.7-max' | 'qwen-plus' | 'qwen-turbo' | 'qwq-plus' | 'deepseek-v4-pro' | 'deepseek-v4-flash' | 'kimi-k2.6' | 'glm-5.1' | 'qwen-vl-plus';
|
|
4
|
+
export type Model = 'qwen3.8-flash' | 'qwen3.7-max' | 'qwen3.7-flash' | 'qwen-plus' | 'qwen-turbo' | 'qwq-plus' | 'deepseek-v4-pro' | 'deepseek-v4-flash' | 'kimi-k2.6' | 'glm-5.1' | 'qwen-vl-plus';
|
|
5
5
|
/** 模型到 Provider 的映射配置 */
|
|
6
6
|
export interface ModelConfig {
|
|
7
7
|
readonly model: string;
|
|
@@ -16,7 +16,10 @@ export declare const MODEL_REGISTRY: Readonly<Record<Model, ReadonlyArray<ModelC
|
|
|
16
16
|
export interface ModelCapabilities {
|
|
17
17
|
/** 是否原生支持 response_format: { type: 'json_object' } */
|
|
18
18
|
readonly jsonMode: boolean;
|
|
19
|
-
/**
|
|
19
|
+
/**
|
|
20
|
+
* 是否支持调节推理深度。开启后由 Provider 的 `adaptRequest` 决定如何映射到底层字段
|
|
21
|
+
* (如 Qwen / DeepSeek → `extra_body.enable_thinking`、Kimi → `extra_body.thinking.type`)。
|
|
22
|
+
*/
|
|
20
23
|
readonly reasoningEffort: boolean;
|
|
21
24
|
}
|
|
22
25
|
/**
|
|
@@ -6,7 +6,9 @@ exports.MODEL_REGISTRY = void 0;
|
|
|
6
6
|
* 当首选 Provider 失败时,Predictor 按此表顺序尝试下一个。
|
|
7
7
|
*/
|
|
8
8
|
exports.MODEL_REGISTRY = {
|
|
9
|
+
'qwen3.8-flash': [{ model: 'qwen3.8-flash', provider: 'bailian' }],
|
|
9
10
|
'qwen3.7-max': [{ model: 'qwen3.7-max', provider: 'bailian' }],
|
|
11
|
+
'qwen3.7-flash': [{ model: 'qwen3.7-flash', provider: 'bailian' }],
|
|
10
12
|
'qwen-plus': [{ model: 'qwen-plus', provider: 'bailian' }],
|
|
11
13
|
'qwen-turbo': [{ model: 'qwen-turbo', provider: 'bailian' }],
|
|
12
14
|
'qwq-plus': [{ model: 'qwq-plus', provider: 'bailian' }],
|
|
@@ -52,7 +52,13 @@ export type StreamChunk = {
|
|
|
52
52
|
readonly delta: string;
|
|
53
53
|
} | {
|
|
54
54
|
readonly type: 'finish';
|
|
55
|
-
|
|
55
|
+
/** 上游返回的生成结束原因,例如 `stop`、`length` 或 `content_filter`。 */
|
|
56
|
+
readonly finishReason?: string;
|
|
57
|
+
/** SSE 流是否收到显式的 `data: [DONE]` 终止标记;未提供表示 Provider 不支持该观测。 */
|
|
58
|
+
readonly sawDone?: boolean;
|
|
59
|
+
/** SSE 流是否收到非空的 `finish_reason`;未提供表示 Provider 不支持该观测。 */
|
|
60
|
+
readonly sawFinishReason?: boolean;
|
|
61
|
+
readonly usage?: LLMResponse['usage'];
|
|
56
62
|
};
|
|
57
63
|
/**
|
|
58
64
|
* Provider 配置。每个 Provider 实例需要一组连接参数。
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@keo-ai/axiom",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.9",
|
|
4
4
|
"description": "基于 LLM 的预测与推理库,支持多 Provider 切换",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -10,7 +10,9 @@
|
|
|
10
10
|
"license": "MIT",
|
|
11
11
|
"scripts": {
|
|
12
12
|
"build": "tsc",
|
|
13
|
-
"
|
|
13
|
+
"typecheck": "tsc --noEmit",
|
|
14
|
+
"test": "vitest run --exclude '**/*.integration.test.ts'",
|
|
15
|
+
"test:integration": "vitest run --config vitest.integration.config.ts"
|
|
14
16
|
},
|
|
15
17
|
"dependencies": {
|
|
16
18
|
"dotenv": "^17.4.2"
|