cotutor 0.3.0 → 0.4.0
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/LICENSE +9 -7
- package/NOTICE +3 -3
- package/README.md +36 -13
- package/avatars/chinese.webp +0 -0
- package/avatars/english.webp +0 -0
- package/avatars/math.webp +0 -0
- package/bin/cotutor.js +13 -3
- package/cards/photo/card.css +10 -3
- package/cards/photo/card.md +2 -2
- package/cards/tutor/card.css +13 -0
- package/cards/tutor/card.md +2 -1
- package/cards//346/235/277/344/271/246/346/200/216/344/271/210/345/206/231.md +1 -1
- package/dist/app/activity-eval.js +3 -2
- package/dist/app/cli/activities.d.ts +8 -1
- package/dist/app/cli/activities.js +42 -4
- package/dist/app/cli/cert.d.ts +5 -0
- package/dist/app/cli/cert.js +20 -7
- package/dist/app/cli/chalk/browser.d.ts +2 -2
- package/dist/app/cli/chalk/browser.js +22 -7
- package/dist/app/cli/chalk/main.js +3 -2
- package/dist/app/cli/doctor.d.ts +6 -5
- package/dist/app/cli/doctor.js +107 -171
- package/dist/app/cli/firewall.d.ts +24 -0
- package/dist/app/cli/firewall.js +79 -0
- package/dist/app/cli/init.js +8 -5
- package/dist/app/cli/main.js +347 -24
- package/dist/app/cli/migrate.d.ts +2 -11
- package/dist/app/cli/migrate.js +1 -59
- package/dist/app/cli/port-holder.d.ts +16 -0
- package/dist/app/cli/port-holder.js +34 -0
- package/dist/app/cli/port.d.ts +37 -0
- package/dist/app/cli/port.js +116 -0
- package/dist/app/cli/sandbox.d.ts +25 -0
- package/dist/app/cli/sandbox.js +166 -0
- package/dist/app/cli/serve-log.d.ts +13 -0
- package/dist/app/cli/serve-log.js +90 -0
- package/dist/app/cli/serve.d.ts +18 -4
- package/dist/app/cli/serve.js +78 -8
- package/dist/app/cli/service-windows.d.ts +50 -0
- package/dist/app/cli/service-windows.js +117 -0
- package/dist/app/cli/service.d.ts +138 -0
- package/dist/app/cli/service.js +429 -0
- package/dist/app/cli/skeleton.d.ts +1 -1
- package/dist/app/cli/skeleton.js +13 -13
- package/dist/app/cli/skills.d.ts +5 -1
- package/dist/app/cli/skills.js +14 -4
- package/dist/app/cli/tray-icons.d.ts +11 -0
- package/dist/app/cli/tray-icons.js +87 -0
- package/dist/app/cli/tray-ids.d.ts +6 -0
- package/dist/app/cli/tray-ids.js +6 -0
- package/dist/app/cli/tray-mac.d.ts +8 -0
- package/dist/app/cli/tray-mac.js +140 -0
- package/dist/app/cli/tray-run.d.ts +1 -0
- package/dist/app/cli/tray-run.js +33 -0
- package/dist/app/cli/tray-windows.d.ts +5 -0
- package/dist/app/cli/tray-windows.js +166 -0
- package/dist/app/cli/tray.d.ts +205 -0
- package/dist/app/cli/tray.js +618 -0
- package/dist/app/cli/usage.d.ts +1 -1
- package/dist/app/cli/usage.js +11 -6
- package/dist/app/dictation/route.js +3 -2
- package/dist/app/http/app.d.ts +5 -10
- package/dist/app/http/app.js +45 -98
- package/dist/app/http/avatars.d.ts +17 -0
- package/dist/app/http/avatars.js +35 -0
- package/dist/app/http/dev-tutor-files.js +1 -1
- package/dist/app/http/parent-home.d.ts +14 -0
- package/dist/app/http/parent-home.js +69 -6
- package/dist/app/http/parent-look.d.ts +2 -0
- package/dist/app/http/parent-look.js +151 -0
- package/dist/app/http/raw-view.js +3 -3
- package/dist/app/http/voice-preview.d.ts +4 -0
- package/dist/app/http/voice-preview.js +34 -0
- package/dist/app/machine/bailian.d.ts +0 -2
- package/dist/app/machine/bailian.js +5 -6
- package/dist/app/mock/mock-live.js +1 -1
- package/dist/app/mock/mock.d.ts +4 -2
- package/dist/app/mock/mock.js +20 -12
- package/dist/app/replay.js +4 -4
- package/dist/app/skills/home-doc.js +1 -1
- package/dist/app/workspace.d.ts +9 -4
- package/dist/app/workspace.js +33 -10
- package/dist/app/ws/route.d.ts +8 -2
- package/dist/app/ws/route.js +7 -3
- package/dist/board/parse.d.ts +0 -2
- package/dist/board/parse.js +0 -6
- package/dist/board/presentation.d.ts +9 -15
- package/dist/board/presentation.js +8 -10
- package/dist/brain/api-tools.js +2 -1
- package/dist/brain/api.d.ts +12 -17
- package/dist/brain/api.js +16 -33
- package/dist/brain/index.d.ts +23 -71
- package/dist/brain/index.js +6 -69
- package/dist/brain/log.d.ts +5 -0
- package/dist/brain/log.js +22 -0
- package/dist/brain/runtime.d.ts +30 -0
- package/dist/brain/runtime.js +23 -0
- package/dist/brain/{cli/stream-json.d.ts → stream-json.d.ts} +2 -4
- package/dist/brain/{cli/stream-json.js → stream-json.js} +0 -4
- package/dist/brain/stream.d.ts +5 -5
- package/dist/brain/stream.js +7 -7
- package/dist/brain/transcript.d.ts +3 -3
- package/dist/brain/transcript.js +6 -6
- package/dist/brain/{cli/types.d.ts → types.d.ts} +3 -48
- package/dist/brain/types.js +14 -0
- package/dist/chalk/glyphs/skeleton.d.ts +3 -3
- package/dist/chalk/glyphs/skeleton.js +1 -1
- package/dist/lib/index.d.ts +1 -1
- package/dist/lib/index.js +1 -1
- package/dist/memory/diary.js +3 -3
- package/dist/realtime/bailian.d.ts +1 -1
- package/dist/realtime/bailian.js +3 -2
- package/dist/realtime/preview.js +1 -1
- package/dist/realtime/voices.d.ts +2 -0
- package/dist/realtime/voices.js +57 -57
- package/dist/runtime/activities.d.ts +13 -1
- package/dist/runtime/activities.js +28 -2
- package/dist/runtime/activity-state.js +2 -2
- package/dist/runtime/agent-file.d.ts +3 -3
- package/dist/runtime/agent-file.js +3 -3
- package/dist/runtime/context/tutor-rules.d.ts +2 -2
- package/dist/runtime/context/tutor-rules.js +3 -3
- package/dist/runtime/home/home.d.ts +12 -1
- package/dist/runtime/home/home.js +8 -1
- package/dist/runtime/home/store.d.ts +1 -1
- package/dist/runtime/home/store.js +36 -21
- package/dist/runtime/journal/mix.d.ts +15 -6
- package/dist/runtime/journal/mix.js +60 -19
- package/dist/runtime/journal/recorder.d.ts +58 -13
- package/dist/runtime/journal/recorder.js +106 -16
- package/dist/runtime/journal/store.d.ts +4 -4
- package/dist/runtime/journal/store.js +6 -6
- package/dist/runtime/kid-input.js +2 -3
- package/dist/runtime/live/call.d.ts +12 -0
- package/dist/runtime/live/call.js +51 -18
- package/dist/runtime/live/turns.js +1 -1
- package/dist/runtime/runner.d.ts +9 -55
- package/dist/runtime/runner.js +52 -173
- package/dist/scenes/paint/painter.js +4 -3
- package/dist/schema/config.d.ts +43 -22
- package/dist/schema/config.js +45 -47
- package/dist/schema/conversation.d.ts +6 -0
- package/dist/schema/home.d.ts +11 -1
- package/dist/schema/home.js +7 -2
- package/dist/schema/workspace.d.ts +27 -0
- package/dist/schema/workspace.js +28 -0
- package/dist/shared/default-port.d.ts +2 -0
- package/dist/shared/default-port.js +2 -0
- package/dist/shared/qianwen.d.ts +14 -10
- package/dist/shared/qianwen.js +26 -22
- package/dist/shared/site-url.d.ts +2 -0
- package/dist/shared/site-url.js +5 -0
- package/dist/ui/dev/dev-page.js +25 -252
- package/dist/ui/dev/dev-theme.d.ts +1 -1
- package/dist/ui/dev/dev-theme.js +1 -1
- package/dist/ui/dev/voice-gallery-page.js +1 -1
- package/dist/ui/kid/board-mark.d.ts +2 -2
- package/dist/ui/kid/board-mark.js +2 -2
- package/dist/ui/kid/home-tiles.d.ts +14 -0
- package/dist/ui/kid/home-tiles.js +44 -0
- package/dist/ui/kid/kid-page.js +68 -103
- package/dist/ui/kid/voice-ui.d.ts +1 -1
- package/dist/ui/kid/voice-ui.js +1 -1
- package/dist/ui/kid/word-emoji.js +5 -3
- package/dist/ui/parent/parent-home-page.js +315 -7
- package/dist/ui/parent/parent-page.js +6 -2
- package/dist/ui/parent/parent-settings-page.d.ts +1 -0
- package/dist/ui/parent/parent-settings-page.js +133 -0
- package/dist/ui/{dev → parent}/qr-page.d.ts +2 -2
- package/dist/ui/{dev → parent}/qr-page.js +19 -14
- package/dist/ui/shared/brand.d.ts +3 -1
- package/dist/ui/shared/brand.js +3 -1
- package/grades/grade-2/README.md +1 -1
- package/grades/grade-2/VALIDATION.md +1 -1
- package/grades/grade-2/agents/chinese-tutor/activities//345/244/215/344/271/240.md +1 -1
- package/grades/grade-2/agents/chinese-tutor/activities//346/{211/271/346/224/271.md → 243/200/346/237/245/344/275/234/344/270/232.md} +4 -4
- package/grades/grade-2/agents/chinese-tutor/activities//346/{211/271/346/224/271. → 243/200/346/237/245/344/275/234/344/270/232.}/344/276/213/345/255/220.md +2 -2
- package/grades/grade-2/agents/chinese-tutor/activities//351/242/204/344/271/240.md +1 -1
- package/grades/grade-2/agents/english-tutor/activities//345/244/215/344/271/240.md +1 -1
- package/grades/grade-2/agents/english-tutor/activities//346/{211/271/346/224/271.md → 243/200/346/237/245/344/275/234/344/270/232.md} +4 -4
- package/grades/grade-2/agents/english-tutor/activities//346/{211/271/346/224/271. → 243/200/346/237/245/344/275/234/344/270/232.}/344/276/213/345/255/220.md +2 -2
- package/grades/grade-2/agents/english-tutor/activities//351/242/204/344/271/240.md +1 -1
- package/grades/grade-2/agents/math-tutor/activities//345/244/215/344/271/240.md +1 -1
- package/grades/grade-2/agents/math-tutor/activities//346/{211/271/346/224/271.md → 243/200/346/237/245/344/275/234/344/270/232.md} +4 -4
- package/grades/grade-2/agents/math-tutor/activities//346/{211/271/346/224/271. → 243/200/346/237/245/344/275/234/344/270/232.}/344/276/213/345/255/220.md +2 -2
- package/grades/grade-2/agents/math-tutor/activities//351/242/204/344/271/240.md +1 -1
- package/package.json +2 -1
- package/skills/cotutor-analyze/references//345/221/275/344/273/244/344/270/216/346/226/207/344/273/266.md +2 -2
- package/skills/cotutor-board/references/photo.md +2 -2
- package/skills/cotutor-home/SKILL.md +1 -0
- package/skills/cotutor-home/references/tutor.md +2 -1
- package/skills/cotutor-home/references//351/246/226/351/241/265/350/257/255/346/263/225.md +2 -1
- package/skills/cotutor-tune/references//345/255/227/346/256/265.md +6 -3
- package/skills/cotutor-tutor/SKILL.md +5 -13
- package/dist/brain/cli/adapters.d.ts +0 -60
- package/dist/brain/cli/adapters.js +0 -91
- package/dist/brain/cli/qwen.d.ts +0 -11
- package/dist/brain/cli/qwen.js +0 -141
- package/dist/brain/cli/run-plan.d.ts +0 -59
- package/dist/brain/cli/run-plan.js +0 -71
- package/dist/brain/cli/run.d.ts +0 -35
- package/dist/brain/cli/run.js +0 -256
- package/dist/brain/cli/types.js +0 -3
- package/dist/brain/cli/warm.d.ts +0 -53
- package/dist/brain/cli/warm.js +0 -103
package/dist/app/workspace.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { ConfigError, UsageError } from '../shared/errors.ts';
|
|
2
2
|
import { redactDeep, redactHome } from '../shared/home.ts';
|
|
3
|
-
import { type RootSource, type Workspace } from '../schema/workspace.ts';
|
|
3
|
+
import { type RootSource, type SandboxMark, type Workspace } from '../schema/workspace.ts';
|
|
4
4
|
import { type CotutorConfig } from '../schema/index.ts';
|
|
5
5
|
export { ConfigError, UsageError, redactDeep, redactHome };
|
|
6
6
|
export type { RootSource, Workspace };
|
|
@@ -29,6 +29,8 @@ export declare function resolveRoot(override?: string, opts?: ResolveOptions): R
|
|
|
29
29
|
/** 校验 cotutor.json 的形状;失败 → ConfigError,信息是逐条修复指南 */
|
|
30
30
|
export declare function parseConfig(raw: unknown, file: string): CotutorConfig;
|
|
31
31
|
export declare function resolvePaths(root: string, paths: Record<string, string>): Workspace['paths'];
|
|
32
|
+
/** 根下的 sandbox 标记(.cotutor/sandbox.json):没有 → null;坏了响亮报错(不当成真 workspace 静默往下跑) */
|
|
33
|
+
export declare function readSandboxMark(root: string): SandboxMark | null;
|
|
32
34
|
export declare function assembleWorkspace(root: string, source: RootSource, config: CotutorConfig): Workspace;
|
|
33
35
|
/** 定根 + 读配置 + 校验 + 展开角色。cotutor.json 缺失也是错(它是必需的)。 */
|
|
34
36
|
export declare function loadWorkspace(override?: string, opts?: ResolveOptions): Workspace;
|
|
@@ -51,8 +53,11 @@ export declare function deepMerge(base: unknown, patch: unknown): unknown;
|
|
|
51
53
|
export declare function patchConfig(ws: Workspace, patch: Record<string, unknown>, opts?: {
|
|
52
54
|
also?: readonly string[];
|
|
53
55
|
}): Promise<CotutorConfig>;
|
|
54
|
-
/**
|
|
55
|
-
|
|
56
|
+
/**
|
|
57
|
+
* 文件内容变了(页面写的、或家长在编辑器改的)就重读;坏了抛 ConfigError,调用方决定是留旧的还是响。
|
|
58
|
+
* 比原文不比修改时间:Windows 上写 .tmp 再 rename 连写两次,修改时间常常一模一样(10-31 实测 200 次里 105 次);文件只有几 K,每次读一遍不贵。
|
|
59
|
+
*/
|
|
60
|
+
export declare function reloadIfChanged(ws: Workspace, lastText: string | null): Promise<{
|
|
56
61
|
ws: Workspace;
|
|
57
|
-
|
|
62
|
+
text: string;
|
|
58
63
|
}>;
|
package/dist/app/workspace.js
CHANGED
|
@@ -7,10 +7,10 @@
|
|
|
7
7
|
* 配置"在但解析不了"响亮报错,不静默走缺省(沿 drawtell 的教训)。
|
|
8
8
|
*/
|
|
9
9
|
import { readFileSync, readdirSync, statSync } from 'node:fs';
|
|
10
|
-
import { writeFile, rename
|
|
10
|
+
import { readFile, writeFile, rename } from 'node:fs/promises';
|
|
11
11
|
import { ConfigError, UsageError } from "../shared/errors.js";
|
|
12
12
|
import { redactDeep, redactHome } from "../shared/home.js";
|
|
13
|
-
import { SKILLS_DIR } from "../schema/workspace.js";
|
|
13
|
+
import { SANDBOX_MARK, SKILLS_DIR, SandboxMarkSchema } from "../schema/workspace.js";
|
|
14
14
|
import { homedir } from 'node:os';
|
|
15
15
|
import { dirname, isAbsolute, join, resolve } from 'node:path';
|
|
16
16
|
import { CotutorConfigSchema, PATH_ROLES, ROLE_DEFAULTS, WORKSPACE_ROLES, explainIssues, } from "../schema/index.js";
|
|
@@ -136,11 +136,24 @@ export function resolvePaths(root, paths) {
|
|
|
136
136
|
out[role] = expandPath(value, vault);
|
|
137
137
|
return out;
|
|
138
138
|
}
|
|
139
|
+
/** 根下的 sandbox 标记(.cotutor/sandbox.json):没有 → null;坏了响亮报错(不当成真 workspace 静默往下跑) */
|
|
140
|
+
export function readSandboxMark(root) {
|
|
141
|
+
const file = join(root, SANDBOX_MARK);
|
|
142
|
+
const raw = readJson(file);
|
|
143
|
+
if (raw === null)
|
|
144
|
+
return null;
|
|
145
|
+
const r = SandboxMarkSchema.safeParse(raw);
|
|
146
|
+
if (!r.success)
|
|
147
|
+
throw new ConfigError(file, explainIssues(r.error.issues).map((l) => ` - ${l}`).join('\n'));
|
|
148
|
+
return r.data;
|
|
149
|
+
}
|
|
139
150
|
export function assembleWorkspace(root, source, config) {
|
|
151
|
+
const sandbox = readSandboxMark(root);
|
|
140
152
|
return {
|
|
141
153
|
root,
|
|
142
154
|
source,
|
|
143
155
|
config,
|
|
156
|
+
...(sandbox ? { sandbox } : {}),
|
|
144
157
|
paths: resolvePaths(root, config.paths),
|
|
145
158
|
dirs: {
|
|
146
159
|
agents: join(root, 'agents'),
|
|
@@ -178,6 +191,7 @@ export function workspaceReport(ws) {
|
|
|
178
191
|
vault: ws.paths.vault,
|
|
179
192
|
tutors: Object.keys(ws.config.tutors),
|
|
180
193
|
runtime: ws.config.runtimes.default,
|
|
194
|
+
...(ws.sandbox ? { sandbox: { from: ws.sandbox.from, forkedAt: ws.sandbox.forkedAt } } : {}),
|
|
181
195
|
});
|
|
182
196
|
}
|
|
183
197
|
// ---- cotutor.json 的补丁写回与热重载(从 runtime/journal/store.ts 搬来,2026-10-29) ----
|
|
@@ -233,13 +247,22 @@ export async function patchConfig(ws, patch, opts = {}) {
|
|
|
233
247
|
await rename(tmp, ws.files.config);
|
|
234
248
|
return r.data;
|
|
235
249
|
}
|
|
236
|
-
/**
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
const
|
|
242
|
-
if (
|
|
250
|
+
/**
|
|
251
|
+
* 文件内容变了(页面写的、或家长在编辑器改的)就重读;坏了抛 ConfigError,调用方决定是留旧的还是响。
|
|
252
|
+
* 比原文不比修改时间:Windows 上写 .tmp 再 rename 连写两次,修改时间常常一模一样(10-31 实测 200 次里 105 次);文件只有几 K,每次读一遍不贵。
|
|
253
|
+
*/
|
|
254
|
+
export async function reloadIfChanged(ws, lastText) {
|
|
255
|
+
const text = await readFile(ws.files.config, 'utf8').catch(() => null);
|
|
256
|
+
if (text === null)
|
|
243
257
|
throw new ConfigError(ws.files.config, '不存在了');
|
|
244
|
-
|
|
258
|
+
if (text === lastText)
|
|
259
|
+
return { ws, text };
|
|
260
|
+
let raw;
|
|
261
|
+
try {
|
|
262
|
+
raw = JSON.parse(text);
|
|
263
|
+
}
|
|
264
|
+
catch (err) {
|
|
265
|
+
throw new ConfigError(ws.files.config, `不是合法 JSON:${err instanceof Error ? err.message : String(err)}`);
|
|
266
|
+
}
|
|
267
|
+
return { ws: assembleWorkspace(ws.root, ws.source, parseConfig(raw, ws.files.config)), text };
|
|
245
268
|
}
|
package/dist/app/ws/route.d.ts
CHANGED
|
@@ -18,8 +18,14 @@ export interface LiveRouteDeps {
|
|
|
18
18
|
echoWaitMs?: number;
|
|
19
19
|
ringMs?: number;
|
|
20
20
|
stallMs?: number;
|
|
21
|
+
idleCutMs?: number;
|
|
22
|
+
maxTakeMs?: number;
|
|
21
23
|
onFinished?(tutor: string, why: string): void;
|
|
22
24
|
}
|
|
23
|
-
|
|
25
|
+
/** 接升级的函数;openCalls = 现在开着几通(serve 看它定这台电脑能不能睡) */
|
|
26
|
+
export type LiveUpgrade = ((req: IncomingMessage, socket: Duplex, head: Buffer, ctx: LiveApp) => Promise<boolean>) & {
|
|
27
|
+
openCalls(): number;
|
|
28
|
+
};
|
|
29
|
+
export declare function makeLiveUpgrade(deps?: LiveRouteDeps): LiveUpgrade;
|
|
24
30
|
/** 探针:COTUTOR_LIVE_ENDPOINT 指到假百炼 */
|
|
25
|
-
export declare const liveUpgrade:
|
|
31
|
+
export declare const liveUpgrade: LiveUpgrade;
|
package/dist/app/ws/route.js
CHANGED
|
@@ -3,7 +3,8 @@ import { acceptWebSocket, isUpgrade } from "./ws.js";
|
|
|
3
3
|
import { cookieOf } from "../http/access.js";
|
|
4
4
|
import { DEVICE_COOKIE } from "../../schema/kid-device.js";
|
|
5
5
|
export function makeLiveUpgrade(deps = {}) {
|
|
6
|
-
|
|
6
|
+
const open = new Set();
|
|
7
|
+
const liveUpgrade = async function liveUpgrade(req, socket, head, ctx) {
|
|
7
8
|
const m = /^\/api\/live\/([a-z0-9][a-z0-9-]*)\/ws(?:\?.*)?$/.exec(req.url ?? '');
|
|
8
9
|
if (!m || !isUpgrade(req))
|
|
9
10
|
return false;
|
|
@@ -20,10 +21,13 @@ export function makeLiveUpgrade(deps = {}) {
|
|
|
20
21
|
onClose: () => call?.onSocketClose(),
|
|
21
22
|
});
|
|
22
23
|
const listen = new URL(req.url ?? '', 'http://x').searchParams.get('listen') === 'open' ? 'open' : 'hold';
|
|
23
|
-
call = new LiveCall(sock, { ctx, tutor, deviceId: cookieOf(req.headers.cookie, DEVICE_COOKIE), listen, endpoint: deps.endpoint, env: deps.env, heartbeatMs: deps.heartbeatMs, keepAliveMs: deps.keepAliveMs, swapTokens: deps.swapTokens, idleSwapMs: deps.idleSwapMs, echoWaitMs: deps.echoWaitMs, ringMs: deps.ringMs, stallMs: deps.stallMs,
|
|
24
|
+
call = new LiveCall(sock, { ctx, tutor, deviceId: cookieOf(req.headers.cookie, DEVICE_COOKIE), listen, endpoint: deps.endpoint, env: deps.env, heartbeatMs: deps.heartbeatMs, keepAliveMs: deps.keepAliveMs, swapTokens: deps.swapTokens, idleSwapMs: deps.idleSwapMs, echoWaitMs: deps.echoWaitMs, ringMs: deps.ringMs, stallMs: deps.stallMs, idleCutMs: deps.idleCutMs, maxTakeMs: deps.maxTakeMs, onFinished: (why) => { if (call)
|
|
25
|
+
open.delete(call); deps.onFinished?.(tutor, why); } });
|
|
26
|
+
open.add(call);
|
|
24
27
|
void call.start();
|
|
25
28
|
return true;
|
|
26
29
|
};
|
|
30
|
+
return Object.assign(liveUpgrade, { openCalls: () => open.size });
|
|
27
31
|
}
|
|
28
32
|
/** 探针:COTUTOR_LIVE_ENDPOINT 指到假百炼 */
|
|
29
|
-
export const liveUpgrade = makeLiveUpgrade(process.env.COTUTOR_LIVE_ENDPOINT ? { endpoint: process.env.COTUTOR_LIVE_ENDPOINT
|
|
33
|
+
export const liveUpgrade = makeLiveUpgrade(process.env.COTUTOR_LIVE_ENDPOINT ? { endpoint: process.env.COTUTOR_LIVE_ENDPOINT } : {});
|
package/dist/board/parse.d.ts
CHANGED
|
@@ -78,8 +78,6 @@ export declare function playsLecture(line: {
|
|
|
78
78
|
cues?: readonly BoardCue[];
|
|
79
79
|
}, cards: readonly BoardCard[]): boolean;
|
|
80
80
|
export declare function parseBoard(text: string, opts?: ParseBoardOptions): ParsedBoard;
|
|
81
|
-
/** 回复停在写成正文的工具调用上(后面只剩空行):老师在等工具的结果,这一轮其实没写完(runner 据此 resume 一次) */
|
|
82
|
-
export declare function endsWithToolCall(text: string): boolean;
|
|
83
81
|
/** 原文一行的角色:解析器把它当什么了 */
|
|
84
82
|
export type SourceRole =
|
|
85
83
|
/** 讲稿一句(念出来) */
|
package/dist/board/parse.js
CHANGED
|
@@ -247,12 +247,6 @@ export function parseBoard(text, opts = {}) {
|
|
|
247
247
|
},
|
|
248
248
|
};
|
|
249
249
|
}
|
|
250
|
-
/** 回复停在写成正文的工具调用上(后面只剩空行):老师在等工具的结果,这一轮其实没写完(runner 据此 resume 一次) */
|
|
251
|
-
export function endsWithToolCall(text) {
|
|
252
|
-
const { spans } = parseBoard(text);
|
|
253
|
-
const last = spans.toolCalls[spans.toolCalls.length - 1];
|
|
254
|
-
return Boolean(last) && text.split('\n').slice(last[1] + 1).every((l) => !l.trim());
|
|
255
|
-
}
|
|
256
250
|
const SECTION_HEAD = /^##\s+(.+?)\s*$/;
|
|
257
251
|
/**
|
|
258
252
|
* 家长端「看原文」用:把老师原文的每一行标上解析器怎么读的。
|
|
@@ -152,7 +152,7 @@ export interface BoardMessage {
|
|
|
152
152
|
reply: string | null;
|
|
153
153
|
pending: boolean;
|
|
154
154
|
section?: BoardSection | null;
|
|
155
|
-
/** 孩子这条带的作业照片(相对 workspace
|
|
155
|
+
/** 孩子这条带的作业照片(相对 workspace 根):答拍给我看卡的进那张卡;别的由老师用 image 卡放上板 */
|
|
156
156
|
photos?: string[];
|
|
157
157
|
/** 看完小课堂后的第一条:这一节前面是小课堂卡 */
|
|
158
158
|
lecture?: KidLecture;
|
|
@@ -163,14 +163,14 @@ export interface BoardMessage {
|
|
|
163
163
|
/** 按住说话的(索引里带 voice):板上去口头禅、一行截断 */
|
|
164
164
|
/** 首页按钮发的:按钮上的字,不是孩子说的 */
|
|
165
165
|
fromHome?: boolean;
|
|
166
|
-
/** 实时的一轮(
|
|
166
|
+
/** 实时的一轮(讲稿嘴照念):板上不看它,一律照实时的画(2026-11-04);只给字幕重放、录像用 */
|
|
167
167
|
live?: boolean;
|
|
168
168
|
}
|
|
169
169
|
/** 板上孩子的一句话:text 是显示的(语音去了口头禅),full 是原话 */
|
|
170
170
|
export interface KidSaid {
|
|
171
171
|
text: string;
|
|
172
172
|
full: string;
|
|
173
|
-
/**
|
|
173
|
+
/** 答拍给我看卡时拍来的照片:放大放在那张卡里(2026-11-04 之前缩在卡下沿) */
|
|
174
174
|
photos?: string[];
|
|
175
175
|
}
|
|
176
176
|
export interface BoardEntry extends BoardSection {
|
|
@@ -178,27 +178,23 @@ export interface BoardEntry extends BoardSection {
|
|
|
178
178
|
at?: string;
|
|
179
179
|
/** 老师还在说:卡只增不改;前 ready 拍就绪了就能播(2026-09-13 之前是整轮跑完才播);整轮跑完换成正式的一节 */
|
|
180
180
|
partial?: boolean;
|
|
181
|
-
/** 孩子问这节时拍的照片(节头上回显) */
|
|
182
|
-
photos?: string[];
|
|
183
181
|
/** 这一节答的是看完小课堂后的第一问:节前画小课堂卡 */
|
|
184
182
|
lecture?: KidLecture;
|
|
185
183
|
/** 这一节答的是孩子从纸上交的画:节前一张孩子的画卡 */
|
|
186
184
|
drawing?: KidDrawing;
|
|
187
|
-
/** 孩子引出这一节的那句话;saidOn =
|
|
185
|
+
/** 孩子引出这一节的那句话;saidOn = 它答的是哪一节的哪张问题卡。节头不是它(看 head):它给左栏用,答拍给我看卡的还进那张卡 */
|
|
188
186
|
said?: KidSaid;
|
|
189
187
|
saidOn?: {
|
|
190
188
|
job: string;
|
|
191
189
|
card: number;
|
|
192
190
|
};
|
|
193
|
-
/**
|
|
194
|
-
live?: true;
|
|
195
|
-
/** 实时:这一轮开头是脑写的节头(只有一行 # 标题的文字卡),从这里起新的一件事,head 是它的标题(liveHead) */
|
|
191
|
+
/** 这一轮开头是脑写的节头(只有一行 # 标题的文字卡),从这里起新的一件事,head 是它的标题(liveHead) */
|
|
196
192
|
head?: string;
|
|
197
|
-
/**
|
|
193
|
+
/** 这一轮开头没写节头,接着上一节画 */
|
|
198
194
|
cont?: true;
|
|
199
195
|
}
|
|
200
196
|
/**
|
|
201
|
-
*
|
|
197
|
+
* 一节怎么接(板上只有实时一种画法,2026-11-04;以前的话题也这样画):新一节的开头由脑定,不再机械是孩子那句。
|
|
202
198
|
* 这一轮第一张卡是节头 → head;没写(只有讲稿、开头是别的卡、还没写出卡)→ cont,接着上一节。一轮中间的节头卡,是从那张卡起新一节
|
|
203
199
|
*/
|
|
204
200
|
export declare function liveHead(s: Pick<BoardSection, 'cards'>): {
|
|
@@ -225,7 +221,7 @@ export declare function kidSaid(messages: readonly BoardMessage[]): Map<string,
|
|
|
225
221
|
}>;
|
|
226
222
|
/**
|
|
227
223
|
* 孩子端条目 → 板书节(没有 section 的条目不出节);
|
|
228
|
-
* 孩子的话挂在它引出的那一节上(said / saidOn,kidSaid);还在跑的只有带 partial 板书(
|
|
224
|
+
* 孩子的话挂在它引出的那一节上(said / saidOn,kidSaid);还在跑的只有带 partial 板书(流式,已有卡或已有句)才出节且标 partial,其余不出。
|
|
229
225
|
*/
|
|
230
226
|
export declare function sectionsFromMessages(messages: readonly BoardMessage[]): BoardEntry[];
|
|
231
227
|
/** 一节在目录里的名字:小节标题 > 有名字的文字卡 > 第一张有字的卡 > 第一句讲稿;截到 14 个字 */
|
|
@@ -269,13 +265,11 @@ export type PlayerEvent = {
|
|
|
269
265
|
type: 'fresh';
|
|
270
266
|
sections: number[];
|
|
271
267
|
};
|
|
272
|
-
/** stop 只停声音(卡的点读、单词);paint = 把某节的标注画齐;
|
|
268
|
+
/** stop 只停声音(卡的点读、单词);paint = 把某节的标注画齐;scrollLast = 滚到最后一节。板上不描「念到哪张」(卡和讲稿不同步,2026-11-04 起以前的话题也这样) */
|
|
273
269
|
export type PlayerEffect = {
|
|
274
270
|
kind: 'stop';
|
|
275
271
|
} | {
|
|
276
272
|
kind: 'render';
|
|
277
|
-
} | {
|
|
278
|
-
kind: 'showNow';
|
|
279
273
|
} | {
|
|
280
274
|
kind: 'paint';
|
|
281
275
|
section: number;
|
|
@@ -433,7 +433,7 @@ export function isQuestion(text) {
|
|
|
433
433
|
return t.endsWith('?') || t.endsWith('?');
|
|
434
434
|
}
|
|
435
435
|
/**
|
|
436
|
-
*
|
|
436
|
+
* 一节怎么接(板上只有实时一种画法,2026-11-04;以前的话题也这样画):新一节的开头由脑定,不再机械是孩子那句。
|
|
437
437
|
* 这一轮第一张卡是节头 → head;没写(只有讲稿、开头是别的卡、还没写出卡)→ cont,接着上一节。一轮中间的节头卡,是从那张卡起新一节
|
|
438
438
|
*/
|
|
439
439
|
export function liveHead(s) {
|
|
@@ -488,16 +488,16 @@ export function kidSaid(messages) {
|
|
|
488
488
|
}
|
|
489
489
|
/**
|
|
490
490
|
* 孩子端条目 → 板书节(没有 section 的条目不出节);
|
|
491
|
-
* 孩子的话挂在它引出的那一节上(said / saidOn,kidSaid);还在跑的只有带 partial 板书(
|
|
491
|
+
* 孩子的话挂在它引出的那一节上(said / saidOn,kidSaid);还在跑的只有带 partial 板书(流式,已有卡或已有句)才出节且标 partial,其余不出。
|
|
492
492
|
*/
|
|
493
493
|
export function sectionsFromMessages(messages) {
|
|
494
494
|
const out = [];
|
|
495
495
|
const says = kidSaid(messages);
|
|
496
496
|
for (const m of messages) {
|
|
497
497
|
const k = says.get(m.job);
|
|
498
|
-
const at = { ...(m.at ? { at: m.at } : {}), ...(m.
|
|
498
|
+
const at = { ...(m.at ? { at: m.at } : {}), ...(m.lecture ? { lecture: m.lecture } : {}), ...(m.drawing ? { drawing: m.drawing } : {}), ...(k ? { said: k.said, ...(k.on ? { saidOn: k.on } : {}) } : {}), ...liveHead(m.section ?? { cards: [] }) };
|
|
499
499
|
if (m.pending) {
|
|
500
|
-
if (m.section && m.section.partial && (m.section.cards.length ||
|
|
500
|
+
if (m.section && m.section.partial && (m.section.cards.length || m.section.lines.length))
|
|
501
501
|
out.push({ job: m.job, ...at, cards: m.section.cards, lines: m.section.lines, partial: true, ready: m.section.ready ?? 0, ...(m.section.layout ? { layout: m.section.layout } : {}) });
|
|
502
502
|
continue;
|
|
503
503
|
}
|
|
@@ -558,7 +558,7 @@ export function step(model, ev, ctx) {
|
|
|
558
558
|
case 'liveStart':
|
|
559
559
|
fx.push({ kind: 'paint', section: ev.section });
|
|
560
560
|
m = { state: { section: ev.section, line: -1, status: 'thinking' } };
|
|
561
|
-
fx.push({ kind: 'render' }
|
|
561
|
+
fx.push({ kind: 'render' });
|
|
562
562
|
break;
|
|
563
563
|
case 'liveBeat':
|
|
564
564
|
if (m.state.section !== ev.section || m.state.status !== 'thinking')
|
|
@@ -569,12 +569,10 @@ export function step(model, ev, ctx) {
|
|
|
569
569
|
fx.push({ kind: 'paint', section: ev.section });
|
|
570
570
|
if (m.state.section !== ev.section)
|
|
571
571
|
break;
|
|
572
|
-
if (m.state.status !== 'thinking')
|
|
573
|
-
fx.push({ kind: 'showNow' });
|
|
572
|
+
if (m.state.status !== 'thinking')
|
|
574
573
|
break;
|
|
575
|
-
}
|
|
576
574
|
m = { state: playerAtEnd(secs) };
|
|
577
|
-
fx.push({ kind: 'render' }
|
|
575
|
+
fx.push({ kind: 'render' });
|
|
578
576
|
break;
|
|
579
577
|
case 'liveDropped':
|
|
580
578
|
fx.push({ kind: 'stop' });
|
|
@@ -587,7 +585,7 @@ export function step(model, ev, ctx) {
|
|
|
587
585
|
for (const i of ev.sections)
|
|
588
586
|
fx.push({ kind: 'paint', section: i });
|
|
589
587
|
m = { state: playerAtEnd(secs) };
|
|
590
|
-
fx.push({ kind: 'render' }, { kind: '
|
|
588
|
+
fx.push({ kind: 'render' }, { kind: 'scrollLast' });
|
|
591
589
|
break;
|
|
592
590
|
}
|
|
593
591
|
return { model: m, effects: fx };
|
package/dist/brain/api-tools.js
CHANGED
|
@@ -137,8 +137,9 @@ export function readOnlyTools(roots, opts = {}) {
|
|
|
137
137
|
const relOf = async (p) => {
|
|
138
138
|
for (const root of await realRoots()) {
|
|
139
139
|
const rel = relative(root, p);
|
|
140
|
+
// 模型给的通配按 / 写(如 随便/*期中*.md),Windows 上 relative 给的是反斜杠
|
|
140
141
|
if (!outside(rel))
|
|
141
|
-
return rel;
|
|
142
|
+
return rel.split(sep).join('/');
|
|
142
143
|
}
|
|
143
144
|
return p;
|
|
144
145
|
};
|
package/dist/brain/api.d.ts
CHANGED
|
@@ -1,57 +1,52 @@
|
|
|
1
1
|
import { type ModelMessage } from 'ai';
|
|
2
|
-
import type
|
|
3
|
-
import { type RunPlan } from './cli/run-plan.ts';
|
|
2
|
+
import { type RunPlan } from './runtime.ts';
|
|
4
3
|
import { type ApiRuntime, type CotutorConfig, type Policy } from '../schema/index.ts';
|
|
5
|
-
import type { BrainHooks, BrainRequest, BrainRun
|
|
4
|
+
import type { BrainHooks, BrainRequest, BrainRun } from './index.ts';
|
|
6
5
|
/** 计划里记的 cli 名:API 的轮次按它分流 */
|
|
7
6
|
export declare const API_CLI = "api";
|
|
8
7
|
/**
|
|
9
8
|
* 出厂的 API 运行时(2026-10-31 起新装缺省 api,Qwen Code 那两条留作备用):模型同那两条——api 对 qwen(qwen3.8-max),api-flash 对 qwen-flash(qwen3.8-flash)
|
|
10
9
|
*/
|
|
11
10
|
export declare const FACTORY_API_RUNTIMES: Record<string, ApiRuntime>;
|
|
12
|
-
/** .log 的一行 → 统一事件(API 轮次的日志就是一行一条统一事件);不是这种行回 [] */
|
|
13
|
-
export declare function parseEventLine(line: string): CliEvent[];
|
|
14
11
|
/** 政策 effort → 思考开关与思考量(同 qwen:low 不想,孩子在等;带工具的轮次至少想一点,不然照片题连资料都不查) */
|
|
15
12
|
export declare function thinkingOf(effort: Policy['effort'] | undefined, tools: BrainRequest['tools']): {
|
|
16
13
|
enableThinking: boolean;
|
|
17
14
|
thinkingBudget?: number;
|
|
18
15
|
};
|
|
19
16
|
/**
|
|
20
|
-
*
|
|
17
|
+
* 有思考档的模型发 reasoning_effort(照当时 Qwen Code 的档):想一点 = low、不限 = xhigh;不想或没有档 → null,照 thinkingOf 发。
|
|
21
18
|
* low 是它自己看着想:闲聊想一两百 token,认照片题想得多;thinking_budget 是死卡,1024 时有照片的话题每轮都想满(2026-10-31)
|
|
22
19
|
*/
|
|
23
20
|
export declare function effortOf(model: string, effort: Policy['effort'] | undefined, tools: BrainRequest['tools']): 'low' | 'xhigh' | null;
|
|
24
21
|
/** AI SDK 的 alibaba 只认 thinking_budget:在 fetch 这一层把请求体里的换成 reasoning_effort(SDK 给的中间件口子) */
|
|
25
22
|
export declare function effortFetch(effort: 'low' | 'xhigh', base?: typeof fetch): typeof fetch;
|
|
26
|
-
/**
|
|
27
|
-
* 百炼 Chat Completions 的端点:知道业务空间 id 就走它的域名(原型验过,同 realtime);只有按量 key 就走公网域名(qwen 那边按量的线也走它,2026-10-29 真跑通)
|
|
28
|
-
*/
|
|
29
|
-
export declare function bailianBase(workspaceId: string | null): string;
|
|
30
23
|
/**
|
|
31
24
|
* 已经结束的一轮,拼回历史时的老师那条:正文照原样,思考不带,工具往返换成一句「(用了 …,回了 N 字;结果这里不再带,要看再查)」。
|
|
32
25
|
* 只剩一条纯文字的回复,历史里永远没有配不上对的工具消息
|
|
33
26
|
*/
|
|
34
27
|
export declare function slimTurn(messages: readonly ModelMessage[]): string;
|
|
28
|
+
/** 这个模型发到哪、带哪把 key(src/shared/qianwen.ts 的 compatibleLine);null = 没配 */
|
|
29
|
+
export type BrainLine = (model: string) => {
|
|
30
|
+
baseURL: string;
|
|
31
|
+
key: string;
|
|
32
|
+
plan: boolean;
|
|
33
|
+
} | null;
|
|
35
34
|
export declare class ApiBrain {
|
|
36
35
|
private readonly workspace;
|
|
37
|
-
private readonly
|
|
38
|
-
/** 测试与探针:…/compatible-mode/v1(假百炼)
|
|
36
|
+
private readonly line;
|
|
37
|
+
/** 测试与探针:…/compatible-mode/v1(假百炼);不给按模型选线 */
|
|
39
38
|
private readonly endpoint;
|
|
40
39
|
constructor(opts: {
|
|
41
40
|
workspace: () => {
|
|
42
41
|
config: CotutorConfig;
|
|
43
42
|
root: string;
|
|
44
43
|
};
|
|
45
|
-
|
|
46
|
-
key: string;
|
|
47
|
-
workspaceId: string | null;
|
|
48
|
-
} | null;
|
|
44
|
+
line: BrainLine;
|
|
49
45
|
endpoint?: string;
|
|
50
46
|
});
|
|
51
47
|
plan(req: BrainRequest): RunPlan;
|
|
52
48
|
start(req: BrainRequest, plan: RunPlan, ctx: {
|
|
53
49
|
tutor: string;
|
|
54
|
-
key: WarmKey;
|
|
55
50
|
files: {
|
|
56
51
|
log: string;
|
|
57
52
|
err: string;
|
package/dist/brain/api.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* 脑执行的 API 实现(2026-10-29):不起 CLI,应用在服务进程里直连百炼的 Chat Completions(AI SDK + @ai-sdk/alibaba,cotutor-api-prototype 验过这条路)。
|
|
3
|
+
* - 发到哪按模型选(src/shared/qianwen.ts 的 compatibleLine):Token Plan 有这个模型、本机有 token-plan.json 就走 Token Plan(2026-11-02 起),否则百炼按量
|
|
3
4
|
* - 系统提示 = runner 给的 systemBody(老师正文或 activity + 板书写法);消息 = 上下文包 + 孩子的话,本轮的照片直接附成图,不靠老师自己去读
|
|
4
5
|
* - 同一话题的模型历史应用自己存:<日期>.<话题>.brain.jsonl,一行一轮(会话 id、模型、这轮的用户消息、模型回的全部消息、成没成);
|
|
5
6
|
* 接着话题时把同一会话的那几行拼回去。照片只记相对 workspace 根的路径,发的时候再编码。没回完的一轮只留写出来的正文,下一条消息前头注明
|
|
@@ -20,11 +21,11 @@ import { isStepCount, streamText } from 'ai';
|
|
|
20
21
|
import { randomUUID } from 'node:crypto';
|
|
21
22
|
import { createWriteStream } from 'node:fs';
|
|
22
23
|
import { appendFile, mkdir, readFile, stat, writeFile } from 'node:fs/promises';
|
|
23
|
-
import { dirname, extname, isAbsolute, join, relative } from 'node:path';
|
|
24
|
-
import { getRuntime } from "./
|
|
24
|
+
import { dirname, extname, isAbsolute, join, relative, sep } from 'node:path';
|
|
25
|
+
import { getRuntime } from "./runtime.js";
|
|
26
|
+
import { parseEventLine } from "./log.js";
|
|
25
27
|
import { createPartialReader } from "./stream.js";
|
|
26
28
|
import { parseTranscript, toolCalls, toolSummary } from "./transcript.js";
|
|
27
|
-
import { compatibleBase, DASHSCOPE_HOST } from "../shared/qianwen.js";
|
|
28
29
|
import { isApiRuntime } from "../schema/index.js";
|
|
29
30
|
import { readOnlyTools } from "./api-tools.js";
|
|
30
31
|
/** 计划里记的 cli 名:API 的轮次按它分流 */
|
|
@@ -39,7 +40,7 @@ export const FACTORY_API_RUNTIMES = {
|
|
|
39
40
|
const DEFAULT_STEPS = 6;
|
|
40
41
|
/** 想一点:没有思考档的模型思考最多多少 token(有档的发 reasoning_effort,见 effortOf) */
|
|
41
42
|
const THINK_LITTLE = 1024;
|
|
42
|
-
/**
|
|
43
|
+
/** 有思考档的模型:qwen3.8 系列(2026-10-31 照当时 Qwen Code 的档对过),百炼兼容接口认 reasoning_effort */
|
|
43
44
|
const TIERED = /^qwen3\.8-/;
|
|
44
45
|
/** 一张照片最大多少字节(百炼按 base64 收,太大的不附,记一条提醒) */
|
|
45
46
|
const IMAGE_MAX = 10 * 1024 * 1024;
|
|
@@ -48,18 +49,6 @@ const MIME = { '.png': 'image/png', '.jpg': 'image/jpeg', '.jpeg': 'image/jpeg',
|
|
|
48
49
|
const CODEC = 'ai-sdk@7';
|
|
49
50
|
/** 上一轮没回完:接在下一条消息前头 */
|
|
50
51
|
const UNFINISHED = '(应用注:上一轮你没有回完,上面是写出来的那部分;孩子可能听到了一些,也可能没有,别当它已经说完。)';
|
|
51
|
-
/** .log 的一行 → 统一事件(API 轮次的日志就是一行一条统一事件);不是这种行回 [] */
|
|
52
|
-
export function parseEventLine(line) {
|
|
53
|
-
if (!line.startsWith('{"kind":'))
|
|
54
|
-
return [];
|
|
55
|
-
try {
|
|
56
|
-
const e = JSON.parse(line);
|
|
57
|
-
return typeof e.kind === 'string' ? [e] : [];
|
|
58
|
-
}
|
|
59
|
-
catch {
|
|
60
|
-
return [];
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
52
|
/** 政策 effort → 思考开关与思考量(同 qwen:low 不想,孩子在等;带工具的轮次至少想一点,不然照片题连资料都不查) */
|
|
64
53
|
export function thinkingOf(effort, tools) {
|
|
65
54
|
if (effort === 'high')
|
|
@@ -69,7 +58,7 @@ export function thinkingOf(effort, tools) {
|
|
|
69
58
|
return { enableThinking: false };
|
|
70
59
|
}
|
|
71
60
|
/**
|
|
72
|
-
*
|
|
61
|
+
* 有思考档的模型发 reasoning_effort(照当时 Qwen Code 的档):想一点 = low、不限 = xhigh;不想或没有档 → null,照 thinkingOf 发。
|
|
73
62
|
* low 是它自己看着想:闲聊想一两百 token,认照片题想得多;thinking_budget 是死卡,1024 时有照片的话题每轮都想满(2026-10-31)
|
|
74
63
|
*/
|
|
75
64
|
export function effortOf(model, effort, tools) {
|
|
@@ -87,12 +76,6 @@ export function effortFetch(effort, base = fetch) {
|
|
|
87
76
|
return base(input, { ...init, body: JSON.stringify({ ...body, reasoning_effort: effort }) });
|
|
88
77
|
};
|
|
89
78
|
}
|
|
90
|
-
/**
|
|
91
|
-
* 百炼 Chat Completions 的端点:知道业务空间 id 就走它的域名(原型验过,同 realtime);只有按量 key 就走公网域名(qwen 那边按量的线也走它,2026-10-29 真跑通)
|
|
92
|
-
*/
|
|
93
|
-
export function bailianBase(workspaceId) {
|
|
94
|
-
return workspaceId ? `https://${workspaceId}.cn-beijing.maas.aliyuncs.com/compatible-mode/v1` : compatibleBase(DASHSCOPE_HOST);
|
|
95
|
-
}
|
|
96
79
|
/** 报错只说状态码:SDK 的异常里带着整个请求 */
|
|
97
80
|
function failure(err) {
|
|
98
81
|
const status = err && typeof err === 'object' && 'statusCode' in err ? err.statusCode : undefined;
|
|
@@ -213,12 +196,12 @@ async function pastOf(file, session, root, current) {
|
|
|
213
196
|
}
|
|
214
197
|
export class ApiBrain {
|
|
215
198
|
workspace;
|
|
216
|
-
|
|
217
|
-
/** 测试与探针:…/compatible-mode/v1(假百炼)
|
|
199
|
+
line;
|
|
200
|
+
/** 测试与探针:…/compatible-mode/v1(假百炼);不给按模型选线 */
|
|
218
201
|
endpoint;
|
|
219
202
|
constructor(opts) {
|
|
220
203
|
this.workspace = opts.workspace;
|
|
221
|
-
this.
|
|
204
|
+
this.line = opts.line;
|
|
222
205
|
this.endpoint = opts.endpoint;
|
|
223
206
|
}
|
|
224
207
|
plan(req) {
|
|
@@ -247,11 +230,11 @@ export class ApiBrain {
|
|
|
247
230
|
const session = plan.session ?? `api-${randomUUID()}`;
|
|
248
231
|
event({ kind: 'session', id: session });
|
|
249
232
|
const images = req.images ?? [];
|
|
250
|
-
const stored = images.map((p) => { const rel = relative(root, p); return rel.startsWith('..') || isAbsolute(rel) ? p : rel; });
|
|
233
|
+
const stored = images.map((p) => { const rel = relative(root, p); return rel.startsWith('..') || isAbsolute(rel) ? p : rel.split(sep).join('/'); });
|
|
251
234
|
const record = { v: 1, session, at: new Date().toISOString(), model: runtime.model, codec: CODEC, user: { text: req.prompt, images: stored }, messages: [], ok: false };
|
|
252
235
|
let final;
|
|
253
|
-
const
|
|
254
|
-
if (!
|
|
236
|
+
const line = this.line(runtime.model);
|
|
237
|
+
if (!line && !this.endpoint) {
|
|
255
238
|
final = { ok: false, text: null, reason: 'auth:没有百炼按量 key' };
|
|
256
239
|
warnings.push('百炼还没配好(按量 key):家长端「首页」里填,这位老师的运行时是 API 的,没有它说不了话');
|
|
257
240
|
}
|
|
@@ -267,7 +250,7 @@ export class ApiBrain {
|
|
|
267
250
|
}
|
|
268
251
|
const user = await userMessage(text, attach, root, warnings);
|
|
269
252
|
const tier = effortOf(runtime.model, req.effort, req.tools);
|
|
270
|
-
const alibaba = createAlibaba({ apiKey:
|
|
253
|
+
const alibaba = createAlibaba({ apiKey: line?.key ?? 'sk-endpoint', baseURL: this.endpoint ?? line.baseURL, includeUsage: true, ...(tier ? { fetch: effortFetch(tier) } : {}) });
|
|
271
254
|
// 断流看门狗:stall.ms 没有一块流到就停(工具在本进程里跑,几毫秒的事,不另算)
|
|
272
255
|
const abort = new AbortController();
|
|
273
256
|
let stalled = false;
|
|
@@ -296,7 +279,7 @@ export class ApiBrain {
|
|
|
296
279
|
try {
|
|
297
280
|
const result = streamText({
|
|
298
281
|
model: alibaba(runtime.model),
|
|
299
|
-
...(req.systemBody
|
|
282
|
+
...(req.systemBody ? { system: req.systemBody } : {}),
|
|
300
283
|
messages: [...past, user],
|
|
301
284
|
...(req.tools !== 'off' ? { tools: readOnlyTools(req.readDirs, { images }) } : {}),
|
|
302
285
|
stopWhen: isStepCount(runtime.maxSteps ?? DEFAULT_STEPS),
|
|
@@ -399,13 +382,13 @@ export class ApiBrain {
|
|
|
399
382
|
hooks.onExit(Date.now());
|
|
400
383
|
await new Promise((r) => out.end(r));
|
|
401
384
|
// 没配凭证的那轮连请求都没发:不进历史
|
|
402
|
-
if (
|
|
385
|
+
if (line || this.endpoint)
|
|
403
386
|
await appendFile(files.history, `${JSON.stringify(record)}\n`).catch(() => { });
|
|
404
387
|
const logText = await readFile(files.log, 'utf8').catch(() => '');
|
|
405
388
|
const transcript = parseTranscript(logText, parseEventLine);
|
|
406
389
|
const tools = toolCalls(logText, parseEventLine).slice(0, 200);
|
|
407
390
|
return { transcript: { ...transcript, final: transcript.final ?? final }, tools, warnings };
|
|
408
391
|
};
|
|
409
|
-
return {
|
|
392
|
+
return { text: () => reader.text(), done: go() };
|
|
410
393
|
}
|
|
411
394
|
}
|