bloby-bot 0.47.3 → 0.47.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bloby-bot",
3
- "version": "0.47.3",
3
+ "version": "0.47.4",
4
4
  "releaseNotes": [
5
5
  "1. # voice note (PTT bubble)",
6
6
  "2. # audio file + caption",
@@ -8,6 +8,7 @@
8
8
  * Endpoint: POST {baseUrl}/models/{modelId}:streamGenerateContent?alt=sse&key={apiKey}
9
9
  * Stream: SSE — each `data: {...}` is one candidate update.
10
10
  */
11
+ import { log } from '../../../../shared/logger.js';
11
12
  import type {
12
13
  PiStreamRequest,
13
14
  PiStreamEvent,
@@ -149,9 +150,18 @@ export async function* streamGoogle(req: PiStreamRequest): AsyncIterable<PiStrea
149
150
  let lastFinish: string | undefined;
150
151
  let promptBlockReason: string | undefined;
151
152
  let usage: { inputTokens?: number; outputTokens?: number } | undefined;
153
+ // Debug counters — drop once this stabilises.
154
+ let chunkCount = 0;
155
+ let thoughtPartCount = 0;
156
+ let emptyTextPartCount = 0;
157
+ let firstChunkSummary = '';
152
158
 
153
159
  try {
154
160
  for await (const chunk of parseSse(res)) {
161
+ chunkCount++;
162
+ if (chunkCount === 1) {
163
+ try { firstChunkSummary = JSON.stringify(chunk).slice(0, 600); } catch {}
164
+ }
155
165
  // The whole prompt can be rejected before we even get a candidate.
156
166
  if (chunk?.promptFeedback?.blockReason) {
157
167
  promptBlockReason = chunk.promptFeedback.blockReason;
@@ -161,10 +171,12 @@ export async function* streamGoogle(req: PiStreamRequest): AsyncIterable<PiStrea
161
171
  for (const part of parts) {
162
172
  // Thinking models emit reasoning parts with `thought: true`. They
163
173
  // shouldn't be shown to the user as part of the visible answer.
164
- if (part?.thought) continue;
174
+ if (part?.thought) { thoughtPartCount++; continue; }
165
175
  if (typeof part?.text === 'string' && part.text.length > 0) {
166
176
  accumulated += part.text;
167
177
  yield { type: 'text_delta', delta: part.text };
178
+ } else {
179
+ emptyTextPartCount++;
168
180
  }
169
181
  }
170
182
  if (candidate?.finishReason) lastFinish = candidate.finishReason;
@@ -185,6 +197,18 @@ export async function* streamGoogle(req: PiStreamRequest): AsyncIterable<PiStrea
185
197
  return;
186
198
  }
187
199
 
200
+ log.info(
201
+ `[pi/google] stream done — chunks=${chunkCount} text=${accumulated.length} ` +
202
+ `thoughtParts=${thoughtPartCount} emptyTextParts=${emptyTextPartCount} ` +
203
+ `finishReason=${lastFinish || 'none'} ` +
204
+ `promptTok=${usage?.inputTokens ?? '?'} outTok=${usage?.outputTokens ?? '?'}`,
205
+ );
206
+ if (chunkCount > 0 && !accumulated) {
207
+ log.info(`[pi/google] first chunk (truncated): ${firstChunkSummary}`);
208
+ } else if (chunkCount === 0) {
209
+ log.warn(`[pi/google] SSE stream parsed zero chunks — check response shape (status=${res.status} content-type=${res.headers.get('content-type') || ''})`);
210
+ }
211
+
188
212
  // Prompt-level block: nothing came back at all.
189
213
  if (promptBlockReason) {
190
214
  yield { type: 'error', error: `Gemini blocked the prompt (${promptBlockReason}).` };
@@ -199,7 +223,10 @@ export async function* streamGoogle(req: PiStreamRequest): AsyncIterable<PiStrea
199
223
  const reason = lastFinish && lastFinish !== 'STOP' && lastFinish !== 'FINISH_REASON_STOP'
200
224
  ? lastFinish
201
225
  : undefined;
202
- yield { type: 'error', error: finishReasonMessage(reason) };
226
+ const hint = thoughtPartCount > 0 && !lastFinish
227
+ ? ' (model emitted thinking but never the final answer — try a non-thinking model like gemini-2.5-flash, or raise maxOutputTokens)'
228
+ : '';
229
+ yield { type: 'error', error: finishReasonMessage(reason) + hint };
203
230
  yield { type: 'done', stopReason: mapStopReason(lastFinish), usage };
204
231
  return;
205
232
  }