Stage list validation and dynamic source snapshots
The policy implementation follows.
ts
export function createStageModelTransport<Result, const Id extends string = string>(
registration: ModelStageTransportRegistration<Result> & { readonly id: Id },
) {
const stages = new Map<string, readonly string[]>();
for (const stage of registration.stages) {
const ids = stage.toolIds;
breakWhile(
!stage.id?.trim() ||
stages.has(stage.id) ||
!Array.isArray(ids) ||
!ids.length ||
new Set(ids).size !== ids.length,
`Invalid model stage: ${stage.id}`,
);
stages.set(stage.id, Object.freeze([...ids]));
}
breakWhile(!stages.size, 'Model transport requires declared stages');
const { id, adapter, options, requireStrictArguments, tools: source } = registration;
const project = (stage: string, tools: ModelToolSource<Result>): ModelToolSource<Result> => {
const ids = stages.get(stage);
breakWhile(!ids, `Unregistered model stage: ${id} -> ${stage}`);
const catalog = new ModelToolRegistry(tools.values());
for (const id of ids!)
breakWhile(!catalog.has(id), `Unregistered model capability: ${stage} -> ${id}`);
const selected = catalog.select(ids!);
const resolve = tools.resolve.bind(tools);
return Object.freeze({
values: () => selected.values() as ReturnType<ModelToolSource<Result>['values']>,
resolve(
selection: Parameters<ModelToolSource<Result>['resolve']>[0],
context: Readonly<{ stageId: string }>,
) {
// createModelTransport pins this stage and rejects selections outside
// the prepared capability IDs before delegating to this resolver.
return resolve(selection, { stageId: context.stageId });
},
});
};
const fixed =
typeof source === 'function'
? undefined
: new Map([...stages.keys()].map((stage) => [stage, project(stage, source)]));
return createModelTransport({
id,
adapter,
options,
requireStrictArguments,
tools(stage) {
breakWhile(!stages.has(stage), `Unregistered model stage: ${id} -> ${stage}`);
return fixed
? fixed.get(stage)!
: project(
stage,
synchronous(
(source as (stage: string) => ModelToolSource<Result>).call(registration, stage),
'Model capability sources must be synchronous',
),
);
},
});
}