fix(minecraft): llm can now obtain expression results
Add rewriteTrailingExpressionToReturn helper that rewrites final non-statement expressions to return statements, apply to both normalizeReplCode and LLM planner evaluation paths to capture trailing expression values (e.g. `inv;` returns inventory array), skip rewrite if code contains explicit return/ends with block syntax/is statement keyword, add tests verifying debug REPL and multi-line LLM scripts capture trailing expression
This commit is contained in:
@@ -72,6 +72,18 @@ function createPerceptionEvent() {
|
||||
}
|
||||
|
||||
describe('brain no-action follow-up', () => {
|
||||
it('returns trailing expression values in debug repl scripts', async () => {
|
||||
const brain: any = new Brain(createDeps('await skip()'))
|
||||
|
||||
const result = await brain.executeDebugRepl(`
|
||||
const inv = [{ name: 'oak_sapling', count: 1 }]
|
||||
inv;
|
||||
`)
|
||||
|
||||
expect(result.error).toBeUndefined()
|
||||
expect(result.returnValue).toContain('oak_sapling')
|
||||
})
|
||||
|
||||
it('queues exactly one synthetic follow-up on no-action result', async () => {
|
||||
const brain: any = new Brain(createDeps('1 + 1'))
|
||||
const enqueueSpy = vi.fn(async () => undefined)
|
||||
@@ -88,6 +100,21 @@ describe('brain no-action follow-up', () => {
|
||||
})
|
||||
})
|
||||
|
||||
it('captures trailing expression return for llm multi-line scripts', async () => {
|
||||
const brain: any = new Brain(createDeps(`
|
||||
const inv = [{ name: 'oak_sapling', count: 1 }]
|
||||
inv;
|
||||
`))
|
||||
const enqueueSpy = vi.fn(async () => undefined)
|
||||
brain.enqueueEvent = enqueueSpy
|
||||
|
||||
await brain.processEvent({} as any, createPerceptionEvent())
|
||||
|
||||
expect(enqueueSpy).toHaveBeenCalledTimes(1)
|
||||
const queuedEvent = (enqueueSpy.mock.calls[0] as any[])?.[1]
|
||||
expect(queuedEvent?.payload?.returnValue).toContain('oak_sapling')
|
||||
})
|
||||
|
||||
it('does not chain follow-up from follow-up event source', async () => {
|
||||
const brain: any = new Brain(createDeps('1 + 1'))
|
||||
const enqueueSpy = vi.fn(async () => undefined)
|
||||
|
||||
@@ -389,10 +389,44 @@ export class Brain {
|
||||
private normalizeReplCode(code: string): string {
|
||||
// Simple top-level var persistence for REPL UX:
|
||||
// const/let/var foo = ... -> globalThis.foo = ...
|
||||
return code.replace(
|
||||
const normalized = code.replace(
|
||||
/^\s*(?:const|let|var)\s+([A-Za-z_$][\w$]*)\s*=\s*([^\n]+?)\s*(?:;\s*)?$/gm,
|
||||
(_full: string, name: string, valueExpr: string) => `globalThis.${name} = ${valueExpr}`,
|
||||
)
|
||||
return this.rewriteTrailingExpressionToReturn(normalized)
|
||||
}
|
||||
|
||||
private rewriteTrailingExpressionToReturn(code: string): string {
|
||||
if (/\breturn\b/.test(code))
|
||||
return code
|
||||
|
||||
const lines = code.split('\n')
|
||||
let lastIndex = -1
|
||||
for (let i = lines.length - 1; i >= 0; i--) {
|
||||
if (lines[i].trim().length > 0) {
|
||||
lastIndex = i
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if (lastIndex < 0)
|
||||
return code
|
||||
|
||||
const rawLine = lines[lastIndex]
|
||||
const trimmedLine = rawLine.trim()
|
||||
if (trimmedLine.length === 0 || trimmedLine.startsWith('//'))
|
||||
return code
|
||||
if (trimmedLine === '}' || trimmedLine.endsWith('{'))
|
||||
return code
|
||||
|
||||
const expression = trimmedLine.replace(/;$/, '').trim()
|
||||
const blocked = /^(?:if|for|while|switch|const|let|var|function|class|try|catch|finally|return|throw|import|export)\b/
|
||||
if (blocked.test(expression))
|
||||
return code
|
||||
|
||||
const indent = rawLine.match(/^\s*/)?.[0] ?? ''
|
||||
lines[lastIndex] = `${indent}return (${expression})`
|
||||
return lines.join('\n')
|
||||
}
|
||||
|
||||
private toDebugReplActions(actions: Array<{
|
||||
@@ -766,7 +800,7 @@ export class Brain {
|
||||
|
||||
const codeToEvaluate = this.planner.canEvaluateAsExpression(result)
|
||||
? `return (\n${result}\n)`
|
||||
: result
|
||||
: this.rewriteTrailingExpressionToReturn(result)
|
||||
|
||||
const runResult = await this.planner.evaluate(
|
||||
codeToEvaluate,
|
||||
|
||||
Reference in New Issue
Block a user