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

better feature flag cache #4683

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
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
17 changes: 14 additions & 3 deletions core/util/GlobalContext.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,30 @@
import fs from "node:fs";
import { PosthogFeatureFlag } from "./posthog";

import { ModelRole } from "@continuedev/config-yaml";

import fs from "node:fs";
import { SiteIndexingConfig } from "..";

import {
salvageSharedConfig,
sharedConfigSchema,
SharedConfigSchema,
} from "../config/sharedConfig";

import { getGlobalContextFilePath } from "./paths";

export type GlobalContextModelSelections = Partial<
Record<ModelRole, string | null>
>;

// Add this type near the top of the file
export type FeatureFlagCacheEntry = {
lastUpdated: number;
value: any;
};

export type FeatureFlagCache = {
flags: Partial<Record<PosthogFeatureFlag, FeatureFlagCacheEntry>>;
};

export type GlobalContextType = {
indexingPaused: boolean;
lastSelectedProfileForWorkspace: {
@@ -37,6 +47,7 @@ export type GlobalContextType = {
hasAlreadyCreatedAPromptFile: boolean;
showConfigUpdateToast: boolean;
isSupportedLanceDbCpuTargetForLinux: boolean;
featureFlagCache: FeatureFlagCache;
sharedConfig: SharedConfigSchema;
failedDocs: SiteIndexingConfig[];
};
55 changes: 49 additions & 6 deletions core/util/posthog.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
import {
FeatureFlagCache,
FeatureFlagCacheEntry,
GlobalContext,
} from "./GlobalContext";

import os from "node:os";

import { TeamAnalytics } from "../control-plane/TeamAnalytics.js";
@@ -107,28 +113,65 @@ export class Telemetry {
}

private static featureValueCache: Record<string, any> = {};
private static globalContext = new GlobalContext();

static async getFeatureFlag(flag: PosthogFeatureFlag) {
const value = Telemetry.client?.getFeatureFlag(flag, Telemetry.uniqueId);
private static shouldRefreshFlag(flag: PosthogFeatureFlag): boolean {
const cache = Telemetry.globalContext.get("featureFlagCache");
if (!cache?.flags[flag]) return true;

const ONE_DAY = 24 * 60 * 60 * 1000; // 24 hours in milliseconds
const now = Date.now();
return now - cache.flags[flag].lastUpdated > ONE_DAY;
}

private static async updateFlagCache(flag: PosthogFeatureFlag, value: any) {
const cache = Telemetry.globalContext.get("featureFlagCache") || {
flags: {},
};

const newEntry: FeatureFlagCacheEntry = {
lastUpdated: Date.now(),
value,
};

const updatedCache: FeatureFlagCache = {
flags: {
...cache.flags,
[flag]: newEntry,
},
};

Telemetry.globalContext.update("featureFlagCache", updatedCache);
Telemetry.featureValueCache[flag] = value;
return value;
}

static async getValueForFeatureFlag(flag: PosthogFeatureFlag) {
try {
if (Telemetry.featureValueCache[flag]) {
return Telemetry.featureValueCache[flag];
// Check if we have a valid cached value
const cache = Telemetry.globalContext.get("featureFlagCache");
if (cache?.flags[flag] && !Telemetry.shouldRefreshFlag(flag)) {
return cache.flags[flag].value;
}

// If we need to refresh, get the new value
const userGroup = await Telemetry.getFeatureFlag(flag);
if (typeof userGroup === "string") {
return EXPERIMENTS[flag][userGroup].value;
const value = EXPERIMENTS[flag][userGroup].value;
await Telemetry.updateFlagCache(flag, value);
return value;
}

// If no value is found, cache undefined to prevent repeated lookups
await Telemetry.updateFlagCache(flag, undefined);
return undefined;
} catch {
return undefined;
}
}

// The getFeatureFlag method can remain unchanged
static async getFeatureFlag(flag: PosthogFeatureFlag) {
const value = Telemetry.client?.getFeatureFlag(flag, Telemetry.uniqueId);
return value;
}
}

Unchanged files with check annotations Beta

import { DEFAULT_AUTOCOMPLETE_OPTS } from "../util/parameters.js";
import { shouldCompleteMultiline } from "./classification/shouldCompleteMultiline.js";
import { ContextRetrievalService } from "./context/ContextRetrievalService.js";

Check warning on line 8 in core/autocomplete/CompletionProvider.ts

GitHub Actions / core-checks

There should be no empty line within import group
import { BracketMatchingService } from "./filtering/BracketMatchingService.js";
import { CompletionStreamer } from "./generation/CompletionStreamer.js";
// Save to cache
if (!outcome.cacheHit && helper.options.useCache) {
(await this.autocompleteCache).put(outcome.prefix, outcome.completion);

Check warning on line 260 in core/autocomplete/CompletionProvider.ts

GitHub Actions / core-checks

Promises must be awaited, end with a call to .catch, end with a call to .then with a rejection handler or be explicitly marked as ignored with the `void` operator
}
// When using the JetBrains extension, Mark as displayed
name: "Python",
// """"#" is for .ipynb files, where we add '"""' surrounding markdown blocks.
// This stops the model from trying to complete the start of a new markdown block
topLevelKeywords: ["def", "class", '"""#'],

Check warning on line 31 in core/autocomplete/constants/AutocompleteLanguageInfo.ts

GitHub Actions / core-checks

Strings must use doublequote
singleLineComment: "#",
endOfLine: [],
};
constructor(private readonly ide: IDE) {
ide.onDidChangeActiveTextEditor((filepath) => {
this.cache.initKey(filepath);

Check warning on line 24 in core/autocomplete/context/ImportDefinitionsService.ts

GitHub Actions / core-checks

Promises must be awaited, end with a call to .catch, end with a call to .then with a rejection handler or be explicitly marked as ignored with the `void` operator
});
}
import { jest } from "@jest/globals";

Check warning on line 1 in core/autocomplete/context/root-path-context/test/testUtils.ts

GitHub Actions / core-checks

There should be at least one empty line between import groups

Check warning on line 1 in core/autocomplete/context/root-path-context/test/testUtils.ts

GitHub Actions / core-checks

`@jest/globals` import should occur after import of `path`
import fs from "fs";
import path from "path";
import Parser from "web-tree-sitter";

Check warning on line 5 in core/autocomplete/context/root-path-context/test/testUtils.ts

GitHub Actions / core-checks

There should be at least one empty line between import groups
import { Position } from "../../../..";
import { testIde } from "../../../../test/fixtures";
import { getAst, getTreePathAtCursor } from "../../../util/ast";
`,
llmOutput: `world!");
`,
expectedCompletion: 'world!");',

Check warning on line 66 in core/autocomplete/filtering/test/testCases.ts

GitHub Actions / core-checks

Strings must use doublequote
},
{
description: "Should autocomplete Java when inside a block",
): Promise<AutocompleteCodeSnippet[]> {
const snippets: AutocompleteCodeSnippet[] = [];
`,
expectedCompletion: `console.log('TEST');`,

Check warning on line 257 in core/autocomplete/filtering/test/testCases.ts

GitHub Actions / core-checks

Strings must use doublequote
},
{
description: "Should autocomplete React effect hook",
return this;
}
`,
llmOutput: ` this;`,

Check warning on line 303 in core/autocomplete/filtering/test/testCases.ts

GitHub Actions / core-checks

Strings must use doublequote
expectedCompletion: `this;`,
},
{
stopStatusBarLoading,
} from "./statusBar";
import type { IDE } from "core";

Check warning on line 26 in extensions/vscode/src/autocomplete/completionProvider.ts

GitHub Actions / vscode-checks

There should be at least one empty line between import groups
import { handleLLMError } from "../util/errorHandling";

Check warning on line 27 in extensions/vscode/src/autocomplete/completionProvider.ts

GitHub Actions / vscode-checks

`../util/errorHandling` import should occur before import of `../util/messages`
interface VsCodeCompletionInput {
document: vscode.TextDocument;
import * as vscode from "vscode";
import type { IDE, Range, RangeInFile, RangeInFileWithContents } from "core";
import type Parser from "web-tree-sitter";

Check warning on line 11 in extensions/vscode/src/autocomplete/lsp.ts

GitHub Actions / vscode-checks

There should be at least one empty line between import groups
import { GetLspDefinitionsFunction } from "core/autocomplete/types";

Check warning on line 12 in extensions/vscode/src/autocomplete/lsp.ts

GitHub Actions / vscode-checks

`core/autocomplete/types` import should occur before import of `core/autocomplete/util/ast`
import {

Check warning on line 13 in extensions/vscode/src/autocomplete/lsp.ts

GitHub Actions / vscode-checks

`core/autocomplete/snippets/types` import should occur before import of `core/autocomplete/util/ast`
AutocompleteCodeSnippet,
AutocompleteSnippetType,
} from "core/autocomplete/snippets/types";
import * as URI from "uri-js";

Check warning on line 17 in extensions/vscode/src/autocomplete/lsp.ts

GitHub Actions / vscode-checks

`uri-js` import should occur before import of `vscode`
type GotoProviderName =
| "vscode.executeDefinitionProvider"
setupStatusBar,
StatusBarStatus,
} from "./autocomplete/statusBar";
import { ContinueGUIWebviewViewProvider } from "./ContinueGUIWebviewViewProvider";

Check warning on line 30 in extensions/vscode/src/commands.ts

GitHub Actions / vscode-checks

There should be no empty line within import group
import { VerticalDiffManager } from "./diff/vertical/manager";
import EditDecorationManager from "./quickEdit/EditDecorationManager";
import { getMetaKeyLabel } from "./util/util";
import { VsCodeIde } from "./VsCodeIde";
import { LOCAL_DEV_DATA_VERSION } from "core/data/log";

Check warning on line 39 in extensions/vscode/src/commands.ts

GitHub Actions / vscode-checks

`core/data/log` import should occur before import of `core/indexing/walkDir`
import { isModelInstaller } from "core/llm";

Check warning on line 40 in extensions/vscode/src/commands.ts

GitHub Actions / vscode-checks

`core/llm` import should occur before import of `core/util/paths`
import { startLocalOllama } from "core/util/ollamaHelper";

Check warning on line 41 in extensions/vscode/src/commands.ts

GitHub Actions / vscode-checks

There should be at least one empty line between import groups
import type { VsCodeWebviewProtocol } from "./webviewProtocol";
let fullScreenPanel: vscode.WebviewPanel | undefined;