further tidying

This commit is contained in:
Your Name
2026-04-10 16:51:47 +00:00
parent abd46a8431
commit 60e9bc8ec5
3 changed files with 66 additions and 52 deletions
@@ -52,7 +52,11 @@ export class SidecarLoader {
);
}
// Extract strictly what we need since we just validated it.
// Extract strictly what we need.
// Why this unsafe cast is acceptable:
// SchemaValidator just ran \`getSidecarConfigSchema(registry)\` against \`parsed\`.
// That function dynamically maps the \`processorOptions\` to strict JSON schema definitions,
// so we know with absolute certainty at runtime that \`parsed\` conforms to this shape.
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
const validConfig = parsed as {
budget?: SidecarConfig['budget'];
+57 -46
View File
@@ -15,6 +15,33 @@ import { createNodeDistillationProcessor } from '../processors/nodeDistillationP
import { createStateSnapshotProcessor } from '../processors/stateSnapshotProcessor.js';
import { createStateSnapshotAsyncProcessor } from '../processors/stateSnapshotAsyncProcessor.js';
/**
* Helper to safely merge static default options with dynamically loaded
* JSON overrides from the SidecarConfig.
*
* Why the unsafe cast is acceptable here:
* Before the \`config\` object ever reaches this function, \`SidecarLoader.ts\`
* passes the raw JSON through \`SchemaValidator\`. The schema dynamically generates
* a \`oneOf\` map linking every \`type\` discriminator to its corresponding processor
* schema definition. By the time we access \`options\` here, its shape has been
* strictly validated against the corresponding Zod/JSONSchema definition at runtime,
* making the generic cast to \`<T>\` structurally safe.
*/
function resolveProcessorOptions<T>(
config: SidecarConfig | undefined,
id: string,
defaultOptions: T,
): T {
if (config?.processorOptions && config.processorOptions[id]) {
return {
...defaultOptions,
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
...(config.processorOptions[id].options as T),
};
}
return defaultOptions;
}
export interface ContextProfile {
config: SidecarConfig;
buildPipelines: (
@@ -42,20 +69,9 @@ export const defaultSidecarProfile: ContextProfile = {
buildPipelines: (
env: ContextEnvironment,
config?: SidecarConfig,
): PipelineDef[] => {
): PipelineDef[] =>
// Helper to merge default options with dynamically loaded processorOptions by ID
const getOptions = <T>(id: string, defaultOptions: T): T => {
if (config?.processorOptions && config.processorOptions[id]) {
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
return {
...defaultOptions,
...(config.processorOptions[id].options as T),
};
}
return defaultOptions;
};
return [
[
{
name: 'Immediate Sanitization',
triggers: ['new_message'],
@@ -63,7 +79,9 @@ export const defaultSidecarProfile: ContextProfile = {
createToolMaskingProcessor(
'ToolMasking',
env,
getOptions('ToolMasking', { stringLengthThresholdTokens: 8000 }),
resolveProcessorOptions(config, 'ToolMasking', {
stringLengthThresholdTokens: 8000,
}),
),
createBlobDegradationProcessor('BlobDegradation', env), // No options
],
@@ -75,12 +93,16 @@ export const defaultSidecarProfile: ContextProfile = {
createNodeTruncationProcessor(
'NodeTruncation',
env,
getOptions('NodeTruncation', { maxTokensPerNode: 3000 }),
resolveProcessorOptions(config, 'NodeTruncation', {
maxTokensPerNode: 3000,
}),
),
createNodeDistillationProcessor(
'NodeDistillation',
env,
getOptions('NodeDistillation', { nodeThresholdTokens: 5000 }),
resolveProcessorOptions(config, 'NodeDistillation', {
nodeThresholdTokens: 5000,
}),
),
],
},
@@ -91,40 +113,29 @@ export const defaultSidecarProfile: ContextProfile = {
createStateSnapshotProcessor(
'StateSnapshotSync',
env,
getOptions('StateSnapshotSync', { target: 'max' }),
resolveProcessorOptions(config, 'StateSnapshotSync', {
target: 'max',
}),
),
],
},
];
},
],
buildAsyncPipelines: (
env: ContextEnvironment,
config?: SidecarConfig,
): AsyncPipelineDef[] => {
const getOptions = <T>(id: string, defaultOptions: T): T => {
if (config?.processorOptions && config.processorOptions[id]) {
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
return {
...defaultOptions,
...(config.processorOptions[id].options as T),
};
}
return defaultOptions;
};
return [
{
name: 'Async Background GC',
triggers: ['nodes_aged_out'],
processors: [
createStateSnapshotAsyncProcessor(
'StateSnapshotAsync',
env,
getOptions('StateSnapshotAsync', { type: 'accumulate' }),
),
],
},
];
},
): AsyncPipelineDef[] => [
{
name: 'Async Background GC',
triggers: ['nodes_aged_out'],
processors: [
createStateSnapshotAsyncProcessor(
'StateSnapshotAsync',
env,
resolveProcessorOptions(config, 'StateSnapshotAsync', {
type: 'accumulate',
}),
),
],
},
],
};
@@ -45,10 +45,10 @@ export class PipelineOrchestrator {
}
private setupTriggers() {
const bindTriggers = (
pipelines: PipelineDef[] | AsyncPipelineDef[],
const bindTriggers = <P extends PipelineDef | AsyncPipelineDef>(
pipelines: P[],
executeFn: (
pipeline: PipelineDef | AsyncPipelineDef,
pipeline: P,
nodes: readonly ConcreteNode[],
targets: ReadonlySet<string>,
protectedIds: ReadonlySet<string>,
@@ -78,9 +78,8 @@ export class PipelineOrchestrator {
};
bindTriggers(this.pipelines, (pipeline, nodes, targets, protectedIds) => {
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
void this.executePipelineAsync(
pipeline as PipelineDef,
pipeline,
nodes,
new Set(targets),
new Set(protectedIds),