mimo2codex 0.1.3 → 0.1.5
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/AGENTS.md +20 -5
- package/README.md +139 -14
- package/README.zh.md +140 -15
- package/dist/admin/router.js +288 -0
- package/dist/admin/router.js.map +1 -0
- package/dist/cli.js +83 -7
- package/dist/cli.js.map +1 -1
- package/dist/config.js +89 -10
- package/dist/config.js.map +1 -1
- package/dist/db/dataDir.js +14 -0
- package/dist/db/dataDir.js.map +1 -0
- package/dist/db/index.js +110 -0
- package/dist/db/index.js.map +1 -0
- package/dist/db/logs.js +94 -0
- package/dist/db/logs.js.map +1 -0
- package/dist/db/models.js +114 -0
- package/dist/db/models.js.map +1 -0
- package/dist/db/schema.js +74 -0
- package/dist/db/schema.js.map +1 -0
- package/dist/db/settings.js +44 -0
- package/dist/db/settings.js.map +1 -0
- package/dist/providers/deepseek.js +76 -0
- package/dist/providers/deepseek.js.map +1 -0
- package/dist/providers/mimo.js +88 -0
- package/dist/providers/mimo.js.map +1 -0
- package/dist/providers/registry.js +25 -0
- package/dist/providers/registry.js.map +1 -0
- package/dist/providers/types.js +2 -0
- package/dist/providers/types.js.map +1 -0
- package/dist/server.js +281 -67
- package/dist/server.js.map +1 -1
- package/dist/upstream/{mimoClient.js → openaiCompatClient.js} +21 -32
- package/dist/upstream/openaiCompatClient.js.map +1 -0
- package/dist/web/assets/index-DAJbSznk.css +1 -0
- package/dist/web/assets/index-DwvEnXbj.js +67 -0
- package/dist/web/index.html +13 -0
- package/package.json +9 -3
- package/dist/upstream/mimoClient.js.map +0 -1
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { homedir } from "node:os";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import { mkdirSync } from "node:fs";
|
|
4
|
+
const DEFAULT_DIR_NAME = ".mimo2codex";
|
|
5
|
+
// Resolve the data directory for sqlite + future config files. Priority:
|
|
6
|
+
// 1. explicit cliOverride (--data-dir)
|
|
7
|
+
// 2. MIMO2CODEX_DATA_DIR env var
|
|
8
|
+
// 3. ~/.mimo2codex
|
|
9
|
+
export function resolveDataDir(cliOverride, env = process.env) {
|
|
10
|
+
const dir = cliOverride ?? env.MIMO2CODEX_DATA_DIR ?? join(homedir(), DEFAULT_DIR_NAME);
|
|
11
|
+
mkdirSync(dir, { recursive: true });
|
|
12
|
+
return dir;
|
|
13
|
+
}
|
|
14
|
+
//# sourceMappingURL=dataDir.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dataDir.js","sourceRoot":"","sources":["../../src/db/dataDir.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAEpC,MAAM,gBAAgB,GAAG,aAAa,CAAC;AAEvC,yEAAyE;AACzE,yCAAyC;AACzC,mCAAmC;AACnC,qBAAqB;AACrB,MAAM,UAAU,cAAc,CAC5B,WAA+B,EAC/B,MAAyB,OAAO,CAAC,GAAG;IAEpC,MAAM,GAAG,GAAG,WAAW,IAAI,GAAG,CAAC,mBAAmB,IAAI,IAAI,CAAC,OAAO,EAAE,EAAE,gBAAgB,CAAC,CAAC;IACxF,SAAS,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACpC,OAAO,GAAG,CAAC;AACb,CAAC"}
|
package/dist/db/index.js
ADDED
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import Database from "better-sqlite3";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import { MIGRATIONS } from "./schema.js";
|
|
4
|
+
import { PROVIDER_LIST } from "../providers/registry.js";
|
|
5
|
+
import { log } from "../util/log.js";
|
|
6
|
+
let instance = null;
|
|
7
|
+
let instancePath = null;
|
|
8
|
+
export function openDb(dataDir) {
|
|
9
|
+
const dbPath = join(dataDir, "data.db");
|
|
10
|
+
if (instance && instancePath === dbPath)
|
|
11
|
+
return instance;
|
|
12
|
+
if (instance) {
|
|
13
|
+
instance.close();
|
|
14
|
+
instance = null;
|
|
15
|
+
}
|
|
16
|
+
const db = new Database(dbPath);
|
|
17
|
+
db.pragma("journal_mode = WAL");
|
|
18
|
+
db.pragma("foreign_keys = ON");
|
|
19
|
+
applyMigrations(db);
|
|
20
|
+
seedBuiltins(db);
|
|
21
|
+
instance = db;
|
|
22
|
+
instancePath = dbPath;
|
|
23
|
+
log.debug(`sqlite opened at ${dbPath}`);
|
|
24
|
+
return db;
|
|
25
|
+
}
|
|
26
|
+
export function closeDb() {
|
|
27
|
+
if (instance) {
|
|
28
|
+
instance.close();
|
|
29
|
+
instance = null;
|
|
30
|
+
instancePath = null;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
export function getDb() {
|
|
34
|
+
if (!instance) {
|
|
35
|
+
throw new Error("db not opened — call openDb(dataDir) before getDb()");
|
|
36
|
+
}
|
|
37
|
+
return instance;
|
|
38
|
+
}
|
|
39
|
+
function applyMigrations(db) {
|
|
40
|
+
db.exec(`CREATE TABLE IF NOT EXISTS schema_version (
|
|
41
|
+
version INTEGER PRIMARY KEY,
|
|
42
|
+
applied_at INTEGER NOT NULL
|
|
43
|
+
);`);
|
|
44
|
+
const row = db.prepare("SELECT MAX(version) AS v FROM schema_version").get();
|
|
45
|
+
const current = row.v ?? 0;
|
|
46
|
+
for (const m of MIGRATIONS) {
|
|
47
|
+
if (m.version <= current)
|
|
48
|
+
continue;
|
|
49
|
+
db.exec(m.sql);
|
|
50
|
+
db.prepare("INSERT INTO schema_version (version, applied_at) VALUES (?, ?)").run(m.version, Date.now());
|
|
51
|
+
log.debug(`applied schema migration v${m.version}`);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
function seedBuiltins(db) {
|
|
55
|
+
const upsertProvider = db.prepare(`
|
|
56
|
+
INSERT INTO providers (id, shortcut, display_name, base_url, default_model, api_key_env, updated_at)
|
|
57
|
+
VALUES (@id, @shortcut, @display_name, @base_url, @default_model, @api_key_env, @updated_at)
|
|
58
|
+
ON CONFLICT(id) DO NOTHING
|
|
59
|
+
`);
|
|
60
|
+
const upsertModel = db.prepare(`
|
|
61
|
+
INSERT INTO models (
|
|
62
|
+
provider_id, upstream_id, display_name,
|
|
63
|
+
supports_images, supports_reasoning, supports_web_search,
|
|
64
|
+
context_window, is_builtin, deprecated_after, sort_order
|
|
65
|
+
) VALUES (
|
|
66
|
+
@provider_id, @upstream_id, @display_name,
|
|
67
|
+
@supports_images, @supports_reasoning, @supports_web_search,
|
|
68
|
+
@context_window, 1, @deprecated_after, @sort_order
|
|
69
|
+
)
|
|
70
|
+
ON CONFLICT(provider_id, upstream_id) DO NOTHING
|
|
71
|
+
`);
|
|
72
|
+
const upsertAlias = db.prepare(`
|
|
73
|
+
INSERT INTO model_aliases (alias, provider_id, upstream_id)
|
|
74
|
+
VALUES (@alias, @provider_id, @upstream_id)
|
|
75
|
+
ON CONFLICT(alias) DO NOTHING
|
|
76
|
+
`);
|
|
77
|
+
const now = Date.now();
|
|
78
|
+
const tx = db.transaction(() => {
|
|
79
|
+
for (const p of PROVIDER_LIST) {
|
|
80
|
+
upsertProvider.run({
|
|
81
|
+
id: p.id,
|
|
82
|
+
shortcut: p.shortcut,
|
|
83
|
+
display_name: p.displayName,
|
|
84
|
+
base_url: p.defaultBaseUrl,
|
|
85
|
+
default_model: p.defaultModel,
|
|
86
|
+
api_key_env: p.envKeys.join(","),
|
|
87
|
+
updated_at: now,
|
|
88
|
+
});
|
|
89
|
+
let order = 0;
|
|
90
|
+
for (const m of p.builtinModels) {
|
|
91
|
+
upsertModel.run({
|
|
92
|
+
provider_id: p.id,
|
|
93
|
+
upstream_id: m.id,
|
|
94
|
+
display_name: m.displayName ?? null,
|
|
95
|
+
supports_images: m.supportsImages ? 1 : 0,
|
|
96
|
+
supports_reasoning: m.supportsReasoning ? 1 : 0,
|
|
97
|
+
supports_web_search: m.supportsWebSearch ? 1 : 0,
|
|
98
|
+
context_window: m.contextWindow ?? null,
|
|
99
|
+
deprecated_after: m.deprecatedAfter ?? null,
|
|
100
|
+
sort_order: order++,
|
|
101
|
+
});
|
|
102
|
+
for (const alias of m.aliases ?? []) {
|
|
103
|
+
upsertAlias.run({ alias, provider_id: p.id, upstream_id: m.id });
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
});
|
|
108
|
+
tx();
|
|
109
|
+
}
|
|
110
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/db/index.ts"],"names":[],"mappings":"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"}
|
package/dist/db/logs.js
ADDED
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { getDb } from "./index.js";
|
|
2
|
+
const MAX_SNIPPET = 500;
|
|
3
|
+
export function insertLog(entry) {
|
|
4
|
+
const snippet = entry.error_snippet
|
|
5
|
+
? entry.error_snippet.length > MAX_SNIPPET
|
|
6
|
+
? entry.error_snippet.slice(0, MAX_SNIPPET) + "…"
|
|
7
|
+
: entry.error_snippet
|
|
8
|
+
: null;
|
|
9
|
+
getDb()
|
|
10
|
+
.prepare(`INSERT INTO chat_logs (
|
|
11
|
+
ts, request_id, provider_id, client_model, upstream_model,
|
|
12
|
+
endpoint, status_code, duration_ms,
|
|
13
|
+
prompt_tokens, completion_tokens, total_tokens,
|
|
14
|
+
stream, error_code, error_snippet
|
|
15
|
+
) VALUES (
|
|
16
|
+
@ts, @request_id, @provider_id, @client_model, @upstream_model,
|
|
17
|
+
@endpoint, @status_code, @duration_ms,
|
|
18
|
+
@prompt_tokens, @completion_tokens, @total_tokens,
|
|
19
|
+
@stream, @error_code, @error_snippet
|
|
20
|
+
)`)
|
|
21
|
+
.run({
|
|
22
|
+
ts: entry.ts,
|
|
23
|
+
request_id: entry.request_id,
|
|
24
|
+
provider_id: entry.provider_id,
|
|
25
|
+
client_model: entry.client_model,
|
|
26
|
+
upstream_model: entry.upstream_model,
|
|
27
|
+
endpoint: entry.endpoint,
|
|
28
|
+
status_code: entry.status_code,
|
|
29
|
+
duration_ms: entry.duration_ms,
|
|
30
|
+
prompt_tokens: entry.prompt_tokens,
|
|
31
|
+
completion_tokens: entry.completion_tokens,
|
|
32
|
+
total_tokens: entry.total_tokens,
|
|
33
|
+
stream: entry.stream ? 1 : 0,
|
|
34
|
+
error_code: entry.error_code,
|
|
35
|
+
error_snippet: snippet,
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
export function queryLogs(filter = {}) {
|
|
39
|
+
const where = [];
|
|
40
|
+
const params = {};
|
|
41
|
+
if (filter.provider) {
|
|
42
|
+
where.push("provider_id = @provider");
|
|
43
|
+
params.provider = filter.provider;
|
|
44
|
+
}
|
|
45
|
+
if (typeof filter.from === "number") {
|
|
46
|
+
where.push("ts >= @from");
|
|
47
|
+
params.from = filter.from;
|
|
48
|
+
}
|
|
49
|
+
if (typeof filter.to === "number") {
|
|
50
|
+
where.push("ts <= @to");
|
|
51
|
+
params.to = filter.to;
|
|
52
|
+
}
|
|
53
|
+
const whereSql = where.length ? `WHERE ${where.join(" AND ")}` : "";
|
|
54
|
+
const limit = Math.min(Math.max(filter.limit ?? 100, 1), 1000);
|
|
55
|
+
const offset = Math.max(filter.offset ?? 0, 0);
|
|
56
|
+
return getDb()
|
|
57
|
+
.prepare(`SELECT * FROM chat_logs ${whereSql} ORDER BY ts DESC LIMIT @limit OFFSET @offset`)
|
|
58
|
+
.all({ ...params, limit, offset });
|
|
59
|
+
}
|
|
60
|
+
export function aggregateMappings() {
|
|
61
|
+
return getDb()
|
|
62
|
+
.prepare(`SELECT provider_id, client_model, upstream_model, COUNT(*) AS count, MAX(ts) AS last_seen
|
|
63
|
+
FROM chat_logs
|
|
64
|
+
GROUP BY provider_id, client_model, upstream_model
|
|
65
|
+
ORDER BY count DESC`)
|
|
66
|
+
.all();
|
|
67
|
+
}
|
|
68
|
+
const RANGE_MS = {
|
|
69
|
+
"24h": 24 * 60 * 60 * 1000,
|
|
70
|
+
"7d": 7 * 24 * 60 * 60 * 1000,
|
|
71
|
+
"30d": 30 * 24 * 60 * 60 * 1000,
|
|
72
|
+
};
|
|
73
|
+
export function aggregateStats(range) {
|
|
74
|
+
const span = RANGE_MS[range] ?? RANGE_MS["24h"];
|
|
75
|
+
const since = Date.now() - span;
|
|
76
|
+
const rows = getDb()
|
|
77
|
+
.prepare(`SELECT provider_id, upstream_model,
|
|
78
|
+
COUNT(*) AS requests,
|
|
79
|
+
SUM(CASE WHEN status_code >= 400 THEN 1 ELSE 0 END) AS errors,
|
|
80
|
+
COALESCE(SUM(prompt_tokens), 0) AS prompt_tokens,
|
|
81
|
+
COALESCE(SUM(completion_tokens), 0) AS completion_tokens,
|
|
82
|
+
COALESCE(SUM(total_tokens), 0) AS total_tokens
|
|
83
|
+
FROM chat_logs
|
|
84
|
+
WHERE ts >= @since
|
|
85
|
+
GROUP BY provider_id, upstream_model
|
|
86
|
+
ORDER BY requests DESC`)
|
|
87
|
+
.all({ since });
|
|
88
|
+
return { since, rows };
|
|
89
|
+
}
|
|
90
|
+
export function deleteLogsBefore(ts) {
|
|
91
|
+
const info = getDb().prepare("DELETE FROM chat_logs WHERE ts < ?").run(ts);
|
|
92
|
+
return info.changes;
|
|
93
|
+
}
|
|
94
|
+
//# sourceMappingURL=logs.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"logs.js","sourceRoot":"","sources":["../../src/db/logs.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import { getDb } from "./index.js";
|
|
2
|
+
export function listModels(providerId) {
|
|
3
|
+
if (providerId) {
|
|
4
|
+
return getDb()
|
|
5
|
+
.prepare("SELECT * FROM models WHERE provider_id = ? ORDER BY sort_order, upstream_id")
|
|
6
|
+
.all(providerId);
|
|
7
|
+
}
|
|
8
|
+
return getDb()
|
|
9
|
+
.prepare("SELECT * FROM models ORDER BY provider_id, sort_order, upstream_id")
|
|
10
|
+
.all();
|
|
11
|
+
}
|
|
12
|
+
export function getModelById(id) {
|
|
13
|
+
return getDb().prepare("SELECT * FROM models WHERE id = ?").get(id) ?? null;
|
|
14
|
+
}
|
|
15
|
+
export function insertCustomModel(providerId, input) {
|
|
16
|
+
const info = getDb()
|
|
17
|
+
.prepare(`INSERT INTO models (
|
|
18
|
+
provider_id, upstream_id, display_name,
|
|
19
|
+
supports_images, supports_reasoning, supports_web_search,
|
|
20
|
+
context_window, is_builtin, deprecated_after, sort_order
|
|
21
|
+
) VALUES (
|
|
22
|
+
@provider_id, @upstream_id, @display_name,
|
|
23
|
+
@supports_images, @supports_reasoning, @supports_web_search,
|
|
24
|
+
@context_window, 0, @deprecated_after, @sort_order
|
|
25
|
+
)`)
|
|
26
|
+
.run({
|
|
27
|
+
provider_id: providerId,
|
|
28
|
+
upstream_id: input.upstream_id,
|
|
29
|
+
display_name: input.display_name ?? null,
|
|
30
|
+
supports_images: input.supports_images ? 1 : 0,
|
|
31
|
+
supports_reasoning: input.supports_reasoning ? 1 : 0,
|
|
32
|
+
supports_web_search: input.supports_web_search ? 1 : 0,
|
|
33
|
+
context_window: input.context_window ?? null,
|
|
34
|
+
deprecated_after: input.deprecated_after ?? null,
|
|
35
|
+
sort_order: input.sort_order ?? 100,
|
|
36
|
+
});
|
|
37
|
+
return getModelById(Number(info.lastInsertRowid));
|
|
38
|
+
}
|
|
39
|
+
export function patchModel(id, patch) {
|
|
40
|
+
const existing = getModelById(id);
|
|
41
|
+
if (!existing)
|
|
42
|
+
return null;
|
|
43
|
+
if (existing.is_builtin) {
|
|
44
|
+
throw new Error("builtin models cannot be modified — add a custom model instead");
|
|
45
|
+
}
|
|
46
|
+
const merged = {
|
|
47
|
+
upstream_id: patch.upstream_id ?? existing.upstream_id,
|
|
48
|
+
display_name: patch.display_name === undefined ? existing.display_name : patch.display_name,
|
|
49
|
+
supports_images: patch.supports_images === undefined
|
|
50
|
+
? existing.supports_images
|
|
51
|
+
: patch.supports_images
|
|
52
|
+
? 1
|
|
53
|
+
: 0,
|
|
54
|
+
supports_reasoning: patch.supports_reasoning === undefined
|
|
55
|
+
? existing.supports_reasoning
|
|
56
|
+
: patch.supports_reasoning
|
|
57
|
+
? 1
|
|
58
|
+
: 0,
|
|
59
|
+
supports_web_search: patch.supports_web_search === undefined
|
|
60
|
+
? existing.supports_web_search
|
|
61
|
+
: patch.supports_web_search
|
|
62
|
+
? 1
|
|
63
|
+
: 0,
|
|
64
|
+
context_window: patch.context_window === undefined ? existing.context_window : patch.context_window,
|
|
65
|
+
deprecated_after: patch.deprecated_after === undefined ? existing.deprecated_after : patch.deprecated_after,
|
|
66
|
+
sort_order: patch.sort_order ?? existing.sort_order,
|
|
67
|
+
};
|
|
68
|
+
getDb()
|
|
69
|
+
.prepare(`UPDATE models SET
|
|
70
|
+
upstream_id = @upstream_id,
|
|
71
|
+
display_name = @display_name,
|
|
72
|
+
supports_images = @supports_images,
|
|
73
|
+
supports_reasoning = @supports_reasoning,
|
|
74
|
+
supports_web_search = @supports_web_search,
|
|
75
|
+
context_window = @context_window,
|
|
76
|
+
deprecated_after = @deprecated_after,
|
|
77
|
+
sort_order = @sort_order
|
|
78
|
+
WHERE id = @id`)
|
|
79
|
+
.run({ ...merged, id });
|
|
80
|
+
return getModelById(id);
|
|
81
|
+
}
|
|
82
|
+
export function deleteModel(id) {
|
|
83
|
+
const existing = getModelById(id);
|
|
84
|
+
if (!existing)
|
|
85
|
+
return false;
|
|
86
|
+
if (existing.is_builtin) {
|
|
87
|
+
throw new Error("builtin models cannot be deleted");
|
|
88
|
+
}
|
|
89
|
+
const info = getDb().prepare("DELETE FROM models WHERE id = ?").run(id);
|
|
90
|
+
return info.changes > 0;
|
|
91
|
+
}
|
|
92
|
+
export function listAliases(providerId) {
|
|
93
|
+
if (providerId) {
|
|
94
|
+
return getDb()
|
|
95
|
+
.prepare("SELECT * FROM model_aliases WHERE provider_id = ? ORDER BY alias")
|
|
96
|
+
.all(providerId);
|
|
97
|
+
}
|
|
98
|
+
return getDb().prepare("SELECT * FROM model_aliases ORDER BY provider_id, alias").all();
|
|
99
|
+
}
|
|
100
|
+
export function upsertAlias(input) {
|
|
101
|
+
getDb()
|
|
102
|
+
.prepare(`INSERT INTO model_aliases (alias, provider_id, upstream_id)
|
|
103
|
+
VALUES (@alias, @provider_id, @upstream_id)
|
|
104
|
+
ON CONFLICT(alias) DO UPDATE SET provider_id = excluded.provider_id, upstream_id = excluded.upstream_id`)
|
|
105
|
+
.run(input);
|
|
106
|
+
}
|
|
107
|
+
export function deleteAlias(alias) {
|
|
108
|
+
const info = getDb().prepare("DELETE FROM model_aliases WHERE alias = ?").run(alias);
|
|
109
|
+
return info.changes > 0;
|
|
110
|
+
}
|
|
111
|
+
export function lookupAlias(alias) {
|
|
112
|
+
return (getDb().prepare("SELECT * FROM model_aliases WHERE alias = ?").get(alias) ?? null);
|
|
113
|
+
}
|
|
114
|
+
//# sourceMappingURL=models.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"models.js","sourceRoot":"","sources":["../../src/db/models.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AAuBnC,MAAM,UAAU,UAAU,CAAC,UAAuB;IAChD,IAAI,UAAU,EAAE,CAAC;QACf,OAAO,KAAK,EAAE;aACX,OAAO,CAAC,6EAA6E,CAAC;aACtF,GAAG,CAAC,UAAU,CAAe,CAAC;IACnC,CAAC;IACD,OAAO,KAAK,EAAE;SACX,OAAO,CAAC,oEAAoE,CAAC;SAC7E,GAAG,EAAgB,CAAC;AACzB,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,EAAU;IACrC,OAAQ,KAAK,EAAE,CAAC,OAAO,CAAC,mCAAmC,CAAC,CAAC,GAAG,CAAC,EAAE,CAA0B,IAAI,IAAI,CAAC;AACxG,CAAC;AAaD,MAAM,UAAU,iBAAiB,CAAC,UAAsB,EAAE,KAAiB;IACzE,MAAM,IAAI,GAAG,KAAK,EAAE;SACjB,OAAO,CACN;;;;;;;;QAQE,CACH;SACA,GAAG,CAAC;QACH,WAAW,EAAE,UAAU;QACvB,WAAW,EAAE,KAAK,CAAC,WAAW;QAC9B,YAAY,EAAE,KAAK,CAAC,YAAY,IAAI,IAAI;QACxC,eAAe,EAAE,KAAK,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QAC9C,kBAAkB,EAAE,KAAK,CAAC,kBAAkB,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QACpD,mBAAmB,EAAE,KAAK,CAAC,mBAAmB,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QACtD,cAAc,EAAE,KAAK,CAAC,cAAc,IAAI,IAAI;QAC5C,gBAAgB,EAAE,KAAK,CAAC,gBAAgB,IAAI,IAAI;QAChD,UAAU,EAAE,KAAK,CAAC,UAAU,IAAI,GAAG;KACpC,CAAC,CAAC;IACL,OAAO,YAAY,CAAC,MAAM,CAAC,IAAI,CAAC,eAAe,CAAC,CAAE,CAAC;AACrD,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,EAAU,EAAE,KAA0B;IAC/D,MAAM,QAAQ,GAAG,YAAY,CAAC,EAAE,CAAC,CAAC;IAClC,IAAI,CAAC,QAAQ;QAAE,OAAO,IAAI,CAAC;IAC3B,IAAI,QAAQ,CAAC,UAAU,EAAE,CAAC;QACxB,MAAM,IAAI,KAAK,CAAC,gEAAgE,CAAC,CAAC;IACpF,CAAC;IACD,MAAM,MAAM,GAAG;QACb,WAAW,EAAE,KAAK,CAAC,WAAW,IAAI,QAAQ,CAAC,WAAW;QACtD,YAAY,EAAE,KAAK,CAAC,YAAY,KAAK,SAAS,CAAC,CAAC,CAAC,QAAQ,CAAC,YAAY,CAAC,CAAC,CAAC,KAAK,CAAC,YAAY;QAC3F,eAAe,EACb,KAAK,CAAC,eAAe,KAAK,SAAS;YACjC,CAAC,CAAC,QAAQ,CAAC,eAAe;YAC1B,CAAC,CAAC,KAAK,CAAC,eAAe;gBACrB,CAAC,CAAC,CAAC;gBACH,CAAC,CAAC,CAAC;QACT,kBAAkB,EAChB,KAAK,CAAC,kBAAkB,KAAK,SAAS;YACpC,CAAC,CAAC,QAAQ,CAAC,kBAAkB;YAC7B,CAAC,CAAC,KAAK,CAAC,kBAAkB;gBACxB,CAAC,CAAC,CAAC;gBACH,CAAC,CAAC,CAAC;QACT,mBAAmB,EACjB,KAAK,CAAC,mBAAmB,KAAK,SAAS;YACrC,CAAC,CAAC,QAAQ,CAAC,mBAAmB;YAC9B,CAAC,CAAC,KAAK,CAAC,mBAAmB;gBACzB,CAAC,CAAC,CAAC;gBACH,CAAC,CAAC,CAAC;QACT,cAAc,EACZ,KAAK,CAAC,cAAc,KAAK,SAAS,CAAC,CAAC,CAAC,QAAQ,CAAC,cAAc,CAAC,CAAC,CAAC,KAAK,CAAC,cAAc;QACrF,gBAAgB,EACd,KAAK,CAAC,gBAAgB,KAAK,SAAS,CAAC,CAAC,CAAC,QAAQ,CAAC,gBAAgB,CAAC,CAAC,CAAC,KAAK,CAAC,gBAAgB;QAC3F,UAAU,EAAE,KAAK,CAAC,UAAU,IAAI,QAAQ,CAAC,UAAU;KACpD,CAAC;IACF,KAAK,EAAE;SACJ,OAAO,CACN;;;;;;;;;qBASe,CAChB;SACA,GAAG,CAAC,EAAE,GAAG,MAAM,EAAE,EAAE,EAAE,CAAC,CAAC;IAC1B,OAAO,YAAY,CAAC,EAAE,CAAC,CAAC;AAC1B,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,EAAU;IACpC,MAAM,QAAQ,GAAG,YAAY,CAAC,EAAE,CAAC,CAAC;IAClC,IAAI,CAAC,QAAQ;QAAE,OAAO,KAAK,CAAC;IAC5B,IAAI,QAAQ,CAAC,UAAU,EAAE,CAAC;QACxB,MAAM,IAAI,KAAK,CAAC,kCAAkC,CAAC,CAAC;IACtD,CAAC;IACD,MAAM,IAAI,GAAG,KAAK,EAAE,CAAC,OAAO,CAAC,iCAAiC,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IACxE,OAAO,IAAI,CAAC,OAAO,GAAG,CAAC,CAAC;AAC1B,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,UAAuB;IACjD,IAAI,UAAU,EAAE,CAAC;QACf,OAAO,KAAK,EAAE;aACX,OAAO,CAAC,kEAAkE,CAAC;aAC3E,GAAG,CAAC,UAAU,CAAe,CAAC;IACnC,CAAC;IACD,OAAO,KAAK,EAAE,CAAC,OAAO,CAAC,yDAAyD,CAAC,CAAC,GAAG,EAAgB,CAAC;AACxG,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,KAAe;IACzC,KAAK,EAAE;SACJ,OAAO,CACN;;+GAEyG,CAC1G;SACA,GAAG,CAAC,KAAK,CAAC,CAAC;AAChB,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,KAAa;IACvC,MAAM,IAAI,GAAG,KAAK,EAAE,CAAC,OAAO,CAAC,2CAA2C,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;IACrF,OAAO,IAAI,CAAC,OAAO,GAAG,CAAC,CAAC;AAC1B,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,KAAa;IACvC,OAAO,CACJ,KAAK,EAAE,CAAC,OAAO,CAAC,6CAA6C,CAAC,CAAC,GAAG,CAAC,KAAK,CAE3D,IAAI,IAAI,CACvB,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
// Initial schema. Kept inline (rather than as a .sql file alongside the
|
|
2
|
+
// compiled .js) so packaging doesn't have to chase asset paths. Future
|
|
3
|
+
// migrations should append numbered statements to MIGRATIONS and bump the
|
|
4
|
+
// schema version checked at startup.
|
|
5
|
+
export const MIGRATIONS = [
|
|
6
|
+
{
|
|
7
|
+
version: 1,
|
|
8
|
+
sql: `
|
|
9
|
+
CREATE TABLE IF NOT EXISTS providers (
|
|
10
|
+
id TEXT PRIMARY KEY,
|
|
11
|
+
shortcut TEXT UNIQUE NOT NULL,
|
|
12
|
+
display_name TEXT NOT NULL,
|
|
13
|
+
base_url TEXT NOT NULL,
|
|
14
|
+
default_model TEXT NOT NULL,
|
|
15
|
+
api_key_env TEXT NOT NULL,
|
|
16
|
+
updated_at INTEGER NOT NULL
|
|
17
|
+
);
|
|
18
|
+
|
|
19
|
+
CREATE TABLE IF NOT EXISTS models (
|
|
20
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
21
|
+
provider_id TEXT NOT NULL REFERENCES providers(id) ON DELETE CASCADE,
|
|
22
|
+
upstream_id TEXT NOT NULL,
|
|
23
|
+
display_name TEXT,
|
|
24
|
+
supports_images INTEGER NOT NULL DEFAULT 0,
|
|
25
|
+
supports_reasoning INTEGER NOT NULL DEFAULT 0,
|
|
26
|
+
supports_web_search INTEGER NOT NULL DEFAULT 0,
|
|
27
|
+
context_window INTEGER,
|
|
28
|
+
is_builtin INTEGER NOT NULL DEFAULT 0,
|
|
29
|
+
deprecated_after TEXT,
|
|
30
|
+
sort_order INTEGER NOT NULL DEFAULT 0,
|
|
31
|
+
UNIQUE(provider_id, upstream_id)
|
|
32
|
+
);
|
|
33
|
+
CREATE INDEX IF NOT EXISTS idx_models_provider ON models(provider_id, sort_order);
|
|
34
|
+
|
|
35
|
+
CREATE TABLE IF NOT EXISTS model_aliases (
|
|
36
|
+
alias TEXT PRIMARY KEY,
|
|
37
|
+
provider_id TEXT NOT NULL REFERENCES providers(id) ON DELETE CASCADE,
|
|
38
|
+
upstream_id TEXT NOT NULL
|
|
39
|
+
);
|
|
40
|
+
|
|
41
|
+
CREATE TABLE IF NOT EXISTS chat_logs (
|
|
42
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
43
|
+
ts INTEGER NOT NULL,
|
|
44
|
+
request_id TEXT,
|
|
45
|
+
provider_id TEXT NOT NULL,
|
|
46
|
+
client_model TEXT NOT NULL,
|
|
47
|
+
upstream_model TEXT NOT NULL,
|
|
48
|
+
endpoint TEXT NOT NULL,
|
|
49
|
+
status_code INTEGER NOT NULL,
|
|
50
|
+
duration_ms INTEGER NOT NULL,
|
|
51
|
+
prompt_tokens INTEGER,
|
|
52
|
+
completion_tokens INTEGER,
|
|
53
|
+
total_tokens INTEGER,
|
|
54
|
+
stream INTEGER NOT NULL DEFAULT 0,
|
|
55
|
+
error_code TEXT,
|
|
56
|
+
error_snippet TEXT
|
|
57
|
+
);
|
|
58
|
+
CREATE INDEX IF NOT EXISTS idx_chat_logs_ts ON chat_logs(ts DESC);
|
|
59
|
+
CREATE INDEX IF NOT EXISTS idx_chat_logs_provider ON chat_logs(provider_id, ts DESC);
|
|
60
|
+
|
|
61
|
+
CREATE TABLE IF NOT EXISTS settings (
|
|
62
|
+
key TEXT PRIMARY KEY,
|
|
63
|
+
value TEXT NOT NULL,
|
|
64
|
+
updated_at INTEGER NOT NULL
|
|
65
|
+
);
|
|
66
|
+
|
|
67
|
+
CREATE TABLE IF NOT EXISTS schema_version (
|
|
68
|
+
version INTEGER PRIMARY KEY,
|
|
69
|
+
applied_at INTEGER NOT NULL
|
|
70
|
+
);
|
|
71
|
+
`,
|
|
72
|
+
},
|
|
73
|
+
];
|
|
74
|
+
//# sourceMappingURL=schema.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schema.js","sourceRoot":"","sources":["../../src/db/schema.ts"],"names":[],"mappings":"AAAA,wEAAwE;AACxE,uEAAuE;AACvE,0EAA0E;AAC1E,qCAAqC;AAErC,MAAM,CAAC,MAAM,UAAU,GAAoD;IACzE;QACE,OAAO,EAAE,CAAC;QACV,GAAG,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA+DR;KACE;CACF,CAAC"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { getDb } from "./index.js";
|
|
2
|
+
const FORBIDDEN_KEYS = new Set([
|
|
3
|
+
"api_key",
|
|
4
|
+
"apikey",
|
|
5
|
+
"key",
|
|
6
|
+
"mimo_api_key",
|
|
7
|
+
"ds_api_key",
|
|
8
|
+
"deepseek_api_key",
|
|
9
|
+
]);
|
|
10
|
+
export class ForbiddenSettingError extends Error {
|
|
11
|
+
key;
|
|
12
|
+
constructor(key) {
|
|
13
|
+
super(`setting "${key}" cannot be stored — API keys must be supplied via environment variables (MIMO_API_KEY, DS_API_KEY, DEEPSEEK_API_KEY) or the --api-key CLI flag.`);
|
|
14
|
+
this.key = key;
|
|
15
|
+
this.name = "ForbiddenSettingError";
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
export function isForbiddenSettingKey(key) {
|
|
19
|
+
return FORBIDDEN_KEYS.has(key.toLowerCase());
|
|
20
|
+
}
|
|
21
|
+
export function getSetting(key) {
|
|
22
|
+
const row = getDb()
|
|
23
|
+
.prepare("SELECT value FROM settings WHERE key = ?")
|
|
24
|
+
.get(key);
|
|
25
|
+
return row?.value ?? null;
|
|
26
|
+
}
|
|
27
|
+
export function listSettings() {
|
|
28
|
+
const rows = getDb().prepare("SELECT key, value FROM settings").all();
|
|
29
|
+
return Object.fromEntries(rows.map((r) => [r.key, r.value]));
|
|
30
|
+
}
|
|
31
|
+
export function setSetting(key, value) {
|
|
32
|
+
if (isForbiddenSettingKey(key)) {
|
|
33
|
+
throw new ForbiddenSettingError(key);
|
|
34
|
+
}
|
|
35
|
+
getDb()
|
|
36
|
+
.prepare(`INSERT INTO settings (key, value, updated_at) VALUES (?, ?, ?)
|
|
37
|
+
ON CONFLICT(key) DO UPDATE SET value = excluded.value, updated_at = excluded.updated_at`)
|
|
38
|
+
.run(key, value, Date.now());
|
|
39
|
+
}
|
|
40
|
+
export function deleteSetting(key) {
|
|
41
|
+
const info = getDb().prepare("DELETE FROM settings WHERE key = ?").run(key);
|
|
42
|
+
return info.changes > 0;
|
|
43
|
+
}
|
|
44
|
+
//# sourceMappingURL=settings.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"settings.js","sourceRoot":"","sources":["../../src/db/settings.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AAEnC,MAAM,cAAc,GAAG,IAAI,GAAG,CAAC;IAC7B,SAAS;IACT,QAAQ;IACR,KAAK;IACL,cAAc;IACd,YAAY;IACZ,kBAAkB;CACnB,CAAC,CAAC;AAEH,MAAM,OAAO,qBAAsB,SAAQ,KAAK;IAClB;IAA5B,YAA4B,GAAW;QACrC,KAAK,CACH,YAAY,GAAG,kJAAkJ,CAClK,CAAC;QAHwB,QAAG,GAAH,GAAG,CAAQ;QAIrC,IAAI,CAAC,IAAI,GAAG,uBAAuB,CAAC;IACtC,CAAC;CACF;AAED,MAAM,UAAU,qBAAqB,CAAC,GAAW;IAC/C,OAAO,cAAc,CAAC,GAAG,CAAC,GAAG,CAAC,WAAW,EAAE,CAAC,CAAC;AAC/C,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,GAAW;IACpC,MAAM,GAAG,GAAG,KAAK,EAAE;SAChB,OAAO,CAAC,0CAA0C,CAAC;SACnD,GAAG,CAAC,GAAG,CAAkC,CAAC;IAC7C,OAAO,GAAG,EAAE,KAAK,IAAI,IAAI,CAAC;AAC5B,CAAC;AAED,MAAM,UAAU,YAAY;IAC1B,MAAM,IAAI,GAAG,KAAK,EAAE,CAAC,OAAO,CAAC,iCAAiC,CAAC,CAAC,GAAG,EAGjE,CAAC;IACH,OAAO,MAAM,CAAC,WAAW,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,GAAG,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;AAC/D,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,GAAW,EAAE,KAAa;IACnD,IAAI,qBAAqB,CAAC,GAAG,CAAC,EAAE,CAAC;QAC/B,MAAM,IAAI,qBAAqB,CAAC,GAAG,CAAC,CAAC;IACvC,CAAC;IACD,KAAK,EAAE;SACJ,OAAO,CACN;+FACyF,CAC1F;SACA,GAAG,CAAC,GAAG,EAAE,KAAK,EAAE,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC;AACjC,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,GAAW;IACvC,MAAM,IAAI,GAAG,KAAK,EAAE,CAAC,OAAO,CAAC,oCAAoC,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IAC5E,OAAO,IAAI,CAAC,OAAO,GAAG,CAAC,CAAC;AAC1B,CAAC"}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { reqToChat } from "../translate/reqToChat.js";
|
|
2
|
+
// Builtin DeepSeek model catalog. Source: https://api-docs.deepseek.com/zh-cn/
|
|
3
|
+
// `deepseek-chat` and `deepseek-reasoner` are the legacy aliases that route to
|
|
4
|
+
// `deepseek-v4-flash` (non-thinking / thinking respectively); they're announced
|
|
5
|
+
// for deprecation 2026-07-24. We keep them as aliases for backwards compat.
|
|
6
|
+
const BUILTIN_MODELS = [
|
|
7
|
+
{
|
|
8
|
+
id: "deepseek-v4-pro",
|
|
9
|
+
displayName: "DeepSeek V4 Pro",
|
|
10
|
+
supportsReasoning: true,
|
|
11
|
+
},
|
|
12
|
+
{
|
|
13
|
+
id: "deepseek-v4-flash",
|
|
14
|
+
displayName: "DeepSeek V4 Flash",
|
|
15
|
+
aliases: ["deepseek-chat", "deepseek-reasoner"],
|
|
16
|
+
supportsReasoning: true,
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
id: "deepseek-chat",
|
|
20
|
+
displayName: "DeepSeek Chat (legacy)",
|
|
21
|
+
deprecatedAfter: "2026-07-24",
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
id: "deepseek-reasoner",
|
|
25
|
+
displayName: "DeepSeek Reasoner (legacy)",
|
|
26
|
+
supportsReasoning: true,
|
|
27
|
+
deprecatedAfter: "2026-07-24",
|
|
28
|
+
},
|
|
29
|
+
];
|
|
30
|
+
export const deepseek = {
|
|
31
|
+
id: "deepseek",
|
|
32
|
+
shortcut: "ds",
|
|
33
|
+
displayName: "DeepSeek",
|
|
34
|
+
defaultBaseUrl: "https://api.deepseek.com/v1",
|
|
35
|
+
baseUrlEnv: "DEEPSEEK_BASE_URL",
|
|
36
|
+
envKeys: ["DS_API_KEY", "DEEPSEEK_API_KEY"],
|
|
37
|
+
defaultModel: "deepseek-v4-pro",
|
|
38
|
+
builtinModels: BUILTIN_MODELS,
|
|
39
|
+
detectFlags(_apiKey, _baseUrl) {
|
|
40
|
+
return {};
|
|
41
|
+
},
|
|
42
|
+
resolveModel(clientModel) {
|
|
43
|
+
for (const m of BUILTIN_MODELS) {
|
|
44
|
+
if (m.id === clientModel)
|
|
45
|
+
return m;
|
|
46
|
+
if (m.aliases?.includes(clientModel))
|
|
47
|
+
return m;
|
|
48
|
+
}
|
|
49
|
+
return null;
|
|
50
|
+
},
|
|
51
|
+
preprocessResponses(req, _ctx) {
|
|
52
|
+
// DeepSeek is OpenAI Chat Completions compatible. No `thinking` field, no
|
|
53
|
+
// `web_search` builtin (drop those tools), no MiMo-style force-parallel
|
|
54
|
+
// override (respect the client's value).
|
|
55
|
+
const chat = reqToChat(req, {
|
|
56
|
+
forceParallelToolCalls: false,
|
|
57
|
+
enableWebSearch: false,
|
|
58
|
+
});
|
|
59
|
+
// Drop any MiMo-specific fields that may have leaked in.
|
|
60
|
+
delete chat.thinking;
|
|
61
|
+
delete chat.enable_thinking;
|
|
62
|
+
return chat;
|
|
63
|
+
},
|
|
64
|
+
preprocessChat(req, _ctx) {
|
|
65
|
+
// Strip MiMo-specific fields from chat passthrough so a misrouted request
|
|
66
|
+
// doesn't 400 at DeepSeek.
|
|
67
|
+
const out = { ...req };
|
|
68
|
+
delete out.thinking;
|
|
69
|
+
delete out.enable_thinking;
|
|
70
|
+
return out;
|
|
71
|
+
},
|
|
72
|
+
enhanceError(_ctx) {
|
|
73
|
+
return null;
|
|
74
|
+
},
|
|
75
|
+
};
|
|
76
|
+
//# sourceMappingURL=deepseek.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"deepseek.js","sourceRoot":"","sources":["../../src/providers/deepseek.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAE,MAAM,2BAA2B,CAAC;AAGtD,+EAA+E;AAC/E,+EAA+E;AAC/E,gFAAgF;AAChF,4EAA4E;AAC5E,MAAM,cAAc,GAA6B;IAC/C;QACE,EAAE,EAAE,iBAAiB;QACrB,WAAW,EAAE,iBAAiB;QAC9B,iBAAiB,EAAE,IAAI;KACxB;IACD;QACE,EAAE,EAAE,mBAAmB;QACvB,WAAW,EAAE,mBAAmB;QAChC,OAAO,EAAE,CAAC,eAAe,EAAE,mBAAmB,CAAC;QAC/C,iBAAiB,EAAE,IAAI;KACxB;IACD;QACE,EAAE,EAAE,eAAe;QACnB,WAAW,EAAE,wBAAwB;QACrC,eAAe,EAAE,YAAY;KAC9B;IACD;QACE,EAAE,EAAE,mBAAmB;QACvB,WAAW,EAAE,4BAA4B;QACzC,iBAAiB,EAAE,IAAI;QACvB,eAAe,EAAE,YAAY;KAC9B;CACF,CAAC;AAEF,MAAM,CAAC,MAAM,QAAQ,GAAa;IAChC,EAAE,EAAE,UAAU;IACd,QAAQ,EAAE,IAAI;IACd,WAAW,EAAE,UAAU;IACvB,cAAc,EAAE,6BAA6B;IAC7C,UAAU,EAAE,mBAAmB;IAC/B,OAAO,EAAE,CAAC,YAAY,EAAE,kBAAkB,CAAU;IACpD,YAAY,EAAE,iBAAiB;IAC/B,aAAa,EAAE,cAAc;IAE7B,WAAW,CAAC,OAAO,EAAE,QAAQ;QAC3B,OAAO,EAAE,CAAC;IACZ,CAAC;IAED,YAAY,CAAC,WAAW;QACtB,KAAK,MAAM,CAAC,IAAI,cAAc,EAAE,CAAC;YAC/B,IAAI,CAAC,CAAC,EAAE,KAAK,WAAW;gBAAE,OAAO,CAAC,CAAC;YACnC,IAAI,CAAC,CAAC,OAAO,EAAE,QAAQ,CAAC,WAAW,CAAC;gBAAE,OAAO,CAAC,CAAC;QACjD,CAAC;QACD,OAAO,IAAI,CAAC;IACd,CAAC;IAED,mBAAmB,CAAC,GAAqB,EAAE,IAAmB;QAC5D,0EAA0E;QAC1E,wEAAwE;QACxE,yCAAyC;QACzC,MAAM,IAAI,GAAG,SAAS,CAAC,GAAG,EAAE;YAC1B,sBAAsB,EAAE,KAAK;YAC7B,eAAe,EAAE,KAAK;SACvB,CAAC,CAAC;QACH,yDAAyD;QACzD,OAAO,IAAI,CAAC,QAAQ,CAAC;QACrB,OAAO,IAAI,CAAC,eAAe,CAAC;QAC5B,OAAO,IAAI,CAAC;IACd,CAAC;IAED,cAAc,CAAC,GAAgB,EAAE,IAAmB;QAClD,0EAA0E;QAC1E,2BAA2B;QAC3B,MAAM,GAAG,GAAG,EAAE,GAAG,GAAG,EAAE,CAAC;QACvB,OAAO,GAAG,CAAC,QAAQ,CAAC;QACpB,OAAO,GAAG,CAAC,eAAe,CAAC;QAC3B,OAAO,GAAG,CAAC;IACb,CAAC;IAED,YAAY,CAAC,IAAI;QACf,OAAO,IAAI,CAAC;IACd,CAAC;CACF,CAAC"}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { reqToChat } from "../translate/reqToChat.js";
|
|
2
|
+
// Marker MiMo emits in 400 responses when web_search is forwarded but the
|
|
3
|
+
// account doesn't have the Web Search Plugin activated.
|
|
4
|
+
const WEB_SEARCH_DISABLED_MARKER = "webSearchEnabled is false";
|
|
5
|
+
const WEB_SEARCH_HINT = "MiMo Web Search Plugin is not activated for this account. " +
|
|
6
|
+
"Activate it at https://platform.xiaomimimo.com/#/console/plugin (separately billed) " +
|
|
7
|
+
"and restart mimo2codex. The model has decided to call web_search; if your account " +
|
|
8
|
+
"doesn't include the plugin, this request will keep failing until activated.";
|
|
9
|
+
const BUILTIN_MODELS = [
|
|
10
|
+
{
|
|
11
|
+
id: "mimo-v2.5-pro",
|
|
12
|
+
displayName: "MiMo V2.5 Pro",
|
|
13
|
+
supportsReasoning: true,
|
|
14
|
+
supportsWebSearch: true,
|
|
15
|
+
contextWindow: 128_000,
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
id: "mimo-v2.5-pro[1m]",
|
|
19
|
+
displayName: "MiMo V2.5 Pro (1M)",
|
|
20
|
+
supportsReasoning: true,
|
|
21
|
+
supportsWebSearch: true,
|
|
22
|
+
contextWindow: 1_000_000,
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
id: "mimo-v2-flash",
|
|
26
|
+
displayName: "MiMo V2 Flash",
|
|
27
|
+
contextWindow: 128_000,
|
|
28
|
+
},
|
|
29
|
+
];
|
|
30
|
+
// MiMo runs two hosts:
|
|
31
|
+
// - pay-as-you-go (`sk-*` keys): https://api.xiaomimimo.com/v1
|
|
32
|
+
// - token-plan (`tp-*` keys): https://token-plan-cn.xiaomimimo.com/v1
|
|
33
|
+
// Sending a tp-* key to the pay-as-you-go host (or vice versa) yields a 401.
|
|
34
|
+
const PAYG_BASE_URL = "https://api.xiaomimimo.com/v1";
|
|
35
|
+
const TOKEN_PLAN_BASE_URL = "https://token-plan-cn.xiaomimimo.com/v1";
|
|
36
|
+
function isTokenPlanRuntime(apiKey, baseUrl) {
|
|
37
|
+
return /token-plan/i.test(baseUrl) || apiKey.startsWith("tp-");
|
|
38
|
+
}
|
|
39
|
+
export const mimo = {
|
|
40
|
+
id: "mimo",
|
|
41
|
+
shortcut: "mimo",
|
|
42
|
+
displayName: "MiMo (via mimo2codex)",
|
|
43
|
+
defaultBaseUrl: PAYG_BASE_URL,
|
|
44
|
+
baseUrlEnv: "MIMO_BASE_URL",
|
|
45
|
+
envKeys: ["MIMO_API_KEY"],
|
|
46
|
+
defaultModel: "mimo-v2.5-pro",
|
|
47
|
+
builtinModels: BUILTIN_MODELS,
|
|
48
|
+
detectFlags(apiKey, baseUrl) {
|
|
49
|
+
return { isTokenPlan: isTokenPlanRuntime(apiKey, baseUrl) };
|
|
50
|
+
},
|
|
51
|
+
inferBaseUrlFromKey(apiKey) {
|
|
52
|
+
if (apiKey.startsWith("tp-"))
|
|
53
|
+
return TOKEN_PLAN_BASE_URL;
|
|
54
|
+
if (apiKey.startsWith("sk-"))
|
|
55
|
+
return PAYG_BASE_URL;
|
|
56
|
+
return null;
|
|
57
|
+
},
|
|
58
|
+
resolveModel(clientModel) {
|
|
59
|
+
return BUILTIN_MODELS.find((m) => m.id === clientModel) ?? null;
|
|
60
|
+
},
|
|
61
|
+
preprocessResponses(req, ctx) {
|
|
62
|
+
// mimo2codex's two default-on behaviors that compensate for MiMo's weaker
|
|
63
|
+
// agentic-coding training compared to GPT-5 / Claude:
|
|
64
|
+
// - parallel_tool_calls: true ← batch tool calls per turn
|
|
65
|
+
// - web_search forwarded to MiMo ← model decides when to search
|
|
66
|
+
//
|
|
67
|
+
// Token-plan accounts don't have the Web Search Plugin, so we proactively
|
|
68
|
+
// strip web_search before forwarding (avoids 400 "webSearchEnabled is false").
|
|
69
|
+
return reqToChat(req, {
|
|
70
|
+
forceParallelToolCalls: true,
|
|
71
|
+
enableWebSearch: !ctx.runtime.flags.isTokenPlan,
|
|
72
|
+
});
|
|
73
|
+
},
|
|
74
|
+
preprocessChat(req, _ctx) {
|
|
75
|
+
// Chat passthrough: forward verbatim. MiMo is itself Chat-Completions-native.
|
|
76
|
+
return req;
|
|
77
|
+
},
|
|
78
|
+
enhanceError({ status, snippet }) {
|
|
79
|
+
if (status === 400 && snippet?.includes(WEB_SEARCH_DISABLED_MARKER)) {
|
|
80
|
+
return {
|
|
81
|
+
code: "web_search_plugin_not_activated",
|
|
82
|
+
message: `${WEB_SEARCH_HINT} (raw: ${snippet})`,
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
return null;
|
|
86
|
+
},
|
|
87
|
+
};
|
|
88
|
+
//# sourceMappingURL=mimo.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mimo.js","sourceRoot":"","sources":["../../src/providers/mimo.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAE,MAAM,2BAA2B,CAAC;AAGtD,0EAA0E;AAC1E,wDAAwD;AACxD,MAAM,0BAA0B,GAAG,2BAA2B,CAAC;AAE/D,MAAM,eAAe,GACnB,4DAA4D;IAC5D,sFAAsF;IACtF,oFAAoF;IACpF,6EAA6E,CAAC;AAEhF,MAAM,cAAc,GAA6B;IAC/C;QACE,EAAE,EAAE,eAAe;QACnB,WAAW,EAAE,eAAe;QAC5B,iBAAiB,EAAE,IAAI;QACvB,iBAAiB,EAAE,IAAI;QACvB,aAAa,EAAE,OAAO;KACvB;IACD;QACE,EAAE,EAAE,mBAAmB;QACvB,WAAW,EAAE,oBAAoB;QACjC,iBAAiB,EAAE,IAAI;QACvB,iBAAiB,EAAE,IAAI;QACvB,aAAa,EAAE,SAAS;KACzB;IACD;QACE,EAAE,EAAE,eAAe;QACnB,WAAW,EAAE,eAAe;QAC5B,aAAa,EAAE,OAAO;KACvB;CACF,CAAC;AAEF,uBAAuB;AACvB,iEAAiE;AACjE,2EAA2E;AAC3E,6EAA6E;AAC7E,MAAM,aAAa,GAAG,+BAA+B,CAAC;AACtD,MAAM,mBAAmB,GAAG,yCAAyC,CAAC;AAEtE,SAAS,kBAAkB,CAAC,MAAc,EAAE,OAAe;IACzD,OAAO,aAAa,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,MAAM,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC;AACjE,CAAC;AAED,MAAM,CAAC,MAAM,IAAI,GAAa;IAC5B,EAAE,EAAE,MAAM;IACV,QAAQ,EAAE,MAAM;IAChB,WAAW,EAAE,uBAAuB;IACpC,cAAc,EAAE,aAAa;IAC7B,UAAU,EAAE,eAAe;IAC3B,OAAO,EAAE,CAAC,cAAc,CAAU;IAClC,YAAY,EAAE,eAAe;IAC7B,aAAa,EAAE,cAAc;IAE7B,WAAW,CAAC,MAAM,EAAE,OAAO;QACzB,OAAO,EAAE,WAAW,EAAE,kBAAkB,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAC;IAC9D,CAAC;IAED,mBAAmB,CAAC,MAAM;QACxB,IAAI,MAAM,CAAC,UAAU,CAAC,KAAK,CAAC;YAAE,OAAO,mBAAmB,CAAC;QACzD,IAAI,MAAM,CAAC,UAAU,CAAC,KAAK,CAAC;YAAE,OAAO,aAAa,CAAC;QACnD,OAAO,IAAI,CAAC;IACd,CAAC;IAED,YAAY,CAAC,WAAW;QACtB,OAAO,cAAc,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,WAAW,CAAC,IAAI,IAAI,CAAC;IAClE,CAAC;IAED,mBAAmB,CAAC,GAAqB,EAAE,GAAkB;QAC3D,0EAA0E;QAC1E,sDAAsD;QACtD,mEAAmE;QACnE,sEAAsE;QACtE,EAAE;QACF,0EAA0E;QAC1E,+EAA+E;QAC/E,OAAO,SAAS,CAAC,GAAG,EAAE;YACpB,sBAAsB,EAAE,IAAI;YAC5B,eAAe,EAAE,CAAC,GAAG,CAAC,OAAO,CAAC,KAAK,CAAC,WAAW;SAChD,CAAC,CAAC;IACL,CAAC;IAED,cAAc,CAAC,GAAgB,EAAE,IAAmB;QAClD,8EAA8E;QAC9E,OAAO,GAAG,CAAC;IACb,CAAC;IAED,YAAY,CAAC,EAAE,MAAM,EAAE,OAAO,EAAE;QAC9B,IAAI,MAAM,KAAK,GAAG,IAAI,OAAO,EAAE,QAAQ,CAAC,0BAA0B,CAAC,EAAE,CAAC;YACpE,OAAO;gBACL,IAAI,EAAE,iCAAiC;gBACvC,OAAO,EAAE,GAAG,eAAe,UAAU,OAAO,GAAG;aAChD,CAAC;QACJ,CAAC;QACD,OAAO,IAAI,CAAC;IACd,CAAC;CACF,CAAC"}
|