Files
orca/src/shared/commit-message-plan-kimi.test.ts
T
Brennan Bensonandbeduldul 47018d4844 fix(source-control): decode Kimi generated text by selected format (#34237)
* fix(source-control): read the Kimi answer from stream-json

Kimi Code's text mode prefixes thinking and hook output with a bullet, so the
commit message arrived mixed with narration. The recipe now asks for
--output-format stream-json and the answer is taken from the last assistant
record, which is the commit message (#33556).

A record that requests a tool is narration rather than an answer, so it clears
the candidate: a stream that ends on a tool call yields no answer, which
finalizeFromAgentOutput already treats as a failed run. Only a body whose every
non-empty line is a record counts as stream output, so a plain answer that
happens to contain a JSON line is left alone.

* fix(source-control): decode Kimi output only for the selected protocol

* fix(source-control): satisfy generation hook checks

* Preserve Kimi generation failures and normalize options linearly

* Fix Kimi argument failure messages

* Bound Kimi option failure recognition

* Fix Kimi generation test fixture types

---------

Co-authored-by: beduldul <alkaysan07@gmail.com>
2026-10-27 16:33:33 -07:00

81 lines
2.7 KiB
TypeScript

import { expect, it } from 'vitest'
import { planCommitMessageGeneration } from './commit-message-plan'
import { KIMI_VALUE_OPTIONS } from './commit-message-generation-options'
function plan(prefix = '', agentArgs = '', prompt = 'P') {
const result = planCommitMessageGeneration(
{ agentId: 'kimi', model: 'default', agentCommandOverride: `kimi ${prefix}`, agentArgs },
prompt
)
if (!result.ok) {
throw new Error(result.error)
}
return result.plan
}
const base = '--prompt P'
const stream = '--output-format stream-json'
const text = '--output-format text'
const equalsText = '--output-format=text'
it.each([
['', `${equalsText} ${stream}`, `${base} ${equalsText}`, false],
[text, '--output-format=stream-json', `${base} --output-format=stream-json`, true],
[`${equalsText} ${stream}`, '', `${equalsText} ${base}`, false],
['', `${text} -- ${stream}`, `${base} ${text} -- ${stream}`, false],
[`-- ${stream}`, '', `-- ${stream} ${base} ${stream}`, false],
[`--model "--" ${text}`, stream, `--model -- ${base} ${stream}`, true],
[
'-mprefix --keep before',
'-mrecipe --model ignored --keep after',
`--keep before ${base} ${stream} -mrecipe --keep after`,
true
]
])('preserves prefix %s / recipe %s', (prefix, recipe, argv, selected) => {
const result = plan(prefix, recipe)
expect(result.args).toEqual(argv.split(' '))
expect(result.outputFormat).toBe(selected ? 'kimi-stream-json' : undefined)
})
it.each(KIMI_VALUE_OPTIONS.filter((option) => option !== '--output-format'))(
'preserves required operands of %s including a literal terminator',
(option) => {
for (const value of ['--', '--output-format=text']) {
expect(plan('', `${option} "${value}"`).args.join(' ')).toBe(
`${base} ${stream} ${option} ${value}`
)
}
}
)
it.each(['--', '--output-format=text', 'stream-json'])(
'preserves flag-looking prompt %s',
(prompt) => {
expect(plan('', '--output-format=text', prompt).args).toEqual([
'--prompt',
prompt,
'--output-format=text'
])
}
)
it('keeps repeated-format planning linear', () => {
const slice = Array.prototype.slice
const counts: number[] = []
for (const n of [20, 200]) {
const recipe = Array(n).fill('--output-format=text').join(' ')
let copied = 0
Array.prototype.slice = function (start, end) {
const result = slice.call(this, start, end)
copied += result.length
return result
}
let result
try {
result = plan('', recipe)
} finally {
Array.prototype.slice = slice
}
expect(result.args).toEqual(['--prompt', 'P', '--output-format=text'])
expect(copied).toBeLessThan(5 * n + 100)
counts.push(copied)
}
expect(counts[1]).toBeLessThanOrEqual(10 * counts[0])
})