Skip to content

Commit

Permalink
Merge pull request #3827 from continuedev/nate/yaml-fixes
Browse files Browse the repository at this point in the history
Nate/yaml-fixes
  • Loading branch information
sestinj authored Jan 24, 2025
2 parents d4b72ca + 3c55f60 commit 1a21a28
Show file tree
Hide file tree
Showing 28 changed files with 287 additions and 156 deletions.
2 changes: 1 addition & 1 deletion core/commands/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ export function slashFromCustomCommand(
): SlashCommand {
return {
name: customCommand.name,
description: customCommand.description,
description: customCommand.description ?? "",
run: async function* ({ input, llm, history, ide }) {
// Remove slash command prefix from input
let userInput = input;
Expand Down
94 changes: 70 additions & 24 deletions core/config/ConfigHandler.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import * as fs from "node:fs";

import {
ControlPlaneClient,
ControlPlaneSessionInfo,
Expand All @@ -12,12 +14,11 @@ import {
} from "../index.js";
import Ollama from "../llm/llms/Ollama.js";
import { GlobalContext } from "../util/GlobalContext.js";
import { getConfigJsonPath } from "../util/paths.js";
import { getConfigJsonPath, getConfigYamlPath } from "../util/paths.js";

import { ConfigResult, ConfigYaml } from "@continuedev/config-yaml";
import { ConfigResult, ConfigYaml, FullSlug } from "@continuedev/config-yaml";
import * as YAML from "yaml";
import { controlPlaneEnv } from "../control-plane/env.js";
import { usePlatform } from "../control-plane/flags.js";
import { getControlPlaneEnv, useHub } from "../control-plane/env.js";
import { localPathToUri } from "../util/pathToUri.js";
import {
LOCAL_ONBOARDING_CHAT_MODEL,
Expand Down Expand Up @@ -61,7 +62,7 @@ export class ConfigHandler {
controlPlaneClient,
writeLog,
);
this.profiles = [new ProfileLifecycleManager(localProfileLoader)];
this.profiles = [new ProfileLifecycleManager(localProfileLoader, this.ide)];
this.selectedProfileId = localProfileLoader.description.id;

// Always load local profile immediately in case control plane doesn't load
Expand Down Expand Up @@ -97,9 +98,16 @@ export class ConfigHandler {
async openConfigProfile(profileId?: string) {
let openProfileId = profileId || this.selectedProfileId;
if (openProfileId === "local") {
await this.ide.openFile(localPathToUri(getConfigJsonPath()));
const ideInfo = await this.ide.getIdeInfo();
const configYamlPath = getConfigYamlPath(ideInfo.ideType);
if (fs.existsSync(configYamlPath)) {
await this.ide.openFile(localPathToUri(configYamlPath));
} else {
await this.ide.openFile(localPathToUri(getConfigJsonPath()));
}
} else {
await this.ide.openUrl(`${controlPlaneEnv.APP_URL}${openProfileId}`);
const env = await getControlPlaneEnv(this.ide.getIdeSettings());
await this.ide.openUrl(`${env.APP_URL}${openProfileId}`);
}
}

Expand All @@ -108,10 +116,7 @@ export class ConfigHandler {
this.controlPlaneClient
.listAssistants()
.then(async (assistants) => {
this.profiles = this.profiles.filter(
(profile) => profile.profileDescription.id === "local",
);
await Promise.all(
const hubProfiles = await Promise.all(
assistants.map(async (assistant) => {
let renderedConfig: ConfigYaml | undefined = undefined;
if (assistant.configResult.config) {
Expand All @@ -126,21 +131,25 @@ export class ConfigHandler {
{ ...assistant.configResult, config: renderedConfig },
assistant.ownerSlug,
assistant.packageSlug,
assistant.configResult.config?.version ?? "latest",
this.controlPlaneClient,
this.ide,
this.ideSettingsPromise,
this.writeLog,
this.reloadConfig.bind(this),
);
this.profiles = [
...this.profiles.filter(
(profile) => profile.profileDescription.id === "local",
),
new ProfileLifecycleManager(profileLoader),
];

return new ProfileLifecycleManager(profileLoader, this.ide);
}),
);

this.profiles = [
...this.profiles.filter(
(profile) => profile.profileDescription.id === "local",
),
...hubProfiles,
];

this.notifyProfileListeners(
this.profiles.map((profile) => profile.profileDescription),
);
Expand Down Expand Up @@ -168,15 +177,49 @@ export class ConfigHandler {
}

private platformProfilesRefreshInterval: NodeJS.Timeout | undefined;
// We use this to keep track of whether we should reload the assistants
private lastFullSlugsList: FullSlug[] = [];

private fullSlugsListsDiffer(a: FullSlug[], b: FullSlug[]): boolean {
if (a.length !== b.length) {
return true;
}
for (let i = 0; i < a.length; i++) {
if (a[i].ownerSlug !== b[i].ownerSlug) {
return true;
}
if (a[i].packageSlug !== b[i].packageSlug) {
return true;
}
if (a[i].versionSlug !== b[i].versionSlug) {
return true;
}
}
return false;
}

private async fetchControlPlaneProfiles() {
if (usePlatform()) {
if (await useHub(this.ideSettingsPromise)) {
clearInterval(this.platformProfilesRefreshInterval);
await this.loadPlatformProfiles();
this.platformProfilesRefreshInterval = setInterval(
this.loadPlatformProfiles.bind(this),
PlatformProfileLoader.RELOAD_INTERVAL,
);

// Every 5 seconds we ask the platform whether there are any assistant updates in the last 5 seconds
// If so, we do the full (more expensive) reload
this.platformProfilesRefreshInterval = setInterval(async () => {
const newFullSlugsList =
await this.controlPlaneClient.listAssistantFullSlugs();

if (newFullSlugsList) {
const shouldReload = this.fullSlugsListsDiffer(
newFullSlugsList,
this.lastFullSlugsList,
);
if (shouldReload) {
await this.loadPlatformProfiles();
}
this.lastFullSlugsList = newFullSlugsList;
}
}, PlatformProfileLoader.RELOAD_INTERVAL);
} else {
this.controlPlaneClient
.listWorkspaces()
Expand All @@ -194,7 +237,9 @@ export class ConfigHandler {
this.writeLog,
this.reloadConfig.bind(this),
);
this.profiles.push(new ProfileLifecycleManager(profileLoader));
this.profiles.push(
new ProfileLifecycleManager(profileLoader, this.ide),
);
});

this.notifyProfileListeners(
Expand Down Expand Up @@ -249,11 +294,12 @@ export class ConfigHandler {
void this.reloadConfig();
}

updateControlPlaneSessionInfo(
async updateControlPlaneSessionInfo(
sessionInfo: ControlPlaneSessionInfo | undefined,
) {
this.controlPlaneClient = new ControlPlaneClient(
Promise.resolve(sessionInfo),
this.ideSettingsPromise,
);
this.fetchControlPlaneProfiles().catch((e) => {
console.error("Failed to fetch control plane profiles: ", e);
Expand Down
19 changes: 16 additions & 3 deletions core/config/ProfileLifecycleManager.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,21 @@
import { ConfigResult, ConfigValidationError } from "@continuedev/config-yaml";
import {
ConfigResult,
ConfigValidationError,
FullSlug,
} from "@continuedev/config-yaml";
import {
BrowserSerializedContinueConfig,
ContinueConfig,
IContextProvider,
IDE,
} from "../index.js";

import { finalToBrowserConfig } from "./load.js";
import { IProfileLoader } from "./profile/IProfileLoader.js";

export interface ProfileDescription {
fullSlug: FullSlug;
profileType: "control-plane" | "local" | "platform";
title: string;
id: string;
errors: ConfigValidationError[] | undefined;
Expand All @@ -19,7 +26,10 @@ export class ProfileLifecycleManager {
private savedBrowserConfigResult?: ConfigResult<BrowserSerializedContinueConfig>;
private pendingConfigPromise?: Promise<ConfigResult<ContinueConfig>>;

constructor(private readonly profileLoader: IProfileLoader) {}
constructor(
private readonly profileLoader: IProfileLoader,
private readonly ide: IDE,
) {}

get profileDescription(): ProfileDescription {
return this.profileLoader.description;
Expand Down Expand Up @@ -87,7 +97,10 @@ export class ProfileLifecycleManager {
config: undefined,
};
}
const serializedConfig = finalToBrowserConfig(result.config);
const serializedConfig = await finalToBrowserConfig(
result.config,
this.ide,
);
return {
...result,
config: serializedConfig,
Expand Down
11 changes: 6 additions & 5 deletions core/config/load.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,6 @@ import {
} from "../util/paths";

import { ConfigResult, ConfigValidationError } from "@continuedev/config-yaml";
import { usePlatform } from "../control-plane/flags";
import {
defaultContextProvidersJetBrains,
defaultContextProvidersVsCode,
Expand All @@ -72,8 +71,9 @@ import {
} from "./default";
import { getSystemPromptDotFile } from "./getSystemPromptDotFile";
// import { isSupportedLanceDbCpuTarget } from "./util";
import { validateConfig } from "./validation.js";
import { useHub } from "../control-plane/env";
import { localPathToUri } from "../util/pathToUri";
import { validateConfig } from "./validation.js";

function resolveSerializedConfig(filepath: string): SerializedContinueConfig {
let content = fs.readFileSync(filepath, "utf8");
Expand Down Expand Up @@ -547,9 +547,10 @@ async function intermediateToFinalConfig(
return { config: continueConfig, errors };
}

function finalToBrowserConfig(
async function finalToBrowserConfig(
final: ContinueConfig,
): BrowserSerializedContinueConfig {
ide: IDE,
): Promise<BrowserSerializedContinueConfig> {
return {
allowAnonymousTelemetry: final.allowAnonymousTelemetry,
models: final.models.map((m) => ({
Expand Down Expand Up @@ -582,7 +583,7 @@ function finalToBrowserConfig(
experimental: final.experimental,
docs: final.docs,
tools: final.tools,
usePlatform: usePlatform(),
usePlatform: await useHub(ide.getIdeSettings()),
};
}

Expand Down
6 changes: 6 additions & 0 deletions core/config/profile/ControlPlaneProfileLoader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,12 @@ export default class ControlPlaneProfileLoader implements IProfileLoader {
) {
this.description = {
id: workspaceId,
profileType: "control-plane",
fullSlug: {
ownerSlug: "",
packageSlug: "",
versionSlug: "",
},
title: workspaceTitle,
errors: undefined,
};
Expand Down
6 changes: 6 additions & 0 deletions core/config/profile/LocalProfileLoader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,12 @@ export default class LocalProfileLoader implements IProfileLoader {
static ID = "local";
description: ProfileDescription = {
id: LocalProfileLoader.ID,
profileType: "local",
fullSlug: {
ownerSlug: "",
packageSlug: "",
versionSlug: "",
},
title: "Local Config",
errors: undefined,
};
Expand Down
41 changes: 9 additions & 32 deletions core/config/profile/PlatformProfileLoader.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@
import { ConfigYaml } from "@continuedev/config-yaml/dist/schemas/index.js";
import * as YAML from "yaml";

import { ControlPlaneClient } from "../../control-plane/client.js";
import { ContinueConfig, IDE, IdeSettings } from "../../index.js";

import { ConfigResult } from "@continuedev/config-yaml";
import { ProfileDescription } from "../ProfileLifecycleManager.js";
import { clientRenderHelper } from "../yaml/clientRender.js";
import doLoadConfig from "./doLoadConfig.js";
import { IProfileLoader } from "./IProfileLoader.js";

Expand All @@ -21,14 +19,15 @@ export interface PlatformConfigMetadata {
}

export default class PlatformProfileLoader implements IProfileLoader {
static RELOAD_INTERVAL = 1000 * 60 * 15; // every 15 minutes
static RELOAD_INTERVAL = 1000 * 5; // 5 seconds

description: ProfileDescription;

constructor(
private configResult: ConfigResult<ConfigYaml>,
private readonly ownerSlug: string,
private readonly packageSlug: string,
versionSlug: string,
private readonly controlPlaneClient: ControlPlaneClient,
private readonly ide: IDE,
private ideSettingsPromise: Promise<IdeSettings>,
Expand All @@ -37,37 +36,15 @@ export default class PlatformProfileLoader implements IProfileLoader {
) {
this.description = {
id: `${ownerSlug}/${packageSlug}`,
title: `${ownerSlug}/${packageSlug}`,
profileType: "platform",
fullSlug: {
ownerSlug,
packageSlug,
versionSlug,
},
title: `${ownerSlug}/${packageSlug}@${versionSlug}`,
errors: configResult.errors,
};

setInterval(async () => {
const assistants = await this.controlPlaneClient.listAssistants();
const newConfigResult = assistants.find(
(assistant) =>
assistant.packageSlug === this.packageSlug &&
assistant.ownerSlug === this.ownerSlug,
)?.configResult;
if (!newConfigResult) {
return;
}

let renderedConfig: ConfigYaml | undefined = undefined;
if (newConfigResult.config) {
renderedConfig = await clientRenderHelper(
YAML.stringify(newConfigResult.config),
this.ide,
this.controlPlaneClient,
);
}

this.configResult = {
config: renderedConfig,
errors: newConfigResult.errors,
configLoadInterrupted: false,
};
this.onReload();
}, PlatformProfileLoader.RELOAD_INTERVAL);
}

async doLoadConfig(): Promise<ConfigResult<ContinueConfig>> {
Expand Down
6 changes: 4 additions & 2 deletions core/config/profile/doLoadConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import {
} from "../../";
import { ControlPlaneProxyInfo } from "../../control-plane/analytics/IAnalyticsProvider.js";
import { ControlPlaneClient } from "../../control-plane/client.js";
import { controlPlaneEnv } from "../../control-plane/env.js";
import { getControlPlaneEnv } from "../../control-plane/env.js";
import { TeamAnalytics } from "../../control-plane/TeamAnalytics.js";
import ContinueProxy from "../../llm/llms/stubs/ContinueProxy";
import { getConfigYamlPath } from "../../util/paths";
Expand Down Expand Up @@ -99,9 +99,11 @@ export default async function doLoadConfig(
const controlPlane = (newConfig as any).controlPlane;
const useOnPremProxy =
controlPlane?.useContinueForTeamsProxy === false && controlPlane?.proxyUrl;

const env = await getControlPlaneEnv(ideSettingsPromise);
let controlPlaneProxyUrl: string = useOnPremProxy
? controlPlane?.proxyUrl
: controlPlaneEnv.DEFAULT_CONTROL_PLANE_PROXY_URL;
: env.DEFAULT_CONTROL_PLANE_PROXY_URL;

if (!controlPlaneProxyUrl.endsWith("/")) {
controlPlaneProxyUrl += "/";
Expand Down
Loading

0 comments on commit 1a21a28

Please sign in to comment.