Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Nate/yaml-fixes #3827

Merged
merged 9 commits into from
Jan 24, 2025
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
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
Loading