@yeaft/webchat-agent 0.1.1085 → 0.1.1086
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 +1 -1
- package/yeaft/engine.js +22 -12
- package/yeaft/web-bridge.js +11 -2
package/package.json
CHANGED
package/yeaft/engine.js
CHANGED
|
@@ -22,7 +22,7 @@ import { promises as fsp } from 'fs';
|
|
|
22
22
|
import { join, resolve as resolvePath } from 'path';
|
|
23
23
|
import { buildSystemPrompt, buildWorkerPrompt } from './prompts.js';
|
|
24
24
|
import { getRuntimePlatformInfo } from './runtime-platform.js';
|
|
25
|
-
import { LLMContextError, LLMAbortError, LLMRateLimitError, LLMServerError } from './llm/adapter.js';
|
|
25
|
+
import { LLMContextError, LLMAbortError, LLMRateLimitError, LLMServerError, LLMStreamIdleTimeoutError } from './llm/adapter.js';
|
|
26
26
|
import { runMemoryPreflow, buildRelevantScopes } from './sessions/pre-flow.js';
|
|
27
27
|
import { readProjectDoc, pickProjectDocFile, DEFAULT_PROJECT_DOC_MAX_BYTES } from './sessions/project-doc.js';
|
|
28
28
|
import { partitionMessages } from './compact/partition.js';
|
|
@@ -300,9 +300,10 @@ export function shouldAllowGroupReflection({
|
|
|
300
300
|
* @typedef {{ type: 'consolidate', archivedCount: number, extractedCount: number }} ConsolidateEvent
|
|
301
301
|
* @typedef {{ type: 'recall', entryCount: number, cached: boolean }} RecallEvent
|
|
302
302
|
* @typedef {{ type: 'fallback', from: string, to: string, reason: string }} FallbackEvent
|
|
303
|
-
* @typedef {{ type: 'llm_retry', attempt: number, maxRetries: number, delayMs: number, reason: 'rate_limit_retry_after'|'rate_limit_backoff'|'transient_backoff', errorName: string, statusCode: number|null, message: string }} LlmRetryEvent
|
|
303
|
+
* @typedef {{ type: 'llm_retry', attempt: number, maxRetries: number, delayMs: number, reason: 'rate_limit_retry_after'|'rate_limit_backoff'|'transient_backoff'|'stream_idle_timeout', errorName: string, statusCode: number|null, message: string }} LlmRetryEvent
|
|
304
|
+
* @typedef {{ type: 'error', error: Error, retryable: boolean, reason?: 'stream_idle_timeout', retryExhausted?: boolean }} ErrorEvent
|
|
304
305
|
*
|
|
305
|
-
* @typedef {import('./llm/adapter.js').StreamEvent | TurnStartEvent | TurnEndEvent | ToolStartEvent | ToolEndEvent | ConsolidateEvent | RecallEvent | FallbackEvent | LlmRetryEvent} EngineEvent
|
|
306
|
+
* @typedef {import('./llm/adapter.js').StreamEvent | TurnStartEvent | TurnEndEvent | ToolStartEvent | ToolEndEvent | ConsolidateEvent | RecallEvent | FallbackEvent | LlmRetryEvent | ErrorEvent} EngineEvent
|
|
306
307
|
*/
|
|
307
308
|
|
|
308
309
|
// ─── Engine ──────────────────────────────────────────────────────
|
|
@@ -2322,12 +2323,13 @@ export class Engine {
|
|
|
2322
2323
|
}
|
|
2323
2324
|
}
|
|
2324
2325
|
|
|
2325
|
-
// ─── Rate-limit / transient retry
|
|
2326
|
+
// ─── Rate-limit / transient / stream-idle retry ───
|
|
2326
2327
|
// Honour server-supplied Retry-After for 429/529; fall back to
|
|
2327
|
-
// exponential backoff for 5xx
|
|
2328
|
-
// LLMServerError. Counts against
|
|
2329
|
-
// exhaustion we fall through to the
|
|
2330
|
-
// ultimately the error event) without
|
|
2328
|
+
// exponential backoff for 5xx, transport failures, and stream-idle
|
|
2329
|
+
// timeouts wrapped as LLMServerError. Counts against
|
|
2330
|
+
// retryPolicy.maxRetries; on exhaustion we fall through to the
|
|
2331
|
+
// fallback-model path (and ultimately the error event) without
|
|
2332
|
+
// further waiting.
|
|
2331
2333
|
const isRateLimit = err instanceof LLMRateLimitError;
|
|
2332
2334
|
const isTransient = err instanceof LLMServerError;
|
|
2333
2335
|
if (isRateLimit || isTransient) {
|
|
@@ -2349,7 +2351,9 @@ export class Engine {
|
|
|
2349
2351
|
reason = 'rate_limit_backoff';
|
|
2350
2352
|
} else {
|
|
2351
2353
|
delayMs = computeBackoffDelay(retryPolicy, consecutiveRetryableErrors - 1);
|
|
2352
|
-
reason =
|
|
2354
|
+
reason = err instanceof LLMStreamIdleTimeoutError
|
|
2355
|
+
? 'stream_idle_timeout'
|
|
2356
|
+
: 'transient_backoff';
|
|
2353
2357
|
}
|
|
2354
2358
|
yield {
|
|
2355
2359
|
type: 'llm_retry',
|
|
@@ -2376,7 +2380,7 @@ export class Engine {
|
|
|
2376
2380
|
// ─── Fallback model ──────────────────────────────
|
|
2377
2381
|
const fallbackModel = this.#config.fallbackModel;
|
|
2378
2382
|
if (fallbackModel && fallbackModel !== currentModel &&
|
|
2379
|
-
(err
|
|
2383
|
+
(err instanceof LLMRateLimitError || err instanceof LLMServerError)) {
|
|
2380
2384
|
yield { type: 'fallback', from: currentModel, to: fallbackModel, reason: err.message };
|
|
2381
2385
|
currentModel = fallbackModel;
|
|
2382
2386
|
consecutiveRetryableErrors = 0; // new model, fresh retry budget
|
|
@@ -2384,11 +2388,17 @@ export class Engine {
|
|
|
2384
2388
|
continue; // retry with fallback model
|
|
2385
2389
|
}
|
|
2386
2390
|
|
|
2387
|
-
|
|
2391
|
+
const isRetryableError = err instanceof LLMRateLimitError || err instanceof LLMServerError;
|
|
2392
|
+
const errorEvent = {
|
|
2388
2393
|
type: 'error',
|
|
2389
2394
|
error: err,
|
|
2390
|
-
retryable:
|
|
2395
|
+
retryable: isRetryableError,
|
|
2391
2396
|
};
|
|
2397
|
+
if (err instanceof LLMStreamIdleTimeoutError) {
|
|
2398
|
+
errorEvent.reason = 'stream_idle_timeout';
|
|
2399
|
+
errorEvent.retryExhausted = consecutiveRetryableErrors >= retryPolicy.maxRetries;
|
|
2400
|
+
}
|
|
2401
|
+
yield errorEvent;
|
|
2392
2402
|
yield { type: 'turn_end', turnNumber, stopReason: 'error', threadId };
|
|
2393
2403
|
break;
|
|
2394
2404
|
}
|
package/yeaft/web-bridge.js
CHANGED
|
@@ -2421,8 +2421,9 @@ function handleEngineEvent(event, hctx) {
|
|
|
2421
2421
|
|
|
2422
2422
|
case 'llm_retry':
|
|
2423
2423
|
// Engine paused before re-issuing the same turn because the LLM
|
|
2424
|
-
// returned a retryable error (rate limit / 5xx / transient network
|
|
2425
|
-
// Surface to the client so the UI can show
|
|
2424
|
+
// returned a retryable error (rate limit / 5xx / transient network /
|
|
2425
|
+
// stream idle timeout). Surface to the client so the UI can show
|
|
2426
|
+
// "retrying in Xs (1/3)"
|
|
2426
2427
|
// instead of looking frozen mid-turn.
|
|
2427
2428
|
sendSessionEvent({
|
|
2428
2429
|
type: 'llm_retry',
|
|
@@ -2589,6 +2590,14 @@ function handleEngineEvent(event, hctx) {
|
|
|
2589
2590
|
|
|
2590
2591
|
case 'error': {
|
|
2591
2592
|
const errMsg = event.error?.message || 'Unknown error';
|
|
2593
|
+
sendSessionEvent({
|
|
2594
|
+
type: 'error',
|
|
2595
|
+
message: errMsg,
|
|
2596
|
+
errorName: event.error?.name || null,
|
|
2597
|
+
retryable: !!event.retryable,
|
|
2598
|
+
...(event.reason ? { reason: event.reason } : {}),
|
|
2599
|
+
...(event.retryExhausted !== undefined ? { retryExhausted: !!event.retryExhausted } : {}),
|
|
2600
|
+
}, envelope);
|
|
2592
2601
|
if (isPermissionErrorMsg(errMsg)) {
|
|
2593
2602
|
if (!_permissionDiagnosticSent) {
|
|
2594
2603
|
_permissionDiagnosticSent = true;
|