fix(coding-teacher): restore context and read project files

This commit is contained in:
2026-09-22 19:12:59 +08:00
parent 7f5131e92f
commit 44e754a43e
14 changed files with 626 additions and 37 deletions

View File

@@ -4,6 +4,9 @@ import * as cloud from '../../electron/coding-teacher/config-client';
import * as transport from '../../electron/utils/proxy-fetch';
import { prepareTeacherModel } from '../../electron/coding-teacher/model-runner';
import type { TeacherDefinition } from '../../shared/coding-teacher';
import { mkdtemp, rm, writeFile } from 'node:fs/promises';
import { tmpdir } from 'node:os';
import path from 'node:path';
const account: cloud.TeacherAccount = {
id: '11111111-1111-4111-8111-111111111111',
@@ -48,6 +51,34 @@ function setup(canDisable = true) {
afterEach(() => vi.restoreAllMocks());
describe('teacher published reasoning wire contract', () => {
it('reads a current-project file requested by the model and returns its result for the answer', async () => {
const fetch = setup();
const root = await mkdtemp(path.join(tmpdir(), 'teacher-model-files-'));
try {
await writeFile(path.join(root, 'game.ts'), 'export const gravity = 0.6;');
fetch.mockResolvedValueOnce(new Response([
{ choices: [{ delta: { tool_calls: [{ index: 0, id: 'call-read', type: 'function', function: { name: 'read_project_file', arguments: '{"path":"game.' } }] } }] },
{ choices: [{ delta: { tool_calls: [{ index: 0, function: { arguments: 'ts"}' } }] }, finish_reason: 'tool_calls' }], usage: { prompt_tokens: 100, completion_tokens: 15 } },
].map(event => 'data: ' + JSON.stringify(event) + '\n\n').join('') + 'data: [DONE]\n\n'));
const prepared = await prepareTeacherModel(account, definition({ mode: 'disabled' }), {
projectPath: root,
source: { messages: [], cursor: { workerGeneration: 1, seq: 1 }, capturedAt: '2026-09-22' },
assertCurrent: () => undefined,
});
const onText = vi.fn();
await prepared.run([{ role: 'user', content: '查看 game.ts 的重力设置' }], new AbortController().signal, onText);
expect(fetch).toHaveBeenCalledTimes(2);
const first = JSON.parse(String(fetch.mock.calls[0][1]?.body));
expect(first.tools.map((tool: { function: { name: string } }) => tool.function.name)).toContain('read_project_file');
const second = JSON.parse(String(fetch.mock.calls[1][1]?.body));
expect(second.messages.at(-1)).toMatchObject({ role: 'tool', tool_call_id: 'call-read' });
expect(second.messages.at(-1).content).toContain('export const gravity = 0.6;');
expect(onText).toHaveBeenCalledWith('Explanation');
} finally {
await rm(root, { recursive: true, force: true });
}
});
it.each([
[{ mode: 'enabled', effort: null }, { thinking: { type: 'enabled' } }],
[{ mode: 'enabled' }, { thinking: { type: 'enabled' } }],
@@ -76,4 +107,4 @@ describe('teacher published reasoning wire contract', () => {
.rejects.toThrow('老师所用思考选项已不可用,请联系运营调整。');
expect(fetch).not.toHaveBeenCalled();
});
});
});