🌊 fix: Preserve Custom Endpoint streamRate When endpoints.all Is Defined (#14645)

* 🌊 fix: Preserve Custom Endpoint `streamRate` When `endpoints.all` Is Defined

`buildCustomOptions` assigned `allConfig.streamRate` unconditionally whenever
an `endpoints.all` block existed, overwriting the per-endpoint `streamRate`
with `undefined` for any `all` block that did not define one of its own.

The value is read back later to set `_lc_stream_delay` on the llmConfig, so
stream smoothing was silently disabled for every custom endpoint whenever
`endpoints.all` was present for unrelated reasons (e.g. `activityLabel`).

Guard on `allConfig?.streamRate`, matching the existing OpenAI path.

* 🌊 fix: Preserve Explicit `streamRate: 0` Through the Custom Endpoint Chain

Codex review: truthy guards dropped zero-valued streamRate at both the
endpoints.all override and the llmConfig assignment. With agents 3.4.0
defaulting stream smoothing ON, 0 becomes the explicit disable, so both
sites now use nullish guards; endpoints.all.streamRate: 0 overrides an
endpoint-level rate and an endpoint-level 0 reaches _lc_stream_delay.
Spec extended with both zero cases.
This commit is contained in:
Danny Avila 2026-08-05 19:43:22 -04:00 committed by GitHub
parent 56175af0b5
commit 305107998d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 106 additions and 2 deletions

View file

@ -115,7 +115,7 @@ function buildCustomOptions(
}
const allConfig = appConfig?.endpoints?.all;
if (allConfig) {
if (allConfig?.streamRate != null) {
customOptions.streamRate = allConfig.streamRate;
}
@ -345,7 +345,7 @@ export async function initializeCustom({
}
const streamRate = clientOptions.streamRate as number | undefined;
if (streamRate) {
if (streamRate != null) {
(options.llmConfig as Record<string, unknown>)._lc_stream_delay = streamRate;
}

View file

@ -0,0 +1,104 @@
import type { BaseInitializeParams } from '~/types';
jest.mock('~/auth', () => ({
validateEndpointURL: jest.fn(),
createSSRFSafeUndiciConnect: jest.fn(() => ({ lookup: jest.fn() })),
}));
const mockGetOpenAIConfig = jest.fn((..._args: unknown[]) => ({
llmConfig: { model: 'claude-haiku-4-5' } as Record<string, unknown>,
configOptions: {},
}));
jest.mock('~/endpoints/openai/config', () => ({
getOpenAIConfig: (...args: unknown[]) => mockGetOpenAIConfig(...args),
}));
jest.mock('~/endpoints/models', () => ({ fetchModels: jest.fn() }));
jest.mock('~/cache', () => ({
standardCache: jest.fn(() => ({ get: jest.fn().mockResolvedValue(null) })),
tokenConfigCache: jest.fn(() => ({ get: jest.fn().mockResolvedValue(null) })),
}));
jest.mock('~/utils', () => ({
isUserProvided: (val: string) => val === 'user_provided',
checkUserKeyExpiry: jest.fn(),
}));
const mockGetCustomEndpointConfig = jest.fn();
jest.mock('~/app/config', () => ({
getCustomEndpointConfig: (...args: unknown[]) => mockGetCustomEndpointConfig(...args),
}));
import { initializeCustom } from './initialize';
function makeParams({
allBlock,
endpointStreamRate,
}: {
allBlock?: Record<string, unknown>;
endpointStreamRate?: number;
} = {}): BaseInitializeParams {
mockGetCustomEndpointConfig.mockReturnValue({
apiKey: 'test-key',
baseURL: 'https://gateway.example.com/v1',
models: { default: ['claude-haiku-4-5'], fetch: false },
streamRate: endpointStreamRate,
});
return {
req: {
user: { id: 'user-1' },
body: {},
config: allBlock ? { endpoints: { all: allBlock } } : {},
} as unknown as BaseInitializeParams['req'],
endpoint: 'ClickHouse',
model_parameters: { model: 'claude-haiku-4-5' },
db: { getUserKeyValues: jest.fn() } as unknown as BaseInitializeParams['db'],
};
}
function streamDelayOf(options: { llmConfig: unknown }): unknown {
return (options.llmConfig as Record<string, unknown>)._lc_stream_delay;
}
describe('custom endpoint streamRate resolution', () => {
beforeEach(() => jest.clearAllMocks());
it('applies the endpoint streamRate when no `endpoints.all` block is present', async () => {
const options = await initializeCustom(makeParams({ endpointStreamRate: 25 }));
expect(streamDelayOf(options)).toBe(25);
});
it('preserves the endpoint streamRate when `endpoints.all` exists without its own streamRate', async () => {
const options = await initializeCustom(
makeParams({
endpointStreamRate: 25,
allBlock: { activityLabel: true, activityModel: 'gpt-5.6-luna' },
}),
);
expect(streamDelayOf(options)).toBe(25);
});
it('lets `endpoints.all.streamRate` override the endpoint streamRate', async () => {
const options = await initializeCustom(
makeParams({ endpointStreamRate: 25, allBlock: { streamRate: 10 } }),
);
expect(streamDelayOf(options)).toBe(10);
});
it('leaves the stream delay unset when neither level configures a streamRate', async () => {
const options = await initializeCustom(makeParams({ allBlock: { activityLabel: true } }));
expect(streamDelayOf(options)).toBeUndefined();
});
it('lets `endpoints.all.streamRate: 0` override an endpoint streamRate (explicit disable)', async () => {
const options = await initializeCustom(
makeParams({ endpointStreamRate: 25, allBlock: { streamRate: 0 } }),
);
expect(streamDelayOf(options)).toBe(0);
});
it('passes an explicit endpoint `streamRate: 0` through to the llmConfig', async () => {
const options = await initializeCustom(makeParams({ endpointStreamRate: 0 }));
expect(streamDelayOf(options)).toBe(0);
});
});