Skip to content

Conversation

@ulugbekna
Copy link
Contributor

  • ghost: fix: align finishReason behavior in fetchAndStreamCompletions2
  • ghost: fetch: go back to DONE as finish_reason for non-server-terminated choices

## Problem

There was a user-observable behavior difference between the old
\`fetchAndStreamCompletions\` and the new \`fetchAndStreamCompletions2\`
functions. This difference affected whether cached follow-up suggestions
were used or a new multiline fetch was triggered after accepting a
completion.

## Root Cause

When the stream ends without all completions being explicitly finished
by the server, the two implementations produced different \`finishReason\`
values in the \`APIChoice\`:

| Scenario                      | Old (SSEProcessor)  | New (convertStreamToApiChoices) |
|-------------------------------|---------------------|----------------------------------|
| Stream ends, no finish_reason | 'stop' (default)    | 'DONE' (was hardcoded)           |

## Impact

The \`hasAcceptedCurrentCompletion\` function in \`current.ts\` (line 79)
uses the \`finishReason\` to determine whether a completion was fully
accepted:

\`\`\`typescript
return exactMatch && finishReason === 'stop';
\`\`\`

This function ONLY returns \`true\` when \`finishReason === 'stop'\`.

With the old behavior:
- \`finishReason\` = 'stop' → \`hasAcceptedCurrentCompletion\` returns \`true\`
- This triggers multiline follow-up completion mode in \`ghostTextStrategy.ts\`

With the new (broken) behavior:
- \`finishReason\` = 'DONE' → \`hasAcceptedCurrentCompletion\` returns \`false\`
- This incorrectly uses cached suggestions instead of triggering new fetch

## Investigation Method

1. Traced the flow in \`SSEProcessor.processSSE\` (stream.ts):
   - When stream ends via [DONE], calls \`finishSolutions\` with reason 'DONE'
   - However, \`prepareSolutionForReturn\` calls \`convertToAPIJsonData\`
   - \`convertToAPIJsonData\` defaults \`finish_reason\` to 'stop':
     \`finish_reason: streamingData.finish_reason ?? 'stop'\`

2. Traced the flow in \`convertStreamToApiChoices\` (fetch.ts):
   - Final loop for unfinished completions had hardcoded 'DONE'
   - This bypassed the default 'stop' semantics

3. Verified via \`hasAcceptedCurrentCompletion\` tests in \`current.test.ts\`:
   - Tests confirm 'stop' → true, 'content_filter'/'snippy' → false
   - 'DONE' would also return false, breaking the multiline flow

## Fix

Changed line 921 in \`convertStreamToApiChoices\` from:
  \`'DONE', // should match original ghost-text fetcher behavior\`
to:
  \`completion.accumulator.finishReason ?? 'stop', // Matches SSEProcessor\`

This ensures the new implementation produces the same default 'stop'
\`finishReason\` as the old implementation when the server doesn't provide
an explicit finish_reason."
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.

2 participants