@yeaft/webchat-agent 0.1.999 → 0.1.1001
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
package/yeaft/effort.js
CHANGED
|
@@ -61,7 +61,7 @@ export const SCENARIO_EFFORT = Object.freeze({
|
|
|
61
61
|
* Pick the effort level for a given query context.
|
|
62
62
|
*
|
|
63
63
|
* Decision order:
|
|
64
|
-
* 1. If userEffort is a valid Effort ('low'|'medium'|'high'|'max'),
|
|
64
|
+
* 1. If userEffort is a valid Effort ('minimal'|'low'|'medium'|'high'|'max'),
|
|
65
65
|
* return it unchanged. This is the explicit override path —
|
|
66
66
|
* `/max` prefix, Settings slider, or API caller.
|
|
67
67
|
* 2. If toolLoopTurns >= LONG_LOOP_TURN_THRESHOLD, upgrade the
|
|
@@ -74,7 +74,7 @@ export const SCENARIO_EFFORT = Object.freeze({
|
|
|
74
74
|
* already consumed in the current `query()` call.
|
|
75
75
|
* @param {unknown} [ctx.userEffort=null] — User-supplied override.
|
|
76
76
|
* Invalid values are ignored (fall through to scenario path).
|
|
77
|
-
* @returns {'low'|'medium'|'high'|'max'} Resolved effort. Never null —
|
|
77
|
+
* @returns {'minimal'|'low'|'medium'|'high'|'max'} Resolved effort. Never null —
|
|
78
78
|
* the adapter/router is responsible for dropping it when the
|
|
79
79
|
* feature flag is off or the model doesn't support thinking.
|
|
80
80
|
*/
|
|
@@ -52,9 +52,9 @@ function thinkingV1Enabled() {
|
|
|
52
52
|
}
|
|
53
53
|
|
|
54
54
|
/**
|
|
55
|
-
* Translate a normalised effort ('low'|'medium'|'high'|'max') into the value
|
|
55
|
+
* Translate a normalised effort ('minimal'|'low'|'medium'|'high'|'max') into the value
|
|
56
56
|
* accepted by the OpenAI Responses `reasoning.effort` field. Responses today
|
|
57
|
-
* accepts 'low'|'medium'|'high' — 'max' degrades to 'high' to match the
|
|
57
|
+
* accepts 'minimal'|'low'|'medium'|'high' — 'max' degrades to 'high' to match the
|
|
58
58
|
* registry's normaliseEffort downgrade rule.
|
|
59
59
|
*/
|
|
60
60
|
function effortForResponses(effort) {
|
package/yeaft/llm/router.js
CHANGED
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
*/
|
|
23
23
|
|
|
24
24
|
import { LLMAdapter } from './adapter.js';
|
|
25
|
-
import { getThinkingCapability, normalizeEffort, parseModelRef } from '../models.js';
|
|
25
|
+
import { getModelEffortOptions, getThinkingCapability, normalizeEffort, parseModelRef } from '../models.js';
|
|
26
26
|
import { normalizeKnownProviderForRuntime } from './known-providers.js';
|
|
27
27
|
import { pairSanitize } from '../pair-sanitize.js';
|
|
28
28
|
|
|
@@ -109,11 +109,16 @@ export function filterEffortForModel(params) {
|
|
|
109
109
|
const { effort: _drop, effortSource: _source, ...rest } = params;
|
|
110
110
|
return rest;
|
|
111
111
|
}
|
|
112
|
-
const
|
|
112
|
+
const modelId = parseModelRef(params.model).modelId;
|
|
113
|
+
const cap = getThinkingCapability(modelId);
|
|
113
114
|
if (!cap.supportsThinking || cap.thinkingProtocol === 'none') {
|
|
114
115
|
const { effort: _drop, effortSource: _source, ...rest } = params;
|
|
115
116
|
return rest;
|
|
116
117
|
}
|
|
118
|
+
if (norm === 'minimal' && !getModelEffortOptions(modelId).includes('minimal')) {
|
|
119
|
+
const { effort: _drop, effortSource: _source, ...rest } = params;
|
|
120
|
+
return rest;
|
|
121
|
+
}
|
|
117
122
|
return { ...params, effort: norm, effortSource: params.effortSource };
|
|
118
123
|
}
|
|
119
124
|
|
package/yeaft/models.js
CHANGED
|
@@ -131,6 +131,18 @@ export const MODEL_REGISTRY = new Map([
|
|
|
131
131
|
adapter: 'openai-responses',
|
|
132
132
|
baseUrl: 'https://api.openai.com/v1',
|
|
133
133
|
displayName: 'GPT-5.4',
|
|
134
|
+
supportsThinking: true,
|
|
135
|
+
thinkingProtocol: 'openai-reasoning',
|
|
136
|
+
defaultEffort: null,
|
|
137
|
+
}],
|
|
138
|
+
['gpt-5.5', {
|
|
139
|
+
provider: 'openai',
|
|
140
|
+
adapter: 'openai-responses',
|
|
141
|
+
baseUrl: 'https://api.openai.com/v1',
|
|
142
|
+
displayName: 'GPT-5.5',
|
|
143
|
+
supportsThinking: true,
|
|
144
|
+
thinkingProtocol: 'openai-reasoning',
|
|
145
|
+
defaultEffort: null,
|
|
134
146
|
}],
|
|
135
147
|
['gpt-4.1', {
|
|
136
148
|
provider: 'openai',
|
|
@@ -371,7 +383,7 @@ export function parseModelRef(ref) {
|
|
|
371
383
|
|
|
372
384
|
/**
|
|
373
385
|
* Valid effort levels accepted by Yeaft adapters.
|
|
374
|
-
* @typedef {'low' | 'medium' | 'high' | 'max'} Effort
|
|
386
|
+
* @typedef {'minimal' | 'low' | 'medium' | 'high' | 'max'} Effort
|
|
375
387
|
*/
|
|
376
388
|
|
|
377
389
|
/**
|
|
@@ -396,11 +408,12 @@ export const ANTHROPIC_THINKING_BUDGETS = {
|
|
|
396
408
|
* but the adapter MUST NOT error.
|
|
397
409
|
*
|
|
398
410
|
* @param {Effort} effort
|
|
399
|
-
* @returns {'low' | 'medium' | 'high' | null}
|
|
411
|
+
* @returns {'minimal' | 'low' | 'medium' | 'high' | null}
|
|
400
412
|
*/
|
|
401
413
|
export function mapEffortToOpenAIReasoning(effort) {
|
|
402
414
|
if (!effort) return null;
|
|
403
415
|
switch (effort) {
|
|
416
|
+
case 'minimal': return 'minimal';
|
|
404
417
|
case 'low': return 'low';
|
|
405
418
|
case 'medium': return 'medium';
|
|
406
419
|
case 'high': return 'high';
|
|
@@ -411,7 +424,8 @@ export function mapEffortToOpenAIReasoning(effort) {
|
|
|
411
424
|
}
|
|
412
425
|
}
|
|
413
426
|
|
|
414
|
-
export const
|
|
427
|
+
export const OPENAI_REASONING_EFFORT_OPTIONS = ['minimal', 'low', 'medium', 'high'];
|
|
428
|
+
export const ANTHROPIC_EFFORT_OPTIONS = ['low', 'medium', 'high'];
|
|
415
429
|
|
|
416
430
|
function inferThinkingCapability(model) {
|
|
417
431
|
const id = parseModelRef(model).modelId.toLowerCase();
|
|
@@ -485,7 +499,9 @@ export function getThinkingCapability(model) {
|
|
|
485
499
|
export function getModelEffortOptions(model) {
|
|
486
500
|
const cap = getThinkingCapability(model);
|
|
487
501
|
if (!cap.supportsThinking || cap.thinkingProtocol === 'none') return [];
|
|
488
|
-
return
|
|
502
|
+
if (cap.thinkingProtocol === 'openai-reasoning') return OPENAI_REASONING_EFFORT_OPTIONS.slice();
|
|
503
|
+
if (cap.thinkingProtocol === 'anthropic') return ANTHROPIC_EFFORT_OPTIONS.slice();
|
|
504
|
+
return [];
|
|
489
505
|
}
|
|
490
506
|
|
|
491
507
|
export function modelSupportsEffort(model) {
|
|
@@ -499,7 +515,7 @@ export function modelSupportsEffort(model) {
|
|
|
499
515
|
* @returns {Effort | null}
|
|
500
516
|
*/
|
|
501
517
|
export function normalizeEffort(effort) {
|
|
502
|
-
if (effort === 'low' || effort === 'medium' || effort === 'high' || effort === 'max') {
|
|
518
|
+
if (effort === 'minimal' || effort === 'low' || effort === 'medium' || effort === 'high' || effort === 'max') {
|
|
503
519
|
return effort;
|
|
504
520
|
}
|
|
505
521
|
return null;
|
|
@@ -28,7 +28,7 @@ const CONFIG_FILE = 'config.json';
|
|
|
28
28
|
|
|
29
29
|
/** Whitelist of persisted session model-override fields. Reject everything else. */
|
|
30
30
|
const ALLOWED_KEYS = new Set(['model', 'modelEffort']);
|
|
31
|
-
const ALLOWED_EFFORTS = new Set(['low', 'medium', 'high']);
|
|
31
|
+
const ALLOWED_EFFORTS = new Set(['minimal', 'low', 'medium', 'high']);
|
|
32
32
|
|
|
33
33
|
export class SessionConfigError extends Error {
|
|
34
34
|
constructor(code, message) {
|
|
@@ -101,7 +101,7 @@ export function validateSessionConfig(cfg) {
|
|
|
101
101
|
}
|
|
102
102
|
if ('modelEffort' in cfg && cfg.modelEffort !== null && cfg.modelEffort !== undefined && cfg.modelEffort !== '') {
|
|
103
103
|
if (typeof cfg.modelEffort !== 'string' || !ALLOWED_EFFORTS.has(cfg.modelEffort.trim())) {
|
|
104
|
-
throw new SessionConfigError('invalid_model_effort', 'modelEffort must be low, medium, or high');
|
|
104
|
+
throw new SessionConfigError('invalid_model_effort', 'modelEffort must be minimal, low, medium, or high');
|
|
105
105
|
}
|
|
106
106
|
}
|
|
107
107
|
}
|
package/yeaft/web-bridge.js
CHANGED
|
@@ -1494,15 +1494,54 @@ export function handleYeaftVpRead(msg) {
|
|
|
1494
1494
|
/**
|
|
1495
1495
|
* Session CRUD wired to WS events.
|
|
1496
1496
|
*/
|
|
1497
|
+
function decorateSessionsWithRuntimeState(sessions) {
|
|
1498
|
+
const rows = Array.isArray(sessions) ? sessions : [];
|
|
1499
|
+
if (rows.length === 0) return rows;
|
|
1500
|
+
let statuses = [];
|
|
1501
|
+
try {
|
|
1502
|
+
statuses = getVpStatusBroker().snapshot();
|
|
1503
|
+
} catch {
|
|
1504
|
+
statuses = [];
|
|
1505
|
+
}
|
|
1506
|
+
const bySession = new Map();
|
|
1507
|
+
for (const status of statuses) {
|
|
1508
|
+
const sessionId = status?.sessionId || status?.groupId || null;
|
|
1509
|
+
if (!sessionId) continue;
|
|
1510
|
+
const state = status.state || 'idle';
|
|
1511
|
+
const running = !['idle', 'offline', 'completed', 'failed', 'aborted'].includes(state);
|
|
1512
|
+
const updatedAt = status.updatedAt || status.since || Date.now();
|
|
1513
|
+
const prev = bySession.get(sessionId) || { running: false, runningVpCount: 0, latestActivityAt: 0 };
|
|
1514
|
+
if (running) prev.runningVpCount += 1;
|
|
1515
|
+
prev.running = prev.running || running;
|
|
1516
|
+
prev.latestActivityAt = Math.max(prev.latestActivityAt || 0, updatedAt || 0);
|
|
1517
|
+
bySession.set(sessionId, prev);
|
|
1518
|
+
}
|
|
1519
|
+
return rows.map(session => {
|
|
1520
|
+
if (!session || !session.id) return session;
|
|
1521
|
+
const runtime = bySession.get(session.id);
|
|
1522
|
+
if (!runtime) return { ...session, running: false, active: false };
|
|
1523
|
+
return {
|
|
1524
|
+
...session,
|
|
1525
|
+
running: !!runtime.running,
|
|
1526
|
+
active: !!runtime.running,
|
|
1527
|
+
runningVpCount: runtime.runningVpCount || 0,
|
|
1528
|
+
latestActivityAt: runtime.latestActivityAt || null,
|
|
1529
|
+
};
|
|
1530
|
+
});
|
|
1531
|
+
}
|
|
1532
|
+
|
|
1497
1533
|
function sendSessionCrudResult(payload) {
|
|
1498
|
-
|
|
1534
|
+
const next = payload && payload.ok && Array.isArray(payload.sessions)
|
|
1535
|
+
? { ...payload, sessions: decorateSessionsWithRuntimeState(payload.sessions) }
|
|
1536
|
+
: payload;
|
|
1537
|
+
sendSessionEvent({ type: 'session_crud_result', ...next });
|
|
1499
1538
|
}
|
|
1500
1539
|
|
|
1501
1540
|
function sendSessionSnapshotBroadcast() {
|
|
1502
1541
|
try {
|
|
1503
1542
|
const yeaftDir = ctx.CONFIG?.yeaftDir;
|
|
1504
1543
|
if (!yeaftDir) return;
|
|
1505
|
-
const sessions = snapshotSessions(yeaftDir);
|
|
1544
|
+
const sessions = decorateSessionsWithRuntimeState(snapshotSessions(yeaftDir));
|
|
1506
1545
|
sendSessionEvent({ type: 'session_list_updated', sessions });
|
|
1507
1546
|
} catch (err) {
|
|
1508
1547
|
console.warn('[Yeaft] sendSessionSnapshotBroadcast failed:', err?.message || err);
|
|
@@ -3944,6 +3983,11 @@ export function handleYeaftModelSwitch(msg) {
|
|
|
3944
3983
|
session.config.model = msg.model;
|
|
3945
3984
|
session.config.primaryModel = msg.model;
|
|
3946
3985
|
session.config.modelEffort = msg.modelEffort || null;
|
|
3986
|
+
// Legacy non-session model switches mutate the shared root config instead
|
|
3987
|
+
// of going through handleYeaftUpdateSessionConfig(), so cached per-VP
|
|
3988
|
+
// Engines would otherwise keep the old effective config and drop newly
|
|
3989
|
+
// selected effort values until process restart.
|
|
3990
|
+
vpEngines.clear();
|
|
3947
3991
|
|
|
3948
3992
|
sendSessionEvent({
|
|
3949
3993
|
type: 'model_switched',
|