magic-agent 1.0.3 → 1.0.49
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/CLIENT-CONTRACT.md +254 -8
- package/README.md +48 -5
- package/client-ui/index.html +32 -0
- package/client-ui/pc/app.css +89 -1
- package/client-ui/pc/index.html +18 -8
- package/client-ui/pc/js/00-base.js +8 -30
- package/client-ui/pc/js/05-task-list-render.js +4 -1
- package/client-ui/pc/js/07-chat-stream-render.js +2 -85
- package/client-ui/pc/js/10-markdown.js +3 -171
- package/client-ui/pc/js/18-askbox.js +2 -176
- package/client-ui/pc/js/90-glue.js +1 -1
- package/client-ui/pc/js/91-workbench-stubs.js +4 -10
- package/client-ui/pc/js/93-rail.js +10 -3
- package/client-ui/shared/conv/01-dom.js +52 -0
- package/client-ui/shared/conv/02-media-kinds.js +34 -0
- package/client-ui/shared/conv/03-chat-message.js +1561 -0
- package/client-ui/shared/conv/06-ask-answer.js +377 -0
- package/client-ui/shared/conv/07-stream-delegation.js +158 -0
- package/client-ui/{pc/js → shared/conv}/09-stream-apply.js +121 -6
- package/client-ui/shared/conv/10-markdown-render.js +252 -0
- package/client-ui/shared/conv/18-askbox-markup.js +185 -0
- package/client-ui/{pc/js → shared/conv}/19-attachments.js +365 -21
- package/client-ui/shared/conv/27-conv-ledger.js +381 -0
- package/client-ui/{pc/js → shared/conv}/29-msg-variants.js +112 -3
- package/client-ui/shared/conv/README.md +257 -0
- package/client-ui/shared/conv/conv.css +1638 -0
- package/client-ui/shared/conv/dom-map.md +170 -0
- package/client-ui/shared/conv/manifest.json +44 -0
- package/client-ui/shared/conversation.css +368 -190
- package/client-ui/shared/conversation.js +1722 -788
- package/client-ui/shared/host-glue.js +143 -0
- package/client-ui/shared/transport-http.js +130 -6
- package/client-ui-mobile/index.html +2417 -103
- package/cmd/magic-client/clientctl.go +273 -0
- package/cmd/magic-client/main.go +71 -5
- package/go.mod +6 -3
- package/go.sum +4 -1
- package/internal/agent/arkclaw.go +3 -2
- package/internal/agent/arkclaw_stream.go +126 -8
- package/internal/agent/arkclaw_stream_test.go +97 -15
- package/internal/agent/arkclaw_test.go +3 -3
- package/internal/agent/attachments.go +18 -5
- package/internal/agent/attachments_test.go +7 -1
- package/internal/agent/capability_test.go +11 -1
- package/internal/agent/claude.go +96 -158
- package/internal/agent/codebuddy.go +108 -133
- package/internal/agent/codebuddy_gateway.go +8 -4
- package/internal/agent/codebuddy_gateway_stream.go +82 -2
- package/internal/agent/codebuddy_gateway_test.go +236 -1
- package/internal/agent/codebuddy_stream_test.go +262 -0
- package/internal/agent/codex.go +50 -9
- package/internal/agent/codex_stream_test.go +30 -0
- package/internal/agent/codex_test.go +24 -0
- package/internal/agent/custom_models.go +109 -3
- package/internal/agent/custom_models_test.go +74 -0
- package/internal/agent/dsh.go +464 -130
- package/internal/agent/dsh_resume.go +258 -0
- package/internal/agent/dsh_sdk.go +68 -17
- package/internal/agent/dsh_sdk_test.go +102 -2
- package/internal/agent/dsh_test.go +409 -36
- package/internal/agent/engine.go +26 -6
- package/internal/agent/engine_base.go +46 -2
- package/internal/agent/engine_base_test.go +29 -0
- package/internal/agent/engine_test.go +101 -0
- package/internal/agent/env.go +69 -1
- package/internal/agent/itemtracker.go +271 -0
- package/internal/agent/launch.go +192 -0
- package/internal/agent/launch_test.go +206 -0
- package/internal/agent/launch_unix.go +15 -0
- package/internal/agent/launch_windows.go +24 -0
- package/internal/agent/limitnotice.go +103 -0
- package/internal/agent/limitnotice_test.go +211 -0
- package/internal/agent/llmengine.go +2 -2
- package/internal/agent/models.go +3 -2
- package/internal/agent/openclaw.go +4 -3
- package/internal/agent/openclaw_acp.go +7 -0
- package/internal/agent/openclaw_test.go +2 -2
- package/internal/agent/permission.go +100 -11
- package/internal/agent/permission_test.go +100 -36
- package/internal/agent/runcmd.go +13 -3
- package/internal/agent/runner.go +7 -1
- package/internal/agent/runner_test.go +1 -1
- package/internal/agent/stream.go +777 -43
- package/internal/agent/stream_test.go +3 -2
- package/internal/agent/stream_timeline_test.go +905 -0
- package/internal/agent/streamjson.go +36 -2
- package/internal/agent/toolkind.go +20 -1
- package/internal/agent/toolkind_test.go +7 -1
- package/internal/agent/tools_suffix_test.go +10 -3
- package/internal/agent/trae.go +5 -3
- package/internal/agent/workspace_test.go +52 -0
- package/internal/cli/ask.go +260 -33
- package/internal/cli/ask_test.go +15 -11
- package/internal/cli/attach_test.go +32 -20
- package/internal/cli/clientcmd.go +156 -0
- package/internal/cli/events.go +53 -8
- package/internal/cli/events_test.go +230 -0
- package/internal/cli/keepalive_test.go +101 -6
- package/internal/cli/permission_test.go +5 -1
- package/internal/cli/root.go +174 -32
- package/internal/cli/session.go +175 -28
- package/internal/cli/session_test.go +144 -5
- package/internal/cli/sessionlog.go +385 -38
- package/internal/cli/sessionlog_test.go +708 -0
- package/internal/cli/sessionlog_user_test.go +142 -1
- package/internal/cli/sessiontitle.go +141 -0
- package/internal/client/browse.go +456 -0
- package/internal/client/browse_test.go +199 -0
- package/internal/client/cors_test.go +35 -0
- package/internal/client/daemon.go +525 -0
- package/internal/client/daemon_test.go +212 -0
- package/internal/client/detach_unix.go +19 -0
- package/internal/client/detach_windows.go +27 -0
- package/internal/client/pair.go +3 -3
- package/internal/client/plugins.go +376 -0
- package/internal/client/plugins_test.go +223 -0
- package/internal/client/proxy.go +374 -0
- package/internal/client/proxy_test.go +212 -0
- package/internal/client/service.go +1464 -48
- package/internal/client/service_test.go +649 -206
- package/internal/client/session_events_test.go +130 -0
- package/internal/client/wsproxy.go +113 -0
- package/internal/client/wsproxy_test.go +102 -0
- package/internal/session/delete.go +151 -0
- package/internal/session/delete_test.go +103 -0
- package/internal/session/session.go +155 -20
- package/internal/session/session_append.go +79 -21
- package/internal/session/session_test.go +124 -10
- package/internal/session/sessionlog.go +572 -6
- package/internal/session/sessionlog_read_test.go +410 -0
- package/internal/session/title.go +486 -0
- package/internal/session/title_test.go +298 -0
- package/npm/bin/magic-agent.js +101 -10
- package/npm/bin/magic-client.js +118 -0
- package/npm/dist/darwin-arm64/magic-agent +0 -0
- package/npm/dist/darwin-arm64/magic-client +0 -0
- package/npm/dist/darwin-x64/magic-agent +0 -0
- package/npm/dist/darwin-x64/magic-client +0 -0
- package/npm/dist/linux-arm64/magic-agent +0 -0
- package/npm/dist/linux-arm64/magic-client +0 -0
- package/npm/dist/linux-x64/magic-agent +0 -0
- package/npm/dist/linux-x64/magic-client +0 -0
- package/npm/dist/win32-x64/magic-agent.exe +0 -0
- package/npm/dist/win32-x64/magic-client.exe +0 -0
- package/npm/install.js +36 -13
- package/npm/lib/platform.js +27 -5
- package/package.json +8 -3
- package/client-ui/pc/js/03-chat-message.js +0 -880
- package/client-ui/pc/js/06-ask-answer.js +0 -204
- /package/client-ui/{pc/js → shared/conv}/08-ask-event-map.js +0 -0
- /package/client-ui/{pc → shared}/vendor/highlight.min.js +0 -0
- /package/client-ui/{pc → shared}/vendor/marked.min.js +0 -0
- /package/client-ui/{pc → shared}/vendor/purify.min.js +0 -0
|
@@ -63,6 +63,30 @@ func TestCodexBuildArgsModel(t *testing.T) {
|
|
|
63
63
|
}
|
|
64
64
|
}
|
|
65
65
|
|
|
66
|
+
// 续接轮的模型不能走 `-m`:`codex exec resume` 的参数表里没有它(同 `-C` 一样会被 clap
|
|
67
|
+
// 以 `unexpected argument` 拒掉、exit 2、stdout 全空 → core 报「empty result · 0.0s」)。
|
|
68
|
+
// 改为配置覆写 `-c model="<m>"` —— codex 帮助里正是拿 model 当 `-c` 的例子。
|
|
69
|
+
func TestCodexResumeModelViaConfigOverride(t *testing.T) {
|
|
70
|
+
dir := t.TempDir()
|
|
71
|
+
w, log := argsCaptureCLI(t, dir, "codex", codexStubWithText("ok"))
|
|
72
|
+
e := &CodexEngine{BinPath: w.bin}
|
|
73
|
+
if _, err := e.Complete(context.Background(), Request{
|
|
74
|
+
SessionID: "th-1",
|
|
75
|
+
Model: "codex/MiniMax-M3",
|
|
76
|
+
Messages: []Message{{Role: "user", Content: "继续"}},
|
|
77
|
+
}); err != nil {
|
|
78
|
+
t.Fatalf("Complete: %v", err)
|
|
79
|
+
}
|
|
80
|
+
data, _ := readFile(log)
|
|
81
|
+
args := string(data)
|
|
82
|
+
if strings.Contains(args, "-m MiniMax-M3") {
|
|
83
|
+
t.Errorf("续接轮不该用 -m(resume 子命令没有这个 flag):%q", args)
|
|
84
|
+
}
|
|
85
|
+
if !strings.Contains(args, `-c model="MiniMax-M3"`) {
|
|
86
|
+
t.Errorf("续接轮的模型应走 -c model=…:%q", args)
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
66
90
|
func TestCodexSystemPrompt(t *testing.T) {
|
|
67
91
|
dir := t.TempDir()
|
|
68
92
|
w, log := argsCaptureCLI(t, dir, "codex", codexStubWithText("ok"))
|
|
@@ -46,6 +46,31 @@ const customModelsBackupSuffix = ".magic-agent.bak"
|
|
|
46
46
|
// customModelIDPrefix 自定义模型 id 前缀(App 与 CLI 共用这个约定)。
|
|
47
47
|
const customModelIDPrefix = "custom-local:"
|
|
48
48
|
|
|
49
|
+
// StripCustomLocalPrefix 去掉模型 id 的 "custom-local:" 前缀(仅用于 --engines / --contract
|
|
50
|
+
// 展示:codebuddy 系模型的噪点前缀,给客户端渲染用)。-m 实际传值时由
|
|
51
|
+
// codebuddyCore.modelArg 按裸名补回前缀,自定义模型依旧可用(见该函数的说明)。
|
|
52
|
+
// 不修改入参,返回新切片。
|
|
53
|
+
func StripCustomLocalPrefix(models []string) []string {
|
|
54
|
+
out := make([]string, 0, len(models))
|
|
55
|
+
for _, m := range models {
|
|
56
|
+
out = append(out, strings.TrimPrefix(m, customModelIDPrefix))
|
|
57
|
+
}
|
|
58
|
+
return out
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// StripCustomLocalPrefixCredits 同 StripCustomLocalPrefix,但作用于「模型 → 倍率」表。
|
|
62
|
+
// custom-local 模型本来就没有倍率(不进表),这里只为键名形态与 models 一致。
|
|
63
|
+
func StripCustomLocalPrefixCredits(credits map[string]string) map[string]string {
|
|
64
|
+
if len(credits) == 0 {
|
|
65
|
+
return credits
|
|
66
|
+
}
|
|
67
|
+
out := make(map[string]string, len(credits))
|
|
68
|
+
for k, v := range credits {
|
|
69
|
+
out[strings.TrimPrefix(k, customModelIDPrefix)] = v
|
|
70
|
+
}
|
|
71
|
+
return out
|
|
72
|
+
}
|
|
73
|
+
|
|
49
74
|
// syncCustomModelsFromApp 把 App 合并配置缓存(acc-product-config)里的
|
|
50
75
|
// `custom-local:*` 模型合并进 `<configDir>/models.json`,返回新增条数。
|
|
51
76
|
//
|
|
@@ -207,15 +232,96 @@ func collectCustomModels(node any, out map[string]map[string]any) {
|
|
|
207
232
|
// ensureCustomModelEndpoint 仅当本次请求用的是一个自定义模型时,才补一次端点。
|
|
208
233
|
//
|
|
209
234
|
// 为什么不止在 listModels 里同步:用户可能在 App 里刚加了一个自定义模型,就直接
|
|
210
|
-
// `-m custom-local:<新>`
|
|
211
|
-
// best-effort:不同步失败也不影响正常模型(官方模型根本不读 models.json)。
|
|
235
|
+
// `-m custom-local:<新>` 或 `-m <裸名>` 调用(没打开过模型选择器)。这条兜底让那种
|
|
236
|
+
// 路径也能用上。best-effort:不同步失败也不影响正常模型(官方模型根本不读 models.json)。
|
|
212
237
|
func (c codebuddyCore) ensureCustomModelEndpoint(model string) {
|
|
213
|
-
|
|
238
|
+
m := stripModelPrefix(model)
|
|
239
|
+
// 已是 custom-local: 前缀(用户直传全 id),或裸名命中自定义模型 → 需要补端点。
|
|
240
|
+
if !strings.HasPrefix(m, customModelIDPrefix) && c.resolveCustomModelID(model) == "" {
|
|
214
241
|
return
|
|
215
242
|
}
|
|
216
243
|
_, _ = syncCustomModelsFromApp(c.accConfigPath, c.effectiveConfigDir())
|
|
217
244
|
}
|
|
218
245
|
|
|
246
|
+
// resolveCustomModelID 把可能带 "engine/" 或 "custom-local:" 前缀的模型名归一成
|
|
247
|
+
// 自定义模型 id("custom-local:<裸名>")。裸名命中 acc 缓存里的自定义模型、且**不是**
|
|
248
|
+
// 同缓存里的官方模型时才补回前缀 —— 避免把官方模型(如 glm-5.3)误改写成
|
|
249
|
+
// custom-local:glm-5.3(用户自定义了一个同名模型时的退化处理:优先官方)。
|
|
250
|
+
// 命中返回该 id,否则返回 ""(不是自定义模型,不补前缀)。
|
|
251
|
+
//
|
|
252
|
+
// 用途:--engines 展示时剥掉了 custom-local: 前缀(见 StripCustomLocalPrefix),用户
|
|
253
|
+
// 选了之后回传的是裸名;-m 需要带前缀才能真正命中自定义模型端点,故在此补回。
|
|
254
|
+
func (c codebuddyCore) resolveCustomModelID(model string) string {
|
|
255
|
+
name := stripModelPrefix(model)
|
|
256
|
+
name = strings.TrimPrefix(name, customModelIDPrefix)
|
|
257
|
+
if name == "" {
|
|
258
|
+
return ""
|
|
259
|
+
}
|
|
260
|
+
customID := customModelIDPrefix + name
|
|
261
|
+
// 同份 acc 缓存里递归收集全部 id(含嵌套、官方与 custom-local:*):裸名若也是官方模型,
|
|
262
|
+
// 优先官方、不补前缀;仅当它是「纯自定义」(无同名官方)时才补回 custom-local:。
|
|
263
|
+
all := readAllAccModelIDs(c.accConfigPath)
|
|
264
|
+
if all[customID] && !all[name] {
|
|
265
|
+
return customID
|
|
266
|
+
}
|
|
267
|
+
return ""
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
// collectAllModelIDs 递归遍历 acc 配置缓存,收集所有模型的 id(官方与 custom-local:*
|
|
271
|
+
// 都收,先到先得)。acc 缓存里官方模型常常嵌在嵌套结构里,故必须递归而非只读顶层。
|
|
272
|
+
func collectAllModelIDs(node any, out map[string]bool) {
|
|
273
|
+
switch v := node.(type) {
|
|
274
|
+
case map[string]any:
|
|
275
|
+
if id, ok := v["id"].(string); ok && id != "" {
|
|
276
|
+
out[id] = true
|
|
277
|
+
}
|
|
278
|
+
for _, child := range v {
|
|
279
|
+
collectAllModelIDs(child, out)
|
|
280
|
+
}
|
|
281
|
+
case []any:
|
|
282
|
+
for _, child := range v {
|
|
283
|
+
collectAllModelIDs(child, out)
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
// readAllAccModelIDs 返回 acc 配置缓存里出现过的全部模型 id 集合(递归,不区分官方 / 自定义)。
|
|
289
|
+
// 文件不存在 / 解析失败 → 空集。
|
|
290
|
+
func readAllAccModelIDs(accConfigPath string) map[string]bool {
|
|
291
|
+
out := map[string]bool{}
|
|
292
|
+
if accConfigPath == "" {
|
|
293
|
+
return out
|
|
294
|
+
}
|
|
295
|
+
b, err := os.ReadFile(accConfigPath)
|
|
296
|
+
if err != nil {
|
|
297
|
+
return out
|
|
298
|
+
}
|
|
299
|
+
var doc any
|
|
300
|
+
if err := json.Unmarshal(b, &doc); err != nil {
|
|
301
|
+
return out
|
|
302
|
+
}
|
|
303
|
+
collectAllModelIDs(doc, out)
|
|
304
|
+
return out
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
// modelArg 返回传给 CLI --model 的值。
|
|
308
|
+
//
|
|
309
|
+
// --engines 展示时剥掉了 custom-local: 前缀(见 StripCustomLocalPrefix),用户选了
|
|
310
|
+
// 自定义模型后回传裸名;这里命中自定义模型就补回 custom-local: 前缀,保证 -m 可用。
|
|
311
|
+
func (c codebuddyCore) modelArg(model string) string {
|
|
312
|
+
m := stripModelPrefix(model)
|
|
313
|
+
if m == "" {
|
|
314
|
+
return ""
|
|
315
|
+
}
|
|
316
|
+
if strings.HasPrefix(m, customModelIDPrefix) {
|
|
317
|
+
return m
|
|
318
|
+
}
|
|
319
|
+
if id := c.resolveCustomModelID(m); id != "" {
|
|
320
|
+
return id
|
|
321
|
+
}
|
|
322
|
+
return m
|
|
323
|
+
}
|
|
324
|
+
|
|
219
325
|
// effectiveConfigDir 本次子进程实际使用的配置目录:用户显式设了 CODEBUDDY_CONFIG_DIR
|
|
220
326
|
// 就跟着走(CLI 也这么读,见 codebuddyAccountEnv),否则用引擎自带的那个。
|
|
221
327
|
func (c codebuddyCore) effectiveConfigDir() string {
|
|
@@ -56,6 +56,80 @@ func appCacheFixture() map[string]any {
|
|
|
56
56
|
}
|
|
57
57
|
}
|
|
58
58
|
|
|
59
|
+
// StripCustomLocalPrefix 剥掉自定义模型 id 的 "custom-local:" 前缀(仅展示用)。
|
|
60
|
+
func TestStripCustomLocalPrefix(t *testing.T) {
|
|
61
|
+
got := StripCustomLocalPrefix([]string{"hy3", "custom-local:MiniMax-M3", "custom-local:glm-5.3"})
|
|
62
|
+
want := []string{"hy3", "MiniMax-M3", "glm-5.3"}
|
|
63
|
+
if len(got) != len(want) {
|
|
64
|
+
t.Fatalf("StripCustomLocalPrefix = %v want %v", got, want)
|
|
65
|
+
}
|
|
66
|
+
for i := range want {
|
|
67
|
+
if got[i] != want[i] {
|
|
68
|
+
t.Errorf("StripCustomLocalPrefix[%d] = %q want %q (full %v)", i, got[i], want[i], got)
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
c := StripCustomLocalPrefixCredits(map[string]string{"hy3": "0.00", "custom-local:X": "0.34"})
|
|
72
|
+
if c["custom-local:X"] != "" || c["X"] != "0.34" || c["hy3"] != "0.00" {
|
|
73
|
+
t.Errorf("StripCustomLocalPrefixCredits = %v", c)
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// resolveCustomModelID:裸名命中纯自定义模型时补回 custom-local: 前缀;
|
|
78
|
+
// 裸名同时是官方模型时**不补**(优先官方,避免把 glm-5.3 误写成 custom-local:glm-5.3)。
|
|
79
|
+
func TestResolveCustomModelID(t *testing.T) {
|
|
80
|
+
dir := t.TempDir()
|
|
81
|
+
acc := filepath.Join(dir, "acc.json")
|
|
82
|
+
writeJSON(t, acc, map[string]any{"models": []any{
|
|
83
|
+
map[string]any{"id": "glm-5.3"},
|
|
84
|
+
map[string]any{"id": "custom-local:MiniMax-M3"},
|
|
85
|
+
// 嵌套结构里的官方模型也要能识别(readAllAccModelIDs 递归收集)。
|
|
86
|
+
map[string]any{"wrapped": map[string]any{"id": "deepseek-v4.1-flash"}},
|
|
87
|
+
}})
|
|
88
|
+
c := codebuddyCore{accConfigPath: acc}
|
|
89
|
+
|
|
90
|
+
if got := c.resolveCustomModelID("MiniMax-M3"); got != "custom-local:MiniMax-M3" {
|
|
91
|
+
t.Errorf("resolve MiniMax-M3 = %q want custom-local:MiniMax-M3", got)
|
|
92
|
+
}
|
|
93
|
+
if got := c.resolveCustomModelID("glm-5.3"); got != "" {
|
|
94
|
+
t.Errorf("resolve glm-5.3 = %q want \"\"(优先官方模型,不补前缀)", got)
|
|
95
|
+
}
|
|
96
|
+
// 已带前缀:原样返回。
|
|
97
|
+
if got := c.resolveCustomModelID("custom-local:MiniMax-M3"); got != "custom-local:MiniMax-M3" {
|
|
98
|
+
t.Errorf("resolve custom-local:MiniMax-M3 = %q", got)
|
|
99
|
+
}
|
|
100
|
+
// 既不是官方也不是自定义 → 不补。
|
|
101
|
+
if got := c.resolveCustomModelID("nonexistent-model"); got != "" {
|
|
102
|
+
t.Errorf("resolve nonexistent = %q want \"\"", got)
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// modelArg:传给 CLI --model 的值。展示时剥了前缀的自定义模型裸名要补回前缀。
|
|
107
|
+
func TestCodeBuddyModelArg(t *testing.T) {
|
|
108
|
+
dir := t.TempDir()
|
|
109
|
+
acc := filepath.Join(dir, "acc.json")
|
|
110
|
+
writeJSON(t, acc, map[string]any{"models": []any{
|
|
111
|
+
map[string]any{"id": "glm-5.3"},
|
|
112
|
+
map[string]any{"id": "custom-local:MiniMax-M3"},
|
|
113
|
+
}})
|
|
114
|
+
c := codebuddyCore{accConfigPath: acc}
|
|
115
|
+
|
|
116
|
+
cases := []struct {
|
|
117
|
+
in, want string
|
|
118
|
+
}{
|
|
119
|
+
{"hy3", "hy3"}, // 官方:原样
|
|
120
|
+
{"codebuddy/hy3", "hy3"}, // 带 engine/ 前缀:剥掉
|
|
121
|
+
{"glm-5.3", "glm-5.3"}, // 同名官方:不补前缀
|
|
122
|
+
{"MiniMax-M3", "custom-local:MiniMax-M3"}, // 自定义裸名:补前缀
|
|
123
|
+
{"custom-local:MiniMax-M3", "custom-local:MiniMax-M3"}, // 已带前缀:原样
|
|
124
|
+
{"", ""}, // 空:空
|
|
125
|
+
}
|
|
126
|
+
for _, tc := range cases {
|
|
127
|
+
if got := c.modelArg(tc.in); got != tc.want {
|
|
128
|
+
t.Errorf("modelArg(%q) = %q want %q", tc.in, got, tc.want)
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
59
133
|
func TestSyncCustomModelsFromApp_FirstSync(t *testing.T) {
|
|
60
134
|
dir := t.TempDir()
|
|
61
135
|
acc := filepath.Join(dir, "acc-product-config-v3.json")
|