Negotiate and decode snapshot-bound canonical model streams

This commit is contained in:
2026-11-09 02:06:07 +08:00
parent 0205f24904
commit a92ae72c8b
9 changed files with 172 additions and 15 deletions
+11 -6
View File
@@ -1,16 +1,21 @@
import { decodeModelStream } from './model-stream.ts';
import type { Id, Registry } from '../canvas-core/types.ts';
import { fault } from '../canvas-core/values.ts';
import type { CanonicalReadResult } from '../canvas-runtime/contracts.ts';
import type { CanonicalReadUpdate } from '../canvas-runtime/contracts.ts';
import { loadQueryKey, type LoadPort } from '../canvas-runtime/loader.ts';
import { loadFoundryModel, type ServiceTransport } from './foundry.ts';
/** The current endpoint is complete-only. One validated chunk is intentional;
* this adapter does not claim to stream an already-buffered response progressively.
*/
export function createFoundryModelLoader(options: { workspaceId: Id; registry: Registry; transport: ServiceTransport }): LoadPort<CanonicalReadResult> {
/** Negotiate real streaming; older servers may explicitly return complete JSON. */
export function createFoundryModelLoader(options: { workspaceId: Id; registry: Registry; transport: ServiceTransport }): LoadPort<CanonicalReadUpdate> {
return async function* (scope, signal) {
if (scope.workspaceId !== options.workspaceId || scope.format !== 'canonical-model-v1' || scope.source !== 'foundry' || scope.viewKey !== 'canonical-project') throw fault('invalid_model_load_scope');
const value = await loadFoundryModel(options.transport, options.workspaceId, scope.canvasId, options.registry, signal);
const response=await options.transport({method:'GET',path:`workspaces/${encodeURIComponent(options.workspaceId)}/materials/${encodeURIComponent(scope.canvasId)}/canvas/model`,signal,responseFormat:'model-stream-v1'});
if(response.stream){
if(response.status!==200)throw fault('invalid_model_stream');
for await(const chunk of decodeModelStream(response.stream,scope.canvasId,options.registry,signal))yield Object.freeze({...chunk,serverVersions:Object.freeze(Object.fromEntries(chunk.versions)),permissionEpoch:scope.authEpoch,queryKey:loadQueryKey(scope)});
return;
}
const value = await loadFoundryModel(async()=>response, options.workspaceId, scope.canvasId, options.registry, signal);
signal.throwIfAborted();
const projectId = value.status === 'ready' ? value.document.projectId : value.rootId;
const versions = Object.fromEntries([...(value.status === 'ready' ? value.versions : value.sourceVersions)].sort(([a], [b]) => a.localeCompare(b)));