magpie-marvis 0.2.0 → 0.2.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/CHANGELOG.md +10 -1
- package/package.json +1 -1
- package/src/context.mjs +6 -4
- package/src/hooks/config.mjs +71 -6
- package/tests/mount.test.mjs +49 -0
package/CHANGELOG.md
CHANGED
|
@@ -3,7 +3,16 @@
|
|
|
3
3
|
> 每个版本一节,**一条一行**,说用户能感知的变化。
|
|
4
4
|
> 格式与规矩见 [UPDATING.md](./UPDATING.md)。
|
|
5
5
|
|
|
6
|
-
## 0.2.
|
|
6
|
+
## 0.2.1 — 2026-11-06
|
|
7
|
+
|
|
8
|
+
**变更**
|
|
9
|
+
|
|
10
|
+
- **上下文窗口声明改为 1,000,000**(除 `hunyuan-turbos-vision` 仍 32k)。
|
|
11
|
+
依据:实测 `main-auto` 发 **400,000 字符 = 190,511 tokens** 返回 200,
|
|
12
|
+
远超 `config_defaults.yaml` 声明的 128k —— 那个 128k 是客户端自己的保守值
|
|
13
|
+
- **用户设置优先**:`config()` 不再整块覆盖 provider。改成**逐模型、逐字段合并** ——
|
|
14
|
+
用户在 magpie 设置里改过的 `limit.context` / `limit.output` / `name` / `api` 一律保留,
|
|
15
|
+
插件只补缺失字段和新增模型
|
|
7
16
|
|
|
8
17
|
**修复(逆向客户端主进程后对齐)**
|
|
9
18
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "magpie-marvis",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.1",
|
|
4
4
|
"description": "magpie / OpenCode provider plugin for Tencent Marvis: its DeepSeek V4 Pro, Kimi K2.5, Claude Opus/Sonnet 4.6, GPT-5.4 and Hunyuan models through the Marvis model gateway — no API key to paste, sign in with QQ or WeChat.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./index.mjs",
|
package/src/context.mjs
CHANGED
|
@@ -69,6 +69,8 @@ const DEFAULT_OUTPUT = 32_000
|
|
|
69
69
|
* 不猜、不补。`local_model` / `warmup_model` / `*placeholder` 是内部占位,不对外。
|
|
70
70
|
*/
|
|
71
71
|
const TABLE = [
|
|
72
|
+
// ⚠ 上下文窗口:实测 main-auto 发 400,000 字符(190,511 tokens)返回 200,
|
|
73
|
+
// 已远超 config_defaults.yaml 声明的 128k —— 所以按 1M 声明(用户要求,出问题再调)。
|
|
72
74
|
// ⚠ 2026-11-06 逐槽位实测(每槽连发 5 次、5 种不同难度提示词,路由 100% 稳定)。
|
|
73
75
|
// 显示名 = **真实模型名**,槽位名写进 id —— 卡片上看到的就是实际在跑什么。
|
|
74
76
|
// ⚠ 真实模型 id 不能直接当 `model` 传(会 400 无效模型id),只能填槽位名。
|
|
@@ -82,12 +84,12 @@ const TABLE = [
|
|
|
82
84
|
// kimi-k2.5(500 RPC)· hunyuan-2.0-instruct-20251111(空响应)· venus-vision-llm(→ default)
|
|
83
85
|
|
|
84
86
|
// ── DeepSeek ────────────────────────────────────────────────
|
|
85
|
-
["main-auto", "deepseek-v4-1-flash-261017",
|
|
86
|
-
["deepseek-v4-pro-external", "deepseek-v4-pro-202606",
|
|
87
|
-
["hy3-preview-agent", "deepseek-v4-flash-ga-260731",
|
|
87
|
+
["main-auto", "deepseek-v4-1-flash-261017", 1_000_000, { reasoning: true, tools: true }],
|
|
88
|
+
["deepseek-v4-pro-external", "deepseek-v4-pro-202606", 1_000_000, { reasoning: true, tools: true }],
|
|
89
|
+
["hy3-preview-agent", "deepseek-v4-flash-ga-260731", 1_000_000, { reasoning: true, tools: true }],
|
|
88
90
|
|
|
89
91
|
// ── 其他厂商 ─────────────────────────────────────────────────
|
|
90
|
-
["hy3-preview-agent-real", "hy3",
|
|
92
|
+
["hy3-preview-agent-real", "hy3", 1_000_000, { reasoning: true, tools: true }],
|
|
91
93
|
["claude-sonnet-4-6", "claude-sonnet-4-6", 1_000_000, { reasoning: true, tools: true, vision: true }],
|
|
92
94
|
["claude-opus-4-6", "claude-opus-4-6", 1_000_000, { reasoning: true, tools: true, vision: true }],
|
|
93
95
|
["gpt-5.4", "gpt-5.4-2026-03-05", 1_000_000, { reasoning: true, tools: true, vision: true }],
|
package/src/hooks/config.mjs
CHANGED
|
@@ -2,18 +2,83 @@
|
|
|
2
2
|
//
|
|
3
3
|
// ⚠ `config.models` 必须有内容:它是**没人登录时**模型列表的唯一来源,
|
|
4
4
|
// 空了就是「插件装上了但显示 0 models」。
|
|
5
|
-
//
|
|
5
|
+
//
|
|
6
|
+
// ⚠⚠ **用户在 magpie 设置里改过的值优先,插件不许覆盖。**
|
|
7
|
+
// 宿主把用户改过的 provider 配置原样传进来(`cfg.provider[PROVIDER]`),
|
|
8
|
+
// 我们要做的是「**补齐缺失**」,不是「重置成默认」。
|
|
9
|
+
// 踩过的坑:早期直接 `??=` 整个 provider —— 用户改了上下文窗口,
|
|
10
|
+
// 插件一加载又变回默认值,用户以为没生效。
|
|
11
|
+
//
|
|
12
|
+
// 合并规则(逐模型、逐字段):
|
|
13
|
+
// · 用户有的字段 → 保留用户的(`limit.context` / `limit.output` / `capabilities` / `name` …)
|
|
14
|
+
// · 用户没有的字段 → 用默认值补
|
|
15
|
+
// · 用户删掉的模型 → 不恢复(尊重用户意图)
|
|
16
|
+
// · 默认表里有、用户配置里没有的模型 → 追加(新版本新增模型能自动出现)
|
|
6
17
|
|
|
7
18
|
import { PROVIDER, PROVIDER_NAME, SDK, BASE_V1, staticModels } from "../context.mjs"
|
|
8
19
|
|
|
20
|
+
/** 合并一个模型:用户字段优先,缺的用默认值补 */
|
|
21
|
+
function mergeModel(userModel, defaultModel) {
|
|
22
|
+
const merged = { ...defaultModel, ...userModel }
|
|
23
|
+
|
|
24
|
+
// limit 逐字段合并 —— 只改 context 的用户不该把 output 也吃掉
|
|
25
|
+
merged.limit = { ...(defaultModel.limit ?? {}), ...(userModel.limit ?? {}) }
|
|
26
|
+
|
|
27
|
+
// config 形状用 modalities 数组 + tool_call 下划线;用户可能只改了其中一个
|
|
28
|
+
if (userModel.modalities || defaultModel.modalities) {
|
|
29
|
+
merged.modalities = {
|
|
30
|
+
...(defaultModel.modalities ?? {}),
|
|
31
|
+
...(userModel.modalities ?? {}),
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
return merged
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* 合并模型表。
|
|
39
|
+
* @param {any[]} defaults 插件内置的默认表
|
|
40
|
+
* @param {any[] | undefined} user 宿主里用户改过的表
|
|
41
|
+
* @returns {any[]}
|
|
42
|
+
*/
|
|
43
|
+
export function mergeModels(defaults, user) {
|
|
44
|
+
if (!Array.isArray(user) || user.length === 0) return defaults
|
|
45
|
+
|
|
46
|
+
const byId = new Map(user.map((m) => [m?.id, m]).filter(([id]) => Boolean(id)))
|
|
47
|
+
const out = []
|
|
48
|
+
|
|
49
|
+
// 按用户顺序,逐条合并
|
|
50
|
+
for (const um of user) {
|
|
51
|
+
if (!um?.id) continue
|
|
52
|
+
const dm = defaults.find((d) => d.id === um.id)
|
|
53
|
+
out.push(dm ? mergeModel(um, dm) : um)
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// 默认表里有、用户没有的 → 追加(新版本新增的模型)
|
|
57
|
+
for (const dm of defaults) {
|
|
58
|
+
if (!byId.has(dm.id)) out.push(dm)
|
|
59
|
+
}
|
|
60
|
+
return out
|
|
61
|
+
}
|
|
62
|
+
|
|
9
63
|
export function configHook() {
|
|
10
64
|
return async (cfg) => {
|
|
11
65
|
cfg.provider ??= {}
|
|
12
|
-
cfg.provider[PROVIDER]
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
66
|
+
const existing = cfg.provider[PROVIDER]
|
|
67
|
+
|
|
68
|
+
if (!existing) {
|
|
69
|
+
cfg.provider[PROVIDER] = {
|
|
70
|
+
name: PROVIDER_NAME,
|
|
71
|
+
npm: SDK,
|
|
72
|
+
api: BASE_V1,
|
|
73
|
+
models: staticModels(),
|
|
74
|
+
}
|
|
75
|
+
return
|
|
17
76
|
}
|
|
77
|
+
|
|
78
|
+
// 已有配置:只补缺,用户改过的优先
|
|
79
|
+
existing.name ??= PROVIDER_NAME
|
|
80
|
+
existing.npm ??= SDK
|
|
81
|
+
existing.api ??= BASE_V1
|
|
82
|
+
existing.models = mergeModels(staticModels(), existing.models)
|
|
18
83
|
}
|
|
19
84
|
}
|
package/tests/mount.test.mjs
CHANGED
|
@@ -175,3 +175,52 @@ test("native 目录 4 个文件必须都在(缺一个就静默返回 null)",
|
|
|
175
175
|
assert.ok(existsSync(join(dir, f)), `native/${f} 必须打包(否则 getUskey() 静默返回 null)`)
|
|
176
176
|
}
|
|
177
177
|
})
|
|
178
|
+
|
|
179
|
+
test("config() 合并:用户改过的值优先,插件只补缺", async () => {
|
|
180
|
+
const { mergeModels } = await import("../src/hooks/config.mjs")
|
|
181
|
+
const { staticModels } = await import("../src/context.mjs")
|
|
182
|
+
const defaults = staticModels()
|
|
183
|
+
|
|
184
|
+
// 用户把 main-auto 的上下文改成 2M,并删掉了 hunyuan-turbos-vision
|
|
185
|
+
const user = defaults
|
|
186
|
+
.filter((m) => m.id !== "hunyuan-turbos-vision")
|
|
187
|
+
.map((m) => (m.id === "main-auto" ? { ...m, limit: { ...m.limit, context: 2_000_000 } } : m))
|
|
188
|
+
|
|
189
|
+
const merged = mergeModels(defaults, user)
|
|
190
|
+
const main = merged.find((m) => m.id === "main-auto")
|
|
191
|
+
|
|
192
|
+
assert.equal(main.limit.context, 2_000_000, "用户设的 2M 必须保留")
|
|
193
|
+
assert.equal(typeof main.limit.output, "number", "用户没改的 output 不能被吃掉")
|
|
194
|
+
assert.ok(merged.some((m) => m.id === "hunyuan-turbos-vision"), "默认表里有、用户没有的模型要追加回来")
|
|
195
|
+
})
|
|
196
|
+
|
|
197
|
+
test("config() 已有 provider 时不重置用户配置", async () => {
|
|
198
|
+
const { MarvisAuthPlugin } = await import("../index.mjs")
|
|
199
|
+
const plugin = await MarvisAuthPlugin({})
|
|
200
|
+
const cfg = {
|
|
201
|
+
provider: {
|
|
202
|
+
marvis: {
|
|
203
|
+
name: "我改的名字",
|
|
204
|
+
npm: "@ai-sdk/openai-compatible",
|
|
205
|
+
api: "https://custom.example.com/v1",
|
|
206
|
+
models: [
|
|
207
|
+
{
|
|
208
|
+
id: "main-auto",
|
|
209
|
+
name: "我的 V4.1 Flash",
|
|
210
|
+
limit: { context: 2_000_000, output: 64_000 },
|
|
211
|
+
modalities: { input: ["text"], output: ["text"] },
|
|
212
|
+
tool_call: true,
|
|
213
|
+
attachment: false,
|
|
214
|
+
reasoning: true,
|
|
215
|
+
},
|
|
216
|
+
],
|
|
217
|
+
},
|
|
218
|
+
},
|
|
219
|
+
}
|
|
220
|
+
await plugin.config(cfg)
|
|
221
|
+
const p = cfg.provider.marvis
|
|
222
|
+
assert.equal(p.name, "我改的名字", "provider 名不能被我覆盖")
|
|
223
|
+
assert.equal(p.api, "https://custom.example.com/v1", "api 不能被我覆盖")
|
|
224
|
+
assert.equal(p.models.find((m) => m.id === "main-auto").limit.context, 2_000_000, "上下文不能被我覆盖")
|
|
225
|
+
assert.equal(p.models.find((m) => m.id === "main-auto").name, "我的 V4.1 Flash")
|
|
226
|
+
})
|