feat: improve agent model selection and remote source analysis; bump to 1.2.23
This commit is contained in:
@@ -11,7 +11,10 @@ function compile(source, globals = {}) {
|
||||
}).outputText, { exports, DOMException, Error, ...globals })
|
||||
return exports
|
||||
}
|
||||
const context = compile(fs.readFileSync(require.resolve('../src/renderer/src/features/workspace/agentContext.ts'), 'utf8'))
|
||||
const evidenceApi = compile(fs.readFileSync(require.resolve('../src/renderer/src/features/workspace/agentEvidence.ts'), 'utf8'))
|
||||
const context = compile(fs.readFileSync(require.resolve('../src/renderer/src/features/workspace/agentContext.ts'), 'utf8'), {
|
||||
require: name => { assert.equal(name, './agentEvidence'); return evidenceApi }
|
||||
})
|
||||
const source = ts.createSourceFile('useAiAgent.ts', fs.readFileSync(require.resolve('../src/renderer/src/features/workspace/useAiAgent.ts'), 'utf8'), ts.ScriptTarget.Latest, true)
|
||||
const hook = source.statements.find(node => ts.isFunctionDeclaration(node) && node.name?.text === 'useAiAgent')
|
||||
const functions = hook.body.statements.filter(node => ts.isVariableStatement(node) && ['requestPlan', 'stop'].includes(node.declarationList.declarations[0].name.getText(source)))
|
||||
@@ -27,11 +30,13 @@ function harness(requestApi) {
|
||||
class Clock extends Date { static now() { return now } }
|
||||
const state = { pending: false, activity: '', steps: [], messages: [], saved: [], notices: 0, requestStartedAt: null }
|
||||
const messageStateRef = { current: { session } }
|
||||
const selectedModel = { provider: 'QWEN', model: 'fixture', imagesEnabled: true }
|
||||
const api = compile(functions.map(node => `export ${node.getText(source)}`).join('\n'), {
|
||||
...context, session, messageStateRef, AbortController,
|
||||
...context, session, messageStateRef, AbortController, FormData,
|
||||
Date: Clock,
|
||||
courseId: 'course', weekNumber: 3, pending: false, historyLoading: false,
|
||||
status: { enabled: true }, abortControllerRef: { current: null },
|
||||
courseId: 'course', weekNumber: 3, pending: false, historyLoading: false, statusLoading: false,
|
||||
selectedModel,
|
||||
status: { enabled: true, selectionSupported: true }, abortControllerRef: { current: null },
|
||||
setPending: value => { state.pending = value },
|
||||
setRequestStartedAt: value => { state.requestStartedAt = value },
|
||||
setActivity: value => { state.activity = value },
|
||||
@@ -39,7 +44,7 @@ function harness(requestApi) {
|
||||
updateMessages: update => { state.messages = update(state.messages); state.saved.push(plain(state.messages)) },
|
||||
onResponseRef: { current: () => { state.notices++ } }, requestApi
|
||||
})
|
||||
return { ...api, state, messageStateRef, advance: ms => { now += ms } }
|
||||
return { ...api, state, selectedModel, messageStateRef, advance: ms => { now += ms } }
|
||||
}
|
||||
|
||||
test('publishes completed stages immediately without persisting progress or adding model calls', async () => {
|
||||
@@ -76,6 +81,58 @@ test('publishes completed stages immediately without persisting progress or addi
|
||||
assert.equal(h.state.notices, 1)
|
||||
})
|
||||
|
||||
test('image uploads and every lookup round retain the original provider without uploading the image twice', async () => {
|
||||
const bodies = []
|
||||
let uploads = 0
|
||||
const h = harness(async (path, request) => {
|
||||
if (path.endsWith('/attachments/images')) {
|
||||
uploads++
|
||||
assert.equal(request.body.get('provider'), 'QWEN')
|
||||
assert.equal(request.body.get('courseId'), 'course')
|
||||
assert.equal(request.body.get('week'), '3')
|
||||
assert.equal(request.body.get('file').name, 'screen.png')
|
||||
return { token: 'image-reference', name: 'screen.png', url: 'https://images.test/screen.png' }
|
||||
}
|
||||
bodies.push(JSON.parse(request.body))
|
||||
h.selectedModel.provider = 'OPENAI'
|
||||
return bodies.length === 1
|
||||
? { ...response(), plan: { ...response().plan, requestedModules: ['Control'] } }
|
||||
: response()
|
||||
})
|
||||
await h.requestPlan('이미지 확인', [{ name: 'Control', files: [] }], [], {
|
||||
attachments: [{ kind: 'image', name: 'screen.png', file: new Blob(['fixture'], { type: 'image/png' }) }],
|
||||
loadRequestedContext: async () => ({ modules: [{ name: 'Control', files: [{ path: 'config/control.yaml', content: 'speed: 0.3' }] }], runtimeLogs: [] })
|
||||
})
|
||||
assert.equal(uploads, 1)
|
||||
assert.equal(bodies.length, 2)
|
||||
assert.ok(bodies.every(body => body.provider === 'QWEN'))
|
||||
assert.ok(bodies.every(body => body.imageTokens[0] === 'image-reference'))
|
||||
assert.equal(h.state.messages.at(-1).model, 'fixture')
|
||||
})
|
||||
|
||||
test('remote target and observed source ranges reach each provider request including finalization', async () => {
|
||||
const bodies = []
|
||||
const tool = { id: 'read', name: 'files.read', moduleName: 'Control', args: { path: 'config/car.yaml', startLine: 20, lineCount: 1 } }
|
||||
const h = harness(async (_, request) => {
|
||||
bodies.push(JSON.parse(request.body))
|
||||
return bodies.length < 3 ? response([tool]) : response()
|
||||
})
|
||||
await h.requestPlan('속도 제한 코드 위치', [{ name: 'Control', state: 'running', parameters: ['sim_mode:=false'], files: [] }], [], {
|
||||
environment: 'remote', workspaceTarget: 'ssh:100.64.0.10',
|
||||
executeTool: async input => result({ id: input.id, moduleName: 'Control', target: 'ssh:100.64.0.10', truncated: true,
|
||||
data: JSON.stringify({ path: 'config/car.yaml', exists: true, hash: 'a'.repeat(64), complete: false, content: 'max_speed: 0.4', startLine: 20, endLine: 20, contentTruncated: false, totalLines: 100 }) })
|
||||
})
|
||||
assert.equal(bodies.length, 3)
|
||||
for (const body of bodies) {
|
||||
assert.equal(body.environment, 'remote')
|
||||
assert.equal(body.workspaceTarget, 'ssh:100.64.0.10')
|
||||
assert.deepEqual(body.modules[0].parameters, ['sim_mode:=false'])
|
||||
}
|
||||
assert.equal(JSON.parse(bodies[1].toolResults[0].data).content, 'max_speed: 0.4')
|
||||
assert.equal(JSON.parse(bodies[2].toolResults[0].data).endLine, 20)
|
||||
assert.match(bodies[2].request, /자료 조회는 종료되었습니다/)
|
||||
})
|
||||
|
||||
test('errors replace temporary progress with the existing error reply', async () => {
|
||||
let calls = 0
|
||||
const h = harness(async () => {
|
||||
|
||||
Reference in New Issue
Block a user