Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 7 additions & 3 deletions src/domain/models/method_execution_service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -545,9 +545,6 @@ export class DefaultMethodExecutionService implements MethodExecutionService {
]),
)
: undefined,
bundle: modelDef.bundleSource
? new TextEncoder().encode(modelDef.bundleSource)
: undefined,
};

let currentHandles: DataHandle[];
Expand Down Expand Up @@ -581,6 +578,13 @@ export class DefaultMethodExecutionService implements MethodExecutionService {
// Use the driver's context with writers for follow-up actions
executionContext = driver.contextWithWriters ?? context;
} else {
// Populate bundle only for out-of-process drivers (raw driver doesn't use it)
if (modelDef.bundleSourceFactory) {
executionRequest.bundle = new TextEncoder().encode(
await modelDef.bundleSourceFactory(),
);
}

// Look up a registered driver type
const driverInfo = driverTypeRegistry.get(driverType);
if (!driverInfo) {
Expand Down
8 changes: 4 additions & 4 deletions src/domain/models/model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -599,11 +599,11 @@ export interface ModelDefinition<
upgrades?: VersionUpgrade[];

/**
* Pre-compiled self-contained bundle source (JS) for out-of-process execution.
* Set by UserModelLoader at load time. Includes all dependencies inlined
* (including zod) so it can run without network access inside containers.
* Lazily builds the self-contained bundle for out-of-process execution (e.g. Docker).
* Called by the execution service when a non-raw driver is used. Memoizes its
* result so multiple executions of the same model in one process only bundle once.
*/
bundleSource?: string;
bundleSourceFactory?: () => Promise<string>;
}

/**
Expand Down
24 changes: 14 additions & 10 deletions src/domain/models/user_model_loader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -350,19 +350,23 @@ export class UserModelLoader {

const modelDef = this.convertToModelDefinition(userModel);

// Create self-contained bundle for out-of-process drivers (e.g., Docker).
// This inlines all deps including zod so the bundle runs without network.
try {
modelDef.bundleSource = await bundleExtension(
// Defer self-contained bundling to first out-of-process execution (e.g. Docker).
// Memoized so multiple executions in one process only bundle once.
// Uses promise-based memoization to avoid duplicate work under concurrent calls.
let bundlePromise: Promise<string> | undefined;
modelDef.bundleSourceFactory = () => {
bundlePromise ??= bundleExtension(
absolutePath,
denoPath,
{ selfContained: true },
);
} catch (error) {
logger
.warn`Failed to create self-contained bundle for ${file}: ${error}`;
// Non-fatal — model still works with raw driver
}
).catch((error) => {
bundlePromise = undefined;
logger
.warn`Failed to create self-contained bundle for ${file}: ${error}`;
throw error;
});
return bundlePromise;
};

if (!modelRegistry.has(modelDef.type)) {
modelRegistry.register(modelDef);
Expand Down
Loading