Skip to content

Fix/issue 367 kimi k2 streaming#477

Open
ayorindeadunse wants to merge 4 commits intoCodebuffAI:mainfrom
ayorindeadunse:fix/issue-367-kimi-k2-streaming
Open

Fix/issue 367 kimi k2 streaming#477
ayorindeadunse wants to merge 4 commits intoCodebuffAI:mainfrom
ayorindeadunse:fix/issue-367-kimi-k2-streaming

Conversation

@ayorindeadunse
Copy link
Contributor

Description

Fixes #367 - The "kimi-k2" Model breaks off after use at the beginning

Problem

The kimi-k2 model was breaking off during streaming responses due to truncated JSON chunks. When JSON is sent at newline boundaries, it can arrive incomplete, causing parsing failures and stream termination.

Solution

This PR adds defensive JSON parsing and error recovery mechanisms to gracefully handle truncated or malformed JSON responses from OpenRouter:

  1. attemptJSONParse() function - Implements multi-stage JSON recovery:

    • Attempts to parse JSON as-is
    • Removes trailing newlines from truncated responses
    • Tries closing incomplete JSON structures (}, ]}, }]}, etc.)
    • Returns null only after all recovery attempts fail
  2. Enhanced error handling - Improved logging and validation:

    • Better detection of truncated chunks vs actual errors
    • Graceful fallback when parsing fails (logs and continues)
    • Detailed debugging information for troubleshooting
  3. Import cleanup - Reorganized imports for consistency and removed unused types

Testing

  • Build succeeds with no TypeScript/type errors
  • All existing tests pass
  • No regressions detected

Related

Fixes #367

…runtime errors

- Add safety check for lineInfo.lineStartCols existence before accessing it
- Prevents 'undefined is not an object' error when OpenTUI TextBufferView doesn't populate lineStartCols
- Fixes issue where CLI would crash on startup with TypeError
- Fixed incorrect property name reference at line 804
- All references use correct lineStartCols (not lineStarts)
- Resolves TypeScript compilation errors
…reaming responses

- Add attemptJSONParse() function with 4 fallback recovery strategies for truncated JSON
  1. Try parsing as-is
  2. Try trimming trailing whitespace
  3. Try closing incomplete JSON structures ({}, ]}, }]}, [])
  4. Return null if all attempts fail

- Integrate defensive parsing into handleLine() function:
  - Replace simple JSON.parse() with attemptJSONParse() with null checks
  - Add error-like pattern detection to handle error responses with malformed schema
  - Enhanced logging showing raw response preview and recovery attempts
  - Detailed debug logs for truncated/malformed chunks

- Improve handleStreamChunk() robustness:
  - Add defensive check for null/undefined/non-array choices
  - Better logging with diagnostic information for empty choices

This fixes issue CodebuffAI#367 where kimi-k2 and similar models that send truncated JSON at newline boundaries would cause silent parsing failures and agent crashes.
…CodebuffAI#367)

- Add attemptJSONParse() function with recovery strategies for truncated/malformed JSON
- Handle incomplete JSON structures by closing them (}, ]}, }]}, etc.)
- Improve error handling and logging for malformed chunks
- Gracefully degrade when parsing fails instead of crashing
- Fixes issue where kimi-k2 model breaks off due to JSON truncation at newline boundaries
let obj = attemptJSONParse(raw)
if (obj === null) {
// All parsing attempts failed - log with detailed context for debugging
logger.debug(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we keep the logger.warn level rather than downgrading to debug?

function isPrintableCharacterKey(key: KeyEvent): boolean {
const name = key.name

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note: it would be better not to include many trivial formatting changes

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll work on this. Thanks!

)

const cursorRow = lineInfo
const cursorRow = lineInfo && lineInfo.lineStartCols
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are you sure that lineInfo doesn't include lineStartCols sometimes?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I thought so, but typescript flagged an error. I'll check this again.

'OpenRouter response matches error pattern but schema validation failed',
)
// Continue processing as error response
return handleResponse({
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If it's error like, doesn't handleResponse already return { state }, e.g. see:

if ('error' in data || !data.usage) {
// Stream not finished
return { state }
}

So this doesn't seem to do anything

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

The “kimi-k2” Model breaks off after use at the beginning

2 participants