mirror of
https://github.com/danny-avila/LibreChat.git
synced 2026-09-21 15:45:22 +00:00
🍵 feat: Continue Late Steers in Warm Agent Runs (#15357)
* feat: continue late steers in warm agent runs * chore: bump agents sdk to v3.7.9 * fix: guard terminal steer admission
This commit is contained in:
parent
180b56531a
commit
cd3768ed1f
23 changed files with 624 additions and 18 deletions
|
|
@ -1,14 +1,20 @@
|
|||
const AgentClient = require('../client');
|
||||
const { isSteeringSupported, isSteerPreemptSupported } = require('@librechat/api');
|
||||
const {
|
||||
isSteeringSupported,
|
||||
isSteerPreemptSupported,
|
||||
isSteerTerminalContinuationSupported,
|
||||
} = require('@librechat/api');
|
||||
|
||||
jest.mock('@librechat/api', () => ({
|
||||
...jest.requireActual('@librechat/api'),
|
||||
isSteeringSupported: jest.fn(() => true),
|
||||
isSteerPreemptSupported: jest.fn(() => true),
|
||||
isSteerTerminalContinuationSupported: jest.fn(() => true),
|
||||
}));
|
||||
|
||||
const mockIsSteeringSupported = isSteeringSupported;
|
||||
const mockIsPreemptSupported = isSteerPreemptSupported;
|
||||
const mockIsTerminalContinuationSupported = isSteerTerminalContinuationSupported;
|
||||
|
||||
/** Minimal `this` for the wiring builder — it only reads these three. */
|
||||
function buildWiring(streamId, { jobCreatedAt = 1700000000000 } = {}) {
|
||||
|
|
@ -25,6 +31,7 @@ describe('AgentClient.buildSteerWiring — preempt capability gating', () => {
|
|||
jest.clearAllMocks();
|
||||
mockIsSteeringSupported.mockReturnValue(true);
|
||||
mockIsPreemptSupported.mockReturnValue(true);
|
||||
mockIsTerminalContinuationSupported.mockReturnValue(true);
|
||||
});
|
||||
|
||||
it('returns both boundary hooks and the poll when preempt is supported', () => {
|
||||
|
|
@ -33,6 +40,16 @@ describe('AgentClient.buildSteerWiring — preempt capability gating', () => {
|
|||
expect(typeof wiring.hook).toBe('function');
|
||||
expect(typeof wiring.preemptHook).toBe('function');
|
||||
expect(typeof wiring.preemption?.shouldPreempt).toBe('function');
|
||||
expect(typeof wiring.terminalHook).toBe('function');
|
||||
});
|
||||
|
||||
it('omits only terminal continuation when the SDK lacks Stop continuation', () => {
|
||||
mockIsTerminalContinuationSupported.mockReturnValue(false);
|
||||
const wiring = buildWiring('stream-terminal-unsupported');
|
||||
|
||||
expect(typeof wiring.hook).toBe('function');
|
||||
expect(typeof wiring.preemptHook).toBe('function');
|
||||
expect(wiring.terminalHook).toBeUndefined();
|
||||
});
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -51,6 +51,7 @@ jest.mock('@librechat/api', () => ({
|
|||
exemptFromConcurrencyLimiter: jest.fn(() => false),
|
||||
toPendingSteer: jest.fn((item) => item),
|
||||
isSteerPreemptSupported: jest.fn(() => true),
|
||||
isSteerTerminalContinuationSupported: jest.fn(() => false),
|
||||
buildRecoveredSteerPayload: jest.fn(() => null),
|
||||
deleteAgentCheckpoint: jest.fn(),
|
||||
getViolationInfo: jest.fn(() => ({
|
||||
|
|
|
|||
|
|
@ -254,6 +254,7 @@ jest.mock('@librechat/api', () => ({
|
|||
/** Recorded onto the job so the steer route can honour the OWNING replica's
|
||||
* seal capability rather than its own probe. */
|
||||
isSteerPreemptSupported: jest.fn(() => true),
|
||||
isSteerTerminalContinuationSupported: jest.fn(() => false),
|
||||
buildRecoveredSteerPayload: jest.fn((text, files) => {
|
||||
if (typeof text !== 'string' || (files != null && !Array.isArray(files))) {
|
||||
return null;
|
||||
|
|
|
|||
|
|
@ -132,6 +132,7 @@ jest.mock('@librechat/api', () => ({
|
|||
decrementPendingRequest: (...args) => mockDecrementPendingRequest(...args),
|
||||
checkAndIncrementPendingRequest: (...args) => mockCheckAndIncrementPendingRequest(...args),
|
||||
isSteerPreemptSupported: jest.fn(() => true),
|
||||
isSteerTerminalContinuationSupported: jest.fn(() => false),
|
||||
createMCPRuntimeRequestBody: ({ messageId, conversationId, parentMessageId }) => ({
|
||||
messageId,
|
||||
conversationId,
|
||||
|
|
|
|||
|
|
@ -65,9 +65,11 @@ const {
|
|||
createSteerIndexOffsetHandlers,
|
||||
createSteerDrainHook,
|
||||
createSteerPreemptBoundaryHook,
|
||||
createSteerTerminalContinuationHook,
|
||||
createSteerPreemptPoll,
|
||||
isSteeringSupported,
|
||||
isSteerPreemptSupported,
|
||||
isSteerTerminalContinuationSupported,
|
||||
buildSteerMedia,
|
||||
collectSteerStampTargets,
|
||||
stampSteerPartMedia,
|
||||
|
|
@ -557,9 +559,9 @@ class AgentClient extends BaseClient {
|
|||
|
||||
/**
|
||||
* The `steering` fragment for `createRun`: the run-scoped PostToolBatch
|
||||
* drain hook — plus, when the SDK can seal mid-stream, the PreemptBoundary
|
||||
* twin and the preempt poll built from the SAME drain closures, so both
|
||||
* boundaries inject byte-identical shapes. `undefined` when there is no
|
||||
* drain hook — plus the capability-gated PreemptBoundary and terminal Stop
|
||||
* twins built from the SAME drain closures, so every boundary injects
|
||||
* byte-identical shapes. `undefined` when there is no
|
||||
* resumable job surface or the installed SDK cannot inject hook messages
|
||||
* (draining would drop them).
|
||||
*
|
||||
|
|
@ -592,6 +594,9 @@ class AgentClient extends BaseClient {
|
|||
preemptHook: createSteerPreemptBoundaryHook(drainOptions),
|
||||
preemption: createSteerPreemptPoll(streamId),
|
||||
}),
|
||||
...(isSteerTerminalContinuationSupported() && {
|
||||
terminalHook: createSteerTerminalContinuationHook(drainOptions),
|
||||
}),
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue