Skip to content

Conversation

devversion
Copy link
Member

@devversion devversion commented Sep 25, 2025

This commit reorganizes the web-codegen-scorer to support remote
environments. A remote environment is similar to the existing concept of
environments, with the exception that the lifecycle of an environment
can be managed in a hosted standalone server within a e.g. corporate
network.

The server would then provide additional features to the
web-codegen-scorer, like:

  • different models for file generation
  • different execution sandboxes for building and serving an app (e.g.
    consider a framework like Wiz that is internal to Google)

In practice, a remote environment exposes all of the important internal
hooks to advanced users, so that they can be fully in charge of:

  • file generation via LLMs
  • building generated apps
  • repairing generated apps
  • serving generated apps

Most users will never have to deal with this, but the architecture is highly
beneficial for further separation of concerns in the codebase, plus potentially
paving the way to support different languages (if we intend to do so), because
the logic for testing a "served app" is easy to disable with these changes.

R: @mturco

@devversion devversion force-pushed the remote-envs branch 2 times, most recently from 4122cf6 to 7107e6c Compare September 26, 2025 14:47
import { mcpServerOptionsSchema } from '../codegen/llm-runner.js';
import { getPossiblePackageManagers } from './environment-config.js';

export const baseEnvironmentConfigSchema = z.strictObject({
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a copy of the existing schema, just with all fields like buildCommand, serveCommand removed. This is because remote environments don't have these options; so this is the "shared base schema".

@@ -0,0 +1,332 @@
import { readdirSync, readFileSync, statSync } from 'fs';
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a copy of the previous environment with the exception that e.g. buildCommand is no longer in there. This is the "base/shared class" for environments. Local + remote environments will extend from this.

Logic is the same as before, just with some of these options removed + notice the abstract gateway.

/** When enabled, the system prompts for this environment won't be included in the report. */
classifyPrompts: z.boolean().optional(),
});
const environmentConfigSchema = z.union([
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Environments can now be defined by either a LocalEnvironmentConfig or a RemoteEnvironmentConfig

>;

/** Represents a single prompt evaluation environment. */
export class LocalEnvironment extends BaseEnvironment {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the logic from the old environment that was pulled out in the files before!

The new LocalGateway() is new here. This is because a local environment always uses the "default" local gateway; interacting with a project boilerplate for building etc.

}

try {
llm = await getRunnerByName(cliArgs.runner as RunnerName);
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

An actual LLM for generation is only created when a local environment is selected; so this logic moved into the generateCodeAndAssess call

import { BrowserAgentTaskInput } from '../testing/browser-agent/models.js';

/** Attempts to run & test an eval app. */
export async function serveAndTestApp(
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a new split from the previous build worker. After the build worker, serving can be initiated by the gateway. Remote might just serve from within the service, while local will start a child process from the serveCommand. Like ng serve --port 0.

rootPromptDef,
progress,
async (serveUrl) => {
const serveParams: ServeTestingWorkerMessage = {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Once the server is running (from the gateway), we start the serve-testing worker for connecting to the app and performing tests

` - MCP servers: ${options.startMcp && env.mcpServerOptions.length ? env.mcpServerOptions.length : 'none'}`,
` - Runner: ${env instanceof LocalEnvironment ? env.llm.displayName : 'Remote'}`,
env instanceof LocalEnvironment
? ` - MCP servers: ${options.startMcp && env.mcpServerOptions.length ? env.mcpServerOptions.length : 'none'}`
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

MCP servers are currently only launched by our local environments. We can look into this setting for remote environments in follow-ups

finalAttempt: {
buildResult: BuildResult;
serveTestingResult: ServeTestingResult | null;
};
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These are breaking changes for older reports! We need to migrate older reports 😞

@@ -0,0 +1,41 @@
import { PackageSummary } from '@safety-web/types';
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same types as with the old builder worker, just that the logic for serving is gone here.

@devversion devversion changed the title WIP: feat: remote environment support feat: remote environment support Sep 26, 2025
@devversion devversion marked this pull request as ready for review September 26, 2025 15:04
@devversion devversion force-pushed the remote-envs branch 3 times, most recently from b8c2a8a to ee61ccb Compare September 26, 2025 15:49
@devversion devversion requested a review from mturco September 26, 2025 16:05
Copy link
Collaborator

@mturco mturco left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this was a ton of work — thank you for doing it!

I think this is exactly what I'm looking for / had in mind for remote envs, just have some non-blocking comments/questions

@@ -0,0 +1,18 @@
// @ts-check
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we just make this a .ts file? is that supported?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unfortunately not I think 😞 We should look into this in a follow-up.

/** Represents a single prompt evaluation environment. */
export abstract class BaseEnvironment {
/** Path at which the environment is defined. */
readonly rootPath: string;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

wouldn't this just be the directory of the environment implementing this class?

Copy link
Member Author

@devversion devversion Sep 26, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, it's just the path where the environment config lives. I don't fully recall why this is needed, but it's unrelated to this PR (and needs to stay for remote envs too I think)

const directoriesToCopy: string[] = [];

if (env.projectTemplatePath) {
if (env instanceof LocalEnvironment && env.projectTemplatePath) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this makes sense to me 👍

rootPromptDef: RootPromptDefinition,
progress: ProgressLogger,
logicWhileServing: (serveUrl: string) => Promise<T>
): Promise<T>;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

just curious: why does this return value need to be generic?

Copy link
Member Author

@devversion devversion Sep 26, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The logic executing while an app is served, collects a result. This result should be exposed to the runner. I guess we could expect logicWhileServing callback to store that itself, but feels a bit unclean. See e.g.


let result: ServeTestingResult;

await gateway.serveApp(..., (serverUrl) => {
   // logic for testing app
   result = bla;
})

// Now expect `result` to be available. Would need a `result!` for type safety.

// via https://stackoverflow.com/questions/9006988/node-js-on-windows-how-to-clear-console
if (options.logging === 'dynamic') {
process.stdout.write('\x1Bc');
// TODO(devversion): Consider validating model names also for remote environments.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this would seemingly be difficult since remote envs could be using private models with unknown names, right?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I would imagine we can either never do this (and remove the TODO), or eventually this validation can happen as part of initializeEval?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oh yeah that's a good idea 👍

This commit reorganizes the `web-codegen-scorer` to support remote
environments. A remote environment is similar to the existing concept of
environments, with the exception that the lifecycle of an environment
can be managed in a hosted standalone server within a e.g. corporate
network.

The server would then provide additional features to the
web-codegen-scorer, like:

- different models for file generation
- different execution sandboxes for building and serving an app (e.g.
  consider a framework like Wiz that is internal to Google)

In practice, a remote environment exposes all of the important internal
hooks to advanced users, so that they can be fully in charge of:

- file generation via LLMs
- building generated apps
- repairing generated apps
- serving generated apps

Most users will never have to deal with this, but the architecture is highly
beneficial for further separation of concerns in the codebase, plus potentially
paving the way to support different languages (if we intend to do so), because
the logic for testing a "served app" is easy to disable with these changes.
@devversion devversion merged commit a4b50ec into angular:main Sep 26, 2025
3 checks passed
@devversion devversion deleted the remote-envs branch September 26, 2025 17:36
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants