From 509bb22fc23cb90c77c05317a332c82e55191000 Mon Sep 17 00:00:00 2001 From: Nate Date: Tue, 17 Dec 2024 08:54:48 -0800 Subject: [PATCH 01/99] bump version --- extensions/vscode/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extensions/vscode/package.json b/extensions/vscode/package.json index ce7338061a..37183e9ab5 100644 --- a/extensions/vscode/package.json +++ b/extensions/vscode/package.json @@ -2,7 +2,7 @@ "name": "continue", "icon": "media/icon.png", "author": "Continue Dev, Inc", - "version": "0.8.64", + "version": "0.8.65", "repository": { "type": "git", "url": "https://github.com/continuedev/continue" From a835ac1126eb5e9bff4dcb4a49536c69dc4b1dcb Mon Sep 17 00:00:00 2001 From: Dallin Romney Date: Fri, 20 Dec 2024 10:10:03 -0800 Subject: [PATCH 02/99] fix chat indexing peek --- core/indexing/docs/DocsService.ts | 8 +++++++- .../components/indexing/ChatIndexingPeeks.tsx | 20 ++++++++++++------- 2 files changed, 20 insertions(+), 8 deletions(-) diff --git a/core/indexing/docs/DocsService.ts b/core/indexing/docs/DocsService.ts index f35f834666..5480dc1613 100644 --- a/core/indexing/docs/DocsService.ts +++ b/core/indexing/docs/DocsService.ts @@ -710,14 +710,20 @@ export default class DocsService { await db.exec("PRAGMA busy_timeout = 3000;"); await runSqliteMigrations(db); - + // First create the table if it doesn't exist await db.exec(`CREATE TABLE IF NOT EXISTS ${DocsService.sqlitebTableName} ( id INTEGER PRIMARY KEY AUTOINCREMENT, title STRING NOT NULL, startUrl STRING NOT NULL UNIQUE, favicon STRING + embeddingsProviderId STRING )`); + // embeddingsProviderId was added later + await db.exec(`ALTER TABLE ${DocsService.sqlitebTableName} + ADD COLUMN IF NOT EXISTS embeddingsProviderId STRING; + `); + this.sqliteDb = db; } diff --git a/gui/src/components/indexing/ChatIndexingPeeks.tsx b/gui/src/components/indexing/ChatIndexingPeeks.tsx index 1ec1076c01..dbca84b051 100644 --- a/gui/src/components/indexing/ChatIndexingPeeks.tsx +++ b/gui/src/components/indexing/ChatIndexingPeeks.tsx @@ -75,25 +75,31 @@ function ChatIndexingPeek({ state }: ChatIndexingPeekProps) { } function ChatIndexingPeeks() { + const config = useAppSelector((store) => store.config.config); const indexingStatuses = useAppSelector( (store) => store.indexing.indexing.statuses, ); const mergedIndexingStates: MergedIndexingState[] = useMemo(() => { const mergedStates: MergedIndexingState[] = []; - const docsStates = Object.values(indexingStatuses).filter( - (status) => status.type === "docs" && status.status === "indexing", - ); - if (docsStates.length > 0) { + const configDocs = config?.docs ?? []; + const docsIndexing: IndexingStatus[] = []; + configDocs.forEach((doc) => { + const status = indexingStatuses[doc.startUrl]; + if (status && status.status === "indexing") { + docsIndexing.push(status); + } + }); + if (docsIndexing.length > 0) { mergedStates.push({ displayName: "Docs indexing", type: "docs", - titles: docsStates.map((state) => state.title), - progressPercentage: mergeProgress(docsStates), + titles: docsIndexing.map((doc) => doc.title), + progressPercentage: mergeProgress(docsIndexing), }); } return mergedStates; - }, [indexingStatuses]); + }, [config, indexingStatuses]); if (!mergedIndexingStates.length) return null; From 8f18577169351785173cca4802b6a0f72a656714 Mon Sep 17 00:00:00 2001 From: Nate Date: Fri, 20 Dec 2024 15:15:15 -0500 Subject: [PATCH 03/99] bump vscode version --- extensions/vscode/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extensions/vscode/package.json b/extensions/vscode/package.json index 37183e9ab5..65bfdd7303 100644 --- a/extensions/vscode/package.json +++ b/extensions/vscode/package.json @@ -2,7 +2,7 @@ "name": "continue", "icon": "media/icon.png", "author": "Continue Dev, Inc", - "version": "0.8.65", + "version": "0.8.66", "repository": { "type": "git", "url": "https://github.com/continuedev/continue" From cb138998d47abf98789d4df47960c0bcf024822a Mon Sep 17 00:00:00 2001 From: Nate Date: Fri, 20 Dec 2024 14:41:32 -0500 Subject: [PATCH 04/99] roll back openai-adapters for Azure --- core/llm/llms/Azure.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/core/llm/llms/Azure.ts b/core/llm/llms/Azure.ts index 5b3ba4338d..b2beae9218 100644 --- a/core/llm/llms/Azure.ts +++ b/core/llm/llms/Azure.ts @@ -1,4 +1,5 @@ import { LLMOptions } from "../../index.js"; +import { LlmApiRequestType } from "../openaiTypeConverters.js"; import OpenAI from "./OpenAI.js"; @@ -9,6 +10,8 @@ class Azure extends OpenAI { return false; } + protected useOpenAIAdapterFor: (LlmApiRequestType | "*")[] = []; + static defaultOptions: Partial = { apiVersion: "2024-02-15-preview", apiType: "azure", From d6c8c067c2066002188f63d44b5544c742537b30 Mon Sep 17 00:00:00 2001 From: Nate Date: Fri, 20 Dec 2024 15:17:18 -0500 Subject: [PATCH 05/99] changelog --- .changes/extensions/vscode/0.8.66.md | 9 +++++++++ .changes/unreleased/Changed-20241213-091505.yaml | 4 ---- .changes/unreleased/Fixed-20241213-091454.yaml | 4 ---- .changes/unreleased/Fixed-20241213-091519.yaml | 4 ---- .changes/unreleased/Fixed-20241216-220802.yaml | 4 ---- .changes/unreleased/Fixed-20241216-220818.yaml | 4 ---- extensions/vscode/CHANGELOG.md | 3 ++- 7 files changed, 11 insertions(+), 21 deletions(-) create mode 100644 .changes/extensions/vscode/0.8.66.md delete mode 100644 .changes/unreleased/Changed-20241213-091505.yaml delete mode 100644 .changes/unreleased/Fixed-20241213-091454.yaml delete mode 100644 .changes/unreleased/Fixed-20241213-091519.yaml delete mode 100644 .changes/unreleased/Fixed-20241216-220802.yaml delete mode 100644 .changes/unreleased/Fixed-20241216-220818.yaml diff --git a/.changes/extensions/vscode/0.8.66.md b/.changes/extensions/vscode/0.8.66.md new file mode 100644 index 0000000000..4a19309785 --- /dev/null +++ b/.changes/extensions/vscode/0.8.66.md @@ -0,0 +1,9 @@ +## 0.8.66 - 2024-12-20 +### Changed +* Improved autocomplete in untitled files +### Fixed +* Display more mid-line completions +* Restored syntax highlighting +* Fix tool use bug for models that don't support tools +* Autodetect mistral API key type +* Fixes Azure OpenAI regressions diff --git a/.changes/unreleased/Changed-20241213-091505.yaml b/.changes/unreleased/Changed-20241213-091505.yaml deleted file mode 100644 index 0ce6136aea..0000000000 --- a/.changes/unreleased/Changed-20241213-091505.yaml +++ /dev/null @@ -1,4 +0,0 @@ -project: extensions/vscode -kind: Changed -body: Improved autocomplete in untitled files -time: 2024-12-13T09:15:05.552877-08:00 diff --git a/.changes/unreleased/Fixed-20241213-091454.yaml b/.changes/unreleased/Fixed-20241213-091454.yaml deleted file mode 100644 index fb8d925847..0000000000 --- a/.changes/unreleased/Fixed-20241213-091454.yaml +++ /dev/null @@ -1,4 +0,0 @@ -project: extensions/vscode -kind: Fixed -body: Display more mid-line completions -time: 2024-12-13T09:14:54.808683-08:00 diff --git a/.changes/unreleased/Fixed-20241213-091519.yaml b/.changes/unreleased/Fixed-20241213-091519.yaml deleted file mode 100644 index 84a193224d..0000000000 --- a/.changes/unreleased/Fixed-20241213-091519.yaml +++ /dev/null @@ -1,4 +0,0 @@ -project: extensions/vscode -kind: Fixed -body: Restored syntax highlighting -time: 2024-12-13T09:15:19.485189-08:00 diff --git a/.changes/unreleased/Fixed-20241216-220802.yaml b/.changes/unreleased/Fixed-20241216-220802.yaml deleted file mode 100644 index db8c0108f0..0000000000 --- a/.changes/unreleased/Fixed-20241216-220802.yaml +++ /dev/null @@ -1,4 +0,0 @@ -project: extensions/vscode -kind: Fixed -body: Fix tool use bug for models that don't support tools -time: 2024-12-16T22:08:02.399772-08:00 diff --git a/.changes/unreleased/Fixed-20241216-220818.yaml b/.changes/unreleased/Fixed-20241216-220818.yaml deleted file mode 100644 index b8e0591813..0000000000 --- a/.changes/unreleased/Fixed-20241216-220818.yaml +++ /dev/null @@ -1,4 +0,0 @@ -project: extensions/vscode -kind: Fixed -body: Autodetect mistral API key type -time: 2024-12-16T22:08:18.369444-08:00 diff --git a/extensions/vscode/CHANGELOG.md b/extensions/vscode/CHANGELOG.md index 4ad93caf6e..88f9906e36 100644 --- a/extensions/vscode/CHANGELOG.md +++ b/extensions/vscode/CHANGELOG.md @@ -5,7 +5,7 @@ adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html), and is generated by [Changie](https://github.com/miniscruff/changie). -Pre-release Changes +## 0.8.66 - 2024-12-20 ### Changed * Improved autocomplete in untitled files ### Fixed @@ -13,6 +13,7 @@ Pre-release Changes * Restored syntax highlighting * Fix tool use bug for models that don't support tools * Autodetect mistral API key type +* Fixes Azure OpenAI regressions ## 0.8.62 - 2024-12-10 ### Added From baca0805bf9935f7322100ca42e29f0224c1afe5 Mon Sep 17 00:00:00 2001 From: Dallin Romney Date: Fri, 20 Dec 2024 12:27:42 -0800 Subject: [PATCH 06/99] embeddings provider in docs --- core/context/providers/DocsContextProvider.ts | 41 ++-- core/core.ts | 2 +- core/indexing/docs/DocsService.ts | 196 ++++++++---------- core/util/GlobalContext.test.ts | 21 +- core/util/GlobalContext.ts | 1 - gui/src/components/dialogs/AddDocsDialog.tsx | 16 ++ .../components/indexing/DocsIndexingPeeks.tsx | 1 + 7 files changed, 135 insertions(+), 143 deletions(-) diff --git a/core/context/providers/DocsContextProvider.ts b/core/context/providers/DocsContextProvider.ts index 6eab55fa46..47542bc1c6 100644 --- a/core/context/providers/DocsContextProvider.ts +++ b/core/context/providers/DocsContextProvider.ts @@ -80,22 +80,28 @@ class DocsContextProvider extends BaseContextProvider { query: string, extras: ContextProviderExtras, ): Promise { + // Get docs service const docsService = DocsService.getSingleton(); - if (!docsService) { console.error(`${DocsService.name} has not been initialized`); return []; } - await docsService.isInitialized; + + // Get chunks let chunks = await docsService.retrieveChunksFromQuery( extras.fullInput, // confusing: fullInput = the query, query = startUrl in this case query, this.options?.nRetrieve ?? DocsContextProvider.nRetrieve, ); + if(!chunks?.length) { + return []; + } + // We found chunks, so check if there's a favicon for the docs page const favicon = await docsService.getFavicon(query); + // Rerank if there's a rerankder if (extras.reranker) { chunks = await this._rerankChunks( chunks, @@ -139,18 +145,30 @@ class DocsContextProvider extends BaseContextProvider { async loadSubmenuItems( args: LoadSubmenuItemsArgs, ): Promise { + // Get docs service const docsService = DocsService.getSingleton(); - if (!docsService) { console.error(`${DocsService.name} has not been initialized`); return []; } + await docsService.isInitialized; - const docs = (await docsService.listMetadata()) ?? []; - const canUsePreindexedDocs = await docsService.canUsePreindexedDocs(); - + // Create map of docs url -> submenu item const submenuItemsMap = new Map(); + // Add custom docs from config + const docs = (await docsService.listMetadata()) ?? []; + for (const { startUrl, title, favicon } of docs) { + submenuItemsMap.set(startUrl, { + title,\ + id: startUrl, + description: new URL(startUrl).hostname, + icon: favicon, + }); + } + + // Add pre-indexed docs if supported + const canUsePreindexedDocs = await docsService.canUsePreindexedDocs(); if (canUsePreindexedDocs) { for (const { startUrl, title } of Object.values(preIndexedDocs)) { submenuItemsMap.set(startUrl, { @@ -164,17 +182,8 @@ class DocsContextProvider extends BaseContextProvider { } } - for (const { startUrl, title, favicon } of docs) { - submenuItemsMap.set(startUrl, { - title, - id: startUrl, - description: new URL(startUrl).hostname, - icon: favicon, - }); - } - + // Create array and sort if pre-indexed is supported const submenuItems = Array.from(submenuItemsMap.values()); - if (canUsePreindexedDocs) { return this._sortByPreIndexedDocs(submenuItems); } diff --git a/core/core.ts b/core/core.ts index fb3840d05d..bea254c818 100644 --- a/core/core.ts +++ b/core/core.ts @@ -286,7 +286,7 @@ export class Core { }); on("context/indexDocs", async (msg) => { - await this.docsService.syncOrReindexAllDocsWithPrompt(msg.data.reIndex); + await this.docsService.syncDocsWithPrompt(msg.data.reIndex); }); on("context/loadSubmenuItems", async (msg) => { diff --git a/core/indexing/docs/DocsService.ts b/core/indexing/docs/DocsService.ts index 5480dc1613..0af6aa28f6 100644 --- a/core/indexing/docs/DocsService.ts +++ b/core/indexing/docs/DocsService.ts @@ -185,7 +185,6 @@ export default class DocsService { abort(startUrl: string) { const status = this.statuses.get(startUrl); if (status) { - this.docsIndexingQueue.delete(startUrl); this.handleStatusUpdate({ ...status, status: "aborted", @@ -193,6 +192,7 @@ export default class DocsService { description: "Canceled", }); } + this.docsIndexingQueue.delete(startUrl); } isAborted(startUrl: string) { @@ -256,93 +256,79 @@ export default class DocsService { this.docsCrawler = new DocsCrawler(this.ide, newConfig); + if (this.config.disableIndexing) { + return; + } + + // No point in indexing if no docs context provider + const hasDocsContextProvider = this.hasDocsContextProvider(); + if (!hasDocsContextProvider) { + return; + } + // Skip docs indexing if not supported + // No warning message here because would show on ANY config update const unsupported = await this.isUsingUnsupportedPreIndexedEmbeddingsProvider(); if (unsupported) { return; } - await this.syncOrReindexAllDocs(newConfig, oldConfig); - } - } - - private async syncOrReindexAllDocs( - newConfig: ContinueConfig, - oldConfig?: ContinueConfig, - ) { - // On embeddings provider change, reindex all non-preindexed docs - // change = doesn't match last successful embeddings provider - const curEmbeddingsProviderId = this.globalContext.get( - "curEmbeddingsProviderId", - ); - if ( - !curEmbeddingsProviderId || - curEmbeddingsProviderId !== newConfig.embeddingsProvider.embeddingId - ) { - // If not set, we're initializing - const currentDocs = await this.listMetadata(); - if (currentDocs.length > 0) { - await this.reindexDocsOnNewEmbeddingsProvider(); - return; - } + await this.syncDocs(oldConfig, newConfig, false); } - - await this.syncDocsOnConfigUpdate(oldConfig, newConfig); } - async syncOrReindexAllDocsWithPrompt(reIndex: boolean = false) { + async syncDocsWithPrompt(reIndex: boolean = false) { if (!this.hasDocsContextProvider()) { - const didAddDocsContextProvider = - await this.showAddDocsContextProviderToast(); + const actionMsg = "Add 'docs' context provider"; + const res = await this.ide.showToast( + "info", + "Starting docs indexing", + actionMsg, + ); + + if (res === actionMsg) { + addContextProvider({ + name: DocsContextProvider.description.title, + params: {}, + }); - if (!didAddDocsContextProvider) { + void this.ide.showToast( + "info", + "Successfuly added docs context provider", + ); + } else { return; } } - await this.syncOrReindexAllDocs(this.config); + await this.syncDocs(undefined, this.config, reIndex); void this.ide.showToast("info", "Docs indexing completed"); } + // Returns true if startUrl has been indexed with current embeddingsProvider async hasMetadata(startUrl: string): Promise> { const db = await this.getOrCreateSqliteDb(); const title = await db.get( - `SELECT title FROM ${DocsService.sqlitebTableName} WHERE startUrl = ?`, + `SELECT title FROM ${DocsService.sqlitebTableName} WHERE startUrl = ? AND embeddingsProviderId = ?`, startUrl, + this.config.embeddingsProvider.embeddingId, ); return !!title; } - async showAddDocsContextProviderToast() { - const actionMsg = "Add 'docs' context provider"; - const res = await this.ide.showToast( - "info", - "Starting docs indexing", - actionMsg, - ); - - if (res === actionMsg) { - addContextProvider({ - name: DocsContextProvider.description.title, - params: {}, - }); - - void this.ide.showToast( - "info", - "Successfuly added docs context provider", - ); - } - - return res === actionMsg; - } - async listMetadata() { + const embeddingsProvider = this.config.embeddingsProvider; + if (!embeddingsProvider) { + return []; + } const db = await this.getOrCreateSqliteDb(); const docs = await db.all( - `SELECT title, startUrl, favicon FROM ${DocsService.sqlitebTableName}`, + `SELECT title, startUrl, favicon FROM ${DocsService.sqlitebTableName} + WHERE embeddingsProviderId = ?`, + embeddingsProvider.embeddingId, ); return docs; @@ -363,15 +349,21 @@ export default class DocsService { ): Promise { const { startUrl } = siteIndexingConfig; + const canUsePreindexedDocs = await this.canUsePreindexedDocs(); + if (canUsePreindexedDocs) { + const preIndexedDoc = preIndexedDocs[startUrl]; + const isPreIndexedDoc = !!preIndexedDoc; + if (isPreIndexedDoc) { + console.warn("Can only use pre"); + } + } + // Queue - indexAndAdd is invoked circularly by config edits. This prevents duplicate runs if (this.docsIndexingQueue.has(startUrl)) { return; } const embeddingsProvider = await this.getEmbeddingsProvider(); - - this.docsIndexingQueue.add(startUrl); - const indexExists = await this.hasMetadata(startUrl); // Build status update - most of it is fixed values @@ -404,12 +396,9 @@ export default class DocsService { return; } } + this.docsIndexingQueue.add(startUrl); - // If not preindexed - const isPreIndexedDoc = !!preIndexedDocs[siteIndexingConfig.startUrl]; - if (!isPreIndexedDoc) { - this.addToConfig(siteIndexingConfig); - } + this.addToConfig(siteIndexingConfig); try { this.handleStatusUpdate({ @@ -650,13 +639,18 @@ export default class DocsService { return await this.retrieveChunks(startUrl, vector, nRetrieve); } + // This is split into its own function so that it can be recursive + // in the case of fetching preindexed docs from s3 async retrieveChunks( startUrl: string, vector: number[], nRetrieve: number, isRetry: boolean = false, ): Promise { - const isPreIndexedDoc = !!preIndexedDocs[startUrl]; + const preIndexedDoc = preIndexedDocs[startUrl]; + const isPreIndexedDoc = !!preIndexedDoc; + + // Lance doesn't have an embeddingsprovider column, instead it includes it in the table name const table = await this.getOrCreateLanceTable({ initializationVector: vector, isPreIndexedDoc, @@ -673,19 +667,21 @@ export default class DocsService { console.warn("Error retrieving chunks from LanceDB", e); } - const hasIndexedDoc = await this.hasMetadata(startUrl); - - if (!hasIndexedDoc && docs.length === 0) { - const preIndexedDoc = preIndexedDocs[startUrl]; - - if (isRetry || !preIndexedDoc) { + // No docs are found for preindexed? try fetching once + if (docs.length === 0 && isPreIndexedDoc) { + if (isRetry) { return []; } - await this.fetchAndAddPreIndexedDocEmbeddings(preIndexedDoc.title); return await this.retrieveChunks(startUrl, vector, nRetrieve, true); } + if (isPreIndexedDoc) { + void Telemetry.capture("docs_pre_indexed_doc_used", { + doc: preIndexedDoc["title"], + }); + } + return docs.map((doc) => ({ digest: doc.path, filepath: doc.path, @@ -747,9 +743,10 @@ export default class DocsService { Sync with no embeddings provider change Ignores pre-indexed docs */ - private async syncDocsOnConfigUpdate( + private async syncDocs( oldConfig: ContinueConfig | undefined, newConfig: ContinueConfig, + reindex: boolean, ) { try { this.isSyncing = true; @@ -869,9 +866,7 @@ export default class DocsService { return name.replace(/[^a-zA-Z0-9_.-]/g, "_"); } - private async getLanceTableNameFromEmbeddingsProvider( - isPreIndexedDoc: boolean, - ) { + private async getLanceTableName(isPreIndexedDoc: boolean) { const embeddingsProvider = await this.getEmbeddingsProvider(isPreIndexedDoc); @@ -892,7 +887,7 @@ export default class DocsService { const conn = await lancedb.connect(getLanceDbPath()); const tableNames = await conn.tableNames(); const tableNameFromEmbeddingsProvider = - await this.getLanceTableNameFromEmbeddingsProvider(!!isPreIndexedDoc); + await this.getLanceTableName(!!isPreIndexedDoc); if (!tableNames.includes(tableNameFromEmbeddingsProvider)) { if (initializationVector) { @@ -949,10 +944,11 @@ export default class DocsService { }: AddParams) { const db = await this.getOrCreateSqliteDb(); await db.run( - `INSERT INTO ${DocsService.sqlitebTableName} (title, startUrl, favicon) VALUES (?, ?, ?)`, + `INSERT INTO ${DocsService.sqlitebTableName} (title, startUrl, favicon, embeddingsProviderId) VALUES (?, ?, ?, ?)`, title, startUrl, favicon, + this.config.embeddingsProvider.embeddingId, ); } @@ -960,13 +956,23 @@ export default class DocsService { // Handles the case where a user has manually added the doc to config.json // so it already exists in the file const doesDocExist = this.config.docs?.some( - (doc) => doc.startUrl === siteIndexingConfig.startUrl, + (doc) => + doc.startUrl === siteIndexingConfig.startUrl && + doc.faviconUrl === siteIndexingConfig.faviconUrl && + siteIndexingConfig.title === doc.title, + // siteIndexingConfig.maxDepth === doc.maxDepth && + // doc.rootUrl === siteIndexingConfig.rootUrl, ); if (!doesDocExist) { editConfigJson((config) => ({ ...config, - docs: [...(config.docs ?? []), siteIndexingConfig], + docs: [ + ...(config.docs?.filter( + (doc) => doc.startUrl !== siteIndexingConfig.startUrl, + ) ?? []), + siteIndexingConfig, + ], })); } } @@ -1017,34 +1023,4 @@ export default class DocsService { this.deleteFromConfig(startUrl); this.messenger?.send("refreshSubmenuItems", undefined); } - - /** - * Currently this deletes re-crawls + re-indexes all docs. - * A more optimal solution in the future will be to create - * a per-embeddings-provider table for docs. - */ - private async reindexDocsOnNewEmbeddingsProvider() { - // We use config as our source of truth here since it contains additional information - // needed for re-crawling such as `faviconUrl` and `maxDepth`. - const { docs, embeddingsProvider } = this.config; - - if (!docs || docs.length === 0) { - return; - } - - console.log( - `Reindexing non-preindexed docs with new embeddings provider: ${embeddingsProvider.embeddingId}`, - ); - await Promise.allSettled(docs.map((doc) => this.indexAndAdd(doc))); - - // Important that this only is invoked after we have successfully - // cleared and reindex the docs so that the table cannot end up in an - // invalid state. - this.globalContext.update( - "curEmbeddingsProviderId", - embeddingsProvider.embeddingId, - ); - - console.log("Completed reindexing of all non-preindexed docs"); - } } diff --git a/core/util/GlobalContext.test.ts b/core/util/GlobalContext.test.ts index 6014a1d1d3..0d6b9d799d 100644 --- a/core/util/GlobalContext.test.ts +++ b/core/util/GlobalContext.test.ts @@ -75,7 +75,7 @@ describe("GlobalContext", () => { const value = globalContext.get("indexingPaused"); expect(value).toBeUndefined(); expect(consoleWarnMock).toHaveBeenCalledWith( - expect.stringContaining("Error parsing global context") + expect.stringContaining("Error parsing global context"), ); // Clean up @@ -99,7 +99,7 @@ describe("GlobalContext", () => { expect(value).toBeUndefined(); expect(consoleWarnMock).toHaveBeenCalledWith( - expect.stringContaining("Error updating global context") + expect.stringContaining("Error updating global context"), ); // Clean up @@ -113,10 +113,10 @@ describe("GlobalContext", () => { expect(globalContext.get("indexingPaused")).toBe(true); expect(globalContext.get("selectedTabAutocompleteModel")).toBe( - "test-model" + "test-model", ); expect(globalContext.get("hasDismissedConfigTsNoticeJetBrains")).toBe( - false + false, ); }); @@ -125,16 +125,7 @@ describe("GlobalContext", () => { expect(globalContext.get("hasAlreadyCreatedAPromptFile")).toBe(true); }); - it("should handle curEmbeddingsProviderId correctly", () => { - globalContext.update("curEmbeddingsProviderId", "test-provider-id"); - expect(globalContext.get("curEmbeddingsProviderId")).toBe( - "test-provider-id" - ); - }); - it("should not crash or throw when getting a key that hasn't been set", () => { - expect( - globalContext.get("hasAlreadyCreatedAPromptFile") - ).toBeUndefined(); + expect(globalContext.get("hasAlreadyCreatedAPromptFile")).toBeUndefined(); }); -}); \ No newline at end of file +}); diff --git a/core/util/GlobalContext.ts b/core/util/GlobalContext.ts index 1095234d82..5e4f7c6d73 100644 --- a/core/util/GlobalContext.ts +++ b/core/util/GlobalContext.ts @@ -12,7 +12,6 @@ export type GlobalContextType = { * * For VS Code users, it is unnecessary since we use transformers.js by default. */ - curEmbeddingsProviderId: string; hasDismissedConfigTsNoticeJetBrains: boolean; hasAlreadyCreatedAPromptFile: boolean; showConfigUpdateToast: boolean; diff --git a/gui/src/components/dialogs/AddDocsDialog.tsx b/gui/src/components/dialogs/AddDocsDialog.tsx index 0bcd40fd94..f816369c8d 100644 --- a/gui/src/components/dialogs/AddDocsDialog.tsx +++ b/gui/src/components/dialogs/AddDocsDialog.tsx @@ -17,8 +17,10 @@ import DocsIndexingPeeks from "../indexing/DocsIndexingPeeks"; import preIndexedDocs from "core/indexing/docs/preIndexedDocs"; import { updateIndexingStatus } from "../../redux/slices/indexingSlice"; import { useAppSelector } from "../../redux/hooks"; +import { setConfig } from "../../redux/slices/configSlice"; function AddDocsDialog() { + const config = useAppSelector((store) => store.config.config); const posthog = usePostHog(); const dispatch = useDispatch(); @@ -125,6 +127,7 @@ function AddDocsDialog() { } const suggestedTitle = docsResult.details.title ?? docsResult.packageInfo.name; + if (docsResult.details?.docsLinkWarning) { setTitle(suggestedTitle); setStartUrl(docsResult.details.docsLink); @@ -143,6 +146,19 @@ function AddDocsDialog() { // Optimistic status update dispatch( + setConfig({ + ...config, + docs: [ + ...(config.docs?.filter( + (doc) => doc.startUrl !== docsResult.details.docsLink, + ) ?? []), + { + startUrl: docsResult.details.docsLink, + title: suggestedTitle, + faviconUrl: undefined, + }, + ], + }), updateIndexingStatus({ type: "docs", description: "Initializing", diff --git a/gui/src/components/indexing/DocsIndexingPeeks.tsx b/gui/src/components/indexing/DocsIndexingPeeks.tsx index f659b48f92..833c71430c 100644 --- a/gui/src/components/indexing/DocsIndexingPeeks.tsx +++ b/gui/src/components/indexing/DocsIndexingPeeks.tsx @@ -3,6 +3,7 @@ import { IndexingStatus } from "core"; import { useMemo } from "react"; import { useNavigate } from "react-router-dom"; import { setDialogMessage, setShowDialog } from "../../redux/slices/uiSlice"; +import { useAppSelector } from "../../redux/hooks"; export interface DocsIndexingPeekProps { status: IndexingStatus; From ada10398f748aa1cd94d44ba42aeee741b3b7d31 Mon Sep 17 00:00:00 2001 From: Dallin Romney Date: Fri, 20 Dec 2024 15:26:19 -0800 Subject: [PATCH 07/99] embeddingsprovider docs column --- core/context/providers/DocsContextProvider.ts | 4 +- core/indexing/docs/DocsService.ts | 25 +++++------- core/indexing/docs/migrations.ts | 39 ++++++++++++------- 3 files changed, 36 insertions(+), 32 deletions(-) diff --git a/core/context/providers/DocsContextProvider.ts b/core/context/providers/DocsContextProvider.ts index 47542bc1c6..9ee50c2563 100644 --- a/core/context/providers/DocsContextProvider.ts +++ b/core/context/providers/DocsContextProvider.ts @@ -94,7 +94,7 @@ class DocsContextProvider extends BaseContextProvider { query, this.options?.nRetrieve ?? DocsContextProvider.nRetrieve, ); - if(!chunks?.length) { + if (!chunks?.length) { return []; } @@ -160,7 +160,7 @@ class DocsContextProvider extends BaseContextProvider { const docs = (await docsService.listMetadata()) ?? []; for (const { startUrl, title, favicon } of docs) { submenuItemsMap.set(startUrl, { - title,\ + title, id: startUrl, description: new URL(startUrl).hostname, icon: favicon, diff --git a/core/indexing/docs/DocsService.ts b/core/indexing/docs/DocsService.ts index 0af6aa28f6..a65c65879b 100644 --- a/core/indexing/docs/DocsService.ts +++ b/core/indexing/docs/DocsService.ts @@ -17,7 +17,6 @@ import DocsContextProvider from "../../context/providers/DocsContextProvider"; import TransformersJsEmbeddingsProvider from "../../llm/llms/TransformersJsEmbeddingsProvider"; import { FromCoreProtocol, ToCoreProtocol } from "../../protocol"; import { fetchFavicon, getFaviconBase64 } from "../../util/fetchFavicon"; -import { GlobalContext } from "../../util/GlobalContext"; import { IMessenger } from "../../protocol/messenger"; import { editConfigJson, @@ -87,7 +86,6 @@ export default class DocsService { public isSyncing: boolean = false; private docsIndexingQueue = new Set(); - private globalContext = new GlobalContext(); private lanceTableNamesSet = new Set(); private config!: ContinueConfig; @@ -144,7 +142,7 @@ export default class DocsService { this.config.docs?.forEach((doc) => { if (!doc.startUrl) { - console.error("Invalid config docs entry, no start"); + console.error("Invalid config docs entry, no start url", doc.title); return; } @@ -396,11 +394,12 @@ export default class DocsService { return; } } - this.docsIndexingQueue.add(startUrl); - - this.addToConfig(siteIndexingConfig); try { + this.docsIndexingQueue.add(startUrl); + + this.addToConfig(siteIndexingConfig); + this.handleStatusUpdate({ ...fixedStatus, status: "indexing", @@ -710,16 +709,11 @@ export default class DocsService { await db.exec(`CREATE TABLE IF NOT EXISTS ${DocsService.sqlitebTableName} ( id INTEGER PRIMARY KEY AUTOINCREMENT, title STRING NOT NULL, - startUrl STRING NOT NULL UNIQUE, - favicon STRING + startUrl STRING NOT NULL, + favicon STRING, embeddingsProviderId STRING )`); - // embeddingsProviderId was added later - await db.exec(`ALTER TABLE ${DocsService.sqlitebTableName} - ADD COLUMN IF NOT EXISTS embeddingsProviderId STRING; - `); - this.sqliteDb = db; } @@ -756,6 +750,7 @@ export default class DocsService { const newConfigDocs = newConfig.docs || []; const newConfigStartUrls = newConfigDocs.map((doc) => doc.startUrl); + // NOTE since listMetadata filters by embeddings provider id embedding model changes are accounted for here const currentlyIndexedDocs = await this.listMetadata(); const currentStartUrls = currentlyIndexedDocs.map((doc) => doc.startUrl); @@ -955,7 +950,7 @@ export default class DocsService { private addToConfig(siteIndexingConfig: SiteIndexingConfig) { // Handles the case where a user has manually added the doc to config.json // so it already exists in the file - const doesDocExist = this.config.docs?.some( + const doesEquivalentDocExist = this.config.docs?.some( (doc) => doc.startUrl === siteIndexingConfig.startUrl && doc.faviconUrl === siteIndexingConfig.faviconUrl && @@ -964,7 +959,7 @@ export default class DocsService { // doc.rootUrl === siteIndexingConfig.rootUrl, ); - if (!doesDocExist) { + if (!doesEquivalentDocExist) { editConfigJson((config) => ({ ...config, docs: [ diff --git a/core/indexing/docs/migrations.ts b/core/indexing/docs/migrations.ts index e3483d507d..b84b0b842b 100644 --- a/core/indexing/docs/migrations.ts +++ b/core/indexing/docs/migrations.ts @@ -27,41 +27,50 @@ export async function runLanceMigrations(table: Table) { export async function runSqliteMigrations(db: Database) { await new Promise((resolve) => { - migrate( + void migrate( "sqlite_modify_docs_columns_and_copy_to_config", async () => { try { const pragma = await db.all( - `PRAGMA table_info(${DocsService.sqlitebTableName})`, + `PRAGMA table_info(${DocsService.sqlitebTableName});`, ); const hasFaviconCol = pragma.some( (pragma) => pragma.name === "favicon", ); - const hasBaseUrlCol = pragma.some( - (pragma) => pragma.name === "baseUrl", - ); - if (!hasFaviconCol) { await db.exec( - `ALTER TABLE ${DocsService.sqlitebTableName} ADD COLUMN favicon BLOB`, + `ALTER TABLE ${DocsService.sqlitebTableName} ADD COLUMN favicon BLOB;`, ); } + const hasBaseUrlCol = pragma.some( + (pragma) => pragma.name === "baseUrl", + ); if (hasBaseUrlCol) { await db.exec( - `ALTER TABLE ${DocsService.sqlitebTableName} RENAME COLUMN baseUrl TO startUrl`, + `ALTER TABLE ${DocsService.sqlitebTableName} RENAME COLUMN baseUrl TO startUrl;`, ); } - const sqliteDocs = await db.all< - Array> - >(`SELECT title, startUrl FROM ${DocsService.sqlitebTableName}`); + const hasEmbeddingsProviderColumn = pragma.some( + (pragma) => pragma.name === "embeddingsProviderId", + ); + if (!hasEmbeddingsProviderColumn) { + // gotta just delete in this case since old docs will be unusable anyway + await db.exec(`DROP TABLE ${DocsService.sqlitebTableName};`); + } - editConfigJson((config) => ({ - ...config, - docs: [...(config.docs || []), ...sqliteDocs], - })); + const needsToUpdateConfig = !hasFaviconCol || hasBaseUrlCol; + if (needsToUpdateConfig) { + const sqliteDocs = await db.all< + Array> + >(`SELECT title, startUrl FROM ${DocsService.sqlitebTableName}`); + editConfigJson((config) => ({ + ...config, + docs: [...(config.docs || []), ...sqliteDocs], + })); + } } finally { resolve(undefined); } From 82fd94d6b1ab64473707f32a63ef3c46ac9cd5fb Mon Sep 17 00:00:00 2001 From: Dallin Romney Date: Fri, 20 Dec 2024 15:51:25 -0800 Subject: [PATCH 08/99] config/addContextProvider message --- core/core.ts | 13 ++++++- core/indexing/docs/DocsService.ts | 7 +++- core/protocol/core.ts | 2 + core/protocol/passThrough.ts | 1 + .../toolWindow/ContinueBrowser.kt | 1 + .../indexing/DocsIndexingStatuses.tsx | 38 ++++++++++++++++++- 6 files changed, 58 insertions(+), 4 deletions(-) diff --git a/core/core.ts b/core/core.ts index bea254c818..3d00db55e4 100644 --- a/core/core.ts +++ b/core/core.ts @@ -12,7 +12,12 @@ import { setupLocalConfigAfterFreeTrial, setupQuickstartConfig, } from "./config/onboarding"; -import { addModel, addOpenAIKey, deleteModel } from "./config/util"; +import { + addContextProvider, + addModel, + addOpenAIKey, + deleteModel, +} from "./config/util"; import { recentlyEditedFilesCache } from "./context/retrieval/recentlyEditedFilesCache"; import { ContinueServerClient } from "./continueServer/stubs/client"; import { getAuthUrlForTokenPage } from "./control-plane/auth/index"; @@ -276,6 +281,10 @@ export class Core { return this.configHandler.listProfiles(); }); + on("config/addContextProvider", async (msg) => { + addContextProvider(msg.data); + }); + // Context providers on("context/addDocs", async (msg) => { void this.docsService.indexAndAdd(msg.data); @@ -794,7 +803,7 @@ export class Core { }); on("indexing/setPaused", async (msg) => { if (msg.data.type === "docs") { - // this.docsService.setPaused(msg.data.id, msg.data.paused); + // this.docsService.setPaused(msg.data.id, msg.data.paused); // not supported yet } }); on("docs/getSuggestedDocs", async (msg) => { diff --git a/core/indexing/docs/DocsService.ts b/core/indexing/docs/DocsService.ts index a65c65879b..d81a4ec196 100644 --- a/core/indexing/docs/DocsService.ts +++ b/core/indexing/docs/DocsService.ts @@ -345,6 +345,10 @@ export default class DocsService { siteIndexingConfig: SiteIndexingConfig, reIndex: boolean = false, ): Promise { + if (this.config.disableIndexing) { + console.warn("Attempting to add/index docs when indexing is disabled"); + return; + } const { startUrl } = siteIndexingConfig; const canUsePreindexedDocs = await this.canUsePreindexedDocs(); @@ -352,7 +356,8 @@ export default class DocsService { const preIndexedDoc = preIndexedDocs[startUrl]; const isPreIndexedDoc = !!preIndexedDoc; if (isPreIndexedDoc) { - console.warn("Can only use pre"); + console.warn("Can not override pre-indexed start urls"); + return; } } diff --git a/core/protocol/core.ts b/core/protocol/core.ts index 9a7d85510d..520f2dc41b 100644 --- a/core/protocol/core.ts +++ b/core/protocol/core.ts @@ -6,6 +6,7 @@ import type { ChatMessage, ContextItem, ContextItemWithId, + ContextProviderWithParams, ContextSubmenuItem, DiffLine, FileSymbolMap, @@ -66,6 +67,7 @@ export type ToCoreFromIdeOrWebviewProtocol = { { config: BrowserSerializedContinueConfig; profileId: string }, ]; "config/deleteModel": [{ title: string }, void]; + "config/addContextProvider": [ContextProviderWithParams, void]; "config/reload": [undefined, BrowserSerializedContinueConfig]; "config/listProfiles": [undefined, ProfileDescription[]]; "config/openProfile": [{ profileId: string | undefined }, void]; diff --git a/core/protocol/passThrough.ts b/core/protocol/passThrough.ts index 0bbb5699ab..756b3db54e 100644 --- a/core/protocol/passThrough.ts +++ b/core/protocol/passThrough.ts @@ -16,6 +16,7 @@ export const WEBVIEW_TO_CORE_PASS_THROUGH: (keyof ToCoreFromWebviewProtocol)[] = "history/save", "devdata/log", "config/addModel", + "config/addContextProvider", "config/newPromptFile", "config/ideSettingsUpdate", "config/getSerializedProfileInfo", diff --git a/extensions/intellij/src/main/kotlin/com/github/continuedev/continueintellijextension/toolWindow/ContinueBrowser.kt b/extensions/intellij/src/main/kotlin/com/github/continuedev/continueintellijextension/toolWindow/ContinueBrowser.kt index d682b8a3a9..5523bdeffc 100644 --- a/extensions/intellij/src/main/kotlin/com/github/continuedev/continueintellijextension/toolWindow/ContinueBrowser.kt +++ b/extensions/intellij/src/main/kotlin/com/github/continuedev/continueintellijextension/toolWindow/ContinueBrowser.kt @@ -26,6 +26,7 @@ class ContinueBrowser(val project: Project, url: String) { "history/save", "devdata/log", "config/addModel", + "config/addContextProvider", "config/newPromptFile", "config/ideSettingsUpdate", "config/getSerializedProfileInfo", diff --git a/gui/src/components/indexing/DocsIndexingStatuses.tsx b/gui/src/components/indexing/DocsIndexingStatuses.tsx index 76feb21e08..9a9ffb4dfa 100644 --- a/gui/src/components/indexing/DocsIndexingStatuses.tsx +++ b/gui/src/components/indexing/DocsIndexingStatuses.tsx @@ -4,11 +4,24 @@ import { setDialogMessage, setShowDialog } from "../../redux/slices/uiSlice"; import AddDocsDialog from "../dialogs/AddDocsDialog"; import DocsIndexingStatus from "./DocsIndexingStatus"; import { useAppSelector } from "../../redux/hooks"; +import { useContext, useMemo } from "react"; +import { ExclamationTriangleIcon } from "@heroicons/react/24/outline"; +import { IdeMessengerContext } from "../../context/IdeMessenger"; function DocsIndexingStatuses() { const dispatch = useDispatch(); const config = useAppSelector((store) => store.config.config); - const configDocs = config.docs ?? []; + const ideMessenger = useContext(IdeMessengerContext); + + const hasDocsProvider = useMemo(() => { + return !!config.contextProviders?.some( + (provider) => provider.title === "docs", + ); + }, [config]); + + const configDocs = useMemo(() => { + return config.docs ?? []; + }, [config]); return (
@@ -33,6 +46,29 @@ function DocsIndexingStatuses() { : "No docs yet"}
+ {!hasDocsProvider && ( +
+
+
+ +
+ + @docs is not in your config + +
+ { + ideMessenger.post("config/addContextProvider", { + name: "docs", + params: {}, + }); + }} + > + Add @docs to my config + +
+ )}
{configDocs.length === 0 && ( Date: Fri, 20 Dec 2024 16:46:19 -0800 Subject: [PATCH 09/99] messaging --- core/indexing/docs/DocsService.ts | 23 +++++++---- .../indexing/DocsIndexingStatus.tsx | 41 ++++++++++--------- .../indexing/DocsIndexingStatuses.tsx | 15 ++++--- .../IndexingProgressSubtext.tsx | 2 +- 4 files changed, 47 insertions(+), 34 deletions(-) diff --git a/core/indexing/docs/DocsService.ts b/core/indexing/docs/DocsService.ts index d81a4ec196..7a35e8f24c 100644 --- a/core/indexing/docs/DocsService.ts +++ b/core/indexing/docs/DocsService.ts @@ -193,8 +193,15 @@ export default class DocsService { this.docsIndexingQueue.delete(startUrl); } - isAborted(startUrl: string) { - return this.statuses.get(startUrl)?.status === "aborted"; + shouldCancel(startUrl: string) { + const isAborted = this.statuses.get(startUrl)?.status === "aborted"; + if (isAborted) { + return true; + } + if (this.config.disableIndexing) { + this.abort(startUrl); + return true; + } } // NOTE Pausing not supported for docs yet @@ -421,7 +428,7 @@ export default class DocsService { estimatedProgress += 1 / 2 ** (processedPages + 1); // NOTE - during "indexing" phase, check if aborted before each status update - if (this.isAborted(startUrl)) { + if (this.shouldCancel(startUrl)) { return; } this.handleStatusUpdate({ @@ -459,7 +466,7 @@ export default class DocsService { for (let i = 0; i < articles.length; i++) { const article = articles[i]; - if (this.isAborted(startUrl)) { + if (this.shouldCancel(startUrl)) { return; } this.handleStatusUpdate({ @@ -496,7 +503,7 @@ export default class DocsService { `No embeddings were created for site: ${siteIndexingConfig.startUrl}\n Num chunks: ${chunks.length}`, ); - if (this.isAborted(startUrl)) { + if (this.shouldCancel(startUrl)) { return; } this.handleStatusUpdate({ @@ -514,7 +521,7 @@ export default class DocsService { // Add docs to databases console.log(`Adding ${embeddings.length} embeddings to db`); - if (this.isAborted(startUrl)) { + if (this.shouldCancel(startUrl)) { return; } this.handleStatusUpdate({ @@ -532,7 +539,7 @@ export default class DocsService { const favicon = await fetchFavicon(new URL(siteIndexingConfig.startUrl)); - if (this.isAborted(startUrl)) { + if (this.shouldCancel(startUrl)) { return; } this.handleStatusUpdate({ @@ -551,7 +558,7 @@ export default class DocsService { this.docsIndexingQueue.delete(startUrl); - if (this.isAborted(startUrl)) { + if (this.shouldCancel(startUrl)) { return; } this.handleStatusUpdate({ diff --git a/gui/src/components/indexing/DocsIndexingStatus.tsx b/gui/src/components/indexing/DocsIndexingStatus.tsx index 31604874fb..7f11d9dfac 100644 --- a/gui/src/components/indexing/DocsIndexingStatus.tsx +++ b/gui/src/components/indexing/DocsIndexingStatus.tsx @@ -28,6 +28,7 @@ const STATUS_TO_ICON: Record = { }; function DocsIndexingStatus({ docConfig }: IndexingStatusViewerProps) { + const config = useAppSelector((store) => store.config.config); const ideMessenger = useContext(IdeMessengerContext); const dispatch = useAppDispatch(); @@ -146,28 +147,30 @@ function DocsIndexingStatus({ docConfig }: IndexingStatusViewerProps) {
{}, - pending: () => {}, - }[status?.status] + config.disableIndexing + ? undefined + : { + complete: reIndex, + indexing: abort, + failed: reIndex, + aborted: reIndex, + paused: () => {}, + pending: () => {}, + }[status?.status] } > - { - { - complete: "Click to re-index", - indexing: "Cancel indexing", - failed: "Click to retry", - aborted: "Click to index", - paused: "", - pending: "", - }[status?.status] - } + {config.disableIndexing + ? "Indexing disabled" + : { + complete: "Click to re-index", + indexing: "Cancel indexing", + failed: "Click to retry", + aborted: "Click to index", + paused: "", + pending: "", + }[status?.status]} diff --git a/gui/src/components/indexing/DocsIndexingStatuses.tsx b/gui/src/components/indexing/DocsIndexingStatuses.tsx index 9a9ffb4dfa..9c52ab0af8 100644 --- a/gui/src/components/indexing/DocsIndexingStatuses.tsx +++ b/gui/src/components/indexing/DocsIndexingStatuses.tsx @@ -41,12 +41,13 @@ function DocsIndexingStatuses() { ) : null}
- {configDocs.length - ? "Manage your documentation sources" - : "No docs yet"} - -
- {!hasDocsProvider && ( + {hasDocsProvider ? ( + configDocs.length ? ( + "Manage your documentation sources" + ) : ( + "No docs yet" + ) + ) : (
@@ -69,6 +70,8 @@ function DocsIndexingStatuses() {
)} + +
{configDocs.length === 0 && ( Date: Fri, 20 Dec 2024 18:30:52 -0800 Subject: [PATCH 10/99] handle config changes mid-index --- core/indexing/docs/DocsService.ts | 47 ++++++++++++++++++++----------- 1 file changed, 30 insertions(+), 17 deletions(-) diff --git a/core/indexing/docs/DocsService.ts b/core/indexing/docs/DocsService.ts index 7a35e8f24c..ab2fb52ab9 100644 --- a/core/indexing/docs/DocsService.ts +++ b/core/indexing/docs/DocsService.ts @@ -181,27 +181,37 @@ export default class DocsService { } abort(startUrl: string) { - const status = this.statuses.get(startUrl); - if (status) { - this.handleStatusUpdate({ - ...status, - status: "aborted", - progress: 0, - description: "Canceled", - }); + if (this.docsIndexingQueue.has(startUrl)) { + const status = this.statuses.get(startUrl); + if (status) { + this.handleStatusUpdate({ + ...status, + status: "aborted", + progress: 0, + description: "Canceled", + }); + } + this.docsIndexingQueue.delete(startUrl); } - this.docsIndexingQueue.delete(startUrl); } - shouldCancel(startUrl: string) { + shouldCancel(startUrl: string, startedWithEmbedder: string) { + // Check if aborted const isAborted = this.statuses.get(startUrl)?.status === "aborted"; if (isAborted) { return true; } + // Handle indexing disabled change mid-indexing if (this.config.disableIndexing) { this.abort(startUrl); return true; } + // Handle embeddings provider change mid-indexing + if (this.config.embeddingsProvider.embeddingId !== startedWithEmbedder) { + this.abort(startUrl); + return true; + } + return false; } // NOTE Pausing not supported for docs yet @@ -374,6 +384,7 @@ export default class DocsService { } const embeddingsProvider = await this.getEmbeddingsProvider(); + const startedWithEmbedder = this.config.embeddingsProvider.embeddingId; const indexExists = await this.hasMetadata(startUrl); // Build status update - most of it is fixed values @@ -428,7 +439,7 @@ export default class DocsService { estimatedProgress += 1 / 2 ** (processedPages + 1); // NOTE - during "indexing" phase, check if aborted before each status update - if (this.shouldCancel(startUrl)) { + if (this.shouldCancel(startUrl, startedWithEmbedder)) { return; } this.handleStatusUpdate({ @@ -466,7 +477,7 @@ export default class DocsService { for (let i = 0; i < articles.length; i++) { const article = articles[i]; - if (this.shouldCancel(startUrl)) { + if (this.shouldCancel(startUrl, startedWithEmbedder)) { return; } this.handleStatusUpdate({ @@ -503,7 +514,7 @@ export default class DocsService { `No embeddings were created for site: ${siteIndexingConfig.startUrl}\n Num chunks: ${chunks.length}`, ); - if (this.shouldCancel(startUrl)) { + if (this.shouldCancel(startUrl, startedWithEmbedder)) { return; } this.handleStatusUpdate({ @@ -521,7 +532,7 @@ export default class DocsService { // Add docs to databases console.log(`Adding ${embeddings.length} embeddings to db`); - if (this.shouldCancel(startUrl)) { + if (this.shouldCancel(startUrl, startedWithEmbedder)) { return; } this.handleStatusUpdate({ @@ -539,7 +550,7 @@ export default class DocsService { const favicon = await fetchFavicon(new URL(siteIndexingConfig.startUrl)); - if (this.shouldCancel(startUrl)) { + if (this.shouldCancel(startUrl, startedWithEmbedder)) { return; } this.handleStatusUpdate({ @@ -558,7 +569,7 @@ export default class DocsService { this.docsIndexingQueue.delete(startUrl); - if (this.shouldCancel(startUrl)) { + if (this.shouldCancel(startUrl, startedWithEmbedder)) { return; } this.handleStatusUpdate({ @@ -577,7 +588,7 @@ export default class DocsService { console.error("Error indexing docs", e); this.handleStatusUpdate({ ...fixedStatus, - description: `No embeddings were created for site: ${siteIndexingConfig.startUrl}`, + description: `Error getting docs from: ${siteIndexingConfig.startUrl}`, status: "failed", progress: 1, }); @@ -785,7 +796,9 @@ export default class DocsService { const oldConfigDoc = oldConfigDocs.find( (d) => d.startUrl === doc.startUrl, ); + // const currentStatus = this.statuses.get(doc.startUrl); if ( + // currentStatus?.status === "complete" && oldConfigDoc && (oldConfigDoc.maxDepth !== doc.maxDepth || oldConfigDoc.title !== doc.title || From 27f9278d4005ab9278ac68b77bdb287141e8309e Mon Sep 17 00:00:00 2001 From: henry Date: Sat, 21 Dec 2024 15:22:36 -0500 Subject: [PATCH 11/99] prompt engineering changes --- core/util/chatDescriber.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/util/chatDescriber.ts b/core/util/chatDescriber.ts index 20eab20b78..a10edbe722 100644 --- a/core/util/chatDescriber.ts +++ b/core/util/chatDescriber.ts @@ -8,7 +8,7 @@ import type { IMessenger } from "../protocol/messenger"; export class ChatDescriber { static maxTokens = 12; static prompt: string | undefined = - "Given the following... please reply with a short summary that is 4-8 words in length, you should summarize what the user is asking for OR what the user is trying to accomplish. You should only respond with the summary, no additional text or explanation, you don't need ending punctuation.\n\n"; + "Given the following... please reply with a title for the chat that is 3-4 words in length, all words used should be directly related to the content of the chat, avoid using verbs unless they are directly related to the content of the chat, no additional text or explanation, you don't need ending punctuation.\n\n"; static messenger: IMessenger; static async describe( From f60a39e944122f19337a19759bacebc3639ef69a Mon Sep 17 00:00:00 2001 From: Hieu Pham Date: Sat, 21 Dec 2024 12:24:15 -0800 Subject: [PATCH 12/99] Fix embedding provider not respecting batch size --- core/llm/index.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/core/llm/index.ts b/core/llm/index.ts index fbea691047..d789f724ef 100644 --- a/core/llm/index.ts +++ b/core/llm/index.ts @@ -809,12 +809,12 @@ export abstract class BaseLLM implements ILLM { if (this.shouldUseOpenAIAdapter("embed") && this.openaiAdapter) { const result = await this.openaiAdapter.embed({ model: this.model, - input: chunks, + input: batch, }); - return result.data.map((chunk) => chunk.embedding); + return result.data.map((batch) => batch.embedding); } - return await this._embed(chunks); + return await this._embed(batch); }, ); From 4fdf5d5bc358f91f8a6569beb685bca368719603 Mon Sep 17 00:00:00 2001 From: Hieu Pham Date: Sat, 21 Dec 2024 12:58:35 -0800 Subject: [PATCH 13/99] Remove unnecessary change --- core/llm/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/llm/index.ts b/core/llm/index.ts index d789f724ef..5c0253e86a 100644 --- a/core/llm/index.ts +++ b/core/llm/index.ts @@ -811,7 +811,7 @@ export abstract class BaseLLM implements ILLM { model: this.model, input: batch, }); - return result.data.map((batch) => batch.embedding); + return result.data.map((chunk) => chunk.embedding); } return await this._embed(batch); From 48ea7ca10a4d918a92e5591e7e357980e3a5b5b3 Mon Sep 17 00:00:00 2001 From: Alexey Palazhchenko Date: Sun, 22 Dec 2024 17:48:52 +0400 Subject: [PATCH 14/99] Fix model names for Ollama --- docs/docs/chat/model-setup.mdx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/docs/chat/model-setup.mdx b/docs/docs/chat/model-setup.mdx index a5668786bb..478d5740c7 100644 --- a/docs/docs/chat/model-setup.mdx +++ b/docs/docs/chat/model-setup.mdx @@ -77,7 +77,7 @@ If you prefer to use an open-weight model, then Llama 3.1 405B from Meta is your { "title": "Llama 3.1 405B", "provider": "ollama", - "model": "llama3.1-405b" + "model": "llama3.1:405b" } ] ``` @@ -141,7 +141,7 @@ If your local machine can run an 8B parameter model, then we recommend running L { "title": "Llama 3.1 8B", "provider": "ollama", - "model": "llama3.1-8b" + "model": "llama3.1:8b" } ] ``` From b0d382277dc07768056a5ebffc948e92fe151850 Mon Sep 17 00:00:00 2001 From: Nate Date: Sun, 22 Dec 2024 22:18:29 -0500 Subject: [PATCH 15/99] new tests for edge cases --- core/edit/lazy/deterministic.test.ts | 8 ++ core/edit/lazy/deterministic.ts | 110 ++++++++++++++++-- core/edit/lazy/findInAst.ts | 6 +- .../calculator-class-neglected.js.diff | 103 ++++++++++++++++ .../calculator-only-method.js.diff | 99 ++++++++++++++++ 5 files changed, 315 insertions(+), 11 deletions(-) create mode 100644 core/edit/lazy/test-examples/calculator-class-neglected.js.diff create mode 100644 core/edit/lazy/test-examples/calculator-only-method.js.diff diff --git a/core/edit/lazy/deterministic.test.ts b/core/edit/lazy/deterministic.test.ts index 997c24c3af..7590e50254 100644 --- a/core/edit/lazy/deterministic.test.ts +++ b/core/edit/lazy/deterministic.test.ts @@ -158,4 +158,12 @@ describe("deterministicApplyLazyEdit(", () => { test("should acknowledge jsx_expression lazy comments", async () => { await expectDiff("migration-page.tsx"); }); + + test.skip("should handle case where surrounding class is neglected, with lazy block surrounding", async () => { + await expectDiff("calculator-class-neglected.js"); + }); + + test("should handle case where surrounding class is neglected, without lazy block surrounding", async () => { + await expectDiff("calculator-only-method.js"); + }); }); diff --git a/core/edit/lazy/deterministic.ts b/core/edit/lazy/deterministic.ts index 564656d23e..99d5c9a01e 100644 --- a/core/edit/lazy/deterministic.ts +++ b/core/edit/lazy/deterministic.ts @@ -102,6 +102,24 @@ function shouldRejectDiff(diff: DiffLine[]): boolean { return false; } +function nodeSurroundedInLazyBlocks( + parser: Parser, + + file: string, + filename: string, +): { newTree: Parser.Tree; newFile: string } | undefined { + const ext = path.extname(filename).slice(1); + const language = LANGUAGES[ext]; + if (language) { + const newFile = `${language.singleLineComment} ... existing code ...\n\n${file}\n\n${language.singleLineComment} ... existing code...`; + const newTree = parser.parse(newFile); + + return { newTree, newFile }; + } + + return undefined; +} + // TODO: If we don't have high confidence, return undefined to fall back to slower methods export async function deterministicApplyLazyEdit( oldFile: string, @@ -115,6 +133,7 @@ export async function deterministicApplyLazyEdit( const oldTree = parser.parse(oldFile); let newTree = parser.parse(newLazyFile); + let reconstructedNewFile: string | undefined = undefined; // If there is no lazy block anywhere, we add our own to the outsides // so that large chunks of the file don't get removed @@ -125,24 +144,51 @@ export async function deterministicApplyLazyEdit( ); if (firstSimilarNode?.parent?.equals(oldTree.rootNode)) { // If so, we tack lazy blocks to start and end, and run the usual algorithm - const ext = path.extname(filename).slice(1); - const language = LANGUAGES[ext]; - if (language) { - newLazyFile = `${language.singleLineComment} ... existing code ...\n\n${newLazyFile}\n\n${language.singleLineComment} ... existing code...`; - newTree = parser.parse(newLazyFile); + const result = nodeSurroundedInLazyBlocks(parser, newLazyFile, filename); + if (result) { + newLazyFile = result.newFile; + newTree = result.newTree; } } else { // If not, we need to recursively search for the nodes that are being rewritten, // and we apply a slightly different algorithm - return undefined; // TODO + const newCodeNumLines = newTree.rootNode.text.split("\n").length; + const matchingNode = findInAst( + oldTree.rootNode, + (node) => programNodeIsSimilar(newTree.rootNode, node), + // This isn't perfect—we want the length of the matching code in the old tree + // and the new version could have more lines, or fewer. But should work a lot. + (node) => node.text.split("\n").length >= newCodeNumLines, + ); + if (matchingNode) { + // Now that we've successfully matched the node from the old tree, + // we create the full new lazy file + const startIndex = matchingNode.startIndex; + const endIndex = matchingNode.endIndex; + const oldText = oldTree.rootNode.text; + reconstructedNewFile = + oldText.slice(0, startIndex) + + newTree.rootNode.text + + oldText.slice(endIndex); + } else { + console.warn("No matching node found for lazy block"); + return []; + } } } - const replacements: AstReplacements = []; - findLazyBlockReplacements(oldTree.rootNode, newTree.rootNode, replacements); + if (!reconstructedNewFile) { + const replacements: AstReplacements = []; + findLazyBlockReplacements(oldTree.rootNode, newTree.rootNode, replacements); - const newFullFile = reconstructNewFile(oldFile, newLazyFile, replacements); - const diff = myersDiff(oldFile, newFullFile); + reconstructedNewFile = reconstructNewFile( + oldFile, + newLazyFile, + replacements, + ); + } + + const diff = myersDiff(oldFile, reconstructedNewFile); // If the diff is too messy and seems likely borked, we fall back to LLM strategy if (shouldRejectDiff(diff)) { @@ -179,6 +225,50 @@ function stringsWithinLevDistThreshold( return dist / Math.min(a.length, b.length) <= threshold; } +function programNodeIsSimilar( + programNode: Parser.SyntaxNode, + otherNode: Parser.SyntaxNode, +): boolean { + // Check purely based on whether they are similar strings + const newLines = programNode.text.split("\n"); + const oldLines = otherNode.text.split("\n"); + + // Check that there is a line that matches the start of the old range + const oldFirstLine = oldLines[0].trim(); + let matchForOldFirstLine = -1; + for (let i = 0; i < newLines.length; i++) { + if (newLines[i].trim() === oldFirstLine) { + matchForOldFirstLine = i; + break; + } + } + + if (matchForOldFirstLine < 0) { + return false; + } + + // Check that the last lines match each other + const oldLastLine = oldLines[oldLines.length - 1].trim(); + const newLastLine = newLines[newLines.length - 1].trim(); + if (oldLastLine !== newLastLine) { + return false; + } + + // Check that the number of matching lines is at least half of the shorter length + let matchingLines = 0; + for (let i = 0; i < Math.min(newLines.length, oldLines.length); i++) { + if (oldLines[i].trim() === newLines[matchForOldFirstLine + i].trim()) { + matchingLines += 1; + } + } + + if (matchingLines >= Math.max(newLines.length, oldLines.length) / 2) { + return true; + } + + return false; +} + /** * Determine whether two nodes are similar * @param a diff --git a/core/edit/lazy/findInAst.ts b/core/edit/lazy/findInAst.ts index 9f47355b8f..93809ea204 100644 --- a/core/edit/lazy/findInAst.ts +++ b/core/edit/lazy/findInAst.ts @@ -3,6 +3,7 @@ import Parser from "web-tree-sitter"; export function findInAst( node: Parser.SyntaxNode, criterion: (node: Parser.SyntaxNode) => boolean, + shouldRecurse: (node: Parser.SyntaxNode) => boolean = () => true, ): Parser.SyntaxNode | null { const stack = [node]; while (stack.length > 0) { @@ -10,7 +11,10 @@ export function findInAst( if (criterion(node)) { return node; } - stack.push(...node.children); + + if (shouldRecurse(node)) { + stack.push(...node.children); + } } return null; } diff --git a/core/edit/lazy/test-examples/calculator-class-neglected.js.diff b/core/edit/lazy/test-examples/calculator-class-neglected.js.diff new file mode 100644 index 0000000000..eb6dcc285a --- /dev/null +++ b/core/edit/lazy/test-examples/calculator-class-neglected.js.diff @@ -0,0 +1,103 @@ +class Calculator { + constructor() { + this.result = 0; + } + + add(number) { + this.result += number; + return this; + } + + subtract(number) { + this.result -= number; + return this; + } + + multiply(number) { + this.result *= number; + return this; + } + + divide(number) { + if (number === 0) { + throw new Error("Cannot divide by zero"); + } + this.result /= number; + return this; + } + + getResult() { + return this.result; + } + + reset() { + this.result = 0; + return this; + } +} + +--- + +// ... existing code ... + + /** + * Divides the current result by the given number + * @param {number} number - The number to divide by + * @throws {Error} If attempting to divide by zero + * @returns {Calculator} The calculator instance for chaining + */ + divide(number) { + if (number === 0) { + throw new Error("Cannot divide by zero"); + } + this.result /= number; + return this; + } + +// ... existing code ... + +--- + +class Calculator { + constructor() { + this.result = 0; + } + + add(number) { + this.result += number; + return this; + } + + subtract(number) { + this.result -= number; + return this; + } + + multiply(number) { + this.result *= number; + return this; + } + ++ /** ++ * Divides the current result by the given number ++ * @param {number} number - The number to divide by ++ * @throws {Error} If attempting to divide by zero ++ * @returns {Calculator} The calculator instance for chaining ++ */ + divide(number) { + if (number === 0) { + throw new Error("Cannot divide by zero"); + } + this.result /= number; + return this; + } + + getResult() { + return this.result; + } + + reset() { + this.result = 0; + return this; + } +} \ No newline at end of file diff --git a/core/edit/lazy/test-examples/calculator-only-method.js.diff b/core/edit/lazy/test-examples/calculator-only-method.js.diff new file mode 100644 index 0000000000..6f69d0142e --- /dev/null +++ b/core/edit/lazy/test-examples/calculator-only-method.js.diff @@ -0,0 +1,99 @@ +class Calculator { + constructor() { + this.result = 0; + } + + add(number) { + this.result += number; + return this; + } + + subtract(number) { + this.result -= number; + return this; + } + + multiply(number) { + this.result *= number; + return this; + } + + divide(number) { + if (number === 0) { + throw new Error("Cannot divide by zero"); + } + this.result /= number; + return this; + } + + getResult() { + return this.result; + } + + reset() { + this.result = 0; + return this; + } +} + +--- + + /** + * Divides the current result by the given number + * @param {number} number - The number to divide by + * @throws {Error} If attempting to divide by zero + * @returns {Calculator} The calculator instance for chaining + */ + divide(number) { + if (number === 0) { + throw new Error("Cannot divide by zero"); + } + this.result /= number; + return this; + } + +--- + +class Calculator { + constructor() { + this.result = 0; + } + + add(number) { + this.result += number; + return this; + } + + subtract(number) { + this.result -= number; + return this; + } + + multiply(number) { + this.result *= number; + return this; + } + ++ /** ++ * Divides the current result by the given number ++ * @param {number} number - The number to divide by ++ * @throws {Error} If attempting to divide by zero ++ * @returns {Calculator} The calculator instance for chaining ++ */ + divide(number) { + if (number === 0) { + throw new Error("Cannot divide by zero"); + } + this.result /= number; + return this; + } + + getResult() { + return this.result; + } + + reset() { + this.result = 0; + return this; + } +} \ No newline at end of file From 94d8d39cfc8d21f2544633330370d99ef17e0fdf Mon Sep 17 00:00:00 2001 From: vercel Date: Mon, 23 Dec 2024 14:23:04 +0800 Subject: [PATCH 16/99] feat: add novita info --- CONTRIBUTING.md | 4 +- core/llm/autodetect.ts | 2 + core/llm/llms/Novita.ts | 53 +++++++++++++++++++ core/llm/llms/index.ts | 2 + docs/docs/chat/model-setup.mdx | 14 ++++- .../customize/model-providers/more/novita.md | 18 +++++++ docs/docs/customize/tutorials/llama3.1.md | 24 ++++++++- docs/docusaurus.config.js | 6 ++- .../current/chat/model-setup.mdx | 14 ++++- .../customize/model-providers/more/novita.md | 18 +++++++ .../current/customize/tutorials/llama3.1.md | 23 +++++++- extensions/vscode/config_schema.json | 46 +++++++++++++++- gui/src/pages/AddNewModel/configs/models.ts | 8 ++- .../pages/AddNewModel/configs/providers.ts | 36 ++++++++++++- packages/openai-adapters/README.md | 1 + packages/openai-adapters/src/index.ts | 2 + packages/openai-adapters/src/types.ts | 1 + packages/openai-adapters/test/main.test.ts | 6 +++ 18 files changed, 266 insertions(+), 12 deletions(-) create mode 100644 core/llm/llms/Novita.ts create mode 100644 docs/docs/customize/model-providers/more/novita.md create mode 100644 docs/i18n/zh-CN/docusaurus-plugin-content-docs/current/customize/model-providers/more/novita.md diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index ca26d38fe4..843f5ad171 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -188,7 +188,7 @@ After you've written your context provider, make sure to complete the following: ### Adding an LLM Provider -Continue has support for more than a dozen different LLM "providers", making it easy to use models running on OpenAI, Ollama, Together, LM Studio, Msty, and more. You can find all of the existing providers [here](https://github.com/continuedev/continue/tree/main/core/llm/llms), and if you see one missing, you can add it with the following steps: +Continue has support for more than a dozen different LLM "providers", making it easy to use models running on OpenAI, Ollama, Together, Novita, LM Studio, Msty, and more. You can find all of the existing providers [here](https://github.com/continuedev/continue/tree/main/core/llm/llms), and if you see one missing, you can add it with the following steps: 1. Create a new file in the `core/llm/llms` directory. The name of the file should be the name of the provider, and it should export a class that extends `BaseLLM`. This class should contain the following minimal implementation. We recommend viewing pre-existing providers for more details. The [LlamaCpp Provider](./core/llm/llms/LlamaCpp.ts) is a good simple example. @@ -209,7 +209,7 @@ While any model that works with a supported provider can be used with Continue, 1. Add a `ModelPackage` entry for the model into [configs/models.ts](./gui/src/pages/AddNewModel/configs/models.ts), following the lead of the many examples near the top of the file 2. Add the model within its provider's array to [AddNewModel.tsx](./gui/src/pages/AddNewModel/AddNewModel.tsx) (add provider if needed) - [index.d.ts](./core/index.d.ts) - This file defines the TypeScript types used throughout Continue. You'll find a `ModelName` type. Be sure to add the name of your model to this. -- LLM Providers: Since many providers use their own custom strings to identify models, you'll have to add the translation from Continue's model name (the one you added to `index.d.ts`) and the model string for each of these providers: [Ollama](./core/llm/llms/Ollama.ts), [Together](./core/llm/llms/Together.ts), and [Replicate](./core/llm/llms/Replicate.ts). You can find their full model lists here: [Ollama](https://ollama.ai/library), [Together](https://docs.together.ai/docs/inference-models), [Replicate](https://replicate.com/collections/streaming-language-models). +- LLM Providers: Since many providers use their own custom strings to identify models, you'll have to add the translation from Continue's model name (the one you added to `index.d.ts`) and the model string for each of these providers: [Ollama](./core/llm/llms/Ollama.ts), [Together](./core/llm/llms/Together.ts), [Novita](./core/llm/llms/Novita.ts), and [Replicate](./core/llm/llms/Replicate.ts). You can find their full model lists here: [Ollama](https://ollama.ai/library), [Together](https://docs.together.ai/docs/inference-models), [Novita](https://novita.ai/llm-api?utm_source=github_continuedev&utm_medium=github_readme&utm_campaign=link), [Replicate](https://replicate.com/collections/streaming-language-models). - [Prompt Templates](./core/llm/index.ts) - In this file you'll find the `autodetectTemplateType` function. Make sure that for the model name you just added, this function returns the correct template type. This is assuming that the chat template for that model is already built in Continue. If not, you will have to add the template type and corresponding edit and chat templates. ### Adding Pre-indexed Documentation diff --git a/core/llm/autodetect.ts b/core/llm/autodetect.ts index 226102e885..2a9986e6cb 100644 --- a/core/llm/autodetect.ts +++ b/core/llm/autodetect.ts @@ -41,6 +41,7 @@ const PROVIDER_HANDLES_TEMPLATING: string[] = [ "openai", "ollama", "together", + "novita", "msty", "anthropic", "bedrock", @@ -130,6 +131,7 @@ const PARALLEL_PROVIDERS: string[] = [ "free-trial", "replicate", "together", + "novita", "sambanova", "nebius", "vertexai", diff --git a/core/llm/llms/Novita.ts b/core/llm/llms/Novita.ts new file mode 100644 index 0000000000..7e90bbae68 --- /dev/null +++ b/core/llm/llms/Novita.ts @@ -0,0 +1,53 @@ +import OpenAI from "./OpenAI.js"; + +import type { CompletionOptions, LLMOptions } from "../../index.js"; + +class Novita extends OpenAI { + static providerName = "novita"; + static defaultOptions: Partial = { + apiBase: "https://api.novita.ai/v3/", + }; + + private static MODEL_IDS: { [name: string]: string } = { + // todo: yexiu + "codellama-7b": "togethercomputer/CodeLlama-7b-Instruct", + "codellama-13b": "togethercomputer/CodeLlama-13b-Instruct", + "codellama-34b": "togethercomputer/CodeLlama-34b-Instruct", + "codellama-70b": "codellama/CodeLlama-70b-Instruct-hf", + "llama3-8b": "meta-llama/Llama-3-8b-chat-hf", + "llama3-70b": "meta-llama/Llama-3-70b-chat-hf", + "llama3.1-8b": "meta-llama/Meta-Llama-3.1-8B-Instruct-Turbo", + "llama3.1-70b": "meta-llama/Meta-Llama-3.1-70B-Instruct-Turbo", + "llama3.1-405b": "meta-llama/Meta-Llama-3.1-405B-Instruct-Turbo", + "llama3.2-3b": "meta-llama/Llama-3.2-3B-Instruct-Turbo", + "llama3.2-11b": "meta-llama/Llama-3.2-11B-Vision-Instruct-Turbo", + "llama3.2-90b": "meta-llama/Llama-3.2-90B-Vision-Instruct-Turbo", + "llama2-7b": "togethercomputer/llama-2-7b-chat", + "llama2-13b": "togethercomputer/llama-2-13b-chat", + "llama2-70b": "togethercomputer/llama-2-70b-chat", + "mistral-7b": "mistralai/Mistral-7B-Instruct-v0.1", + "mistral-8x7b": "mistralai/Mixtral-8x7B-Instruct-v0.1", + "phind-codellama-34b": "Phind/Phind-CodeLlama-34B-v2", + "wizardcoder-34b": "WizardLM/WizardCoder-Python-34B-V1.0", + }; + + protected _convertModelName(model: string) { + return Novita.MODEL_IDS[model] || this.model; + } + + protected async *_streamComplete( + prompt: string, + signal: AbortSignal, + options: CompletionOptions, + ): AsyncGenerator { + for await (const chunk of this._legacystreamComplete( + prompt, + signal, + options, + )) { + yield chunk; + } + } +} + +export default Novita; diff --git a/core/llm/llms/index.ts b/core/llm/llms/index.ts index 7e1e2fdc51..20bc8b710f 100644 --- a/core/llm/llms/index.ts +++ b/core/llm/llms/index.ts @@ -48,6 +48,7 @@ import ContinueProxy from "./stubs/ContinueProxy"; import TestLLM from "./Test"; import TextGenWebUI from "./TextGenWebUI"; import Together from "./Together"; +import Novita from "./Novita"; import VertexAI from "./VertexAI"; import Vllm from "./Vllm"; import WatsonX from "./WatsonX"; @@ -65,6 +66,7 @@ export const LLMClasses = [ Replicate, TextGenWebUI, Together, + Novita, HuggingFaceTGI, HuggingFaceInferenceAPI, Kindo, diff --git a/docs/docs/chat/model-setup.mdx b/docs/docs/chat/model-setup.mdx index a5668786bb..0838b47201 100644 --- a/docs/docs/chat/model-setup.mdx +++ b/docs/docs/chat/model-setup.mdx @@ -33,7 +33,7 @@ Our current top recommendation is Claude Sonnet 3.5 from [Anthropic](../customiz ### Llama 3.1 405B from Meta -If you prefer to use an open-weight model, then Llama 3.1 405B from Meta is your best option right now. You will need to decide if you use it through a SaaS model provider (e.g. [Together](../customize/model-providers/more/together.md) or [Groq](../customize/model-providers/more/groq.md)) or self-host it (e.g. using [vLLM](../customize/model-providers//more/vllm.md) or [Ollama](../customize/model-providers/top-level/ollama.md)). +If you prefer to use an open-weight model, then Llama 3.1 405B from Meta is your best option right now. You will need to decide if you use it through a SaaS model provider (e.g. [Together](../customize/model-providers/more/together.md) or [Novita](../customize/model-providers/more/novita.md) or [Groq](../customize/model-providers/more/groq.md)) or self-host it (e.g. using [vLLM](../customize/model-providers//more/vllm.md) or [Ollama](../customize/model-providers/top-level/ollama.md)). @@ -48,6 +48,18 @@ If you prefer to use an open-weight model, then Llama 3.1 405B from Meta is your ] ``` + + ```json title="config.json" + "models": [ + { + "title": "Llama 3.1 405B", + "provider": "novita", + "model": "llama3.1-405b", + "apiKey": "[NOVITA_API_KEY]" + } + ] + ``` + ```json title="config.json" "models": [ diff --git a/docs/docs/customize/model-providers/more/novita.md b/docs/docs/customize/model-providers/more/novita.md new file mode 100644 index 0000000000..d3aa359a49 --- /dev/null +++ b/docs/docs/customize/model-providers/more/novita.md @@ -0,0 +1,18 @@ +# Novita + +The Novita API is a cloud platform for running large AI models. You can sign up [here](https://novita.ai/user/login?&redirect=/&utm_source=github_continuedev), copy your API key on the initial welcome screen, and then hit the play button on any model from the [Novita Models list](https://novita.ai/llm-api?utm_source=github_continuedev&utm_medium=github_readme&utm_campaign=link). Change `~/.continue/config.json` to look like this: + +```json title="config.json" +{ + "models": [ + { + "title": "Novita Qwen2.5 Coder", + "provider": "novita", + "model": "Qwen/Qwen2.5-Coder-32B-Instruct", + "apiKey": "YOUR_API_KEY" + } + ] +} +``` + +[View the source](https://github.com/continuedev/continue/blob/main/core/llm/llms/Novita.ts) diff --git a/docs/docs/customize/tutorials/llama3.1.md b/docs/docs/customize/tutorials/llama3.1.md index 2cec71cda9..38c0f8b1b2 100644 --- a/docs/docs/customize/tutorials/llama3.1.md +++ b/docs/docs/customize/tutorials/llama3.1.md @@ -1,7 +1,7 @@ --- title: Using Llama 3.1 with Continue description: How to use Llama 3.1 with Continue -keywords: [llama, meta, togetherai, ollama, replicate] +keywords: [llama, meta, togetherai, novita, ollama, replicate] --- Continue makes it easy to code with the latest open-source models, including the entire Llama 3.1 family of models. Llama 3.2 models are also supported but not recommended for chat, because they are specifically designed to be small or multi-modal. @@ -71,6 +71,28 @@ Together AI provides fast and reliable inference of open-source models. You'll b } ``` + +## Novita AI + +Novita AI provides fast and reliable inference of open-source models. You'll be able to run the 405b model with good speed. + +1. Create an account [here](https://novita.ai/user/login?&redirect=/&utm_source=github_continuedev) +2. Copy your API key that appears on the welcome screen +3. Update your Continue config file like this: + +```json title="config.json" +{ + "models": [ + { + "title": "Llama 3.1 405b", + "provider": "novita", + "model": "llama3.1-405b", + "apiKey": "" + } + ] +} +``` + ## Replicate Replicate makes it easy to host and run open-source AI with an API. diff --git a/docs/docusaurus.config.js b/docs/docusaurus.config.js index be2fa5f594..82c11d8dec 100644 --- a/docs/docusaurus.config.js +++ b/docs/docusaurus.config.js @@ -388,7 +388,11 @@ const config = { }, { to: "/customize/model-providers/more/together", - from: "/reference/Model Providers/togetherllm", + from: "/reference/Model Providers/together", + }, + { + to: "/customize/model-providers/more/novita", + from: "/reference/Model Providers/novita", }, { to: "/customize/model-providers/more/vllm", diff --git a/docs/i18n/zh-CN/docusaurus-plugin-content-docs/current/chat/model-setup.mdx b/docs/i18n/zh-CN/docusaurus-plugin-content-docs/current/chat/model-setup.mdx index b95d976f1a..1157def5ec 100644 --- a/docs/i18n/zh-CN/docusaurus-plugin-content-docs/current/chat/model-setup.mdx +++ b/docs/i18n/zh-CN/docusaurus-plugin-content-docs/current/chat/model-setup.mdx @@ -33,7 +33,7 @@ import TabItem from "@theme/TabItem"; ### 来自 Meta 的 Llama 3.1 405B -如果你倾向于使用开放权重模型,那么来自 Meta 的 Llama 3.1 405B 是你当前的最好选择。你需要决定,通过 SaaS 模型提供者使用它(比如 [Together](../customize/model-providers/more/together.md) 或 [Groq](../customize/model-providers/more/groq.md))或者自托管使用它(比如使用 [vLLM](../customize/model-providers//more/vllm.md) 或 [Ollama](../customize/model-providers/top-level/ollama.md)) 。 +如果你倾向于使用开放权重模型,那么来自 Meta 的 Llama 3.1 405B 是你当前的最好选择。你需要决定,通过 SaaS 模型提供者使用它(比如 [Together](../customize/model-providers/more/together.md) 、[Novita](../customize/model-providers/more/novita.md)或 [Groq](../customize/model-providers/more/groq.md))或者自托管使用它(比如使用 [vLLM](../customize/model-providers//more/vllm.md) 或 [Ollama](../customize/model-providers/top-level/ollama.md)) 。 @@ -48,6 +48,18 @@ import TabItem from "@theme/TabItem"; ] ``` + + ```json title="config.json" + "models": [ + { + "title": "Llama 3.1 405B", + "provider": "novita", + "model": "llama3.1-405b", + "apiKey": "[NOVITA_API_KEY]" + } + ] + ``` + ```json title="config.json" "models": [ diff --git a/docs/i18n/zh-CN/docusaurus-plugin-content-docs/current/customize/model-providers/more/novita.md b/docs/i18n/zh-CN/docusaurus-plugin-content-docs/current/customize/model-providers/more/novita.md new file mode 100644 index 0000000000..d3e335dca4 --- /dev/null +++ b/docs/i18n/zh-CN/docusaurus-plugin-content-docs/current/customize/model-providers/more/novita.md @@ -0,0 +1,18 @@ +# Novita + +Novita API 是一个运行大 AI 模型的云平台。你可以在 [这里](https://novita.ai/user/login?&redirect=/&utm_source=github_continuedev) 注册,在最初欢迎屏幕复制你的 API key ,然后在 [Novita 模型列表](https://novita.ai/llm-api?utm_source=github_continuedev&utm_medium=github_readme&utm_campaign=link) 的任何模型上点击 play 按钮。修改 `~/.continue/config.json` 像这样: + +```json title="config.json" +{ + "models": [ + { + "title": "Novita CodeLlama", + "provider": "Novita", + "model": "codellama-13b", + "apiKey": "YOUR_API_KEY" + } + ] +} +``` + +[查看代码](https://github.com/continuedev/continue/blob/main/core/llm/llms/Novita.ts) diff --git a/docs/i18n/zh-CN/docusaurus-plugin-content-docs/current/customize/tutorials/llama3.1.md b/docs/i18n/zh-CN/docusaurus-plugin-content-docs/current/customize/tutorials/llama3.1.md index 2453fbd948..1ca491b8c8 100644 --- a/docs/i18n/zh-CN/docusaurus-plugin-content-docs/current/customize/tutorials/llama3.1.md +++ b/docs/i18n/zh-CN/docusaurus-plugin-content-docs/current/customize/tutorials/llama3.1.md @@ -1,7 +1,7 @@ --- title: Continue 使用 Llama 3.1 description: Continue 如何使用 Llama 3.1 -keywords: [llama, meta, togetherai, ollama, replicate] +keywords: [llama, meta, togetherai, novita, ollama, replicate] --- Continue 让使用最新的开元模型编码变得简单,包括整个 Llama 3.1 家族模型。 @@ -71,6 +71,27 @@ Together AI 提供开源模型的快速和可信任的推理。你可以以良 } ``` +## Novita AI + +Novita AI 提供开源模型的快速和可信任的推理。你可以以良好的速度运行 405b 模型。 + +1. 创建账号 [在这里](https://novita.ai/llm-api?utm_source=github_continuedev&utm_medium=github_readme&utm_campaign=link) +2. 复制出现在Key Management你的 API key +3. 更新你的 Continue 配置文件像这样: + +```json title="config.json" +{ + "models": [ + { + "title": "Llama 3.1 405b", + "provider": "novita", + "model": "llama3.1-405b", + "apiKey": "" + } + ] +} +``` + ## Replicate Replicate 让使用 API 托管和运行开源 AI 变得简单。 diff --git a/extensions/vscode/config_schema.json b/extensions/vscode/config_schema.json index f2c98642f8..4886ba3264 100644 --- a/extensions/vscode/config_schema.json +++ b/extensions/vscode/config_schema.json @@ -180,6 +180,7 @@ "bedrockimport", "sagemaker", "together", + "novita", "ollama", "huggingface-tgi", "huggingface-inference-api", @@ -222,6 +223,7 @@ "### Bedrock Imported Models\nTo get started with Bedrock you need to sign up on AWS [here](https://aws.amazon.com/bedrock)", "### Sagemaker\nSagemaker is AWS' machine learning platform.", "### Together\nTogether is a hosted service that provides extremely fast streaming of open-source language models. To get started with Together:\n1. Obtain an API key from [here](https://together.ai)\n2. Paste below\n3. Select a model preset\n> [Reference](https://docs.continue.dev/reference/Model%20Providers/togetherllm)", + "### Novita\nNovita is a hosted service that provides extremely fast streaming of open-source language models. To get started with Novita:\n1. Obtain an API key from [here](https://novita.ai)\n2. Paste below\n3. Select a model preset\n> [Reference](https://docs.continue.dev/reference/Model%20Providers/novitallm)", "### Ollama\nTo get started with Ollama, follow these steps:\n1. Download from [ollama.ai](https://ollama.ai/) and open the application\n2. Open a terminal and run `ollama run `. Example model names are `codellama:7b-instruct` or `llama2:7b-text`. You can find the full list [here](https://ollama.ai/library).\n3. Make sure that the model name used in step 2 is the same as the one in config.json (e.g. `model=\"codellama:7b-instruct\"`)\n4. Once the model has finished downloading, you can start asking questions through Continue.\n> [Reference](https://docs.continue.dev/reference/Model%20Providers/ollama)", "### Huggingface TGI\n\n> [Reference](https://docs.continue.dev/reference/Model%20Providers/huggingfacetgi)", "### Huggingface Inference API\n\n> [Reference](https://docs.continue.dev/reference/Model%20Providers/huggingfaceinferenceapi)", @@ -262,7 +264,7 @@ }, "apiKey": { "title": "Api Key", - "description": "OpenAI, Anthropic, Cohere, Together, or other API key", + "description": "OpenAI, Anthropic, Cohere, Together, Novita, or other API key", "type": "string" }, "apiBase": { @@ -475,6 +477,7 @@ "huggingface-inference-api", "replicate", "together", + "novita", "cloudflare", "sambanova", "nebius", @@ -980,6 +983,47 @@ } } }, + { + "if": { + "properties": { + "provider": { + "enum": ["novita"] + } + }, + "required": ["provider"] + }, + "then": { + "properties": { + "model": { + "anyOf": [ + { + "enum": [ + "mistral-7b", + "mistral-8x7b", + "llama2-7b", + "llama2-13b", + "llama3-8b", + "llama3-70b", + "llama3.1-8b", + "llama3.1-70b", + "llama3.1-405b", + "codellama-7b", + "codellama-13b", + "codellama-34b", + "codellama-70b", + "phind-codellama-34b" + ] + }, + { + "type": "string" + } + ], + "markdownDescription": "Select a pre-defined option, or find an exact model string from Novita AI [here](https://novita.ai/llm-api?utm_source=github_continuedev&utm_medium=github_readme&utm_campaign=link).", + "x-intellij-html-description": "Select a pre-defined option, or find an exact model string from Novita AI here." + } + } + } + }, { "if": { "properties": { diff --git a/gui/src/pages/AddNewModel/configs/models.ts b/gui/src/pages/AddNewModel/configs/models.ts index f3e3203051..ed28e7a8df 100644 --- a/gui/src/pages/AddNewModel/configs/models.ts +++ b/gui/src/pages/AddNewModel/configs/models.ts @@ -65,6 +65,7 @@ export const models: { [key: string]: ModelPackage } = { "ollama", "lmstudio", "together", + "novita", "llama.cpp", "replicate", "sambanova", @@ -253,6 +254,7 @@ export const models: { [key: string]: ModelPackage } = { "ollama", "lmstudio", "together", + "novita", "llama.cpp", "replicate", "nebius", @@ -298,6 +300,7 @@ export const models: { [key: string]: ModelPackage } = { "ollama", "lmstudio", "together", + "novita", "llama.cpp", "replicate", ], @@ -445,7 +448,7 @@ export const models: { [key: string]: ModelPackage } = { contextLength: 8192, }, icon: "meta.png", - providerOptions: ["ollama", "groq", "llama.cpp", "sambanova", "together"], + providerOptions: ["ollama", "groq", "llama.cpp", "sambanova", "together", "novita"], isOpenSource: false, }, llama3211bChat: { @@ -458,7 +461,7 @@ export const models: { [key: string]: ModelPackage } = { contextLength: 8192, }, icon: "meta.png", - providerOptions: ["ollama", "groq", "llama.cpp", "together"], + providerOptions: ["ollama", "groq", "llama.cpp", "together", "novita"], isOpenSource: false, }, llama3290bChat: { @@ -504,6 +507,7 @@ export const models: { [key: string]: ModelPackage } = { "ollama", "lmstudio", "together", + "novita", "llama.cpp", "replicate", "nebius", diff --git a/gui/src/pages/AddNewModel/configs/providers.ts b/gui/src/pages/AddNewModel/configs/providers.ts index b3b80e6e13..f005dd6fa7 100644 --- a/gui/src/pages/AddNewModel/configs/providers.ts +++ b/gui/src/pages/AddNewModel/configs/providers.ts @@ -404,6 +404,38 @@ Select the \`GPT-4o\` model below to complete your provider configuration, but n }), apiKeyUrl: "https://api.together.xyz/settings/api-keys", }, + novita: { + title: "NovitaAI", + provider: "novita", + refPage: "novita", + description: + "Use the NovitaAI API for extremely fast streaming of open-source models", + icon: "novita.png", + longDescription: `Novita is a hosted service that provides extremely fast streaming of open-source language models. To get started with Novita:\n1. Obtain an API key from [here](https://novita.ai)\n2. Paste below\n3. Select a model preset`, + tags: [ModelProviderTags.RequiresApiKey, ModelProviderTags.OpenSource], + params: { + apiKey: "", + }, + collectInputFor: [ + { + inputType: "text", + key: "apiKey", + label: "API Key", + placeholder: "Enter your NovitaAI API key", + required: true, + }, + ...completionParamsInputsConfigs, + ], + packages: [ + models.llama31Chat, + models.codeLlamaInstruct, + models.mistralOs, + ].map((p) => { + p.params.contextLength = 4096; + return p; + }), + apiKeyUrl: "https://novita.ai/settings/key-management", + }, gemini: { title: "Google Gemini API", provider: "gemini", @@ -640,8 +672,8 @@ To get started, [register](https://dataplatform.cloud.ibm.com/registration/stepo provider: "free-trial", refPage: "freetrial", description: - "New users can try out Continue for free using a proxy server that securely makes calls to OpenAI, Anthropic, or Together using our API key", - longDescription: `New users can try out Continue for free using a proxy server that securely makes calls to OpenAI, Anthropic, or Together using our API key. If you are ready to set up a model for long-term use or have used all ${FREE_TRIAL_LIMIT_REQUESTS} free uses, you can enter your API key or use a local model.`, + "New users can try out Continue for free using a proxy server that securely makes calls to OpenAI, Anthropic, Together, or Novita using our API key", + longDescription: `New users can try out Continue for free using a proxy server that securely makes calls to OpenAI, Anthropic, Together or Novita using our API key. If you are ready to set up a model for long-term use or have used all ${FREE_TRIAL_LIMIT_REQUESTS} free uses, you can enter your API key or use a local model.`, icon: "openai.png", tags: [ModelProviderTags.Free], packages: [ diff --git a/packages/openai-adapters/README.md b/packages/openai-adapters/README.md index cebaa129f2..3d09e4afb9 100644 --- a/packages/openai-adapters/README.md +++ b/packages/openai-adapters/README.md @@ -59,6 +59,7 @@ They are concerned with: - [ ] Silicon Flow - [x] TextGen Web UI - [x] Together +- [x] Novita - [x] Vllm - [ ] Vertex AI - [x] Voyage AI diff --git a/packages/openai-adapters/src/index.ts b/packages/openai-adapters/src/index.ts index c3db18bcf2..2e7b362fc5 100644 --- a/packages/openai-adapters/src/index.ts +++ b/packages/openai-adapters/src/index.ts @@ -73,6 +73,8 @@ export function constructLlmApi(config: LLMConfig): BaseLlmApi | undefined { return openAICompatible("https://api.fireworks.ai/inference/v1/", config); case "together": return openAICompatible("https://api.together.xyz/v1/", config); + case "novita": + return openAICompatible("https://api.novita.ai/v3", config); case "nebius": return openAICompatible("https://api.studio.nebius.ai/v1/", config); case "function-network": diff --git a/packages/openai-adapters/src/types.ts b/packages/openai-adapters/src/types.ts index fbb21e2e04..29d0310f25 100644 --- a/packages/openai-adapters/src/types.ts +++ b/packages/openai-adapters/src/types.ts @@ -39,6 +39,7 @@ export const OpenAIConfigSchema = BasePlusConfig.extend({ z.literal("nvidia"), z.literal("fireworks"), z.literal("together"), + z.literal("novita"), z.literal("sambanova"), z.literal("nebius"), z.literal("function-network"), diff --git a/packages/openai-adapters/test/main.test.ts b/packages/openai-adapters/test/main.test.ts index 4a9117cabe..01e3e93002 100644 --- a/packages/openai-adapters/test/main.test.ts +++ b/packages/openai-adapters/test/main.test.ts @@ -267,6 +267,12 @@ const COMPLETION_TESTS: ({ chatOnly?: boolean } & LlmApiConfig)[] = [ apiKey: process.env.TOGETHER_API_KEY!, chatOnly: true, }, + { + provider: "novita", + model: "meta-llama/Meta-Llama-3.1-8B-Instruct-Turbo", + apiKey: process.env.NOVITA_API_KEY!, + chatOnly: true, + }, { provider: "sambanova", model: "Meta-Llama-3.1-8B-Instruct", From f5e7f9ce71d4c34e8ee72b0d78fe89c68d0b7170 Mon Sep 17 00:00:00 2001 From: Test Date: Mon, 23 Dec 2024 11:01:35 -0600 Subject: [PATCH 17/99] feat: use meyers diff after initial edit/apply --- .../filtering/streamTransforms/lineStream.ts | 13 +++ core/edit/lazy/streamLazyApply.ts | 7 +- core/edit/streamDiffLines.ts | 14 +-- core/util/ideUtils.ts | 5 +- .../vscode/src/diff/vertical/handler.ts | 95 +++++++++++++++++-- .../vscode/src/extension/VsCodeMessenger.ts | 10 +- .../StepContainerPreToolbar.tsx | 8 +- .../markdown/StyledMarkdownPreview.tsx | 6 +- 8 files changed, 128 insertions(+), 30 deletions(-) diff --git a/core/autocomplete/filtering/streamTransforms/lineStream.ts b/core/autocomplete/filtering/streamTransforms/lineStream.ts index 851dea88ab..edad1febc3 100644 --- a/core/autocomplete/filtering/streamTransforms/lineStream.ts +++ b/core/autocomplete/filtering/streamTransforms/lineStream.ts @@ -316,6 +316,19 @@ export async function* skipLines(stream: LineStream): LineStream { } } +/** + * Handles cases where original lines have a trailing whitespace, but new lines do not. + * @param {LineStream} stream - The input stream of lines. + * @yields {string} Filtered lines that are stripped of trailing whitespace + */ +export async function* removeTrailingWhitespace( + stream: LineStream, +): LineStream { + for await (const line of stream) { + yield line.trimEnd(); + } +} + /** * Filters and processes lines from a code block, removing unnecessary markers and handling edge cases. * diff --git a/core/edit/lazy/streamLazyApply.ts b/core/edit/lazy/streamLazyApply.ts index a5c85ee4d7..982e263e8f 100644 --- a/core/edit/lazy/streamLazyApply.ts +++ b/core/edit/lazy/streamLazyApply.ts @@ -1,6 +1,7 @@ import { filterLeadingAndTrailingNewLineInsertion, filterLeadingNewline, + removeTrailingWhitespace, stopAtLines, } from "../../autocomplete/filtering/streamTransforms/lineStream.js"; import { streamDiff } from "../../diff/streamDiff.js"; @@ -23,7 +24,10 @@ export async function* streamLazyApply( } const promptMessages = promptFactory(oldCode, filename, newCode); - const lazyCompletion = llm.streamChat(promptMessages, new AbortController().signal); + const lazyCompletion = llm.streamChat( + promptMessages, + new AbortController().signal, + ); // Do find and replace over the lazy edit response async function* replacementFunction( @@ -46,6 +50,7 @@ export async function* streamLazyApply( // lazyCompletionLines = filterEnglishLinesAtStart(lazyCompletionLines); lazyCompletionLines = stopAtLines(lazyCompletionLines, () => {}, ["```"]); lazyCompletionLines = filterLeadingNewline(lazyCompletionLines); + lazyCompletionLines = removeTrailingWhitespace(lazyCompletionLines); // Fill in unchanged code let lines = streamFillUnchangedCode( diff --git a/core/edit/streamDiffLines.ts b/core/edit/streamDiffLines.ts index d0a6154c10..1d1202b919 100644 --- a/core/edit/streamDiffLines.ts +++ b/core/edit/streamDiffLines.ts @@ -1,16 +1,17 @@ +import { ChatMessage, DiffLine, ILLM, Prediction } from "../"; import { filterCodeBlockLines, filterEnglishLinesAtEnd, filterEnglishLinesAtStart, filterLeadingAndTrailingNewLineInsertion, + removeTrailingWhitespace, skipLines, stopAtLines, -} from "../autocomplete/filtering/streamTransforms/lineStream.js"; -import { streamDiff } from "../diff/streamDiff.js"; -import { streamLines } from "../diff/util.js"; -import { ChatMessage, DiffLine, ILLM, Prediction } from "../index.js"; -import { gptEditPrompt } from "../llm/templates/edit.js"; -import { Telemetry } from "../util/posthog.js"; +} from "../autocomplete/filtering/streamTransforms/lineStream"; +import { streamDiff } from "../diff/streamDiff"; +import { streamLines } from "../diff/util"; +import { gptEditPrompt } from "../llm/templates/edit"; +import { Telemetry } from "../util/posthog"; function constructPrompt( prefix: string, @@ -110,6 +111,7 @@ export async function* streamDiffLines( lines = filterCodeBlockLines(lines); lines = stopAtLines(lines, () => {}); lines = skipLines(lines); + lines = removeTrailingWhitespace(lines); if (inept) { // lines = fixCodeLlamaFirstLineIndentation(lines); lines = filterEnglishLinesAtEnd(lines); diff --git a/core/util/ideUtils.ts b/core/util/ideUtils.ts index 2dbf201473..93cb5008da 100644 --- a/core/util/ideUtils.ts +++ b/core/util/ideUtils.ts @@ -1,4 +1,5 @@ import { IDE } from ".."; + import { joinPathsToUri, pathToUriPathSegment } from "./uri"; /* @@ -34,17 +35,17 @@ export async function inferResolvedUriFromRelativePath( dirCandidates?: string[], ): Promise { const dirs = dirCandidates ?? (await ide.getWorkspaceDirs()); - console.log(path, dirs); + if (dirs.length === 0) { throw new Error("inferResolvedUriFromRelativePath: no dirs provided"); } + const segments = pathToUriPathSegment(path).split("/"); // Generate all possible suffixes from shortest to longest const suffixes: string[] = []; for (let i = segments.length - 1; i >= 0; i--) { suffixes.push(segments.slice(i).join("/")); } - console.log(suffixes); // For each suffix, try to find a unique matching directory for (const suffix of suffixes) { diff --git a/extensions/vscode/src/diff/vertical/handler.ts b/extensions/vscode/src/diff/vertical/handler.ts index 024aea2d21..a99c9c1c33 100644 --- a/extensions/vscode/src/diff/vertical/handler.ts +++ b/extensions/vscode/src/diff/vertical/handler.ts @@ -1,3 +1,5 @@ +import { myersDiff } from "core/diff/myers"; +import * as URI from "uri-js"; import * as vscode from "vscode"; import { @@ -10,7 +12,6 @@ import { import type { VerticalDiffCodeLens } from "./manager"; import type { ApplyState, DiffLine } from "core"; -import * as URI from "uri-js"; export interface VerticalDiffHandlerOptions { input?: string; @@ -249,11 +250,7 @@ export class VerticalDiffHandler implements vscode.Disposable { ? this.redDecorationManager.getRanges() : this.greenDecorationManager.getRanges(); - this.redDecorationManager.clear(); - this.greenDecorationManager.clear(); - this.clearIndexLineDecorations(); - - this.editorToVerticalDiffCodeLens.delete(this.fileUri); + this.clearDecorations(); await this.editor.edit( (editBuilder) => { @@ -360,9 +357,8 @@ export class VerticalDiffHandler implements vscode.Disposable { // Clear deletion buffer await this.insertDeletionBuffer(); - this.clearIndexLineDecorations(); - this.refreshCodeLens(); + this.reapplyWithMeyersDiff(diffLines); this.options.onStatusUpdate( "done", @@ -469,4 +465,87 @@ export class VerticalDiffHandler implements vscode.Disposable { const diffBlocks = this.editorToVerticalDiffCodeLens.get(this.fileUri); return diffBlocks !== undefined && diffBlocks.length > 0; } + + clearDecorations() { + this.redDecorationManager.clear(); + this.greenDecorationManager.clear(); + this.clearIndexLineDecorations(); + this.editorToVerticalDiffCodeLens.delete(this.fileUri); + this.refreshCodeLens(); + } + + /** + * This method is used to apply diff decorations after the intiial stream. + * This is to handle scenarios where we miscalculate the original diff blocks, + * and decide to follow up with a deterministic algorithm like Meyers Diff once + * we have received all of the diff lines. + */ + async reapplyWithMeyersDiff(diffLines: DiffLine[]) { + this.clearDecorations(); + + // First, get our old/new file content based on the diff lines + const oldFileContent = diffLines + .filter((line) => line.type === "same" || line.type === "old") + .map((line) => line.line) + .join("\n"); + + const newFileContent = diffLines + .filter((line) => line.type === "same" || line.type === "new") + .map((line) => line.line) + .join("\n"); + + const diffs = myersDiff(oldFileContent, newFileContent); + + const meyersDiffLines = diffs.map((diff) => diff.line).join("\n"); + console.log({ newFileContent }); + + // Then, we overwrite the potentially miscalcualted diff blocks with our Meyers Diff + const fullRange = new vscode.Range( + this.editor.document.positionAt(0), + this.editor.document.positionAt(this.editor.document.getText().length), + ); + + await this.editor.edit((editBuilder) => { + editBuilder.replace(fullRange, meyersDiffLines); + }); + + let currentBlock = { + start: 0, + numRed: 0, + numGreen: 0, + }; + + const blocks: VerticalDiffCodeLens[] = []; + + // Lastly, we apply decorations + diffs.forEach((diff, index) => { + if (diff.type === "old") { + this.redDecorationManager.addLine(index); + currentBlock.numRed++; + } else if (diff.type === "new") { + this.greenDecorationManager.addLine(index); + currentBlock.numGreen++; + } else if ( + diff.type === "same" && + (currentBlock.numRed > 0 || currentBlock.numGreen > 0) + ) { + blocks.push({ + ...currentBlock, + start: index - currentBlock.numRed - currentBlock.numGreen, + }); + currentBlock = { + start: index + 1, + numRed: 0, + numGreen: 0, + }; + } + }); + + if (currentBlock.numRed > 0 || currentBlock.numGreen > 0) { + blocks.push(currentBlock); + } + + this.editorToVerticalDiffCodeLens.set(this.fileUri, blocks); + this.refreshCodeLens(); + } } diff --git a/extensions/vscode/src/extension/VsCodeMessenger.ts b/extensions/vscode/src/extension/VsCodeMessenger.ts index d8b2400346..d562fdb295 100644 --- a/extensions/vscode/src/extension/VsCodeMessenger.ts +++ b/extensions/vscode/src/extension/VsCodeMessenger.ts @@ -9,25 +9,25 @@ import { import { ToWebviewFromCoreProtocol } from "core/protocol/coreWebview"; import { ToIdeFromWebviewOrCoreProtocol } from "core/protocol/ide"; import { ToIdeFromCoreProtocol } from "core/protocol/ideCore"; +import { InProcessMessenger, Message } from "core/protocol/messenger"; import { CORE_TO_WEBVIEW_PASS_THROUGH, WEBVIEW_TO_CORE_PASS_THROUGH, } from "core/protocol/passThrough"; -import { InProcessMessenger, Message } from "core/protocol/messenger"; +import { stripImages } from "core/util/messageContent"; +import { getUriPathBasename } from "core/util/uri"; import * as vscode from "vscode"; -import { stripImages } from "core/util/messageContent"; import { VerticalDiffManager } from "../diff/vertical/manager"; import EditDecorationManager from "../quickEdit/EditDecorationManager"; import { getControlPlaneSessionInfo, WorkOsAuthProvider, } from "../stubs/WorkOsAuthProvider"; +import { showTutorial } from "../util/tutorial"; import { getExtensionUri } from "../util/vscode"; import { VsCodeIde } from "../VsCodeIde"; import { VsCodeWebviewProtocol } from "../webviewProtocol"; -import { getUriPathBasename } from "core/util/uri"; -import { showTutorial } from "../util/tutorial"; /** * A shared messenger class between Core and Webview @@ -134,7 +134,7 @@ export class VsCodeMessenger { status: "streaming", fileContent: data.text, }); - console.log("applyToFile", data); + if (data.filepath) { const fileExists = await this.ide.fileExists(data.filepath); if (!fileExists) { diff --git a/gui/src/components/markdown/StepContainerPreToolbar/StepContainerPreToolbar.tsx b/gui/src/components/markdown/StepContainerPreToolbar/StepContainerPreToolbar.tsx index d0bd7caa66..c6a0d58f0e 100644 --- a/gui/src/components/markdown/StepContainerPreToolbar/StepContainerPreToolbar.tsx +++ b/gui/src/components/markdown/StepContainerPreToolbar/StepContainerPreToolbar.tsx @@ -22,9 +22,9 @@ import { import { inferResolvedUriFromRelativePath } from "core/util/ideUtils"; const TopDiv = styled.div` - outline: 1px solid rgba(153, 153, 152); + outline: 0.5px solid rgba(153, 153, 152); outline-offset: -0.5px; - border-radius: ${defaultBorderRadius}; + border-radius: 2.5px; margin-bottom: 8px !important; background-color: ${vscEditorBackground}; min-width: 0; @@ -93,12 +93,12 @@ export default function StepContainerPreToolbar( if (!defaultModel) { return; } - console.log(props.relativeFilepath); + const fileUri = await inferResolvedUriFromRelativePath( props.relativeFilepath, ideMessenger.ide, ); - console.log(fileUri); + ideMessenger.post("applyToFile", { streamId: streamIdRef.current, filepath: fileUri, diff --git a/gui/src/components/markdown/StyledMarkdownPreview.tsx b/gui/src/components/markdown/StyledMarkdownPreview.tsx index 49eec1d818..a6eb2c5696 100644 --- a/gui/src/components/markdown/StyledMarkdownPreview.tsx +++ b/gui/src/components/markdown/StyledMarkdownPreview.tsx @@ -1,5 +1,5 @@ import { ctxItemToRifWithContents } from "core/commands/util"; -import { memo, useContext, useEffect, useMemo, useRef } from "react"; +import { memo, useEffect, useMemo } from "react"; import { useRemark } from "react-remark"; import rehypeHighlight, { Options } from "rehype-highlight"; import rehypeKatex from "rehype-katex"; @@ -26,7 +26,6 @@ import { patchNestedMarkdown } from "./utils/patchNestedMarkdown"; import { useAppSelector } from "../../redux/hooks"; import { fixDoubleDollarNewLineLatex } from "./utils/fixDoubleDollarLatex"; import { selectUIConfig } from "../../redux/slices/configSlice"; -import { IdeMessengerContext } from "../../context/IdeMessenger"; import { ToolTip } from "../gui/Tooltip"; import { v4 as uuidv4 } from "uuid"; @@ -43,8 +42,7 @@ const StyledMarkdown = styled.div<{ overflow-x: scroll; overflow-y: hidden; - margin: 10px 0; - padding: 6px 8px; + padding: 16px 8px; } code { From abe2f24a342bf90deee4715800aba2998237a5cf Mon Sep 17 00:00:00 2001 From: Test Date: Mon, 23 Dec 2024 11:19:59 -0600 Subject: [PATCH 18/99] Update handler.ts --- extensions/vscode/src/diff/vertical/handler.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/extensions/vscode/src/diff/vertical/handler.ts b/extensions/vscode/src/diff/vertical/handler.ts index a99c9c1c33..206962f56b 100644 --- a/extensions/vscode/src/diff/vertical/handler.ts +++ b/extensions/vscode/src/diff/vertical/handler.ts @@ -497,7 +497,6 @@ export class VerticalDiffHandler implements vscode.Disposable { const diffs = myersDiff(oldFileContent, newFileContent); const meyersDiffLines = diffs.map((diff) => diff.line).join("\n"); - console.log({ newFileContent }); // Then, we overwrite the potentially miscalcualted diff blocks with our Meyers Diff const fullRange = new vscode.Range( From 654608f5b8e30903aa30910e24b6d6c40e73962f Mon Sep 17 00:00:00 2001 From: Test Date: Mon, 23 Dec 2024 11:25:25 -0600 Subject: [PATCH 19/99] Update ApplyActions.tsx --- .../markdown/StepContainerPreToolbar/ApplyActions.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gui/src/components/markdown/StepContainerPreToolbar/ApplyActions.tsx b/gui/src/components/markdown/StepContainerPreToolbar/ApplyActions.tsx index d71d258c55..ae42cdbeb9 100644 --- a/gui/src/components/markdown/StepContainerPreToolbar/ApplyActions.tsx +++ b/gui/src/components/markdown/StepContainerPreToolbar/ApplyActions.tsx @@ -91,7 +91,7 @@ export default function ApplyActions(props: ApplyActionsProps) { ); } - return applyButton("Re-apply"); + return applyButton("Reapply"); } default: return applyButton("Apply"); From 252f8b259828d5aa9f299c34d409210fd6784030 Mon Sep 17 00:00:00 2001 From: Test Date: Mon, 23 Dec 2024 11:44:57 -0600 Subject: [PATCH 20/99] Update handler.ts --- extensions/vscode/src/diff/vertical/handler.ts | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/extensions/vscode/src/diff/vertical/handler.ts b/extensions/vscode/src/diff/vertical/handler.ts index 206962f56b..ce56a7022e 100644 --- a/extensions/vscode/src/diff/vertical/handler.ts +++ b/extensions/vscode/src/diff/vertical/handler.ts @@ -504,9 +504,15 @@ export class VerticalDiffHandler implements vscode.Disposable { this.editor.document.positionAt(this.editor.document.getText().length), ); - await this.editor.edit((editBuilder) => { - editBuilder.replace(fullRange, meyersDiffLines); - }); + await this.editor.edit( + (editBuilder) => { + editBuilder.replace(fullRange, meyersDiffLines); + }, + { + undoStopAfter: false, + undoStopBefore: false, + }, + ); let currentBlock = { start: 0, From 0e2587cc71f497141ccd5fc807624eac80f8f183 Mon Sep 17 00:00:00 2001 From: Test Date: Mon, 23 Dec 2024 13:50:24 -0600 Subject: [PATCH 21/99] fix: broken JB build --- binary/build.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/binary/build.js b/binary/build.js index e91a3b6b3d..7e971e0408 100644 --- a/binary/build.js +++ b/binary/build.js @@ -276,6 +276,9 @@ async function installNodeModuleInTempDirAndCopyToCurrent(packageName, toCopy) { `node_modules/${targetToLanceDb[target]}/index.node`, `${targetDir}/index.node`, ); + + // Copy over our dummy package.json + fs.copyFileSync("out/package.json", `${targetDir}/package.json`); } // Our dummy `package.json` is no longer needed so we can remove it. @@ -284,13 +287,12 @@ async function installNodeModuleInTempDirAndCopyToCurrent(packageName, toCopy) { fs.unlinkSync("out/package.json"); const pathsToVerify = []; - for (target of targets) { + for (const target of targets) { const exe = target.startsWith("win") ? ".exe" : ""; const targetDir = `bin/${target}`; pathsToVerify.push( `${targetDir}/continue-binary${exe}`, `${targetDir}/index.node`, // @lancedb - "package.json", // Informs of where to look for node_sqlite3.node https://www.npmjs.com/package/bindings#:~:text=The%20searching%20for,file%20is%20found `${targetDir}/build/Release/node_sqlite3.node`, ); } From d6bcf4cea4890a243e6619aedd8780f01e5e7fbb Mon Sep 17 00:00:00 2001 From: Test Date: Mon, 23 Dec 2024 14:12:14 -0600 Subject: [PATCH 22/99] Update build.js --- binary/build.js | 26 +++----------------------- 1 file changed, 3 insertions(+), 23 deletions(-) diff --git a/binary/build.js b/binary/build.js index 7e971e0408..045fb64f22 100644 --- a/binary/build.js +++ b/binary/build.js @@ -118,22 +118,6 @@ async function installNodeModuleInTempDirAndCopyToCurrent(packageName, toCopy) { } (async () => { - // Informs of where to look for node_sqlite3.node https://www.npmjs.com/package/bindings#:~:text=The%20searching%20for,file%20is%20found - // This is only needed for our `pkg` command - fs.writeFileSync( - "out/package.json", - JSON.stringify( - { - name: "binary", - version: "1.0.0", - author: "Continue Dev, Inc", - license: "Apache-2.0", - }, - undefined, - 2, - ), - ); - console.log("[info] Downloading prebuilt lancedb..."); for (const target of targets) { if (targetToLanceDb[target]) { @@ -277,15 +261,11 @@ async function installNodeModuleInTempDirAndCopyToCurrent(packageName, toCopy) { `${targetDir}/index.node`, ); - // Copy over our dummy package.json - fs.copyFileSync("out/package.json", `${targetDir}/package.json`); + // Informs the `continue-binary` of where to look for node_sqlite3.node + // https://www.npmjs.com/package/bindings#:~:text=The%20searching%20for,file%20is%20found + fs.writeFileSync(`${targetDir}/package.json`, ""); } - // Our dummy `package.json` is no longer needed so we can remove it. - // If it isn't removed, then running locally via `node out/index.js` will fail - // with a `Failed to locate bindings` error - fs.unlinkSync("out/package.json"); - const pathsToVerify = []; for (const target of targets) { const exe = target.startsWith("win") ? ".exe" : ""; From b3ac14332e8bd89237dfa41847701e3378487145 Mon Sep 17 00:00:00 2001 From: tomasz-io Date: Mon, 23 Dec 2024 11:27:30 -0800 Subject: [PATCH 23/99] fix: cycling through chat messages --- extensions/vscode/e2e/tests/GUI.test.ts | 59 ++++++++++++++++++- gui/src/components/mainInput/TipTapEditor.tsx | 7 +-- gui/src/hooks/useInputHistory.ts | 4 +- 3 files changed, 63 insertions(+), 7 deletions(-) diff --git a/extensions/vscode/e2e/tests/GUI.test.ts b/extensions/vscode/e2e/tests/GUI.test.ts index c3c2cdf7e3..d4c005eae7 100644 --- a/extensions/vscode/e2e/tests/GUI.test.ts +++ b/extensions/vscode/e2e/tests/GUI.test.ts @@ -5,6 +5,7 @@ import { Key, WebElement, VSBrowser, + until, } from "vscode-extension-tester"; import { expect } from "chai"; import { GUIActions } from "../actions/GUI.actions"; @@ -192,7 +193,63 @@ describe("GUI Test", () => { }); describe("Chat Paths", () => { - it("Open chat and type → open history → cmd+l → chat opens, empty and in focus", async () => { + it("Open chat and send message → press arrow up and arrow down to cycle through messages → submit another message → press arrow up and arrow down to cycle through messages", async () => { + await GUIActions.sendMessage({ + view, + message: "MESSAGE 1", + inputFieldIndex: 0, + }); + + const input1 = await TestUtils.waitForSuccess(async () => { + return GUISelectors.getMessageInputFieldAtIndex(view, 1); + }); + expect(await input1.getText()).to.equal(""); + + await input1.sendKeys(Key.ARROW_UP); + await driver.wait( + until.elementTextIs(input1, "MESSAGE 1"), + DEFAULT_TIMEOUT.SM, + ); + + await input1.sendKeys(Key.ARROW_DOWN); // First press - bring caret to the end of the message + await input1.sendKeys(Key.ARROW_DOWN); // Second press - trigger message change + await driver.wait(until.elementTextIs(input1, ""), DEFAULT_TIMEOUT.SM); + + await GUIActions.sendMessage({ + view, + message: "MESSAGE 2", + inputFieldIndex: 1, + }); + + const input2 = await TestUtils.waitForSuccess(async () => { + return GUISelectors.getMessageInputFieldAtIndex(view, 2); + }); + expect(await input2.getText()).to.equal(""); + + await input2.sendKeys(Key.ARROW_UP); + await driver.wait( + until.elementTextIs(input2, "MESSAGE 2"), + DEFAULT_TIMEOUT.SM, + ); + + await input2.sendKeys(Key.ARROW_UP); + await driver.wait( + until.elementTextIs(input2, "MESSAGE 1"), + DEFAULT_TIMEOUT.SM, + ); + + await input2.sendKeys(Key.ARROW_DOWN); // First press - bring caret to the end of the message + await input2.sendKeys(Key.ARROW_DOWN); // Second press - trigger message change + await driver.wait( + until.elementTextIs(input2, "MESSAGE 2"), + DEFAULT_TIMEOUT.SM, + ); + + await input2.sendKeys(Key.ARROW_DOWN); + await driver.wait(until.elementTextIs(input2, ""), DEFAULT_TIMEOUT.SM); + }).timeout(DEFAULT_TIMEOUT.XL); + + it("Open chat and type → open history → press new session button → chat opens, empty and in focus", async () => { const originalTextInput = await GUISelectors.getMessageInputFieldAtIndex( view, 0, diff --git a/gui/src/components/mainInput/TipTapEditor.tsx b/gui/src/components/mainInput/TipTapEditor.tsx index 29c6b108a1..24f874eb64 100644 --- a/gui/src/components/mainInput/TipTapEditor.tsx +++ b/gui/src/components/mainInput/TipTapEditor.tsx @@ -637,12 +637,11 @@ function TipTapEditor(props: TipTapEditorProps) { return; } - props.onEnter(json, modifiers, editor); - if (props.isMainInput) { - const content = editor.state.toJSON().doc; - addRef.current(content); + addRef.current(json); } + + props.onEnter(json, modifiers, editor); }, [props.onEnter, editor, props.isMainInput], ); diff --git a/gui/src/hooks/useInputHistory.ts b/gui/src/hooks/useInputHistory.ts index 8a664a9e4e..3d63886fe9 100644 --- a/gui/src/hooks/useInputHistory.ts +++ b/gui/src/hooks/useInputHistory.ts @@ -52,9 +52,9 @@ export function useInputHistory(historyKey: string) { ) { setCurrentIndex(inputHistory.length); return; - } else { - setCurrentIndex(inputHistory.length + 1); } + + setCurrentIndex(inputHistory.length + 1); setInputHistory((prev) => { return [...prev, inputValue].slice(-MAX_HISTORY_LENGTH); }); From d3f7795e6133fe7452c0626e6a54fb229bacbd7b Mon Sep 17 00:00:00 2001 From: tomasz-io Date: Mon, 23 Dec 2024 14:22:39 -0800 Subject: [PATCH 24/99] fix: empty last message --- gui/src/redux/thunks/cancelStream.ts | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/gui/src/redux/thunks/cancelStream.ts b/gui/src/redux/thunks/cancelStream.ts index dc23c04a62..c7346557c4 100644 --- a/gui/src/redux/thunks/cancelStream.ts +++ b/gui/src/redux/thunks/cancelStream.ts @@ -1,5 +1,9 @@ import { createAsyncThunk } from "@reduxjs/toolkit"; -import { abortStream, setInactive } from "../slices/sessionSlice"; +import { + abortStream, + clearLastEmptyResponse, + setInactive, +} from "../slices/sessionSlice"; import { ThunkApiType } from "../store"; export const cancelStream = createAsyncThunk( @@ -10,6 +14,6 @@ export const cancelStream = createAsyncThunk( // If the assistant message is empty, then remove it and the user message, placing the user input in the main text box // TODO: Waiting until next release - // dispatch(clearLastEmptyResponse()); + dispatch(clearLastEmptyResponse()); }, ); From ad0fdb988b76bebadcbb26a3069b7f821207680f Mon Sep 17 00:00:00 2001 From: vercel Date: Tue, 24 Dec 2024 14:19:48 +0800 Subject: [PATCH 25/99] feat: fix version and address --- binary/package-lock.json | 1 + core/llm/llms/Novita.ts | 27 +++----------- .../customize/model-providers/more/novita.md | 4 +- extensions/vscode/package-lock.json | 4 +- gui/public/logos/novita.png | Bin 0 -> 6726 bytes gui/src/pages/AddNewModel/configs/models.ts | 35 +++++++++++++++--- .../pages/AddNewModel/configs/providers.ts | 4 +- packages/openai-adapters/src/index.ts | 2 +- 8 files changed, 42 insertions(+), 35 deletions(-) create mode 100644 gui/public/logos/novita.png diff --git a/binary/package-lock.json b/binary/package-lock.json index 6f7c394d1d..c026c940c9 100644 --- a/binary/package-lock.json +++ b/binary/package-lock.json @@ -48,6 +48,7 @@ "@aws-sdk/client-sagemaker-runtime": "^3.621.0", "@aws-sdk/credential-providers": "^3.620.1", "@continuedev/config-types": "^1.0.13", + "@continuedev/config-yaml": "^1.0.0", "@continuedev/fetch": "^1.0.4", "@continuedev/llm-info": "^1.0.2", "@continuedev/openai-adapters": "^1.0.10", diff --git a/core/llm/llms/Novita.ts b/core/llm/llms/Novita.ts index 7e90bbae68..9121bd6c25 100644 --- a/core/llm/llms/Novita.ts +++ b/core/llm/llms/Novita.ts @@ -5,30 +5,15 @@ import type { CompletionOptions, LLMOptions } from "../../index.js"; class Novita extends OpenAI { static providerName = "novita"; static defaultOptions: Partial = { - apiBase: "https://api.novita.ai/v3/", + apiBase: "https://api.novita.ai/v3/openai/", }; private static MODEL_IDS: { [name: string]: string } = { - // todo: yexiu - "codellama-7b": "togethercomputer/CodeLlama-7b-Instruct", - "codellama-13b": "togethercomputer/CodeLlama-13b-Instruct", - "codellama-34b": "togethercomputer/CodeLlama-34b-Instruct", - "codellama-70b": "codellama/CodeLlama-70b-Instruct-hf", - "llama3-8b": "meta-llama/Llama-3-8b-chat-hf", - "llama3-70b": "meta-llama/Llama-3-70b-chat-hf", - "llama3.1-8b": "meta-llama/Meta-Llama-3.1-8B-Instruct-Turbo", - "llama3.1-70b": "meta-llama/Meta-Llama-3.1-70B-Instruct-Turbo", - "llama3.1-405b": "meta-llama/Meta-Llama-3.1-405B-Instruct-Turbo", - "llama3.2-3b": "meta-llama/Llama-3.2-3B-Instruct-Turbo", - "llama3.2-11b": "meta-llama/Llama-3.2-11B-Vision-Instruct-Turbo", - "llama3.2-90b": "meta-llama/Llama-3.2-90B-Vision-Instruct-Turbo", - "llama2-7b": "togethercomputer/llama-2-7b-chat", - "llama2-13b": "togethercomputer/llama-2-13b-chat", - "llama2-70b": "togethercomputer/llama-2-70b-chat", - "mistral-7b": "mistralai/Mistral-7B-Instruct-v0.1", - "mistral-8x7b": "mistralai/Mixtral-8x7B-Instruct-v0.1", - "phind-codellama-34b": "Phind/Phind-CodeLlama-34B-v2", - "wizardcoder-34b": "WizardLM/WizardCoder-Python-34B-V1.0", + "llama3.1-8b": "meta-llama/llama-3.1-8b-instruct", + "llama3.1-70b": "meta-llama/llama-3.1-70b-instruct", + "llama3.1-405b": "meta-llama/llama-3.1-405b-instruct", + "llama3.2-3b": "meta-llama/llama-3.2-3b-instruct", + "llama3.2-11b": "meta-llama/llama-3.2-11b-vision-instruct", }; protected _convertModelName(model: string) { diff --git a/docs/docs/customize/model-providers/more/novita.md b/docs/docs/customize/model-providers/more/novita.md index d3aa359a49..92fe572a14 100644 --- a/docs/docs/customize/model-providers/more/novita.md +++ b/docs/docs/customize/model-providers/more/novita.md @@ -6,9 +6,9 @@ The Novita API is a cloud platform for running large AI models. You can sign up { "models": [ { - "title": "Novita Qwen2.5 Coder", + "title": "Llama 3.1 8B", "provider": "novita", - "model": "Qwen/Qwen2.5-Coder-32B-Instruct", + "model": "meta-llama/llama-3.1-8b-instruct", "apiKey": "YOUR_API_KEY" } ] diff --git a/extensions/vscode/package-lock.json b/extensions/vscode/package-lock.json index 3e213c9998..ca4edfc38b 100644 --- a/extensions/vscode/package-lock.json +++ b/extensions/vscode/package-lock.json @@ -1,12 +1,12 @@ { "name": "continue", - "version": "0.9.244", + "version": "0.9.246", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "continue", - "version": "0.9.244", + "version": "0.9.246", "license": "Apache-2.0", "dependencies": { "@continuedev/fetch": "^1.0.3", diff --git a/gui/public/logos/novita.png b/gui/public/logos/novita.png new file mode 100644 index 0000000000000000000000000000000000000000..b744cd35cc7825bba5c201be63d76e3993a75ec9 GIT binary patch literal 6726 zcmV-M8oA|(P)b`>$2vY1tM6d%M!!jF(Kj5*%-r3p+W*mt{kce$Xh#*L8*hNBQ!U|=9$6mk= zoL^w)AAs1f%DfRmGO)<(h#gNC_uf-a)l;Y2B!cPJhIFC$81%v7ew6b= zl{K~{Z70LxIhk4IPWN?`cSL*T?KFe!l9pco_P>1JULlW{?ayERia&P&{NwucQ40y^ zi-NVfLWFDM&&~yXKCWAywf06C8;>K;v$F$%`amobP z&N9ZiKDzb!-}IY1PyYI|ervs6kH#p`(Hfadjq+|~IUr?DA4GbWB>7#24e6 z6-XZ`1hJlaR`l%3xRqSS;{yJ&;G%68@QRhUc4n_npGyFy zrq+O!7;2@>9a8Ht!AP2;GEU1C6BAC7Xe-kUW-5f3AvKR}7#TB`aNC|#T+3MwebnJyDO2&*uN zZ3fd5DtBI)CK5LrMY2s1 z2Fo-{*pV1Hlm{=9FAXN6Tn1~>iDjn=Yt%3$s%fq`)b_s2KD9O#f$gEJ zii&{l91FGtP*ss|?9dCZbsQxaOU3XU>uBXLA`#JkEQ&4Z(bv}nD->OW0A>`U4Co=7 zSr5?=NK*H{ujn+ii(AQ1^u!9tS` zOiAb_+}e}G`tDvr@DqbTXo`CY4<>>u?bp!DY~q}eab6k1bS^wqzcP!>F0GG)EOQw( z{a+g_!Xc;a09+9oakd`^?d-rjLLo;^$J3;O^D?cSom!cjbUop?hMG0SS6n63MJWj% z%Xf4MyI7euESBj%X^X32G8CKO2ZHbI8EK4kEzB!HU>PfMBxk?wtDQciN2z(o+>!zf zLhvp)YAOsbZEUp(Q+pqWq^AnVB!lg8N?r6CQLN_TJRz)9y z;;R@Y+Wci_HYG*+MDozafi$B%YYB{7g>ZMhfAhUZa0lx&X%8sly66?-{=bEB@8dYJ z>-V-l7pI?1*?V`im>=PEdLJL27CUZlu6J#$iat$MKa{`n;E{9?QsA6notjffUV zX!C*(D+gEVevQnuV^iYVFBkR;TdkZF5T*<~whHLx8g$?Ki^u(P&Azklp1kqeyM4Ff z5%t{-Ht|wT9y!JNq96$VM|LqZMd137z;}R{B&$lqMRCJ&0{V5ody% zuOB{zyI+6YU;oq3`VD+clgTv|O!H(?w((<`<_Q^%9E5SKAMSWx;E8=a+p*llQ^)-w zgpwF$A{*O%z^7NtzqLyB=AW%Fv-bDK{S{`u`Eh}nvS4VW1>*SRWW}!~M;8=4%#y1e z4k-f(;fz>M^Rbf>&i=Rih+?j<_pe@D;O^H}nE8_xW?p{CVTM-E@Yj&AIoKk*Kk3RM zL?os!T$><+7im8w32Cq<0&YOt8e{nxyJ$SZYa;~<9e2gO###xd*2vg$K*Ttbz=%fII=rHnoG6qtRf#;d zLeZ_|0yqEYbAGu(&Krjh@W$hErJmx=L%nW z+5rFP+$SfIab{w3*H+bROS4mlQW1|@5+k7}*VpZfU%=ac@VGyRt4XA-E2KtV;m6P? ziM3NhT#i?k5HkQY+mmC-SJQl;?_PV`a8+UKf;t6T90VW`#Rw<_xD1oMzM9NizyE|k zjWegE7Ml7*rsxCclQdkeczkS&A*SWoz$kR2Sqr5bD3r5K>A1}{)saRS#dGe!%T9bQ zpH2dI(SB%|Ej?oR2b?>38)KkjPZWmu9xCOYmTahIe;KZ+CT4%KUNU#sm`uS;n!D8M zVlo)@Oqu8!;bgw)*s|DGx7AJ5IvR8DeY}%t9tc z<)%H8DQ7!Tn|}?{^xT>;WZ-vnBuyHv>3_UqJ2~3W9+aj-X5If_6HKY{Owv5;t^aZkN;O>#s~MaO^_XdfsAF=58p z62~xlmgQt5eXdq$F8D3u;Zdx7AaDR2FDj*GM<>UcZH>U@%x3LR&F0LsECGIE(nRKp z36ELpg0m*>7A)2+5Y%?DX1Q{i)%W9PbV@**inpD0bce8=H}+}$yDgGKG+(r!7^%=g z!T7O|425}3FrJojbP(p8Ng2HTS*(}|iI}3S2@m#t&E+6#73l~WjdZQzs$GvrT+9oN zVV2q`8h~mGB7`I?7u;p)J`FgXYQDAyXNHl)?7->$5YA?Ohj!!vGfuPo(Co@IYVls4 zp~r znH75w7k{?@i#1JF1!+z^V1$Z=cH~U7*mHf>lCf3J!~#TfH4OAAijcg7tc+Cw^>k@v zv;Y-k&_SW7Mx8A!r65S!XtmakMys-N;Ay!+N~B)Z4k5F z+Br4Gz2e>EB^oI$R#4JIA)ADkglz;YH4`+cLpUL0vy4mR1H#i4gnskWWpF2_NG%dBKcJm?2x&bZcjz&m}IBL~q)nIQtvC@U{K?m{n$kD(qgmH@9 z0Ut-ipyRckH&Hs(nGQzjV!R9_zfnPOGI!)n9PAaPu>R%@;W7d9%u9>w(D@;xHcduE z6bbHO7G7Iox%e+Ze2XDHmt{?2=oTK5vG5}cuo$drd-k#5{Bd@kh|)zem$j0@?W74} zeQbjOJ1(^8Lk#-ZX*!)D#JWsY4FQ3H=)fwfP^6=t)R~Ofj9~rN&IHZWa!1#ob0%st zonYkUSY#b2l`y_lM^^nR3UJOM=Rvc-j5gG9mC5UDvpG8r2SWkcRuZg3iMEFkh47nu zs4>XX09TO|K7j61YD^WTYIFjgVvS1PJ}O$NX_af=IpFPpd^%NXetrO|Fgjg?!UHXf zP!f^N&RmZQLv4zr{7B&my3UbAA*)w|e#CI(TFnC@T-hz*lO{7r?@TmEW$CZMurMcd zy?Nz?ox&QlZ%B}VxU}80M~G?d1;o`-z?pGGtj*zVrL!NGE59Ry;I`Ok$(s5G=4c4Z z&V<`JyM&{hm-mG#pknP--`m2J$$f(&Z3tZ`I@l{&&n8wYjOawhG|C!d-fNj|iv@#O zFf~;ycs%nuh05n9A)#piuS|DzoC|AEJc~AJ)-~|nCt;i3vktBx$)0ICzayD&YEV(5Srqz`*iOTp=@c%uYrK-F*=G`#mXm0 z$mpO=NaE?F;i~bYgrjYd;n-1c72jEJE;G#Q@KWG)=0xZ)v?bsHf!03JI}Ypv+?DDu zLX3!ph`gyd$v{RNEPFs5aP(TFBJ&(=PqfYn5^o{1#_;z0kzoqqfLmYtcYF4|NAQKU z$Q^LguI+RCNtOWvsdpJlfbWJDq05k@e@0 z70UF-GfFO{P19Ex@o5c3+DHgtJJW<{C-~w%^7D@y!`HlT>vLQogy?IcLH(GWsD$`O(8b-#;FYrYj^a?R=%k$B; z;rv)qj+m>IE##F6Ptk837?#6W%{c}Ot1yQpva=A#fttxEBBF#vCMYI^a!ngJj?JZ< zYWFrN`pUOH>(`laKg_eC*_G%v7?Rrj{e$t0%=Hh`{-qrv_fGaShC*o+f2fi0WD??1Jcm*&aXk!K%$<_=V#oIWL&Pxsq)$$lOdqWn)m0cq|Yr(0EN7bZd+t zgPIGL&Px_FqtNXAVKD7zdnlG+xkMV}@;3u4TNJV!*H?z${pRD2jAe1Wc608Ocw6Jm zNBpzpbK`m|s~g$5Q{z%#zt-d(M*?s?GZ{|Ms#AE|1kmrMYB;95KnE6O&BT4$ab}%qU1eS= z)b>zy^Q)z=iqShAD9w%c6=ivg*lbZ}FT7f2oFOxj*~r+?k3rn0c)OgOcRCkYwAnMg zX=P!$=b%W$a4wqXoksZiT_l;qZ8>5%g0Ao{wGQMcEswNY$048qbDZnBR+8_v^TUV? zFgXj>7@j+vhf9b!F|!tQ&^6UCdG-z^jLm2KJWC=S^2IbRRyxK@pM~yFOS4fhg2JM< ztCHny8Z6Mc59m>b{PrAZEFF`Vokxy-=3<1E(qXMF7;1N#mZMC*qG+TTXqeZ6Oc*_Q zSbXrLvF$&~!m3L_vU`dRo!ARnhBbWS)V-`iV^nKefW-IaeMQFBaFb5x{AaTzS*jzN zIn7GQa5&D9SRSktjUHm5Q}iC*VN~$IFHUZV)%lgkuAf);4hJ6w5$zm`WuFy=_;A89 z38E}(@&$=E^l4L5(#-zcO zc@!^0m}uU$KQ%l@UyNVQd_H@b=|Dn7YCP$)7&{fx6tklRTU1ZNnJqYq_5B)u7*DOmgW#gt47#Z zx^kZNPEZ^*t{4XtR%9YW z<(o9ST21VQ6l|edl&hyG?Ni*J)(>*U&r&lj%M^k9t%A10K5PHJ4YdevUO^HX>Nn3Y zn2%B{CKrYuYqqk+3!g0~kfx^4(+u#we{`&MqB)U3?@0X5Mm51VF+)-J_N z4gIRD`Ts_&m*(d8nplUEFiqCci49r=;1Vib!PgckSSl~FOlfy&ctG8~JC}C3EEjl- zjm0lFH_f(DL0h$n3Oh+y_BqPvA{LoTCpc+!R;HFyuuZd(xIbR!4*fjOEoLAPD}A~j zX=;JYTg&Y{UY>u~-dUOX7IcMgT4GJDi%w1vp^0o_;i%`I^Hk;#Ea#J*d>1veS zxj%GT@I{(T>58!tRx)qB{16xT^R4c#5AVH?7uVc>=HTR#9x0hwAzwL4&XOej%PO3< zQf`)*HVZMY)+Y-IB|E`7tE9#Fez524iDOH+(0oNwOU1djzWvgj`2F>r#24?`y%+w| zzHqQz0++0m0k(F;M>@6Vs$Z0Wbw08g$1!tb)y|X1Zi4OH-!g@k6sOsh9nu-Jx$r~A zk%~6xd*}WIe(TP^*;{PGPBr}Z|x2*e-l}yCM;OSY`8O+~*RV2}8W%kQwWcGtd*YJSS^W!J3 zwio{HGk$Zm>syHLM@M$55m(La(;_RV7BjN?sZ(^YyS|Z{jjC8K-~-f zWp6zHANK6Zsi)T0i?$v2MMfke>6(b2@aogjtvX2nrc?Oc0bk{I&Hpi-mBdxsFW1+d zmGMt~V0d$Q^wO($n%)K$= zI^o0wPByL|4i(rg5xXBG0`s4D&Hk{oYx}zcW_*Jg8``eB?bhGVpFP$uQS=IH9`5v! z>ca*zzq2y_g?DXZ`m-y~-dwXU<%B*V#1umI`$aI{KH`D=lMCnKw6>aok*a zc*7#Z%Y8f>$A-BJv{(Lljz<2!>GxJ`zqLj)D+f1?|I!xCKz4HCQ_1N#xs cozYwRAM>ak5A>yRrT_o{07*qoM6N<$g587>V*mgE literal 0 HcmV?d00001 diff --git a/gui/src/pages/AddNewModel/configs/models.ts b/gui/src/pages/AddNewModel/configs/models.ts index ed28e7a8df..d43ed2ec1f 100644 --- a/gui/src/pages/AddNewModel/configs/models.ts +++ b/gui/src/pages/AddNewModel/configs/models.ts @@ -31,6 +31,33 @@ export interface ModelPackage { } export const models: { [key: string]: ModelPackage } = { + llama318BChat: { + title: "Llama 3.1 8B", + description: "A model from Meta, fine-tuned for chat", + refUrl: "", + params: { + title: "Llama3.1-8b", + model: "meta-llama/llama-3.1-8b-instruct", + contextLength: 8192, + }, + icon: "meta.png", + dimensions: [ + { + name: "Parameter Count", + description: "The number of parameters in the model", + options: { + "8b": { + model: "meta-llama/llama-3.1-8b-instruct", + title: "Llama3.1-8b", + } + }, + }, + ], + providerOptions: [ + "novita" + ], + isOpenSource: true, + }, llama31Chat: { title: "Llama3.1 Chat", description: "A model from Meta, fine-tuned for chat", @@ -65,7 +92,6 @@ export const models: { [key: string]: ModelPackage } = { "ollama", "lmstudio", "together", - "novita", "llama.cpp", "replicate", "sambanova", @@ -254,7 +280,6 @@ export const models: { [key: string]: ModelPackage } = { "ollama", "lmstudio", "together", - "novita", "llama.cpp", "replicate", "nebius", @@ -300,7 +325,6 @@ export const models: { [key: string]: ModelPackage } = { "ollama", "lmstudio", "together", - "novita", "llama.cpp", "replicate", ], @@ -448,7 +472,7 @@ export const models: { [key: string]: ModelPackage } = { contextLength: 8192, }, icon: "meta.png", - providerOptions: ["ollama", "groq", "llama.cpp", "sambanova", "together", "novita"], + providerOptions: ["ollama", "groq", "llama.cpp", "sambanova", "together"], isOpenSource: false, }, llama3211bChat: { @@ -461,7 +485,7 @@ export const models: { [key: string]: ModelPackage } = { contextLength: 8192, }, icon: "meta.png", - providerOptions: ["ollama", "groq", "llama.cpp", "together", "novita"], + providerOptions: ["ollama", "groq", "llama.cpp", "together"], isOpenSource: false, }, llama3290bChat: { @@ -507,7 +531,6 @@ export const models: { [key: string]: ModelPackage } = { "ollama", "lmstudio", "together", - "novita", "llama.cpp", "replicate", "nebius", diff --git a/gui/src/pages/AddNewModel/configs/providers.ts b/gui/src/pages/AddNewModel/configs/providers.ts index f005dd6fa7..647828c7fa 100644 --- a/gui/src/pages/AddNewModel/configs/providers.ts +++ b/gui/src/pages/AddNewModel/configs/providers.ts @@ -427,9 +427,7 @@ Select the \`GPT-4o\` model below to complete your provider configuration, but n ...completionParamsInputsConfigs, ], packages: [ - models.llama31Chat, - models.codeLlamaInstruct, - models.mistralOs, + models.llama318BChat, ].map((p) => { p.params.contextLength = 4096; return p; diff --git a/packages/openai-adapters/src/index.ts b/packages/openai-adapters/src/index.ts index 2e7b362fc5..4a42bbfeef 100644 --- a/packages/openai-adapters/src/index.ts +++ b/packages/openai-adapters/src/index.ts @@ -74,7 +74,7 @@ export function constructLlmApi(config: LLMConfig): BaseLlmApi | undefined { case "together": return openAICompatible("https://api.together.xyz/v1/", config); case "novita": - return openAICompatible("https://api.novita.ai/v3", config); + return openAICompatible("https://api.novita.ai/v3/openai", config); case "nebius": return openAICompatible("https://api.studio.nebius.ai/v1/", config); case "function-network": From 8d3ecafa3dbd9a3373ec7784eda1feac6b77b684 Mon Sep 17 00:00:00 2001 From: vercel Date: Tue, 24 Dec 2024 15:05:22 +0800 Subject: [PATCH 26/99] feat: change model desc --- docs/docs/chat/model-setup.mdx | 2 +- docs/docs/customize/tutorials/llama3.1.md | 2 +- .../current/chat/model-setup.mdx | 2 +- .../customize/model-providers/more/novita.md | 6 +++--- .../current/customize/tutorials/llama3.1.md | 2 +- extensions/vscode/config_schema.json | 14 +++++--------- packages/openai-adapters/test/main.test.ts | 2 +- 7 files changed, 13 insertions(+), 17 deletions(-) diff --git a/docs/docs/chat/model-setup.mdx b/docs/docs/chat/model-setup.mdx index 0838b47201..f0a1b95085 100644 --- a/docs/docs/chat/model-setup.mdx +++ b/docs/docs/chat/model-setup.mdx @@ -54,7 +54,7 @@ If you prefer to use an open-weight model, then Llama 3.1 405B from Meta is your { "title": "Llama 3.1 405B", "provider": "novita", - "model": "llama3.1-405b", + "model": "meta-llama/llama-3.1-405b-instruct", "apiKey": "[NOVITA_API_KEY]" } ] diff --git a/docs/docs/customize/tutorials/llama3.1.md b/docs/docs/customize/tutorials/llama3.1.md index 38c0f8b1b2..a5505f6d5e 100644 --- a/docs/docs/customize/tutorials/llama3.1.md +++ b/docs/docs/customize/tutorials/llama3.1.md @@ -86,7 +86,7 @@ Novita AI provides fast and reliable inference of open-source models. You'll be { "title": "Llama 3.1 405b", "provider": "novita", - "model": "llama3.1-405b", + "model": "meta-llama/llama-3.1-405b-instruct", "apiKey": "" } ] diff --git a/docs/i18n/zh-CN/docusaurus-plugin-content-docs/current/chat/model-setup.mdx b/docs/i18n/zh-CN/docusaurus-plugin-content-docs/current/chat/model-setup.mdx index 1157def5ec..35db62cc3b 100644 --- a/docs/i18n/zh-CN/docusaurus-plugin-content-docs/current/chat/model-setup.mdx +++ b/docs/i18n/zh-CN/docusaurus-plugin-content-docs/current/chat/model-setup.mdx @@ -54,7 +54,7 @@ import TabItem from "@theme/TabItem"; { "title": "Llama 3.1 405B", "provider": "novita", - "model": "llama3.1-405b", + "model": "meta-llama/llama-3.1-405b-instruct", "apiKey": "[NOVITA_API_KEY]" } ] diff --git a/docs/i18n/zh-CN/docusaurus-plugin-content-docs/current/customize/model-providers/more/novita.md b/docs/i18n/zh-CN/docusaurus-plugin-content-docs/current/customize/model-providers/more/novita.md index d3e335dca4..4c413c2e8a 100644 --- a/docs/i18n/zh-CN/docusaurus-plugin-content-docs/current/customize/model-providers/more/novita.md +++ b/docs/i18n/zh-CN/docusaurus-plugin-content-docs/current/customize/model-providers/more/novita.md @@ -1,14 +1,14 @@ # Novita -Novita API 是一个运行大 AI 模型的云平台。你可以在 [这里](https://novita.ai/user/login?&redirect=/&utm_source=github_continuedev) 注册,在最初欢迎屏幕复制你的 API key ,然后在 [Novita 模型列表](https://novita.ai/llm-api?utm_source=github_continuedev&utm_medium=github_readme&utm_campaign=link) 的任何模型上点击 play 按钮。修改 `~/.continue/config.json` 像这样: +Novita API 是一个运行大 AI 模型的云平台。你可以在 [这里](https://novita.ai/user/login?&redirect=/&utm_source=github_continuedev) 注册,在最初欢迎屏幕复制你的 API key ,然后在 [Novita 模型列表](https://novita.ai/llm-api?utm_source=github_continuedev&utm_medium=github_readme&utm_campaign=link) 的任何模型上点击 Try it now 按钮。修改 `~/.continue/config.json` 像这样: ```json title="config.json" { "models": [ { - "title": "Novita CodeLlama", + "title": "Llama 3.1 8b", "provider": "Novita", - "model": "codellama-13b", + "model": "meta-llama/llama-3.1-8b-instruct", "apiKey": "YOUR_API_KEY" } ] diff --git a/docs/i18n/zh-CN/docusaurus-plugin-content-docs/current/customize/tutorials/llama3.1.md b/docs/i18n/zh-CN/docusaurus-plugin-content-docs/current/customize/tutorials/llama3.1.md index 1ca491b8c8..a36b5070d8 100644 --- a/docs/i18n/zh-CN/docusaurus-plugin-content-docs/current/customize/tutorials/llama3.1.md +++ b/docs/i18n/zh-CN/docusaurus-plugin-content-docs/current/customize/tutorials/llama3.1.md @@ -85,7 +85,7 @@ Novita AI 提供开源模型的快速和可信任的推理。你可以以良好 { "title": "Llama 3.1 405b", "provider": "novita", - "model": "llama3.1-405b", + "model": "meta-llama/llama-3.1-405b-instruct", "apiKey": "" } ] diff --git a/extensions/vscode/config_schema.json b/extensions/vscode/config_schema.json index 4886ba3264..7ecff0aa48 100644 --- a/extensions/vscode/config_schema.json +++ b/extensions/vscode/config_schema.json @@ -998,20 +998,16 @@ "anyOf": [ { "enum": [ - "mistral-7b", - "mistral-8x7b", - "llama2-7b", - "llama2-13b", "llama3-8b", "llama3-70b", "llama3.1-8b", "llama3.1-70b", "llama3.1-405b", - "codellama-7b", - "codellama-13b", - "codellama-34b", - "codellama-70b", - "phind-codellama-34b" + "llama3.2-3b", + "llama3.2-11b", + "llama-3.3-70b", + "mistral-nemo", + "mistral-7b" ] }, { diff --git a/packages/openai-adapters/test/main.test.ts b/packages/openai-adapters/test/main.test.ts index 01e3e93002..7e4a56d063 100644 --- a/packages/openai-adapters/test/main.test.ts +++ b/packages/openai-adapters/test/main.test.ts @@ -269,7 +269,7 @@ const COMPLETION_TESTS: ({ chatOnly?: boolean } & LlmApiConfig)[] = [ }, { provider: "novita", - model: "meta-llama/Meta-Llama-3.1-8B-Instruct-Turbo", + model: "meta-llama/llama-3.1-8b-instruct", apiKey: process.env.NOVITA_API_KEY!, chatOnly: true, }, From a25afeeda10c86072f80c025c20c21b167ddaaf0 Mon Sep 17 00:00:00 2001 From: vercel Date: Tue, 24 Dec 2024 18:57:16 +0800 Subject: [PATCH 27/99] feat: brand text fix --- CONTRIBUTING.md | 4 ++-- docs/docs/chat/model-setup.mdx | 2 +- docs/docs/customize/model-providers/more/novita.md | 2 +- docs/docs/customize/tutorials/llama3.1.md | 2 +- .../current/chat/model-setup.mdx | 2 +- .../current/customize/model-providers/more/novita.md | 2 +- .../current/customize/tutorials/llama3.1.md | 6 +++--- extensions/vscode/config_schema.json | 4 ++-- gui/src/pages/AddNewModel/configs/providers.ts | 10 +++++----- packages/openai-adapters/README.md | 2 +- 10 files changed, 18 insertions(+), 18 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 843f5ad171..458fc6038f 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -188,7 +188,7 @@ After you've written your context provider, make sure to complete the following: ### Adding an LLM Provider -Continue has support for more than a dozen different LLM "providers", making it easy to use models running on OpenAI, Ollama, Together, Novita, LM Studio, Msty, and more. You can find all of the existing providers [here](https://github.com/continuedev/continue/tree/main/core/llm/llms), and if you see one missing, you can add it with the following steps: +Continue has support for more than a dozen different LLM "providers", making it easy to use models running on OpenAI, Ollama, Together, Novita AI, LM Studio, Msty, and more. You can find all of the existing providers [here](https://github.com/continuedev/continue/tree/main/core/llm/llms), and if you see one missing, you can add it with the following steps: 1. Create a new file in the `core/llm/llms` directory. The name of the file should be the name of the provider, and it should export a class that extends `BaseLLM`. This class should contain the following minimal implementation. We recommend viewing pre-existing providers for more details. The [LlamaCpp Provider](./core/llm/llms/LlamaCpp.ts) is a good simple example. @@ -209,7 +209,7 @@ While any model that works with a supported provider can be used with Continue, 1. Add a `ModelPackage` entry for the model into [configs/models.ts](./gui/src/pages/AddNewModel/configs/models.ts), following the lead of the many examples near the top of the file 2. Add the model within its provider's array to [AddNewModel.tsx](./gui/src/pages/AddNewModel/AddNewModel.tsx) (add provider if needed) - [index.d.ts](./core/index.d.ts) - This file defines the TypeScript types used throughout Continue. You'll find a `ModelName` type. Be sure to add the name of your model to this. -- LLM Providers: Since many providers use their own custom strings to identify models, you'll have to add the translation from Continue's model name (the one you added to `index.d.ts`) and the model string for each of these providers: [Ollama](./core/llm/llms/Ollama.ts), [Together](./core/llm/llms/Together.ts), [Novita](./core/llm/llms/Novita.ts), and [Replicate](./core/llm/llms/Replicate.ts). You can find their full model lists here: [Ollama](https://ollama.ai/library), [Together](https://docs.together.ai/docs/inference-models), [Novita](https://novita.ai/llm-api?utm_source=github_continuedev&utm_medium=github_readme&utm_campaign=link), [Replicate](https://replicate.com/collections/streaming-language-models). +- LLM Providers: Since many providers use their own custom strings to identify models, you'll have to add the translation from Continue's model name (the one you added to `index.d.ts`) and the model string for each of these providers: [Ollama](./core/llm/llms/Ollama.ts), [Together](./core/llm/llms/Together.ts), [Novita AI](./core/llm/llms/Novita.ts), and [Replicate](./core/llm/llms/Replicate.ts). You can find their full model lists here: [Ollama](https://ollama.ai/library), [Together](https://docs.together.ai/docs/inference-models), [Novita AI](https://novita.ai/llm-api?utm_source=github_continuedev&utm_medium=github_readme&utm_campaign=link), [Replicate](https://replicate.com/collections/streaming-language-models). - [Prompt Templates](./core/llm/index.ts) - In this file you'll find the `autodetectTemplateType` function. Make sure that for the model name you just added, this function returns the correct template type. This is assuming that the chat template for that model is already built in Continue. If not, you will have to add the template type and corresponding edit and chat templates. ### Adding Pre-indexed Documentation diff --git a/docs/docs/chat/model-setup.mdx b/docs/docs/chat/model-setup.mdx index f0a1b95085..41f7709d64 100644 --- a/docs/docs/chat/model-setup.mdx +++ b/docs/docs/chat/model-setup.mdx @@ -33,7 +33,7 @@ Our current top recommendation is Claude Sonnet 3.5 from [Anthropic](../customiz ### Llama 3.1 405B from Meta -If you prefer to use an open-weight model, then Llama 3.1 405B from Meta is your best option right now. You will need to decide if you use it through a SaaS model provider (e.g. [Together](../customize/model-providers/more/together.md) or [Novita](../customize/model-providers/more/novita.md) or [Groq](../customize/model-providers/more/groq.md)) or self-host it (e.g. using [vLLM](../customize/model-providers//more/vllm.md) or [Ollama](../customize/model-providers/top-level/ollama.md)). +If you prefer to use an open-weight model, then Llama 3.1 405B from Meta is your best option right now. You will need to decide if you use it through a SaaS model provider (e.g. [Together](../customize/model-providers/more/together.md) or [Novita AI](../customize/model-providers/more/novita.md) or [Groq](../customize/model-providers/more/groq.md)) or self-host it (e.g. using [vLLM](../customize/model-providers//more/vllm.md) or [Ollama](../customize/model-providers/top-level/ollama.md)). diff --git a/docs/docs/customize/model-providers/more/novita.md b/docs/docs/customize/model-providers/more/novita.md index 92fe572a14..841d3e11e2 100644 --- a/docs/docs/customize/model-providers/more/novita.md +++ b/docs/docs/customize/model-providers/more/novita.md @@ -1,6 +1,6 @@ # Novita -The Novita API is a cloud platform for running large AI models. You can sign up [here](https://novita.ai/user/login?&redirect=/&utm_source=github_continuedev), copy your API key on the initial welcome screen, and then hit the play button on any model from the [Novita Models list](https://novita.ai/llm-api?utm_source=github_continuedev&utm_medium=github_readme&utm_campaign=link). Change `~/.continue/config.json` to look like this: +[Novita AI](https://novita.ai) offers an affordable, reliable, and simple inference platform with scalable [LLM API](https://novita.ai/docs/model-api/reference/introduction.html), empowering developers to build AI applications. Try the [Novita AI Llama 3 API Demo](https://novita.ai/model-api/product/llm-api/playground/meta-llama-llama-3.1-70b-instruct) today!. You can sign up [here](https://novita.ai/user/login?&redirect=/&utm_source=github_continuedev), copy your API key on the [Key Management](https://novita.ai/settings/key-management), and then hit the play button on any model from the [Novita AI Models list](https://novita.ai/llm-api?utm_source=github_continuedev&utm_medium=github_readme&utm_campaign=link). Change `~/.continue/config.json` to look like this: ```json title="config.json" { diff --git a/docs/docs/customize/tutorials/llama3.1.md b/docs/docs/customize/tutorials/llama3.1.md index a5505f6d5e..28decd81e5 100644 --- a/docs/docs/customize/tutorials/llama3.1.md +++ b/docs/docs/customize/tutorials/llama3.1.md @@ -74,7 +74,7 @@ Together AI provides fast and reliable inference of open-source models. You'll b ## Novita AI -Novita AI provides fast and reliable inference of open-source models. You'll be able to run the 405b model with good speed. +[Novita AI](https://novita.ai) offers an affordable, reliable, and simple inference platform with scalable [LLM API](https://novita.ai/docs/model-api/reference/introduction.html), empowering developers to build AI applications. Try the [Novita AI Llama 3 API Demo](https://novita.ai/model-api/product/llm-api/playground/meta-llama-llama-3.1-70b-instruct) today! 1. Create an account [here](https://novita.ai/user/login?&redirect=/&utm_source=github_continuedev) 2. Copy your API key that appears on the welcome screen diff --git a/docs/i18n/zh-CN/docusaurus-plugin-content-docs/current/chat/model-setup.mdx b/docs/i18n/zh-CN/docusaurus-plugin-content-docs/current/chat/model-setup.mdx index 35db62cc3b..4ef37a41e4 100644 --- a/docs/i18n/zh-CN/docusaurus-plugin-content-docs/current/chat/model-setup.mdx +++ b/docs/i18n/zh-CN/docusaurus-plugin-content-docs/current/chat/model-setup.mdx @@ -33,7 +33,7 @@ import TabItem from "@theme/TabItem"; ### 来自 Meta 的 Llama 3.1 405B -如果你倾向于使用开放权重模型,那么来自 Meta 的 Llama 3.1 405B 是你当前的最好选择。你需要决定,通过 SaaS 模型提供者使用它(比如 [Together](../customize/model-providers/more/together.md) 、[Novita](../customize/model-providers/more/novita.md)或 [Groq](../customize/model-providers/more/groq.md))或者自托管使用它(比如使用 [vLLM](../customize/model-providers//more/vllm.md) 或 [Ollama](../customize/model-providers/top-level/ollama.md)) 。 +如果你倾向于使用开放权重模型,那么来自 Meta 的 Llama 3.1 405B 是你当前的最好选择。你需要决定,通过 SaaS 模型提供者使用它(比如 [Together](../customize/model-providers/more/together.md) 、[Novita AI](../customize/model-providers/more/novita.md)或 [Groq](../customize/model-providers/more/groq.md))或者自托管使用它(比如使用 [vLLM](../customize/model-providers//more/vllm.md) 或 [Ollama](../customize/model-providers/top-level/ollama.md)) 。 diff --git a/docs/i18n/zh-CN/docusaurus-plugin-content-docs/current/customize/model-providers/more/novita.md b/docs/i18n/zh-CN/docusaurus-plugin-content-docs/current/customize/model-providers/more/novita.md index 4c413c2e8a..97adaa0815 100644 --- a/docs/i18n/zh-CN/docusaurus-plugin-content-docs/current/customize/model-providers/more/novita.md +++ b/docs/i18n/zh-CN/docusaurus-plugin-content-docs/current/customize/model-providers/more/novita.md @@ -1,6 +1,6 @@ # Novita -Novita API 是一个运行大 AI 模型的云平台。你可以在 [这里](https://novita.ai/user/login?&redirect=/&utm_source=github_continuedev) 注册,在最初欢迎屏幕复制你的 API key ,然后在 [Novita 模型列表](https://novita.ai/llm-api?utm_source=github_continuedev&utm_medium=github_readme&utm_campaign=link) 的任何模型上点击 Try it now 按钮。修改 `~/.continue/config.json` 像这样: +[Novita AI](https://novita.ai) 提供了一个经济实惠、可靠且简单的推理平台。你可以在 [这里](https://novita.ai/user/login?&redirect=/&utm_source=github_continuedev) 注册,在 [Key 管理页面](https://novita.ai/settings/key-management)复制你的 API key ,然后在 [Novita 模型列表](https://novita.ai/llm-api?utm_source=github_continuedev&utm_medium=github_readme&utm_campaign=link) 的任何模型上点击 Try it now 按钮。修改 `~/.continue/config.json` 像这样: ```json title="config.json" { diff --git a/docs/i18n/zh-CN/docusaurus-plugin-content-docs/current/customize/tutorials/llama3.1.md b/docs/i18n/zh-CN/docusaurus-plugin-content-docs/current/customize/tutorials/llama3.1.md index a36b5070d8..126be8f502 100644 --- a/docs/i18n/zh-CN/docusaurus-plugin-content-docs/current/customize/tutorials/llama3.1.md +++ b/docs/i18n/zh-CN/docusaurus-plugin-content-docs/current/customize/tutorials/llama3.1.md @@ -73,11 +73,11 @@ Together AI 提供开源模型的快速和可信任的推理。你可以以良 ## Novita AI -Novita AI 提供开源模型的快速和可信任的推理。你可以以良好的速度运行 405b 模型。 +[Novita AI](https://novita.ai) 提供了一个经济实惠、可靠且简单的推理平台。你可以以良好的速度运行 405b 模型。 1. 创建账号 [在这里](https://novita.ai/llm-api?utm_source=github_continuedev&utm_medium=github_readme&utm_campaign=link) -2. 复制出现在Key Management你的 API key -3. 更新你的 Continue 配置文件像这样: +2. 复制[Key Management](https://novita.ai/settings/key-management)中的你的 API key +3. 更新你的 Continue 配置文件,像这样: ```json title="config.json" { diff --git a/extensions/vscode/config_schema.json b/extensions/vscode/config_schema.json index 7ecff0aa48..76eedf998f 100644 --- a/extensions/vscode/config_schema.json +++ b/extensions/vscode/config_schema.json @@ -223,7 +223,7 @@ "### Bedrock Imported Models\nTo get started with Bedrock you need to sign up on AWS [here](https://aws.amazon.com/bedrock)", "### Sagemaker\nSagemaker is AWS' machine learning platform.", "### Together\nTogether is a hosted service that provides extremely fast streaming of open-source language models. To get started with Together:\n1. Obtain an API key from [here](https://together.ai)\n2. Paste below\n3. Select a model preset\n> [Reference](https://docs.continue.dev/reference/Model%20Providers/togetherllm)", - "### Novita\nNovita is a hosted service that provides extremely fast streaming of open-source language models. To get started with Novita:\n1. Obtain an API key from [here](https://novita.ai)\n2. Paste below\n3. Select a model preset\n> [Reference](https://docs.continue.dev/reference/Model%20Providers/novitallm)", + "### Novita AI\n[Novita AI](https://novita.ai) offers an affordable, reliable, and simple inference platform with scalable [LLM APIs](https://novita.ai/docs/model-api/reference/introduction.html), empowering developers to build AI applications. To get started with Novita AI:\n1. Obtain an API key from [here](https://novita.ai/settings/key-management)\n2. Paste below\n3. Select a model preset\n> [Reference](https://docs.continue.dev/reference/Model%20Providers/novitallm)", "### Ollama\nTo get started with Ollama, follow these steps:\n1. Download from [ollama.ai](https://ollama.ai/) and open the application\n2. Open a terminal and run `ollama run `. Example model names are `codellama:7b-instruct` or `llama2:7b-text`. You can find the full list [here](https://ollama.ai/library).\n3. Make sure that the model name used in step 2 is the same as the one in config.json (e.g. `model=\"codellama:7b-instruct\"`)\n4. Once the model has finished downloading, you can start asking questions through Continue.\n> [Reference](https://docs.continue.dev/reference/Model%20Providers/ollama)", "### Huggingface TGI\n\n> [Reference](https://docs.continue.dev/reference/Model%20Providers/huggingfacetgi)", "### Huggingface Inference API\n\n> [Reference](https://docs.continue.dev/reference/Model%20Providers/huggingfaceinferenceapi)", @@ -264,7 +264,7 @@ }, "apiKey": { "title": "Api Key", - "description": "OpenAI, Anthropic, Cohere, Together, Novita, or other API key", + "description": "OpenAI, Anthropic, Cohere, Together, Novita AI, or other API key", "type": "string" }, "apiBase": { diff --git a/gui/src/pages/AddNewModel/configs/providers.ts b/gui/src/pages/AddNewModel/configs/providers.ts index 647828c7fa..8ef4129eb1 100644 --- a/gui/src/pages/AddNewModel/configs/providers.ts +++ b/gui/src/pages/AddNewModel/configs/providers.ts @@ -409,9 +409,9 @@ Select the \`GPT-4o\` model below to complete your provider configuration, but n provider: "novita", refPage: "novita", description: - "Use the NovitaAI API for extremely fast streaming of open-source models", + "Use Novita AI API for extremely fast streaming of open-source models", icon: "novita.png", - longDescription: `Novita is a hosted service that provides extremely fast streaming of open-source language models. To get started with Novita:\n1. Obtain an API key from [here](https://novita.ai)\n2. Paste below\n3. Select a model preset`, + longDescription: `[Novita AI](https://novita.ai) offers an affordable, reliable, and simple inference platform with scalable [LLM APIs](https://novita.ai/docs/model-api/reference/introduction.html), empowering developers to build AI applications. To get started with Novita AI:\n1. Obtain an API key from [here](https://novita.ai)\n2. Paste below\n3. Select a model preset`, tags: [ModelProviderTags.RequiresApiKey, ModelProviderTags.OpenSource], params: { apiKey: "", @@ -421,7 +421,7 @@ Select the \`GPT-4o\` model below to complete your provider configuration, but n inputType: "text", key: "apiKey", label: "API Key", - placeholder: "Enter your NovitaAI API key", + placeholder: "Enter your Novita AI API key", required: true, }, ...completionParamsInputsConfigs, @@ -670,8 +670,8 @@ To get started, [register](https://dataplatform.cloud.ibm.com/registration/stepo provider: "free-trial", refPage: "freetrial", description: - "New users can try out Continue for free using a proxy server that securely makes calls to OpenAI, Anthropic, Together, or Novita using our API key", - longDescription: `New users can try out Continue for free using a proxy server that securely makes calls to OpenAI, Anthropic, Together or Novita using our API key. If you are ready to set up a model for long-term use or have used all ${FREE_TRIAL_LIMIT_REQUESTS} free uses, you can enter your API key or use a local model.`, + "New users can try out Continue for free using a proxy server that securely makes calls to OpenAI, Anthropic, Together, or Novita AI using our API key", + longDescription: `New users can try out Continue for free using a proxy server that securely makes calls to OpenAI, Anthropic, Together or Novita AI using our API key. If you are ready to set up a model for long-term use or have used all ${FREE_TRIAL_LIMIT_REQUESTS} free uses, you can enter your API key or use a local model.`, icon: "openai.png", tags: [ModelProviderTags.Free], packages: [ diff --git a/packages/openai-adapters/README.md b/packages/openai-adapters/README.md index 3d09e4afb9..b84b198e7f 100644 --- a/packages/openai-adapters/README.md +++ b/packages/openai-adapters/README.md @@ -59,7 +59,7 @@ They are concerned with: - [ ] Silicon Flow - [x] TextGen Web UI - [x] Together -- [x] Novita +- [x] Novita AI - [x] Vllm - [ ] Vertex AI - [x] Voyage AI From d59b3b5297924b2b6343a990c09551be4af9c670 Mon Sep 17 00:00:00 2001 From: vercel Date: Tue, 24 Dec 2024 20:00:43 +0800 Subject: [PATCH 28/99] feat: fix address --- CONTRIBUTING.md | 2 +- docs/docs/customize/model-providers/more/novita.md | 2 +- docs/docs/customize/tutorials/llama3.1.md | 4 ++-- .../current/customize/model-providers/more/novita.md | 2 +- .../current/customize/tutorials/llama3.1.md | 6 +++--- extensions/vscode/config_schema.json | 6 +++--- gui/src/pages/AddNewModel/configs/providers.ts | 4 ++-- 7 files changed, 13 insertions(+), 13 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 458fc6038f..eddf7b45bb 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -209,7 +209,7 @@ While any model that works with a supported provider can be used with Continue, 1. Add a `ModelPackage` entry for the model into [configs/models.ts](./gui/src/pages/AddNewModel/configs/models.ts), following the lead of the many examples near the top of the file 2. Add the model within its provider's array to [AddNewModel.tsx](./gui/src/pages/AddNewModel/AddNewModel.tsx) (add provider if needed) - [index.d.ts](./core/index.d.ts) - This file defines the TypeScript types used throughout Continue. You'll find a `ModelName` type. Be sure to add the name of your model to this. -- LLM Providers: Since many providers use their own custom strings to identify models, you'll have to add the translation from Continue's model name (the one you added to `index.d.ts`) and the model string for each of these providers: [Ollama](./core/llm/llms/Ollama.ts), [Together](./core/llm/llms/Together.ts), [Novita AI](./core/llm/llms/Novita.ts), and [Replicate](./core/llm/llms/Replicate.ts). You can find their full model lists here: [Ollama](https://ollama.ai/library), [Together](https://docs.together.ai/docs/inference-models), [Novita AI](https://novita.ai/llm-api?utm_source=github_continuedev&utm_medium=github_readme&utm_campaign=link), [Replicate](https://replicate.com/collections/streaming-language-models). +- LLM Providers: Since many providers use their own custom strings to identify models, you'll have to add the translation from Continue's model name (the one you added to `index.d.ts`) and the model string for each of these providers: [Ollama](./core/llm/llms/Ollama.ts), [Together](./core/llm/llms/Together.ts), [Novita AI](./core/llm/llms/Novita.ts), and [Replicate](./core/llm/llms/Replicate.ts). You can find their full model lists here: [Ollama](https://ollama.ai/library), [Together](https://docs.together.ai/docs/inference-models), [Novita AI](https://novita.ai/llm-api?utm_source=github_continuedev&utm_medium=github_readme&utm_campaign=github_link), [Replicate](https://replicate.com/collections/streaming-language-models). - [Prompt Templates](./core/llm/index.ts) - In this file you'll find the `autodetectTemplateType` function. Make sure that for the model name you just added, this function returns the correct template type. This is assuming that the chat template for that model is already built in Continue. If not, you will have to add the template type and corresponding edit and chat templates. ### Adding Pre-indexed Documentation diff --git a/docs/docs/customize/model-providers/more/novita.md b/docs/docs/customize/model-providers/more/novita.md index 841d3e11e2..02f7cefb5e 100644 --- a/docs/docs/customize/model-providers/more/novita.md +++ b/docs/docs/customize/model-providers/more/novita.md @@ -1,6 +1,6 @@ # Novita -[Novita AI](https://novita.ai) offers an affordable, reliable, and simple inference platform with scalable [LLM API](https://novita.ai/docs/model-api/reference/introduction.html), empowering developers to build AI applications. Try the [Novita AI Llama 3 API Demo](https://novita.ai/model-api/product/llm-api/playground/meta-llama-llama-3.1-70b-instruct) today!. You can sign up [here](https://novita.ai/user/login?&redirect=/&utm_source=github_continuedev), copy your API key on the [Key Management](https://novita.ai/settings/key-management), and then hit the play button on any model from the [Novita AI Models list](https://novita.ai/llm-api?utm_source=github_continuedev&utm_medium=github_readme&utm_campaign=link). Change `~/.continue/config.json` to look like this: +[Novita AI](https://novita.ai?utm_source=github_continuedev&utm_medium=github_readme&utm_campaign=github_link) offers an affordable, reliable, and simple inference platform with scalable [LLM API](https://novita.ai/docs/model-api/reference/introduction.html), empowering developers to build AI applications. Try the [Novita AI Llama 3 API Demo](https://novita.ai/model-api/product/llm-api/playground/meta-llama-llama-3.1-70b-instruct?utm_source=github_continuedev&utm_medium=github_readme&utm_campaign=github_link) today!. You can sign up [here](https://novita.ai/user/login?&redirect=/&utm_source=github_continuedev&utm_medium=github_readme&utm_campaign=github_link), copy your API key on the [Key Management](https://novita.ai/settings/key-management?utm_source=github_continuedev&utm_medium=github_readme&utm_campaign=github_link), and then hit the play button on any model from the [Novita AI Models list](https://novita.ai/llm-api?utm_source=github_continuedev&utm_medium=github_readme&utm_campaign=github_link). Change `~/.continue/config.json` to look like this: ```json title="config.json" { diff --git a/docs/docs/customize/tutorials/llama3.1.md b/docs/docs/customize/tutorials/llama3.1.md index 28decd81e5..38e90bee9f 100644 --- a/docs/docs/customize/tutorials/llama3.1.md +++ b/docs/docs/customize/tutorials/llama3.1.md @@ -74,9 +74,9 @@ Together AI provides fast and reliable inference of open-source models. You'll b ## Novita AI -[Novita AI](https://novita.ai) offers an affordable, reliable, and simple inference platform with scalable [LLM API](https://novita.ai/docs/model-api/reference/introduction.html), empowering developers to build AI applications. Try the [Novita AI Llama 3 API Demo](https://novita.ai/model-api/product/llm-api/playground/meta-llama-llama-3.1-70b-instruct) today! +[Novita AI](https://novita.ai?utm_source=github_continuedev&utm_medium=github_readme&utm_campaign=github_link) offers an affordable, reliable, and simple inference platform with scalable [LLM API](https://novita.ai/docs/model-api/reference/introduction.html), empowering developers to build AI applications. Try the [Novita AI Llama 3 API Demo](https://novita.ai/model-api/product/llm-api/playground/meta-llama-llama-3.1-70b-instruct?utm_source=github_continuedev&utm_medium=github_readme&utm_campaign=github_link) today! -1. Create an account [here](https://novita.ai/user/login?&redirect=/&utm_source=github_continuedev) +1. Create an account [here](https://novita.ai/user/login?&redirect=/&utm_source=github_continuedev&utm_medium=github_readme&utm_campaign=github_link) 2. Copy your API key that appears on the welcome screen 3. Update your Continue config file like this: diff --git a/docs/i18n/zh-CN/docusaurus-plugin-content-docs/current/customize/model-providers/more/novita.md b/docs/i18n/zh-CN/docusaurus-plugin-content-docs/current/customize/model-providers/more/novita.md index 97adaa0815..fffdd195a3 100644 --- a/docs/i18n/zh-CN/docusaurus-plugin-content-docs/current/customize/model-providers/more/novita.md +++ b/docs/i18n/zh-CN/docusaurus-plugin-content-docs/current/customize/model-providers/more/novita.md @@ -1,6 +1,6 @@ # Novita -[Novita AI](https://novita.ai) 提供了一个经济实惠、可靠且简单的推理平台。你可以在 [这里](https://novita.ai/user/login?&redirect=/&utm_source=github_continuedev) 注册,在 [Key 管理页面](https://novita.ai/settings/key-management)复制你的 API key ,然后在 [Novita 模型列表](https://novita.ai/llm-api?utm_source=github_continuedev&utm_medium=github_readme&utm_campaign=link) 的任何模型上点击 Try it now 按钮。修改 `~/.continue/config.json` 像这样: +[Novita AI](https://novita.ai?utm_source=github_continuedev&utm_medium=github_readme&utm_campaign=github_link) 提供了一个经济实惠、可靠且简单的推理平台。你可以在 [这里](https://novita.ai/user/login?&redirect=/&utm_source=github_continuedev&utm_medium=github_readme&utm_campaign=github_link) 注册,在 [Key Management](https://novita.ai/settings/key-management?utm_source=github_continuedev&utm_medium=github_readme&utm_campaign=github_link)复制你的 API key ,然后在 [Novita 模型列表](https://novita.ai/llm-api?utm_source=github_continuedev&utm_medium=github_readme&utm_campaign=github_link) 的任何模型上点击 Try it now 按钮。修改 `~/.continue/config.json` 像这样: ```json title="config.json" { diff --git a/docs/i18n/zh-CN/docusaurus-plugin-content-docs/current/customize/tutorials/llama3.1.md b/docs/i18n/zh-CN/docusaurus-plugin-content-docs/current/customize/tutorials/llama3.1.md index 126be8f502..dc285627a3 100644 --- a/docs/i18n/zh-CN/docusaurus-plugin-content-docs/current/customize/tutorials/llama3.1.md +++ b/docs/i18n/zh-CN/docusaurus-plugin-content-docs/current/customize/tutorials/llama3.1.md @@ -73,10 +73,10 @@ Together AI 提供开源模型的快速和可信任的推理。你可以以良 ## Novita AI -[Novita AI](https://novita.ai) 提供了一个经济实惠、可靠且简单的推理平台。你可以以良好的速度运行 405b 模型。 +[Novita AI](https://novita.ai?utm_source=github_continuedev&utm_medium=github_readme&utm_campaign=github_link) 提供了一个经济实惠、可靠且简单的推理平台。你可以以良好的速度运行 405b 模型。 -1. 创建账号 [在这里](https://novita.ai/llm-api?utm_source=github_continuedev&utm_medium=github_readme&utm_campaign=link) -2. 复制[Key Management](https://novita.ai/settings/key-management)中的你的 API key +1. 创建账号 [在这里](https://novita.ai/user/login?&redirect=/&utm_source=github_continuedev&utm_medium=github_readme&utm_campaign=github_link) +2. 复制[Key Management](https://novita.ai/settings/key-management?utm_source=github_continuedev&utm_medium=github_readme&utm_campaign=github_link)中的你的 API key 3. 更新你的 Continue 配置文件,像这样: ```json title="config.json" diff --git a/extensions/vscode/config_schema.json b/extensions/vscode/config_schema.json index 76eedf998f..3ae0e3cc5e 100644 --- a/extensions/vscode/config_schema.json +++ b/extensions/vscode/config_schema.json @@ -223,7 +223,7 @@ "### Bedrock Imported Models\nTo get started with Bedrock you need to sign up on AWS [here](https://aws.amazon.com/bedrock)", "### Sagemaker\nSagemaker is AWS' machine learning platform.", "### Together\nTogether is a hosted service that provides extremely fast streaming of open-source language models. To get started with Together:\n1. Obtain an API key from [here](https://together.ai)\n2. Paste below\n3. Select a model preset\n> [Reference](https://docs.continue.dev/reference/Model%20Providers/togetherllm)", - "### Novita AI\n[Novita AI](https://novita.ai) offers an affordable, reliable, and simple inference platform with scalable [LLM APIs](https://novita.ai/docs/model-api/reference/introduction.html), empowering developers to build AI applications. To get started with Novita AI:\n1. Obtain an API key from [here](https://novita.ai/settings/key-management)\n2. Paste below\n3. Select a model preset\n> [Reference](https://docs.continue.dev/reference/Model%20Providers/novitallm)", + "### Novita AI\n[Novita AI](https://novita.ai?utm_source=github_continuedev&utm_medium=github_readme&utm_campaign=github_link) offers an affordable, reliable, and simple inference platform with scalable [LLM APIs](https://novita.ai/docs/model-api/reference/introduction.html), empowering developers to build AI applications. To get started with Novita AI:\n1. Obtain an API key from [here](https://novita.ai/settings/key-management?utm_source=github_continuedev&utm_medium=github_readme&utm_campaign=github_link)\n2. Paste below\n3. Select a model preset\n> [Reference](https://docs.continue.dev/reference/Model%20Providers/novita)", "### Ollama\nTo get started with Ollama, follow these steps:\n1. Download from [ollama.ai](https://ollama.ai/) and open the application\n2. Open a terminal and run `ollama run `. Example model names are `codellama:7b-instruct` or `llama2:7b-text`. You can find the full list [here](https://ollama.ai/library).\n3. Make sure that the model name used in step 2 is the same as the one in config.json (e.g. `model=\"codellama:7b-instruct\"`)\n4. Once the model has finished downloading, you can start asking questions through Continue.\n> [Reference](https://docs.continue.dev/reference/Model%20Providers/ollama)", "### Huggingface TGI\n\n> [Reference](https://docs.continue.dev/reference/Model%20Providers/huggingfacetgi)", "### Huggingface Inference API\n\n> [Reference](https://docs.continue.dev/reference/Model%20Providers/huggingfaceinferenceapi)", @@ -1014,8 +1014,8 @@ "type": "string" } ], - "markdownDescription": "Select a pre-defined option, or find an exact model string from Novita AI [here](https://novita.ai/llm-api?utm_source=github_continuedev&utm_medium=github_readme&utm_campaign=link).", - "x-intellij-html-description": "Select a pre-defined option, or find an exact model string from Novita AI here." + "markdownDescription": "Select a pre-defined option, or find an exact model string from Novita AI [here](https://novita.ai/llm-api?utm_source=github_continuedev&utm_medium=github_readme&utm_campaign=github_link).", + "x-intellij-html-description": "Select a pre-defined option, or find an exact model string from Novita AI here." } } } diff --git a/gui/src/pages/AddNewModel/configs/providers.ts b/gui/src/pages/AddNewModel/configs/providers.ts index 8ef4129eb1..49b30e409f 100644 --- a/gui/src/pages/AddNewModel/configs/providers.ts +++ b/gui/src/pages/AddNewModel/configs/providers.ts @@ -411,7 +411,7 @@ Select the \`GPT-4o\` model below to complete your provider configuration, but n description: "Use Novita AI API for extremely fast streaming of open-source models", icon: "novita.png", - longDescription: `[Novita AI](https://novita.ai) offers an affordable, reliable, and simple inference platform with scalable [LLM APIs](https://novita.ai/docs/model-api/reference/introduction.html), empowering developers to build AI applications. To get started with Novita AI:\n1. Obtain an API key from [here](https://novita.ai)\n2. Paste below\n3. Select a model preset`, + longDescription: `[Novita AI](https://novita.ai?utm_source=github_continuedev&utm_medium=github_readme&utm_campaign=github_link) offers an affordable, reliable, and simple inference platform with scalable [LLM APIs](https://novita.ai/docs/model-api/reference/introduction.html), empowering developers to build AI applications. To get started with Novita AI:\n1. Obtain an API key from [here](https://novita.ai/settings/key-management?utm_source=github_continuedev&utm_medium=github_readme&utm_campaign=github_link)\n2. Paste below\n3. Select a model preset`, tags: [ModelProviderTags.RequiresApiKey, ModelProviderTags.OpenSource], params: { apiKey: "", @@ -432,7 +432,7 @@ Select the \`GPT-4o\` model below to complete your provider configuration, but n p.params.contextLength = 4096; return p; }), - apiKeyUrl: "https://novita.ai/settings/key-management", + apiKeyUrl: "https://novita.ai/settings/key-management?utm_source=github_continuedev&utm_medium=github_readme&utm_campaign=github_link", }, gemini: { title: "Google Gemini API", From 26a847cd120926730f6c687bfb782128bf1085f7 Mon Sep 17 00:00:00 2001 From: Nate Date: Tue, 24 Dec 2024 10:54:40 -0500 Subject: [PATCH 29/99] file stats method --- core/index.d.ts | 12 +++++++++++- core/indexing/CodebaseIndexer.test.ts | 7 +++---- core/indexing/CodebaseIndexer.ts | 6 +++--- core/indexing/refreshIndex.ts | 9 ++++----- core/indexing/types.ts | 4 ---- core/protocol/ide.ts | 3 ++- core/protocol/messenger/messageIde.ts | 7 ++++--- core/protocol/messenger/reverseMessageIde.ts | 4 ++-- core/util/filesystem.ts | 14 ++++++++------ .../constants/MessageTypes.kt | 2 +- .../continue/IdeProtocolClient.kt | 8 ++++---- .../continue/IntelliJIde.kt | 4 ++-- .../continueintellijextension/protocol/ide.kt | 2 +- .../continuedev/continueintellijextension/types.kt | 7 ++++++- extensions/vscode/src/VsCodeIde.ts | 14 ++++++++------ extensions/vscode/src/extension/VsCodeMessenger.ts | 4 ++-- 16 files changed, 61 insertions(+), 46 deletions(-) diff --git a/core/index.d.ts b/core/index.d.ts index f9e6aa0e7c..d916a19f11 100644 --- a/core/index.d.ts +++ b/core/index.d.ts @@ -582,6 +582,16 @@ export interface IdeSettings { enableDebugLogs: boolean; } +export interface FileStats { + size: number; + lastModified: number; +} + +/** Map of file name to stats */ +export type FileStatsMap = { + [path: string]: FileStats; +}; + export interface IDE { getIdeInfo(): Promise; @@ -665,7 +675,7 @@ export interface IDE { listDir(dir: string): Promise<[string, FileType][]>; - getLastModified(files: string[]): Promise<{ [path: string]: number }>; + getFileStats(files: string[]): Promise; getGitHubAuthToken(args: GetGhTokenArgs): Promise; diff --git a/core/indexing/CodebaseIndexer.test.ts b/core/indexing/CodebaseIndexer.test.ts index 7db40d75ed..211bc37329 100644 --- a/core/indexing/CodebaseIndexer.test.ts +++ b/core/indexing/CodebaseIndexer.test.ts @@ -1,7 +1,7 @@ +import { jest } from "@jest/globals"; import { execSync } from "node:child_process"; import fs from "node:fs"; import path from "path"; -import { jest } from "@jest/globals"; import { ContinueServerClient } from "../continueServer/stubs/client.js"; import { testConfigHandler, testIde } from "../test/fixtures.js"; @@ -14,13 +14,12 @@ import { } from "../test/testDir.js"; import { getIndexSqlitePath } from "../util/paths.js"; +import { pathToFileURL } from "node:url"; import { CodebaseIndexer, PauseToken } from "./CodebaseIndexer.js"; import { getComputeDeleteAddRemove } from "./refreshIndex.js"; import { TestCodebaseIndex } from "./TestCodebaseIndex.js"; import { CodebaseIndex } from "./types.js"; import { walkDir } from "./walkDir.js"; -import { pathToFileURL } from "node:url"; -import { joinPathsToUri } from "../util/uri.js"; jest.useFakeTimers(); @@ -120,7 +119,7 @@ describe("CodebaseIndexer", () => { async function getIndexPlan() { const workspaceFiles = await walkDir(TEST_DIR, testIde); const [tag] = await testIde.getTags(testIndex.artifactId); - const stats = await testIde.getLastModified(workspaceFiles); + const stats = await testIde.getFileStats(workspaceFiles); const [results, lastUpdated, markComplete] = await getComputeDeleteAddRemove( diff --git a/core/indexing/CodebaseIndexer.ts b/core/indexing/CodebaseIndexer.ts index 4e8355c9be..e309549eb4 100644 --- a/core/indexing/CodebaseIndexer.ts +++ b/core/indexing/CodebaseIndexer.ts @@ -6,6 +6,7 @@ import { IDE, IndexingProgressUpdate, IndexTag } from "../index.js"; import { extractMinimalStackTraceInfo } from "../util/extractMinimalStackTraceInfo.js"; import { getIndexSqlitePath, getLanceDbPath } from "../util/paths.js"; +import { findUriInDirs, getUriPathBasename } from "../util/uri.js"; import { ChunkCodebaseIndex } from "./chunk/ChunkCodebaseIndex.js"; import { CodeSnippetsCodebaseIndex } from "./CodeSnippetsIndex.js"; import { FullTextSearchCodebaseIndex } from "./FullTextSearchCodebaseIndex.js"; @@ -17,7 +18,6 @@ import { RefreshIndexResults, } from "./types.js"; import { walkDirAsync } from "./walkDir.js"; -import { findUriInDirs, getUriPathBasename } from "../util/uri.js"; export class PauseToken { constructor(private _paused: boolean) {} @@ -110,7 +110,7 @@ export class CodebaseIndexer { const branch = await this.ide.getBranch(foundInDir); const repoName = await this.ide.getRepoName(foundInDir); const indexesToBuild = await this.getIndexesToBuild(); - const stats = await this.ide.getLastModified([file]); + const stats = await this.ide.getFileStats([file]); for (const index of indexesToBuild) { const tag = { directory: foundInDir, @@ -389,7 +389,7 @@ export class CodebaseIndexer { branch: string, repoName: string | undefined, ): AsyncGenerator { - const stats = await this.ide.getLastModified(files); + const stats = await this.ide.getFileStats(files); const indexesToBuild = await this.getIndexesToBuild(); let completedIndexCount = 0; let progress = 0; diff --git a/core/indexing/refreshIndex.ts b/core/indexing/refreshIndex.ts index fc71bb0466..30fb048cc9 100644 --- a/core/indexing/refreshIndex.ts +++ b/core/indexing/refreshIndex.ts @@ -5,13 +5,12 @@ import plimit from "p-limit"; import { open, type Database } from "sqlite"; import sqlite3 from "sqlite3"; -import { IndexTag, IndexingProgressUpdate } from "../index.js"; +import { FileStatsMap, IndexTag, IndexingProgressUpdate } from "../index.js"; import { getIndexSqlitePath } from "../util/paths.js"; import { CodebaseIndex, IndexResultType, - LastModifiedMap, MarkCompleteCallback, PathAndCacheKey, RefreshIndexResults, @@ -129,7 +128,7 @@ enum AddRemoveResultType { async function getAddRemoveForTag( tag: IndexTag, - currentFiles: LastModifiedMap, + currentFiles: FileStatsMap, readFile: (path: string) => Promise, ): Promise< [ @@ -157,7 +156,7 @@ async function getAddRemoveForTag( remove.push(pathAndCacheKey); } else { // Exists in old and new, so determine whether it was updated - if (lastUpdated < files[item.path]) { + if (lastUpdated < files[item.path].lastModified) { // Change was made after last update const newHash = calculateHash(await readFile(pathAndCacheKey.path)); if (pathAndCacheKey.cacheKey !== newHash) { @@ -345,7 +344,7 @@ function mapIndexResultTypeToAddRemoveResultType( export async function getComputeDeleteAddRemove( tag: IndexTag, - currentFiles: LastModifiedMap, + currentFiles: FileStatsMap, readFile: (path: string) => Promise, repoName: string | undefined, ): Promise<[RefreshIndexResults, PathAndCacheKey[], MarkCompleteCallback]> { diff --git a/core/indexing/types.ts b/core/indexing/types.ts index 3e3e287b68..bc4183aeae 100644 --- a/core/indexing/types.ts +++ b/core/indexing/types.ts @@ -36,8 +36,4 @@ export type RefreshIndexResults = { removeTag: PathAndCacheKey[]; }; -export type LastModifiedMap = { - [path: string]: number; -}; - export type RefreshIndex = (tag: IndexTag) => Promise; diff --git a/core/protocol/ide.ts b/core/protocol/ide.ts index 667ebd7b21..4df3cb95b0 100644 --- a/core/protocol/ide.ts +++ b/core/protocol/ide.ts @@ -3,6 +3,7 @@ import { ControlPlaneSessionInfo } from "../control-plane/client"; import type { ContinueRcJson, DiffLine, + FileStatsMap, FileType, IDE, IdeInfo, @@ -84,7 +85,7 @@ export type ToIdeFromWebviewOrCoreProtocol = { ]; getGitRootPath: [{ dir: string }, string | undefined]; listDir: [{ dir: string }, [string, FileType][]]; - getLastModified: [{ files: string[] }, { [path: string]: number }]; + getFileStats: [{ files: string[] }, FileStatsMap]; gotoDefinition: [{ location: Location }, RangeInFile[]]; diff --git a/core/protocol/messenger/messageIde.ts b/core/protocol/messenger/messageIde.ts index e2cd50c781..9f46602c1e 100644 --- a/core/protocol/messenger/messageIde.ts +++ b/core/protocol/messenger/messageIde.ts @@ -1,8 +1,9 @@ -import { GetGhTokenArgs, ToIdeFromWebviewOrCoreProtocol } from "../ide"; import { FromIdeProtocol } from ".."; +import { GetGhTokenArgs, ToIdeFromWebviewOrCoreProtocol } from "../ide"; import type { ContinueRcJson, + FileStatsMap, FileType, IDE, IdeInfo, @@ -43,8 +44,8 @@ export class MessageIde implements IDE { getGitHubAuthToken(args: GetGhTokenArgs): Promise { return this.request("getGitHubAuthToken", args); } - getLastModified(files: string[]): Promise<{ [path: string]: number }> { - return this.request("getLastModified", { files }); + getFileStats(files: string[]): Promise { + return this.request("getFileStats", { files }); } getGitRootPath(dir: string): Promise { return this.request("getGitRootPath", { dir }); diff --git a/core/protocol/messenger/reverseMessageIde.ts b/core/protocol/messenger/reverseMessageIde.ts index e3f97ea4fb..620055a75e 100644 --- a/core/protocol/messenger/reverseMessageIde.ts +++ b/core/protocol/messenger/reverseMessageIde.ts @@ -38,8 +38,8 @@ export class ReverseMessageIde { return this.ide.getGitHubAuthToken(data); }); - this.on("getLastModified", (data) => { - return this.ide.getLastModified(data.files); + this.on("getFileStats", (data) => { + return this.ide.getFileStats(data.files); }); this.on("getGitRootPath", (data) => { diff --git a/core/util/filesystem.ts b/core/util/filesystem.ts index d0821b6ef7..45ac7a7c5f 100644 --- a/core/util/filesystem.ts +++ b/core/util/filesystem.ts @@ -1,7 +1,9 @@ import * as fs from "node:fs"; +import { fileURLToPath } from "node:url"; import { ContinueRcJson, + FileStatsMap, FileType, IDE, IdeInfo, @@ -15,7 +17,6 @@ import { ToastType, } from "../index.js"; import { GetGhTokenArgs } from "../protocol/ide.js"; -import { fileURLToPath } from "node:url"; class FileSystemIde implements IDE { constructor(private readonly workspaceDir: string) {} @@ -51,15 +52,16 @@ class FileSystemIde implements IDE { async getGitHubAuthToken(args: GetGhTokenArgs): Promise { return undefined; } - async getLastModified( - fileUris: string[], - ): Promise<{ [path: string]: number }> { - const result: { [path: string]: number } = {}; + async getFileStats(fileUris: string[]): Promise { + const result: FileStatsMap = {}; for (const uri of fileUris) { try { const filepath = fileURLToPath(uri); const stats = fs.statSync(filepath); - result[uri] = stats.mtimeMs; + result[uri] = { + lastModified: stats.mtimeMs, + size: stats.size, + }; } catch (error) { console.error(`Error getting last modified time for ${uri}:`, error); } diff --git a/extensions/intellij/src/main/kotlin/com/github/continuedev/continueintellijextension/constants/MessageTypes.kt b/extensions/intellij/src/main/kotlin/com/github/continuedev/continueintellijextension/constants/MessageTypes.kt index 0374e9346b..b309cbbc68 100644 --- a/extensions/intellij/src/main/kotlin/com/github/continuedev/continueintellijextension/constants/MessageTypes.kt +++ b/extensions/intellij/src/main/kotlin/com/github/continuedev/continueintellijextension/constants/MessageTypes.kt @@ -39,7 +39,7 @@ class MessageTypes { "getRepoName", "listDir", "getGitRootPath", - "getLastModified", + "getFileStats", "insertAtCursor", "applyToFile", "getGitHubAuthToken", diff --git a/extensions/intellij/src/main/kotlin/com/github/continuedev/continueintellijextension/continue/IdeProtocolClient.kt b/extensions/intellij/src/main/kotlin/com/github/continuedev/continueintellijextension/continue/IdeProtocolClient.kt index 69e06e89e6..b4078beeac 100644 --- a/extensions/intellij/src/main/kotlin/com/github/continuedev/continueintellijextension/continue/IdeProtocolClient.kt +++ b/extensions/intellij/src/main/kotlin/com/github/continuedev/continueintellijextension/continue/IdeProtocolClient.kt @@ -215,13 +215,13 @@ class IdeProtocolClient( respond(null) } - "getLastModified" -> { + "getFileStats" -> { val params = Gson().fromJson( dataElement.toString(), - GetLastModifiedParams::class.java + GetFileStatsParams::class.java ) - val lastModifiedMap = ide.getLastModified(params.files) - respond(lastModifiedMap) + val fileStatsMap = ide.getFileStats(params.files) + respond(fileStatsMap) } "listDir" -> { diff --git a/extensions/intellij/src/main/kotlin/com/github/continuedev/continueintellijextension/continue/IntelliJIde.kt b/extensions/intellij/src/main/kotlin/com/github/continuedev/continueintellijextension/continue/IntelliJIde.kt index 154476f5e0..84ff3fc10c 100644 --- a/extensions/intellij/src/main/kotlin/com/github/continuedev/continueintellijextension/continue/IntelliJIde.kt +++ b/extensions/intellij/src/main/kotlin/com/github/continuedev/continueintellijextension/continue/IntelliJIde.kt @@ -519,9 +519,9 @@ class IntelliJIDE( return files } - override suspend fun getLastModified(files: List): Map { + override suspend fun getFileStats(files: List): Map { return files.associateWith { file -> - File(URI(file)).lastModified() + FileStats(File(URI(file)).lastModified(), File(URI(file)).length()) } } diff --git a/extensions/intellij/src/main/kotlin/com/github/continuedev/continueintellijextension/protocol/ide.kt b/extensions/intellij/src/main/kotlin/com/github/continuedev/continueintellijextension/protocol/ide.kt index 432ac4f6e9..4aa433f528 100644 --- a/extensions/intellij/src/main/kotlin/com/github/continuedev/continueintellijextension/protocol/ide.kt +++ b/extensions/intellij/src/main/kotlin/com/github/continuedev/continueintellijextension/protocol/ide.kt @@ -58,4 +58,4 @@ data class GetGitRootPathParams(val dir: String) data class ListDirParams(val dir: String) -data class GetLastModifiedParams(val files: List) \ No newline at end of file +data class GetFileStatsParams(val files: List) \ No newline at end of file diff --git a/extensions/intellij/src/main/kotlin/com/github/continuedev/continueintellijextension/types.kt b/extensions/intellij/src/main/kotlin/com/github/continuedev/continueintellijextension/types.kt index b1e051777b..1400e5c548 100644 --- a/extensions/intellij/src/main/kotlin/com/github/continuedev/continueintellijextension/types.kt +++ b/extensions/intellij/src/main/kotlin/com/github/continuedev/continueintellijextension/types.kt @@ -77,6 +77,11 @@ data class Account( val id: String ) +data class FileStats( + val lastModified: Long, + val size: Long +) + data class IdeSettings( val remoteConfigServerUrl: String?, val remoteConfigSyncPeriod: Int, @@ -183,7 +188,7 @@ interface IDE { // will serialize to `first and `second` rather than `0` and `1` like in JavaScript suspend fun listDir(dir: String): List> - suspend fun getLastModified(files: List): Map + suspend fun getFileStats(files: List): Map suspend fun getGitHubAuthToken(args: GetGhTokenArgs): String? diff --git a/extensions/vscode/src/VsCodeIde.ts b/extensions/vscode/src/VsCodeIde.ts index 17fb847894..faa60cfd4d 100644 --- a/extensions/vscode/src/VsCodeIde.ts +++ b/extensions/vscode/src/VsCodeIde.ts @@ -3,20 +3,20 @@ import { exec } from "node:child_process"; import { Range } from "core"; import { EXTENSION_NAME } from "core/control-plane/env"; -import { walkDir } from "core/indexing/walkDir"; import { GetGhTokenArgs } from "core/protocol/ide"; import { editConfigJson, getConfigJsonPath } from "core/util/paths"; import * as vscode from "vscode"; +import * as URI from "uri-js"; import { executeGotoProvider } from "./autocomplete/lsp"; import { Repository } from "./otherExtensions/git"; import { VsCodeIdeUtils } from "./util/ideUtils"; import { getExtensionUri, openEditorAndRevealRange } from "./util/vscode"; import { VsCodeWebviewProtocol } from "./webviewProtocol"; -import * as URI from "uri-js"; import type { ContinueRcJson, + FileStatsMap, FileType, IDE, IdeInfo, @@ -27,7 +27,6 @@ import type { RangeInFile, Thread, } from "core"; -import { findUriInDirs } from "core/util/uri"; class VsCodeIde implements IDE { ideUtils: VsCodeIdeUtils; @@ -274,12 +273,15 @@ class VsCodeIde implements IDE { ); } - async getLastModified(files: string[]): Promise<{ [path: string]: number }> { - const pathToLastModified: { [path: string]: number } = {}; + async getFileStats(files: string[]): Promise { + const pathToLastModified: FileStatsMap = {}; await Promise.all( files.map(async (file) => { const stat = await vscode.workspace.fs.stat(vscode.Uri.parse(file)); - pathToLastModified[file] = stat.mtime; + pathToLastModified[file] = { + lastModified: stat.mtime, + size: stat.size, + }; }), ); diff --git a/extensions/vscode/src/extension/VsCodeMessenger.ts b/extensions/vscode/src/extension/VsCodeMessenger.ts index d562fdb295..2b88fbca65 100644 --- a/extensions/vscode/src/extension/VsCodeMessenger.ts +++ b/extensions/vscode/src/extension/VsCodeMessenger.ts @@ -473,8 +473,8 @@ export class VsCodeMessenger { return await ide.gotoDefinition(msg.data.location); }); - this.onWebviewOrCore("getLastModified", async (msg) => { - return await ide.getLastModified(msg.data.files); + this.onWebviewOrCore("getFileStats", async (msg) => { + return await ide.getFileStats(msg.data.files); }); this.onWebviewOrCore("getGitRootPath", async (msg) => { From 157ac38aa3e5c4f2740a77279bba571817fc8588 Mon Sep 17 00:00:00 2001 From: Nate Date: Tue, 24 Dec 2024 11:12:00 -0500 Subject: [PATCH 30/99] indexing max file size --- core/indexing/refreshIndex.ts | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/core/indexing/refreshIndex.ts b/core/indexing/refreshIndex.ts index 30fb048cc9..34c1ac4a2a 100644 --- a/core/indexing/refreshIndex.ts +++ b/core/indexing/refreshIndex.ts @@ -126,6 +126,9 @@ enum AddRemoveResultType { Compute = "compute", } +// Don't attempt to index anything over 5MB +const MAX_FILE_SIZE_BYTES = 5 * 1024 * 1024; + async function getAddRemoveForTag( tag: IndexTag, currentFiles: FileStatsMap, @@ -141,6 +144,12 @@ async function getAddRemoveForTag( const newLastUpdatedTimestamp = Date.now(); const files = { ...currentFiles }; + for (const path in files) { + if (files[path].size > MAX_FILE_SIZE_BYTES) { + delete files[path]; + } + } + const saved = await getSavedItemsForTag(tag); const updateNewVersion: PathAndCacheKey[] = []; From 88e34fb69caae3750621f9f93253a7425535928f Mon Sep 17 00:00:00 2001 From: tomasz-io Date: Tue, 24 Dec 2024 08:36:46 -0800 Subject: [PATCH 31/99] fix: tip tap undo and redo --- gui/src/components/mainInput/handleMetaKeyIssues.ts | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/gui/src/components/mainInput/handleMetaKeyIssues.ts b/gui/src/components/mainInput/handleMetaKeyIssues.ts index 867203a14d..baf824bd68 100644 --- a/gui/src/components/mainInput/handleMetaKeyIssues.ts +++ b/gui/src/components/mainInput/handleMetaKeyIssues.ts @@ -48,6 +48,11 @@ export const handleVSCMetaKeyIssues = async ( x: () => handleCutOperation(text, editor), c: () => handleCopyOperation(text), v: () => handlePasteOperation(editor), + z: () => { + return e.shiftKey + ? handleRedoOperation(editor) + : handleUndoOperation(editor); + }, }; if (e.key in handlers) { @@ -130,3 +135,11 @@ export const handlePasteOperation = async (editor: Editor) => { document.execCommand("paste"); } }; + +export const handleUndoOperation = async (editor: Editor) => { + editor.commands.undo(); +}; + +export const handleRedoOperation = async (editor: Editor) => { + editor.commands.redo(); +}; From f3de9d85b0603920a240a2ef5c11098772d8b5b1 Mon Sep 17 00:00:00 2001 From: Nate Date: Tue, 24 Dec 2024 11:38:06 -0500 Subject: [PATCH 32/99] bump package.json version --- extensions/vscode/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extensions/vscode/package.json b/extensions/vscode/package.json index 8470f84fdb..4cb0237171 100644 --- a/extensions/vscode/package.json +++ b/extensions/vscode/package.json @@ -2,7 +2,7 @@ "name": "continue", "icon": "media/icon.png", "author": "Continue Dev, Inc", - "version": "0.9.246", + "version": "0.9.247", "repository": { "type": "git", "url": "https://github.com/continuedev/continue" From fb2740d5d4a9a752fbaa87bfa15b277d3b6b38eb Mon Sep 17 00:00:00 2001 From: tomasz-io Date: Tue, 24 Dec 2024 08:50:17 -0800 Subject: [PATCH 33/99] test: chat input undo / redo --- .../e2e/tests/KeyboardShortcuts.test.ts | 57 ++++++++++++++++++- 1 file changed, 55 insertions(+), 2 deletions(-) diff --git a/extensions/vscode/e2e/tests/KeyboardShortcuts.test.ts b/extensions/vscode/e2e/tests/KeyboardShortcuts.test.ts index cc3f6691d3..c737156127 100644 --- a/extensions/vscode/e2e/tests/KeyboardShortcuts.test.ts +++ b/extensions/vscode/e2e/tests/KeyboardShortcuts.test.ts @@ -8,13 +8,13 @@ import { WebView, WebElement, until, + Key, } from "vscode-extension-tester"; import { DEFAULT_TIMEOUT } from "../constants"; import { GUISelectors } from "../selectors/GUI.selectors"; import { GUIActions } from "../actions/GUI.actions"; import { expect } from "chai"; import { TestUtils } from "../TestUtils"; -import { Test } from "mocha"; import { KeyboardShortcutsActions } from "../actions/KeyboardShortcuts.actions"; describe("Cmd+L Shortcut Test", () => { @@ -49,6 +49,59 @@ describe("Cmd+L Shortcut Test", () => { await new EditorView().closeAllEditors(); }); + it("Should correctly undo and redo using keyboard shortcuts when writing a chat message", async () => { + await GUIActions.executeFocusContinueInputShortcut(driver); + ({ view } = await GUIActions.switchToReactIframe()); + const chatInput = await TestUtils.waitForSuccess(async () => { + return GUISelectors.getMessageInputFieldAtIndex(view, 0); + }); + + await chatInput.sendKeys("HELLO "); + await TestUtils.waitForTimeout(DEFAULT_TIMEOUT.XS); + + await chatInput.sendKeys("WORLD "); + await TestUtils.waitForTimeout(DEFAULT_TIMEOUT.XS); + + await chatInput.sendKeys("HELLO "); + await TestUtils.waitForTimeout(DEFAULT_TIMEOUT.XS); + + await chatInput.sendKeys("CONTINUE"); + await TestUtils.waitForTimeout(DEFAULT_TIMEOUT.XS); + + await chatInput.sendKeys(Key.COMMAND + "z"); + await driver.wait( + until.elementTextIs(chatInput, "HELLO WORLD"), + DEFAULT_TIMEOUT.SM, + ); + + await chatInput.sendKeys(Key.COMMAND + "z"); + await driver.wait(until.elementTextIs(chatInput, ""), DEFAULT_TIMEOUT.SM); + + await chatInput.sendKeys(Key.SHIFT + Key.COMMAND + "z"); + await driver.wait( + until.elementTextIs(chatInput, "HELLO"), + DEFAULT_TIMEOUT.SM, + ); + + await chatInput.sendKeys(Key.SHIFT + Key.COMMAND + "z"); + await driver.wait( + until.elementTextIs(chatInput, "HELLO WORLD"), + DEFAULT_TIMEOUT.SM, + ); + + await chatInput.sendKeys(Key.SHIFT + Key.COMMAND + "z"); + await driver.wait( + until.elementTextIs(chatInput, "HELLO WORLD HELLO"), + DEFAULT_TIMEOUT.SM, + ); + + await chatInput.sendKeys(Key.SHIFT + Key.COMMAND + "z"); + await driver.wait( + until.elementTextIs(chatInput, "HELLO WORLD HELLO CONTINUE"), + DEFAULT_TIMEOUT.SM, + ); + }).timeout(DEFAULT_TIMEOUT.XL); + it("Should not create a code block when Cmd+L is pressed without text highlighted", async () => { const text = "Hello, world!"; @@ -118,7 +171,7 @@ describe("Cmd+L Shortcut Test", () => { await driver.wait(until.elementIsNotVisible(textInput), DEFAULT_TIMEOUT.XS); expect(await textInput.isDisplayed()).to.equal(false); - }).timeout(DEFAULT_TIMEOUT.XL * 1000); + }).timeout(DEFAULT_TIMEOUT.XL); it("Should create a code block when Cmd+L is pressed with text highlighted", async () => { const text = "Hello, world!"; From 9ab29ea76cc597fba2fd6a8adfffce49033ef6e9 Mon Sep 17 00:00:00 2001 From: tomasz-io Date: Tue, 24 Dec 2024 10:32:43 -0800 Subject: [PATCH 34/99] improv: naming --- extensions/vscode/e2e/tests/KeyboardShortcuts.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extensions/vscode/e2e/tests/KeyboardShortcuts.test.ts b/extensions/vscode/e2e/tests/KeyboardShortcuts.test.ts index c737156127..908bfb6c4e 100644 --- a/extensions/vscode/e2e/tests/KeyboardShortcuts.test.ts +++ b/extensions/vscode/e2e/tests/KeyboardShortcuts.test.ts @@ -17,7 +17,7 @@ import { expect } from "chai"; import { TestUtils } from "../TestUtils"; import { KeyboardShortcutsActions } from "../actions/KeyboardShortcuts.actions"; -describe("Cmd+L Shortcut Test", () => { +describe("Keyboard Shortcuts", () => { let driver: WebDriver; let editor: TextEditor; let view: WebView; From e45aa164c64a52936c7dd1c0467548aadf821639 Mon Sep 17 00:00:00 2001 From: tomasz-io Date: Tue, 24 Dec 2024 10:37:26 -0800 Subject: [PATCH 35/99] fix: keyboard shortcuts test on linux --- .../vscode/e2e/tests/KeyboardShortcuts.test.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/extensions/vscode/e2e/tests/KeyboardShortcuts.test.ts b/extensions/vscode/e2e/tests/KeyboardShortcuts.test.ts index 908bfb6c4e..0191d1d636 100644 --- a/extensions/vscode/e2e/tests/KeyboardShortcuts.test.ts +++ b/extensions/vscode/e2e/tests/KeyboardShortcuts.test.ts @@ -68,34 +68,34 @@ describe("Keyboard Shortcuts", () => { await chatInput.sendKeys("CONTINUE"); await TestUtils.waitForTimeout(DEFAULT_TIMEOUT.XS); - await chatInput.sendKeys(Key.COMMAND + "z"); + await chatInput.sendKeys(TestUtils.osControlKey + "z"); await driver.wait( until.elementTextIs(chatInput, "HELLO WORLD"), DEFAULT_TIMEOUT.SM, ); - await chatInput.sendKeys(Key.COMMAND + "z"); + await chatInput.sendKeys(TestUtils.osControlKey + "z"); await driver.wait(until.elementTextIs(chatInput, ""), DEFAULT_TIMEOUT.SM); - await chatInput.sendKeys(Key.SHIFT + Key.COMMAND + "z"); + await chatInput.sendKeys(Key.SHIFT + TestUtils.osControlKey + "z"); await driver.wait( until.elementTextIs(chatInput, "HELLO"), DEFAULT_TIMEOUT.SM, ); - await chatInput.sendKeys(Key.SHIFT + Key.COMMAND + "z"); + await chatInput.sendKeys(Key.SHIFT + TestUtils.osControlKey + "z"); await driver.wait( until.elementTextIs(chatInput, "HELLO WORLD"), DEFAULT_TIMEOUT.SM, ); - await chatInput.sendKeys(Key.SHIFT + Key.COMMAND + "z"); + await chatInput.sendKeys(Key.SHIFT + TestUtils.osControlKey + "z"); await driver.wait( until.elementTextIs(chatInput, "HELLO WORLD HELLO"), DEFAULT_TIMEOUT.SM, ); - await chatInput.sendKeys(Key.SHIFT + Key.COMMAND + "z"); + await chatInput.sendKeys(Key.SHIFT + TestUtils.osControlKey + "z"); await driver.wait( until.elementTextIs(chatInput, "HELLO WORLD HELLO CONTINUE"), DEFAULT_TIMEOUT.SM, From 9edd5e3e241862d8189cf17332f7c3a2933125c4 Mon Sep 17 00:00:00 2001 From: Nate Date: Tue, 24 Dec 2024 14:03:23 -0500 Subject: [PATCH 36/99] Use ConfigResult everywhere --- binary/test/binary.test.ts | 24 ++++----- core/autocomplete/CompletionProvider.ts | 6 +-- core/config/ConfigHandler.test.ts | 4 +- core/config/ConfigHandler.ts | 32 +++++------- core/config/ProfileLifecycleManager.ts | 37 ++++++------- core/context/mcp/index.ts | 2 +- .../providers/_context-providers.test.ts | 9 ++-- core/core.ts | 52 +++++++++++++------ core/indexing/CodebaseIndexer.ts | 10 ++-- core/indexing/LanceDbIndex.test.ts | 7 ++- core/indexing/docs/DocsCrawler.skip.ts | 3 +- core/indexing/docs/DocsService.skip.ts | 4 +- core/indexing/docs/DocsService.ts | 11 ++-- core/protocol/core.ts | 8 ++- core/protocol/webview.ts | 6 ++- extensions/vscode/src/commands.ts | 7 ++- .../vscode/src/extension/VsCodeExtension.ts | 6 +-- .../vscode/src/extension/VsCodeMessenger.ts | 7 ++- .../codeLens/registerAllCodeLensProviders.ts | 12 +++-- .../src/quickEdit/QuickEditQuickPick.ts | 16 ++++-- .../vscode/src/util/loadAutocompleteModel.ts | 6 ++- gui/src/hooks/useSetup.ts | 19 ++++--- gui/src/redux/slices/configSlice.ts | 16 ++++-- 23 files changed, 189 insertions(+), 115 deletions(-) diff --git a/binary/test/binary.test.ts b/binary/test/binary.test.ts index f84b23a2d7..bd85049861 100644 --- a/binary/test/binary.test.ts +++ b/binary/test/binary.test.ts @@ -1,8 +1,8 @@ import { SerializedContinueConfig } from "core"; // import Mock from "core/llm/llms/Mock.js"; import { FromIdeProtocol, ToIdeProtocol } from "core/protocol/index.js"; -import FileSystemIde from "core/util/filesystem"; import { IMessenger } from "core/protocol/messenger"; +import FileSystemIde from "core/util/filesystem"; import fs from "fs"; import { ChildProcessWithoutNullStreams, @@ -186,10 +186,11 @@ describe("Test Suite", () => { }); it("should return valid config object", async () => { - const { config } = await messenger.request( + const { result } = await messenger.request( "config/getSerializedProfileInfo", undefined, ); + const { config } = result; expect(config).toHaveProperty("models"); expect(config).toHaveProperty("embeddingsProvider"); expect(config).toHaveProperty("contextProviders"); @@ -229,18 +230,17 @@ describe("Test Suite", () => { await messenger.request("config/addModel", { model, }); - const { config } = await messenger.request( - "config/getSerializedProfileInfo", - undefined, - ); - expect(config.models.some((m) => m.title === model.title)).toBe(true); + const { + result: { config }, + } = await messenger.request("config/getSerializedProfileInfo", undefined); + + expect(config!.models.some((m) => m.title === model.title)).toBe(true); await messenger.request("config/deleteModel", { title: model.title }); - const { config: configAfterDelete } = await messenger.request( - "config/getSerializedProfileInfo", - undefined, - ); - expect(configAfterDelete.models.some((m) => m.title === model.title)).toBe( + const { + result: { config: configAfterDelete }, + } = await messenger.request("config/getSerializedProfileInfo", undefined); + expect(configAfterDelete!.models.some((m) => m.title === model.title)).toBe( false, ); }); diff --git a/core/autocomplete/CompletionProvider.ts b/core/autocomplete/CompletionProvider.ts index 479b9061c9..77aadbdf53 100644 --- a/core/autocomplete/CompletionProvider.ts +++ b/core/autocomplete/CompletionProvider.ts @@ -3,11 +3,9 @@ import { TRIAL_FIM_MODEL } from "../config/onboarding.js"; import { IDE, ILLM } from "../index.js"; import OpenAI from "../llm/llms/OpenAI.js"; import { DEFAULT_AUTOCOMPLETE_OPTS } from "../util/parameters.js"; -import { PosthogFeatureFlag, Telemetry } from "../util/posthog.js"; import { shouldCompleteMultiline } from "./classification/shouldCompleteMultiline.js"; import { ContextRetrievalService } from "./context/ContextRetrievalService.js"; -// @prettier-ignore import { BracketMatchingService } from "./filtering/BracketMatchingService.js"; import { CompletionStreamer } from "./generation/CompletionStreamer.js"; @@ -123,10 +121,10 @@ export class CompletionProvider { } private async _getAutocompleteOptions() { - const config = await this.configHandler.loadConfig(); + const { config } = await this.configHandler.loadConfig(); const options = { ...DEFAULT_AUTOCOMPLETE_OPTS, - ...config.tabAutocompleteOptions, + ...config?.tabAutocompleteOptions, }; return options; } diff --git a/core/config/ConfigHandler.test.ts b/core/config/ConfigHandler.test.ts index e7d90f492e..5a25e71fc9 100644 --- a/core/config/ConfigHandler.test.ts +++ b/core/config/ConfigHandler.test.ts @@ -18,8 +18,8 @@ describe.skip("Test the ConfigHandler and E2E config loading", () => { }); test("should load the default config successfully", async () => { - const config = await testConfigHandler.loadConfig(); - expect(config.models.length).toBe(defaultConfig.models.length); + const result = await testConfigHandler.loadConfig(); + expect(result.config!.models.length).toBe(defaultConfig.models.length); }); test.skip("should add a system message from config.ts", async () => { diff --git a/core/config/ConfigHandler.ts b/core/config/ConfigHandler.ts index 0106a96c50..9ef3cc8d29 100644 --- a/core/config/ConfigHandler.ts +++ b/core/config/ConfigHandler.ts @@ -60,13 +60,13 @@ export class ConfigHandler { // Always load local profile immediately in case control plane doesn't load try { - this.loadConfig(); + void this.loadConfig(); } catch (e) { console.error("Failed to load config: ", e); } // Load control plane profiles - this.fetchControlPlaneProfiles(); + void this.fetchControlPlaneProfiles(); } // This will be the local profile @@ -146,12 +146,8 @@ export class ConfigHandler { async setSelectedProfile(profileId: string) { this.selectedProfileId = profileId; - const newConfig = await this.loadConfig(); - this.notifyConfigListeners({ - config: newConfig, - errors: undefined, - configLoadInterrupted: false, - }); + const result = await this.loadConfig(); + this.notifyConfigListeners(result); const selectedProfiles = this.globalContext.get("lastSelectedProfileForWorkspace") ?? {}; selectedProfiles[await this.getWorkspaceId()] = profileId; @@ -170,7 +166,7 @@ export class ConfigHandler { // Automatically refresh config when Continue-related IDE (e.g. VS Code) settings are changed updateIdeSettings(ideSettings: IdeSettings) { this.ideSettingsPromise = Promise.resolve(ideSettings); - this.reloadConfig(); + void this.reloadConfig(); } updateControlPlaneSessionInfo( @@ -236,15 +232,15 @@ export class ConfigHandler { return this.profiles.map((p) => p.profileDescription); } - async loadConfig(): Promise { - return ( - await this.currentProfile.loadConfig(this.additionalContextProviders) - ).config!; // <-- TODO + async loadConfig(): Promise> { + return await this.currentProfile.loadConfig( + this.additionalContextProviders, + ); } async llmFromTitle(title?: string): Promise { - const config = await this.loadConfig(); - const model = config.models.find((m) => m.title === title); + const { config } = await this.loadConfig(); + const model = config?.models.find((m) => m.title === title); if (!model) { if (title === ONBOARDING_LOCAL_MODEL_TITLE) { // Special case, make calls to Ollama before we have it in the config @@ -252,8 +248,8 @@ export class ConfigHandler { model: LOCAL_ONBOARDING_CHAT_MODEL, }); return ollama; - } else if (config.models.length > 0) { - return config.models[0]; + } else if (config?.models?.length) { + return config?.models[0]; } throw new Error("No model found"); @@ -264,6 +260,6 @@ export class ConfigHandler { registerCustomContextProvider(contextProvider: IContextProvider) { this.additionalContextProviders.push(contextProvider); - this.reloadConfig(); + void this.reloadConfig(); } } diff --git a/core/config/ProfileLifecycleManager.ts b/core/config/ProfileLifecycleManager.ts index 7b595c0e62..c5ae16f913 100644 --- a/core/config/ProfileLifecycleManager.ts +++ b/core/config/ProfileLifecycleManager.ts @@ -13,8 +13,8 @@ export interface ProfileDescription { } export class ProfileLifecycleManager { - private savedConfig: ConfigResult | undefined; - private savedBrowserConfig?: BrowserSerializedContinueConfig; + private savedConfigResult: ConfigResult | undefined; + private savedBrowserConfigResult?: ConfigResult; private pendingConfigPromise?: Promise>; constructor(private readonly profileLoader: IProfileLoader) {} @@ -35,15 +35,15 @@ export class ProfileLifecycleManager { } clearConfig() { - this.savedConfig = undefined; - this.savedBrowserConfig = undefined; + this.savedConfigResult = undefined; + this.savedBrowserConfigResult = undefined; this.pendingConfigPromise = undefined; } // Clear saved config and reload async reloadConfig(): Promise> { - this.savedConfig = undefined; - this.savedBrowserConfig = undefined; + this.savedConfigResult = undefined; + this.savedBrowserConfigResult = undefined; this.pendingConfigPromise = undefined; return this.loadConfig([], true); @@ -55,8 +55,8 @@ export class ProfileLifecycleManager { ): Promise> { // If we already have a config, return it if (!forceReload) { - if (this.savedConfig) { - return this.savedConfig; + if (this.savedConfigResult) { + return this.savedConfigResult; } else if (this.pendingConfigPromise) { return this.pendingConfigPromise; } @@ -72,7 +72,7 @@ export class ProfileLifecycleManager { result.config.contextProviders ?? [] ).concat(additionalContextProviders); - this.savedConfig = result; + this.savedConfigResult = result; resolve(result); } else if (result.errors) { reject( @@ -82,15 +82,17 @@ export class ProfileLifecycleManager { }); // Wait for the config promise to resolve - this.savedConfig = await this.pendingConfigPromise; + this.savedConfigResult = await this.pendingConfigPromise; this.pendingConfigPromise = undefined; - return this.savedConfig; + return this.savedConfigResult; } async getSerializedConfig( additionalContextProviders: IContextProvider[], ): Promise> { - if (!this.savedBrowserConfig) { + if (this.savedBrowserConfigResult) { + return this.savedBrowserConfigResult; + } else { const result = await this.loadConfig(additionalContextProviders); if (!result.config) { return { @@ -98,12 +100,11 @@ export class ProfileLifecycleManager { config: undefined, }; } - this.savedBrowserConfig = finalToBrowserConfig(result.config); + const serializedConfig = finalToBrowserConfig(result.config); + return { + ...result, + config: serializedConfig, + }; } - return { - config: this.savedBrowserConfig, - errors: [], - configLoadInterrupted: false, - }; } } diff --git a/core/context/mcp/index.ts b/core/context/mcp/index.ts index 9a14e49cd3..bcf11492f9 100644 --- a/core/context/mcp/index.ts +++ b/core/context/mcp/index.ts @@ -132,7 +132,7 @@ class MCPConnection { if (!error.message.startsWith("StdioClientTransport already started")) { return { fatal: false, - message: `Failed to connect to MCP ${mcpId}: ${error.message}`, + message: `Failed to connect to MCP: ${error.message}`, }; } } diff --git a/core/context/providers/_context-providers.test.ts b/core/context/providers/_context-providers.test.ts index 7e0fdbd731..24cac2c444 100644 --- a/core/context/providers/_context-providers.test.ts +++ b/core/context/providers/_context-providers.test.ts @@ -1,15 +1,15 @@ import fetch from "node-fetch"; +import { contextProviderClassFromName } from "."; import { ContextProviderExtras, ContextProviderWithParams, IContextProvider, } from "../.."; import { ConfigHandler } from "../../config/ConfigHandler"; -import { contextProviderClassFromName } from "."; import { ControlPlaneClient } from "../../control-plane/client"; -import FileSystemIde from "../../util/filesystem"; import { TEST_DIR } from "../../test/testDir"; +import FileSystemIde from "../../util/filesystem"; const CONTEXT_PROVIDERS_TO_TEST: ContextProviderWithParams[] = [ { name: "diff", params: {} }, @@ -33,7 +33,10 @@ async function getContextProviderExtras( async (text) => {}, new ControlPlaneClient(Promise.resolve(undefined)), ); - const config = await configHandler.loadConfig(); + const { config } = await configHandler.loadConfig(); + if (!config) { + throw new Error("Config not found"); + } return { fullInput, diff --git a/core/core.ts b/core/core.ts index aa9c1eb5bf..14389910cb 100644 --- a/core/core.ts +++ b/core/core.ts @@ -65,7 +65,7 @@ export class Core { private abortedMessageIds: Set = new Set(); private async config() { - return this.configHandler.loadConfig(); + return (await this.configHandler.loadConfig()).config; } invoke( @@ -120,11 +120,9 @@ export class Core { ); this.configHandler.onConfigUpdate(async (result) => { - const serialized = await ( - await this.configHandler.getSerializedConfig() - ).config!; + const serializedResult = await this.configHandler.getSerializedConfig(); this.messenger.send("configUpdate", { - config: serialized, + result: serializedResult, profileId: this.configHandler.currentProfile.profileId, }); }); @@ -178,12 +176,12 @@ export class Core { }); const getLlm = async () => { - const config = await this.configHandler.loadConfig(); + const { config } = await this.configHandler.loadConfig(); const selected = this.globalContext.get("selectedTabAutocompleteModel"); return ( - config.tabAutocompleteModels?.find( + config?.tabAutocompleteModels?.find( (model) => model.title === selected, - ) ?? config.tabAutocompleteModels?.[0] + ) ?? config?.tabAutocompleteModels?.[0] ); }; this.completionProvider = new CompletionProvider( @@ -261,7 +259,7 @@ export class Core { on("config/newPromptFile", async (msg) => { await createNewPromptFileV2( this.ide, - (await this.config()).experimental?.promptPath, + (await this.config())?.experimental?.promptPath, ); await this.configHandler.reloadConfig(); }); @@ -272,7 +270,7 @@ export class Core { on("config/reload", async (msg) => { void this.configHandler.reloadConfig(); - return (await this.configHandler.getSerializedConfig()).config!; // <-- TODO + return await this.configHandler.getSerializedConfig(); }); on("config/ideSettingsUpdate", (msg) => { @@ -297,6 +295,10 @@ export class Core { on("context/loadSubmenuItems", async (msg) => { const config = await this.config(); + if (!config) { + return []; + } + const items = await config.contextProviders ?.find((provider) => provider.description.title === msg.data.title) ?.loadSubmenuItems({ @@ -312,6 +314,10 @@ export class Core { const { name, query, fullInput, selectedCode, selectedModelTitle } = msg.data; const config = await this.config(); + if (!config) { + return []; + } + const llm = await this.configHandler.llmFromTitle(selectedModelTitle); const provider = config.contextProviders?.find( (provider) => provider.description.title === name, @@ -366,7 +372,7 @@ export class Core { on("config/getSerializedProfileInfo", async (msg) => { return { - config: (await this.configHandler.getSerializedConfig()).config!, // <-- TODO + result: await this.configHandler.getSerializedConfig(), profileId: this.configHandler.currentProfile.profileId, }; }); @@ -376,7 +382,11 @@ export class Core { abortedMessageIds: Set, msg: Message, ) { - const config = await configHandler.loadConfig(); + const { config } = await configHandler.loadConfig(); + if (!config) { + throw new Error("Config not loaded"); + } + // Stop TTS on new StreamChat if (config.experimental?.readResponseTTS) { void TTS.kill(); @@ -480,7 +490,11 @@ export class Core { return completion; }); on("llm/listModels", async (msg) => { - const config = await this.configHandler.loadConfig(); + const { config } = await this.configHandler.loadConfig(); + if (!config) { + return []; + } + const model = config.models.find((model) => model.title === msg.data.title) ?? config.models.find((model) => model.title?.startsWith(msg.data.title)); @@ -533,7 +547,11 @@ export class Core { selectedCode, } = msg.data; - const config = await configHandler.loadConfig(); + const { config } = await configHandler.loadConfig(); + if (!config) { + throw new Error("Config not loaded"); + } + const llm = await configHandler.llmFromTitle(modelTitle); const slashCommand = config.slashCommands?.find( (sc) => sc.name === slashCommandName, @@ -846,7 +864,11 @@ export class Core { }); on("tools/call", async ({ data: { toolCall, selectedModelTitle } }) => { - const config = await this.configHandler.loadConfig(); + const { config } = await this.configHandler.loadConfig(); + if (!config) { + throw new Error("Config not loaded"); + } + const tool = config.tools.find( (t) => t.function.name === toolCall.function.name, ); diff --git a/core/indexing/CodebaseIndexer.ts b/core/indexing/CodebaseIndexer.ts index e309549eb4..677cc43057 100644 --- a/core/indexing/CodebaseIndexer.ts +++ b/core/indexing/CodebaseIndexer.ts @@ -75,7 +75,11 @@ export class CodebaseIndexer { } protected async getIndexesToBuild(): Promise { - const config = await this.configHandler.loadConfig(); + const { config } = await this.configHandler.loadConfig(); + if (!config) { + return []; + } + const indexes = [ new ChunkCodebaseIndex( this.ide.readFile.bind(this.ide), @@ -198,8 +202,8 @@ export class CodebaseIndexer { return; } - const config = await this.configHandler.loadConfig(); - if (config.disableIndexing) { + const { config } = await this.configHandler.loadConfig(); + if (config?.disableIndexing) { yield { progress, desc: "Indexing is disabled in config.json", diff --git a/core/indexing/LanceDbIndex.test.ts b/core/indexing/LanceDbIndex.test.ts index 6cf36f78da..08c8f99825 100644 --- a/core/indexing/LanceDbIndex.test.ts +++ b/core/indexing/LanceDbIndex.test.ts @@ -2,13 +2,13 @@ import { jest } from "@jest/globals"; import lance from "vectordb"; import { testConfigHandler, testIde } from "../test/fixtures"; +import { getLanceDbPath } from "../util/paths"; import { mockPathAndCacheKey, mockTag, testContinueServerClient, updateIndexAndAwaitGenerator, } from "./test/indexing"; -import { getLanceDbPath } from "../util/paths"; import { LanceDbIndex } from "./LanceDbIndex"; import { DatabaseConnection, SqliteDb } from "./refreshIndex"; @@ -26,7 +26,10 @@ describe.skip("ChunkCodebaseIndex", () => { } beforeAll(async () => { - const mockConfig = await testConfigHandler.loadConfig(); + const { config: mockConfig } = await testConfigHandler.loadConfig(); + if (!mockConfig) { + throw new Error("Failed to load config"); + } index = new LanceDbIndex( mockConfig.embeddingsProvider, diff --git a/core/indexing/docs/DocsCrawler.skip.ts b/core/indexing/docs/DocsCrawler.skip.ts index 97226c2961..93d1749c9a 100644 --- a/core/indexing/docs/DocsCrawler.skip.ts +++ b/core/indexing/docs/DocsCrawler.skip.ts @@ -28,7 +28,8 @@ describe.skip("DocsCrawler", () => { let docsCrawler: DocsCrawler; beforeAll(async () => { - config = await testConfigHandler.loadConfig(); + const result = await testConfigHandler.loadConfig(); + config = result.config!; mockIde = new FileSystemIde(process.cwd()); chromiumInstaller = new ChromiumInstaller(mockIde, config); docsCrawler = new DocsCrawler(mockIde, config); diff --git a/core/indexing/docs/DocsService.skip.ts b/core/indexing/docs/DocsService.skip.ts index 22c307f35a..8b4b6a8bfc 100644 --- a/core/indexing/docs/DocsService.skip.ts +++ b/core/indexing/docs/DocsService.skip.ts @@ -74,7 +74,7 @@ describe.skip("DocsService Integration Tests", () => { expect(await docsService.hasMetadata(mockSiteConfig.startUrl)).toBe(true); // config.json check - expect(latestConfig.docs).toContainEqual(mockSiteConfig); + expect(latestConfig.config!.docs).toContainEqual(mockSiteConfig); // Lance DB check let retrievedChunks = await docsService.retrieveChunksFromQuery( @@ -92,7 +92,7 @@ describe.skip("DocsService Integration Tests", () => { // config.json check latestConfig = await getReloadedConfig(); - expect(latestConfig.docs).not.toContainEqual( + expect(latestConfig.config!.docs).not.toContainEqual( expect.objectContaining(mockSiteConfig), ); diff --git a/core/indexing/docs/DocsService.ts b/core/indexing/docs/DocsService.ts index f35f834666..ddebd47a8d 100644 --- a/core/indexing/docs/DocsService.ts +++ b/core/indexing/docs/DocsService.ts @@ -16,9 +16,9 @@ import { addContextProvider } from "../../config/util"; import DocsContextProvider from "../../context/providers/DocsContextProvider"; import TransformersJsEmbeddingsProvider from "../../llm/llms/TransformersJsEmbeddingsProvider"; import { FromCoreProtocol, ToCoreProtocol } from "../../protocol"; +import { IMessenger } from "../../protocol/messenger"; import { fetchFavicon, getFaviconBase64 } from "../../util/fetchFavicon"; import { GlobalContext } from "../../util/GlobalContext"; -import { IMessenger } from "../../protocol/messenger"; import { editConfigJson, getDocsSqlitePath, @@ -26,6 +26,7 @@ import { } from "../../util/paths"; import { Telemetry } from "../../util/posthog"; +import { ConfigResult } from "../../config/load"; import { Article, chunkArticle, pageToArticle } from "./article"; import DocsCrawler from "./DocsCrawler"; import { runLanceMigrations, runSqliteMigrations } from "./migrations"; @@ -123,8 +124,8 @@ export default class DocsService { // Initialization - load config and attach config listener private async init(configHandler: ConfigHandler) { - const config = await configHandler.loadConfig(); - await this.handleConfigUpdate({ config }); + const result = await configHandler.loadConfig(); + await this.handleConfigUpdate(result); configHandler.onConfigUpdate(this.handleConfigUpdate.bind(this)); } @@ -247,9 +248,7 @@ export default class DocsService { private async handleConfigUpdate({ config: newConfig, - }: { - config: ContinueConfig | undefined; - }) { + }: ConfigResult) { if (newConfig) { const oldConfig = this.config; this.config = newConfig; // IMPORTANT - need to set up top, other methods below use this without passing it in diff --git a/core/protocol/core.ts b/core/protocol/core.ts index 9a7d85510d..e05ce1bc4f 100644 --- a/core/protocol/core.ts +++ b/core/protocol/core.ts @@ -20,6 +20,7 @@ import type { SiteIndexingConfig, ToolCall, } from "../"; +import { ConfigResult } from "../config/load"; export type ProtocolGeneratorType = AsyncGenerator<{ done?: boolean; @@ -63,10 +64,13 @@ export type ToCoreFromIdeOrWebviewProtocol = { "config/ideSettingsUpdate": [IdeSettings, void]; "config/getSerializedProfileInfo": [ undefined, - { config: BrowserSerializedContinueConfig; profileId: string }, + { + result: ConfigResult; + profileId: string; + }, ]; "config/deleteModel": [{ title: string }, void]; - "config/reload": [undefined, BrowserSerializedContinueConfig]; + "config/reload": [undefined, ConfigResult]; "config/listProfiles": [undefined, ProfileDescription[]]; "config/openProfile": [{ profileId: string | undefined }, void]; "context/getContextItems": [ diff --git a/core/protocol/webview.ts b/core/protocol/webview.ts index 65ecde912e..11512fa2c7 100644 --- a/core/protocol/webview.ts +++ b/core/protocol/webview.ts @@ -1,3 +1,4 @@ +import { ConfigResult } from "../config/load.js"; import { ConfigValidationError } from "../config/validation.js"; import type { @@ -10,7 +11,10 @@ import type { export type ToWebviewFromIdeOrCoreProtocol = { configUpdate: [ - { config: BrowserSerializedContinueConfig; profileId: string }, + { + result: ConfigResult; + profileId: string; + }, void, ]; configError: [ConfigValidationError[] | undefined, void]; diff --git a/extensions/vscode/src/commands.ts b/extensions/vscode/src/commands.ts index 690063628f..b83d3d380b 100644 --- a/extensions/vscode/src/commands.ts +++ b/extensions/vscode/src/commands.ts @@ -335,7 +335,10 @@ const getCommandsMap: ( onlyOneInsertion?: boolean, range?: vscode.Range, ) { - const config = await configHandler.loadConfig(); + const { config } = await configHandler.loadConfig(); + if (!config) { + throw new Error("Config not loaded"); + } const defaultModelTitle = await sidebar.webviewProtocol.request( "getDefaultModelTitle", @@ -867,7 +870,7 @@ const getCommandsMap: ( const config = vscode.workspace.getConfiguration(EXTENSION_NAME); const quickPick = vscode.window.createQuickPick(); const autocompleteModels = - (await configHandler.loadConfig())?.tabAutocompleteModels ?? []; + (await configHandler.loadConfig()).config?.tabAutocompleteModels ?? []; let selected = new GlobalContext().get("selectedTabAutocompleteModel"); if ( diff --git a/extensions/vscode/src/extension/VsCodeExtension.ts b/extensions/vscode/src/extension/VsCodeExtension.ts index ac3f7fbf78..4ff02a736d 100644 --- a/extensions/vscode/src/extension/VsCodeExtension.ts +++ b/extensions/vscode/src/extension/VsCodeExtension.ts @@ -149,7 +149,7 @@ export class VsCodeExtension { this.configHandler.reloadConfig.bind(this.configHandler), ); - this.configHandler.loadConfig().then((config) => { + this.configHandler.loadConfig().then(({ config }) => { const { verticalDiffCodeLens } = registerAllCodeLensProviders( context, this.verticalDiffManager.fileUriToCodeLens, @@ -168,9 +168,9 @@ export class VsCodeExtension { } else if (newConfig) { setupStatusBar(undefined, undefined, false); - const serialized = await this.configHandler.getSerializedConfig(); + const result = await this.configHandler.getSerializedConfig(); this.sidebar.webviewProtocol?.request("configUpdate", { - config: serialized.config!, // <-- TODO + result, profileId: this.configHandler.currentProfile.profileId, }); diff --git a/extensions/vscode/src/extension/VsCodeMessenger.ts b/extensions/vscode/src/extension/VsCodeMessenger.ts index 2b88fbca65..52238cc1e2 100644 --- a/extensions/vscode/src/extension/VsCodeMessenger.ts +++ b/extensions/vscode/src/extension/VsCodeMessenger.ts @@ -171,7 +171,12 @@ export class VsCodeMessenger { // Get LLM from config const configHandler = await configHandlerPromise; - const config = await configHandler.loadConfig(); + const { config } = await configHandler.loadConfig(); + + if (!config) { + vscode.window.showErrorMessage("Config not loaded"); + return; + } let llm = getModelByRole(config, "applyCodeBlock"); diff --git a/extensions/vscode/src/lang-server/codeLens/registerAllCodeLensProviders.ts b/extensions/vscode/src/lang-server/codeLens/registerAllCodeLensProviders.ts index ec168de539..9138fba8bd 100644 --- a/extensions/vscode/src/lang-server/codeLens/registerAllCodeLensProviders.ts +++ b/extensions/vscode/src/lang-server/codeLens/registerAllCodeLensProviders.ts @@ -74,7 +74,7 @@ function registerQuickActionsProvider( export function registerAllCodeLensProviders( context: vscode.ExtensionContext, editorToVerticalDiffCodeLens: Map, - config: ContinueConfig, + config: ContinueConfig | undefined, ) { if (verticalPerLineCodeLensProvider) { verticalPerLineCodeLensProvider.dispose(); @@ -110,11 +110,13 @@ export function registerAllCodeLensProviders( new providers.SuggestionsCodeLensProvider(), ); - registerQuickActionsProvider(config, context); + if (config) { + registerQuickActionsProvider(config, context); - subscribeToVSCodeQuickActionsSettings(() => - registerQuickActionsProvider(config, context), - ); + subscribeToVSCodeQuickActionsSettings(() => + registerQuickActionsProvider(config, context), + ); + } context.subscriptions.push(verticalPerLineCodeLensProvider); context.subscriptions.push(suggestionsCodeLensDisposable); diff --git a/extensions/vscode/src/quickEdit/QuickEditQuickPick.ts b/extensions/vscode/src/quickEdit/QuickEditQuickPick.ts index aefc7408f7..09cf8e2cda 100644 --- a/extensions/vscode/src/quickEdit/QuickEditQuickPick.ts +++ b/extensions/vscode/src/quickEdit/QuickEditQuickPick.ts @@ -242,6 +242,10 @@ export class QuickEdit { path: string | undefined, ) => { const modelTitle = await this.getCurModelTitle(); + if (!modelTitle) { + throw new Error("No model selected"); + } + await this._streamEditWithInputAndContext(prompt, modelTitle); this.openAcceptRejectMenu(prompt, path); }; @@ -258,12 +262,15 @@ export class QuickEdit { /** * Gets the model title the user has chosen, or their default model */ - private async getCurModelTitle() { + private async getCurModelTitle(): Promise { if (this._curModelTitle) { return this._curModelTitle; } - const config = await this.configHandler.loadConfig(); + const { config } = await this.configHandler.loadConfig(); + if (!config) { + return undefined; + } return ( getModelByRole(config, "inlineEdit")?.title ?? @@ -517,7 +524,10 @@ export class QuickEdit { editor: vscode.TextEditor; params: QuickEditShowParams | undefined; }) { - const config = await this.configHandler.loadConfig(); + const { config } = await this.configHandler.loadConfig(); + if (!config) { + throw new Error("Config not loaded"); + } let prompt: string | undefined; switch (selectedLabel) { diff --git a/extensions/vscode/src/util/loadAutocompleteModel.ts b/extensions/vscode/src/util/loadAutocompleteModel.ts index b4aa9289b9..fda69f0235 100644 --- a/extensions/vscode/src/util/loadAutocompleteModel.ts +++ b/extensions/vscode/src/util/loadAutocompleteModel.ts @@ -34,7 +34,11 @@ export class TabAutocompleteModel { async get() { if (!this._llm) { - const config = await this.configHandler.loadConfig(); + const { config } = await this.configHandler.loadConfig(); + if (!config) { + return undefined; + } + if (config.tabAutocompleteModels?.length) { const selected = this.globalContext.get("selectedTabAutocompleteModel"); if (selected) { diff --git a/gui/src/hooks/useSetup.ts b/gui/src/hooks/useSetup.ts index 5534fbc3bf..65d72557c5 100644 --- a/gui/src/hooks/useSetup.ts +++ b/gui/src/hooks/useSetup.ts @@ -3,8 +3,9 @@ import { VSC_THEME_COLOR_VARS } from "../components"; import { IdeMessengerContext } from "../context/IdeMessenger"; import { BrowserSerializedContinueConfig } from "core"; +import { ConfigResult } from "core/config/load"; import { useAppDispatch, useAppSelector } from "../redux/hooks"; -import { setConfig, setConfigError } from "../redux/slices/configSlice"; +import { setConfigError, setConfigResult } from "../redux/slices/configSlice"; import { updateIndexingStatus } from "../redux/slices/indexingSlice"; import { updateDocsSuggestions } from "../redux/slices/miscSlice"; import { @@ -33,20 +34,23 @@ function useSetup() { const handleConfigUpdate = useCallback( async ( initial: boolean, - result: { config: BrowserSerializedContinueConfig; profileId: string }, + result: { + result: ConfigResult; + profileId: string; + }, ) => { - const { config, profileId } = result; + const { result: configResult, profileId } = result; if (initial && hasLoadedConfig.current) { return; } hasLoadedConfig.current = true; - dispatch(setConfig(config)); + dispatch(setConfigResult(configResult)); dispatch(setSelectedProfileId(profileId)); // Perform any actions needed with the config - if (config.ui?.fontSize) { - setLocalStorage("fontSize", config.ui.fontSize); - document.body.style.fontSize = `${config.ui.fontSize}px`; + if (configResult.config?.ui?.fontSize) { + setLocalStorage("fontSize", configResult.config.ui.fontSize); + document.body.style.fontSize = `${configResult.config.ui.fontSize}px`; } }, [dispatch, hasLoadedConfig], @@ -61,6 +65,7 @@ function useSetup() { if (result.status === "error") { return; } + console.log("Config loaded", result.content); await handleConfigUpdate(initial, result.content); }, [ideMessenger, handleConfigUpdate], diff --git a/gui/src/redux/slices/configSlice.ts b/gui/src/redux/slices/configSlice.ts index 95c7e3e8a5..35e530e474 100644 --- a/gui/src/redux/slices/configSlice.ts +++ b/gui/src/redux/slices/configSlice.ts @@ -1,5 +1,6 @@ import { createSlice, PayloadAction } from "@reduxjs/toolkit"; import { BrowserSerializedContinueConfig } from "core"; +import { ConfigResult } from "core/config/load"; import { ConfigValidationError } from "core/config/validation"; import { DEFAULT_MAX_TOKENS } from "core/llm/constants"; @@ -33,10 +34,19 @@ export const configSlice = createSlice({ name: "config", initialState, reducers: { - setConfig: ( + setConfigResult: ( state, - { payload: config }: PayloadAction, + { + payload: result, + }: PayloadAction>, ) => { + const { config, errors } = result; + state.configError = errors; + + if (!config) { + return; + } + const defaultModelTitle = config.models.find((model) => model.title === state.defaultModelTitle) ?.title || @@ -83,7 +93,7 @@ export const configSlice = createSlice({ }, }); -export const { setDefaultModel, setConfig, setConfigError } = +export const { setDefaultModel, setConfigResult, setConfigError } = configSlice.actions; export const { From 57afa403a8cd5c10286f93314dfd90969535cb47 Mon Sep 17 00:00:00 2001 From: tomasz-io Date: Tue, 24 Dec 2024 11:07:04 -0800 Subject: [PATCH 37/99] improv: contain chat image to a max height --- gui/src/components/mainInput/TipTapEditor.tsx | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/gui/src/components/mainInput/TipTapEditor.tsx b/gui/src/components/mainInput/TipTapEditor.tsx index 24f874eb64..8f7191e184 100644 --- a/gui/src/components/mainInput/TipTapEditor.tsx +++ b/gui/src/components/mainInput/TipTapEditor.tsx @@ -340,6 +340,10 @@ function TipTapEditor(props: TipTapEditorProps) { }); return [plugin]; }, + }).configure({ + HTMLAttributes: { + class: "editor-image bg-black object-contain max-h-[250px] w-full", + }, }), Placeholder.configure({ placeholder: getPlaceholderText( From 38a7c3a9a20ef9a723d226c3acb56ddb83cc85fe Mon Sep 17 00:00:00 2001 From: Nate Date: Tue, 24 Dec 2024 14:42:59 -0500 Subject: [PATCH 38/99] fix mcp decoding for tools --- core/context/mcp/index.ts | 3 ++- core/tools/callTool.ts | 5 ++++- extensions/vscode/package-lock.json | 4 ++-- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/core/context/mcp/index.ts b/core/context/mcp/index.ts index bcf11492f9..e214b1e026 100644 --- a/core/context/mcp/index.ts +++ b/core/context/mcp/index.ts @@ -7,6 +7,7 @@ import { Transport } from "@modelcontextprotocol/sdk/shared/transport.js"; import { ContinueConfig, MCPOptions, SlashCommand, Tool } from "../.."; import { constructMcpSlashCommand } from "../../commands/slash/mcp"; import { ConfigValidationError } from "../../config/validation"; +import { encodeMCPToolUri } from "../../tools/callTool"; import MCPContextProvider from "../providers/MCPContextProvider"; export class MCPManagerSingleton { @@ -172,7 +173,7 @@ class MCPConnection { readonly: false, type: "function", wouldLikeTo: `use the ${tool.name} tool`, - uri: `mcp://${tool.name}`, + uri: encodeMCPToolUri(mcpId, tool.name), })); config.tools = [...config.tools, ...continueTools]; diff --git a/core/tools/callTool.ts b/core/tools/callTool.ts index 3de329a0bd..2bca7c2d11 100644 --- a/core/tools/callTool.ts +++ b/core/tools/callTool.ts @@ -44,7 +44,10 @@ export function decodeMCPToolUri(uri: string): [string, string] | null { if (url.protocol !== "mcp:") { return null; } - return [decodeURIComponent(url.hostname), decodeURIComponent(url.pathname)]; + return [ + decodeURIComponent(url.hostname), + decodeURIComponent(url.pathname).slice(1), // to remove leading '/' + ]; } async function callToolFromUri( diff --git a/extensions/vscode/package-lock.json b/extensions/vscode/package-lock.json index ca4edfc38b..8f5cad8a0d 100644 --- a/extensions/vscode/package-lock.json +++ b/extensions/vscode/package-lock.json @@ -1,12 +1,12 @@ { "name": "continue", - "version": "0.9.246", + "version": "0.9.247", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "continue", - "version": "0.9.246", + "version": "0.9.247", "license": "Apache-2.0", "dependencies": { "@continuedev/fetch": "^1.0.3", From 2b5ed9e59b0b2ff5bdc3e83b7ee2cd1ced21985e Mon Sep 17 00:00:00 2001 From: Nate Date: Tue, 24 Dec 2024 14:45:02 -0500 Subject: [PATCH 39/99] bump package.json --- extensions/vscode/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extensions/vscode/package.json b/extensions/vscode/package.json index 4cb0237171..6a416096cb 100644 --- a/extensions/vscode/package.json +++ b/extensions/vscode/package.json @@ -2,7 +2,7 @@ "name": "continue", "icon": "media/icon.png", "author": "Continue Dev, Inc", - "version": "0.9.247", + "version": "0.9.248", "repository": { "type": "git", "url": "https://github.com/continuedev/continue" From 955117019366c6d286526478497d62cca6c11d5f Mon Sep 17 00:00:00 2001 From: Nate Date: Tue, 24 Dec 2024 17:17:09 -0500 Subject: [PATCH 40/99] fix tsc err --- core/tools/callTool.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/core/tools/callTool.ts b/core/tools/callTool.ts index 2bca7c2d11..706fa57d9d 100644 --- a/core/tools/callTool.ts +++ b/core/tools/callTool.ts @@ -55,6 +55,7 @@ async function callToolFromUri( args: any, extras: ToolExtras, ): Promise { + // @ts-ignore const canParse = URL.canParse(uri); if (!canParse) { throw new Error(`Invalid URI: ${uri}`); From b94ccc6db557f705ba4f810ed56c55ce21f383dd Mon Sep 17 00:00:00 2001 From: tomasz-io Date: Tue, 24 Dec 2024 12:32:19 -0800 Subject: [PATCH 41/99] fix: scroll to the bottom on history change --- extensions/vscode/e2e/tests/GUI.test.ts | 62 ++++++++++++++ gui/src/components/ChatScrollAnchor.tsx | 38 --------- gui/src/pages/gui/Chat.tsx | 105 +++++++++++++----------- 3 files changed, 118 insertions(+), 87 deletions(-) delete mode 100644 gui/src/components/ChatScrollAnchor.tsx diff --git a/extensions/vscode/e2e/tests/GUI.test.ts b/extensions/vscode/e2e/tests/GUI.test.ts index d4c005eae7..b5e3cabc36 100644 --- a/extensions/vscode/e2e/tests/GUI.test.ts +++ b/extensions/vscode/e2e/tests/GUI.test.ts @@ -193,6 +193,68 @@ describe("GUI Test", () => { }); describe("Chat Paths", () => { + it("Send many messages → chat auto scrolls → go to history → open previous chat → it is scrolled to the bottom", async () => { + for (let i = 0; i <= 20; i++) { + const { userMessage, llmResponse } = + TestUtils.generateTestMessagePair(i); + await GUIActions.sendMessage({ + view, + message: userMessage, + inputFieldIndex: i, + }); + const response = await TestUtils.waitForSuccess(() => + GUISelectors.getThreadMessageByText(view, llmResponse), + ); + + const viewportHeight = await driver.executeScript( + "return window.innerHeight", + ); + + const isInViewport = await driver.executeScript( + ` + const rect = arguments[0].getBoundingClientRect(); + return ( + rect.top >= 0 && + rect.bottom <= ${viewportHeight} + ); + `, + response, + ); + + expect(isInViewport).to.eq(true); + } + + await view.switchBack(); + + await (await GUISelectors.getHistoryNavButton(view)).click(); + await GUIActions.switchToReactIframe(); + TestUtils.waitForSuccess(async () => { + await (await GUISelectors.getNthHistoryTableRow(view, 0)).click(); + }); + + const { llmResponse } = TestUtils.generateTestMessagePair(20); + const response = await TestUtils.waitForSuccess(() => + GUISelectors.getThreadMessageByText(view, llmResponse), + ); + + const viewportHeight = await driver.executeScript( + "return window.innerHeight", + ); + + const isInViewport = await driver.executeScript( + ` + const rect = arguments[0].getBoundingClientRect(); + return ( + rect.top >= 0 && + rect.bottom <= ${viewportHeight} + ); + `, + response, + ); + + expect(isInViewport).to.eq(true); + }).timeout(DEFAULT_TIMEOUT.XL * 1000); + it("Open chat and send message → press arrow up and arrow down to cycle through messages → submit another message → press arrow up and arrow down to cycle through messages", async () => { await GUIActions.sendMessage({ view, diff --git a/gui/src/components/ChatScrollAnchor.tsx b/gui/src/components/ChatScrollAnchor.tsx deleted file mode 100644 index 9b32e7283b..0000000000 --- a/gui/src/components/ChatScrollAnchor.tsx +++ /dev/null @@ -1,38 +0,0 @@ -import { useEffect } from "react"; -import { useInView } from "react-intersection-observer"; -import { vscBackground } from "."; - -interface ChatScrollAnchorProps { - trackVisibility: boolean; - isAtBottom: boolean; - scrollAreaRef: React.RefObject; -} - -export function ChatScrollAnchor({ - trackVisibility, - isAtBottom, - scrollAreaRef, -}: ChatScrollAnchorProps) { - const { ref, inView, entry } = useInView(); - - useEffect(() => { - if (isAtBottom && trackVisibility && !inView) { - if (!scrollAreaRef.current) return; - - const scrollAreaElement = scrollAreaRef.current; - - scrollAreaElement.scrollTop = - scrollAreaElement.scrollHeight - scrollAreaElement.clientHeight; - } - }, [inView, entry, isAtBottom, trackVisibility]); - - return ( -
- ); -} diff --git a/gui/src/pages/gui/Chat.tsx b/gui/src/pages/gui/Chat.tsx index b7748bf22e..8e11e39e5b 100644 --- a/gui/src/pages/gui/Chat.tsx +++ b/gui/src/pages/gui/Chat.tsx @@ -19,7 +19,6 @@ import { lightGray, vscBackground, } from "../../components"; -import { ChatScrollAnchor } from "../../components/ChatScrollAnchor"; import CodeToEditCard from "../../components/CodeToEditCard"; import FeedbackDialog from "../../components/dialogs/FeedbackDialog"; import { useFindWidget } from "../../components/find/FindWidget"; @@ -75,6 +74,7 @@ import { loadLastSession, saveCurrentSession, } from "../../redux/thunks/session"; +import { C } from "core/autocomplete/constants/AutocompleteLanguageInfo"; const StopButton = styled.div` background-color: ${vscBackground}; @@ -132,6 +132,58 @@ function fallbackRender({ error, resetErrorBoundary }: any) { ); } +const useAutoScroll = ( + ref: React.RefObject, + history: unknown[], +) => { + const [userHasScrolled, setUserHasScrolled] = useState(false); + + useEffect(() => { + if (history.length) { + setUserHasScrolled(false); + } + }, [history.length]); + + useEffect(() => { + if (!ref.current || history.length === 0) return; + + const handleScroll = () => { + const elem = ref.current; + if (!elem) return; + + const isAtBottom = + Math.abs(elem.scrollHeight - elem.scrollTop - elem.clientHeight) < 1; + + /** + * We stop auto scrolling if a user manually scrolled up. + * We resume auto scrolling if a user manually scrolled to the bottom. + */ + setUserHasScrolled(!isAtBottom); + }; + + const resizeObserver = new ResizeObserver(() => { + const elem = ref.current; + if (!elem || userHasScrolled) return; + elem.scrollTop = elem.scrollHeight; + }); + + ref.current.addEventListener("scroll", handleScroll); + + // Observe the container + resizeObserver.observe(ref.current); + + // Observe all immediate children + Array.from(ref.current.children).forEach((child) => { + resizeObserver.observe(child); + }); + + return () => { + resizeObserver.disconnect(); + ref.current?.removeEventListener("scroll", handleScroll); + }; + }, [ref, history.length, userHasScrolled]); +}; + export function Chat() { const posthog = usePostHog(); const dispatch = useAppDispatch(); @@ -147,7 +199,6 @@ export function Chat() { const [stepsOpen, setStepsOpen] = useState<(boolean | undefined)[]>([]); const mainTextInputRef = useRef(null); const stepsDivRef = useRef(null); - const [isAtBottom, setIsAtBottom] = useState(false); const history = useAppSelector((state) => state.session.history); const showChatScrollbar = useAppSelector( (state) => state.config.config.ui?.showChatScrollbar, @@ -168,34 +219,6 @@ export function Chat() { selectIsSingleRangeEditOrInsertion, ); const lastSessionId = useAppSelector((state) => state.session.lastSessionId); - const snapToBottom = useCallback(() => { - if (!stepsDivRef.current) return; - const elem = stepsDivRef.current; - elem.scrollTop = elem.scrollHeight - elem.clientHeight; - - setIsAtBottom(true); - }, [stepsDivRef, setIsAtBottom]); - - const smoothScrollToBottom = useCallback(async () => { - if (!stepsDivRef.current) return; - const elem = stepsDivRef.current; - elem.scrollTo({ - top: elem.scrollHeight - elem.clientHeight, - behavior: "smooth", - }); - - setIsAtBottom(true); - }, [stepsDivRef, setIsAtBottom]); - - useEffect(() => { - if (isStreaming) snapToBottom(); - }, [isStreaming, snapToBottom]); - - // useEffect(() => { - // setTimeout(() => { - // smoothScrollToBottom(); - // }, 400); - // }, [smoothScrollToBottom, state.sessionId]); useEffect(() => { // Cmd + Backspace to delete current step @@ -215,18 +238,6 @@ export function Chat() { }; }, [isStreaming]); - const handleScroll = () => { - // Temporary fix to account for additional height when code blocks are added - const OFFSET_HERUISTIC = 300; - if (!stepsDivRef.current) return; - - const { scrollTop, scrollHeight, clientHeight } = stepsDivRef.current; - const atBottom = - scrollHeight - clientHeight <= scrollTop + OFFSET_HERUISTIC; - - setIsAtBottom(atBottom); - }; - const { widget, highlights } = useFindWidget(stepsDivRef); const sendInput = useCallback( @@ -340,6 +351,8 @@ export function Chat() { const showScrollbar = showChatScrollbar || window.innerHeight > 5000; + useAutoScroll(stepsDivRef, history); + return ( <> {isInEditMode && ( @@ -356,14 +369,13 @@ export function Chat() { 0 ? "flex-1" : ""}`} - onScroll={handleScroll} > {highlights} {history.map((item, index: number) => (
))} -
From 3f991b41b6d3035889dd6cf402febac7903ab07d Mon Sep 17 00:00:00 2001 From: tomasz-io Date: Tue, 24 Dec 2024 14:29:42 -0800 Subject: [PATCH 42/99] improv: clean up code --- gui/src/pages/gui/Chat.tsx | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/gui/src/pages/gui/Chat.tsx b/gui/src/pages/gui/Chat.tsx index 8e11e39e5b..de8453445c 100644 --- a/gui/src/pages/gui/Chat.tsx +++ b/gui/src/pages/gui/Chat.tsx @@ -70,11 +70,7 @@ import ConfigErrorIndicator from "./ConfigError"; import { ToolCallDiv } from "./ToolCallDiv"; import { ToolCallButtons } from "./ToolCallDiv/ToolCallButtonsDiv"; import ToolOutput from "./ToolCallDiv/ToolOutput"; -import { - loadLastSession, - saveCurrentSession, -} from "../../redux/thunks/session"; -import { C } from "core/autocomplete/constants/AutocompleteLanguageInfo"; +import { loadLastSession } from "../../redux/thunks/session"; const StopButton = styled.div` background-color: ${vscBackground}; From d401a5c11fdf5b3267269ff3603f83318a9dd9ad Mon Sep 17 00:00:00 2001 From: vercel Date: Wed, 25 Dec 2024 14:45:14 +0800 Subject: [PATCH 43/99] feat: fix models --- core/llm/llms/Novita.ts | 6 +++++ extensions/vscode/config_schema.json | 3 ++- gui/src/pages/AddNewModel/configs/models.ts | 27 +++++++++++++++++++ .../pages/AddNewModel/configs/providers.ts | 2 +- 4 files changed, 36 insertions(+), 2 deletions(-) diff --git a/core/llm/llms/Novita.ts b/core/llm/llms/Novita.ts index 9121bd6c25..2f5c61b551 100644 --- a/core/llm/llms/Novita.ts +++ b/core/llm/llms/Novita.ts @@ -9,11 +9,17 @@ class Novita extends OpenAI { }; private static MODEL_IDS: { [name: string]: string } = { + "llama3-8b": "meta-llama/llama-3-8b-instruct", + "llama3-70b": "meta-llama/llama-3-70b-instruct", "llama3.1-8b": "meta-llama/llama-3.1-8b-instruct", "llama3.1-70b": "meta-llama/llama-3.1-70b-instruct", "llama3.1-405b": "meta-llama/llama-3.1-405b-instruct", + "llama3.2-1b": "meta-llama/llama-3.2-1b-instruct", "llama3.2-3b": "meta-llama/llama-3.2-3b-instruct", "llama3.2-11b": "meta-llama/llama-3.2-11b-vision-instruct", + "llama3.3-70b": "meta-llama/llama-3.3-70b-instruct", + "mistral-nemo": "mistralai/mistral-nemo", + "mistral-7b": "mistralai/mistral-7b-instruct", }; protected _convertModelName(model: string) { diff --git a/extensions/vscode/config_schema.json b/extensions/vscode/config_schema.json index 3ae0e3cc5e..0bd81a53e7 100644 --- a/extensions/vscode/config_schema.json +++ b/extensions/vscode/config_schema.json @@ -1003,9 +1003,10 @@ "llama3.1-8b", "llama3.1-70b", "llama3.1-405b", + "llama3.2-1b", "llama3.2-3b", "llama3.2-11b", - "llama-3.3-70b", + "llama3.3-70b", "mistral-nemo", "mistral-7b" ] diff --git a/gui/src/pages/AddNewModel/configs/models.ts b/gui/src/pages/AddNewModel/configs/models.ts index d43ed2ec1f..d3ca71c5db 100644 --- a/gui/src/pages/AddNewModel/configs/models.ts +++ b/gui/src/pages/AddNewModel/configs/models.ts @@ -58,6 +58,33 @@ export const models: { [key: string]: ModelPackage } = { ], isOpenSource: true, }, + mistralChat: { + title: "Mistral Chat", + description: + "A series of open-weight models created by Mistral AI, highly competent for code generation and other tasks", + params: { + title: "Mistral", + model: "mistralai/mistral-7b-instruct", + contextLength: 4096, + }, + dimensions: [ + { + name: "Parameter Count", + description: "The number of parameters in the model", + options: { + "7b": { + model: "mistralai/mistral-7b-instruct", + title: "Mistral-7b", + }, + }, + }, + ], + icon: "mistral.png", + providerOptions: [ + "novita", + ], + isOpenSource: true, + }, llama31Chat: { title: "Llama3.1 Chat", description: "A model from Meta, fine-tuned for chat", diff --git a/gui/src/pages/AddNewModel/configs/providers.ts b/gui/src/pages/AddNewModel/configs/providers.ts index 49b30e409f..f294829757 100644 --- a/gui/src/pages/AddNewModel/configs/providers.ts +++ b/gui/src/pages/AddNewModel/configs/providers.ts @@ -427,7 +427,7 @@ Select the \`GPT-4o\` model below to complete your provider configuration, but n ...completionParamsInputsConfigs, ], packages: [ - models.llama318BChat, + models.llama318BChat, models.mistralChat ].map((p) => { p.params.contextLength = 4096; return p; From e270b206808a35d5156f851fe66c1e1d13752812 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=8E=9F=E4=BF=8A=E6=9D=B0?= Date: Wed, 25 Dec 2024 17:37:49 +0800 Subject: [PATCH 44/99] Remove the extra "+" sign from the IDEA shortcut --- docs/docs/getting-started/install.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/docs/getting-started/install.md b/docs/docs/getting-started/install.md index 20aa52baaa..3729edbfdb 100644 --- a/docs/docs/getting-started/install.md +++ b/docs/docs/getting-started/install.md @@ -18,7 +18,7 @@ If you have any problems, see the [troubleshooting guide](troubleshooting.md) or ## JetBrains -1. Open your JetBrains IDE and open **Settings** using Ctrl + +Alt + S +1. Open your JetBrains IDE and open **Settings** using Ctrl + Alt + S 2. Select **Plugins** on the sidebar and search for "Continue" in the marketplace 3. Click `Install`, which will cause the Continue logo to show up on the right toolbar From decd04eddd4965b9f76c7a9a3abd276d8d2e1692 Mon Sep 17 00:00:00 2001 From: liuk Date: Wed, 25 Dec 2024 23:03:53 +0800 Subject: [PATCH 45/99] fix: Skip duplicate checks at document end or blank text Prevent autocomplete from truncating when the cursor is at the end of the document or when the text after the cursor is blank. --- .../autocomplete/AutocompleteService.kt | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/extensions/intellij/src/main/kotlin/com/github/continuedev/continueintellijextension/autocomplete/AutocompleteService.kt b/extensions/intellij/src/main/kotlin/com/github/continuedev/continueintellijextension/autocomplete/AutocompleteService.kt index 6279e03f74..2dc6ba021d 100644 --- a/extensions/intellij/src/main/kotlin/com/github/continuedev/continueintellijextension/autocomplete/AutocompleteService.kt +++ b/extensions/intellij/src/main/kotlin/com/github/continuedev/continueintellijextension/autocomplete/AutocompleteService.kt @@ -120,6 +120,10 @@ class AutocompleteService(private val project: Project) { return ApplicationManager.getApplication().runReadAction { val document = editor.document val caretOffset = editor.caretModel.offset + + // Don't care about it if it's at the end of the document + if (caretOffset == document.textLength) return@runReadAction completion + val N = 10 var textAfterCursor = if (caretOffset + N <= document.textLength) { document.getText(com.intellij.openapi.util.TextRange(caretOffset, caretOffset + N)) @@ -127,6 +131,9 @@ class AutocompleteService(private val project: Project) { document.getText(com.intellij.openapi.util.TextRange(caretOffset, document.textLength)) } + // Avoid truncating the completion text when the text after the cursor is blank + if (textAfterCursor.isBlank()) return@runReadAction completion + // Determine the index of a newline character within the text following the cursor. val newlineIndex = textAfterCursor.indexOf("\r\n").takeIf { it >= 0 } ?: textAfterCursor.indexOf('\n') // If a newline character is found and the current line is not empty, truncate the text at that point. From f6304057970b7326d462e56b60721657e75a3892 Mon Sep 17 00:00:00 2001 From: Guspan Tanadi <36249910+guspan-tanadi@users.noreply.github.com> Date: Thu, 26 Dec 2024 12:31:16 +0700 Subject: [PATCH 46/99] docs(autocomplete): prompts example links --- docs/docs/customize/deep-dives/autocomplete.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/docs/customize/deep-dives/autocomplete.md b/docs/docs/customize/deep-dives/autocomplete.md index 35789710ca..4aa5f768d2 100644 --- a/docs/docs/customize/deep-dives/autocomplete.md +++ b/docs/docs/customize/deep-dives/autocomplete.md @@ -91,7 +91,7 @@ This object allows you to customize the behavior of tab-autocomplete. The availa ### I want better completions, should I use GPT-4? -Perhaps surprisingly, the answer is no. The models that we suggest for autocomplete are trained with a highly specific prompt format, which allows them to respond to requests for completing code (see examples of these prompts [here](https://github.com/continuedev/continue/blob/main/core/autocomplete/templates.ts)). Some of the best commercial models like GPT-4 or Claude are not trained with this prompt format, which means that they won't generate useful completions. Luckily, a huge model is not required for great autocomplete. Most of the state-of-the-art autocomplete models are no more than 10b parameters, and increasing beyond this does not significantly improve performance. +Perhaps surprisingly, the answer is no. The models that we suggest for autocomplete are trained with a highly specific prompt format, which allows them to respond to requests for completing code (see examples of these prompts [here](https://github.com/continuedev/continue/blob/main/core/autocomplete/templating/AutocompleteTemplate.ts)). Some of the best commercial models like GPT-4 or Claude are not trained with this prompt format, which means that they won't generate useful completions. Luckily, a huge model is not required for great autocomplete. Most of the state-of-the-art autocomplete models are no more than 10b parameters, and increasing beyond this does not significantly improve performance. ### I'm not seeing any completions From 1a1585e8800cb8f33425b6ddb4d83d107da0c7b9 Mon Sep 17 00:00:00 2001 From: Prajna Kandarpa Date: Fri, 27 Dec 2024 13:25:21 +0530 Subject: [PATCH 47/99] minor documentationtip for aws bedrock --- .../model-providers/top-level/bedrock.md | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/docs/docs/customize/model-providers/top-level/bedrock.md b/docs/docs/customize/model-providers/top-level/bedrock.md index fb88d5ae02..67d919a2f1 100644 --- a/docs/docs/customize/model-providers/top-level/bedrock.md +++ b/docs/docs/customize/model-providers/top-level/bedrock.md @@ -22,6 +22,22 @@ We recommend configuring **Claude 3.5 Sonnet** as your chat model. ] } ``` +> If you run into the following error when connecting to the new Claude 3.5 Sonnet 2 models from AWS - `400 Invocation of model ID anthropic.claude-3-5-sonnet-20241022-v2:0 with on-demand throughput isn’t supported. Retry your request with the ID or ARN of an inference profile that contains this model.` + +> You can fix this using the following config.json +```json title="config.json" +{ + "models": [ + { + "title": "Claude 3.5 Sonnet", + "provider": "bedrock", + "model": "us.anthropic.claude-3-5-sonnet-20241022-v2:0", + "region": "us-east-1", + "profile": "bedrock" + } + ] +} +``` ## Autocomplete model From caf31ed11f55d7be40cb74225dfd1068594deaec Mon Sep 17 00:00:00 2001 From: Dallin Romney Date: Sun, 29 Dec 2024 15:14:56 +0100 Subject: [PATCH 48/99] clipboard context provider p1 --- .../providers/ClipboardContextProvider.ts | 57 +++++++++++++++++++ core/core.ts | 5 ++ core/protocol/core.ts | 1 + core/util/clipboardCache.ts | 54 ++++++++++++++++++ extensions/vscode/package-lock.json | 4 +- extensions/vscode/src/commands.ts | 13 ++++- 6 files changed, 130 insertions(+), 4 deletions(-) create mode 100644 core/context/providers/ClipboardContextProvider.ts create mode 100644 core/util/clipboardCache.ts diff --git a/core/context/providers/ClipboardContextProvider.ts b/core/context/providers/ClipboardContextProvider.ts new file mode 100644 index 0000000000..eb128ea7f7 --- /dev/null +++ b/core/context/providers/ClipboardContextProvider.ts @@ -0,0 +1,57 @@ +import { BaseContextProvider } from ".."; +import { + ContextItem, + ContextProviderDescription, + ContextProviderExtras, + ContextSubmenuItem, + LoadSubmenuItemsArgs, +} from "../.."; +import { clipboardCache } from "../../util/clipboardCache"; + +const MAX_CLIPBOARD_ITEMS = 10; + +class ClipboardContextProvider extends BaseContextProvider { + static description: ContextProviderDescription = { + title: "clipboard", + displayTitle: "Clipboard", + description: "Use recent copies", + type: "submenu", + }; + + async getContextItems( + query: string, + extras: ContextProviderExtras, + ): Promise { + // Assume the query is a cache id + const id = query.trim(); + const content = clipboardCache.get(id); + + if (content) { + clipboardCache.select(id); + return [ + { + name: "Clipboard item", + description: content.slice(0, 20), + content, + }, + ]; + } + return []; + } + + async loadSubmenuItems( + args: LoadSubmenuItemsArgs, + ): Promise { + const recentClipboardItems = clipboardCache.getNItems(MAX_CLIPBOARD_ITEMS); + + return recentClipboardItems.map((item, index) => { + return { + id: item.id, + title: item.content.slice(0, 20), + description: `#${index + 1}`, + }; + }); + } +} + +export default ClipboardContextProvider; diff --git a/core/core.ts b/core/core.ts index 14389910cb..90a6246c3f 100644 --- a/core/core.ts +++ b/core/core.ts @@ -46,6 +46,7 @@ import * as URI from "uri-js"; import { SYSTEM_PROMPT_DOT_FILE } from "./config/getSystemPromptDotFile"; import type { IMessenger, Message } from "./protocol/messenger"; import { localPathToUri } from "./util/pathToUri"; +import { clipboardCache } from "./util/clipboardCache"; export class Core { // implements IMessenger @@ -377,6 +378,10 @@ export class Core { }; }); + on("clipboardCache/add", (msg) => { + clipboardCache.add(uuidv4(), msg.data.content); + }); + async function* llmStreamChat( configHandler: ConfigHandler, abortedMessageIds: Set, diff --git a/core/protocol/core.ts b/core/protocol/core.ts index e05ce1bc4f..d0c84121f3 100644 --- a/core/protocol/core.ts +++ b/core/protocol/core.ts @@ -193,4 +193,5 @@ export type ToCoreFromIdeOrWebviewProtocol = { { toolCall: ToolCall; selectedModelTitle: string }, { contextItems: ContextItem[] }, ]; + "clipboardCache/add": [{ content: string }, void]; }; diff --git a/core/util/clipboardCache.ts b/core/util/clipboardCache.ts new file mode 100644 index 0000000000..4ecd16afcb --- /dev/null +++ b/core/util/clipboardCache.ts @@ -0,0 +1,54 @@ +class ClipboardCache { + private cache: Map; + private order: string[]; + private readonly maxSize = 30; + + constructor() { + this.cache = new Map(); + this.order = []; + } + + add(id: string, content: string): void { + if (!content) { + return; + } + if (this.order.length >= this.maxSize) { + const oldest = this.order.pop(); + if (oldest) { + this.cache.delete(oldest); + } + } + + this.cache.set(id, content); + this.order.unshift(id); + } + + getNItems(count: number): { + id: string; + content: string; + }[] { + return this.order.slice(0, count).map((id) => ({ + id, + content: this.cache.get(id) || "", + })); + } + + get(id: string): string | undefined { + return this.cache.get(id); + } + + select(id: string): void { + const index = this.order.indexOf(id); + if (index > -1) { + this.order.splice(index, 1); + this.order.unshift(id); + } + } + + clear(): void { + this.cache.clear(); + this.order = []; + } +} + +export const clipboardCache = new ClipboardCache(); diff --git a/extensions/vscode/package-lock.json b/extensions/vscode/package-lock.json index 8f5cad8a0d..0a2dd83304 100644 --- a/extensions/vscode/package-lock.json +++ b/extensions/vscode/package-lock.json @@ -1,12 +1,12 @@ { "name": "continue", - "version": "0.9.247", + "version": "0.9.248", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "continue", - "version": "0.9.247", + "version": "0.9.248", "license": "Apache-2.0", "dependencies": { "@continuedev/fetch": "^1.0.3", diff --git a/extensions/vscode/src/commands.ts b/extensions/vscode/src/commands.ts index b83d3d380b..147a1f118e 100644 --- a/extensions/vscode/src/commands.ts +++ b/extensions/vscode/src/commands.ts @@ -1001,7 +1001,10 @@ const getCommandsMap: ( }; }; -const registerCopyBufferSpy = (context: vscode.ExtensionContext) => { +const registerCopyBufferSpy = ( + context: vscode.ExtensionContext, + core: Core, +) => { const typeDisposable = vscode.commands.registerCommand( "editor.action.clipboardCopyAction", async (arg) => doCopy(typeDisposable), @@ -1014,6 +1017,12 @@ const registerCopyBufferSpy = (context: vscode.ExtensionContext) => { const clipboardText = await vscode.env.clipboard.readText(); + if (clipboardText) { + core.invoke("clipboardCache/add", { + content: clipboardText, + }); + } + await context.workspaceState.update("continue.copyBuffer", { text: clipboardText, copiedAt: new Date().toISOString(), @@ -1043,7 +1052,7 @@ export function registerAllCommands( core: Core, editDecorationManager: EditDecorationManager, ) { - registerCopyBufferSpy(context); + registerCopyBufferSpy(context, core); for (const [command, callback] of Object.entries( getCommandsMap( From 465362e407ac7ee0da1951f3ee78d6f9c60a19a3 Mon Sep 17 00:00:00 2001 From: Dallin Romney Date: Sun, 29 Dec 2024 15:34:13 +0100 Subject: [PATCH 49/99] clipboard context provider p2 --- core/context/providers/ClipboardContextProvider.ts | 4 ++-- core/context/providers/index.ts | 2 ++ core/core.ts | 3 +++ core/index.d.ts | 1 + docs/docs/customize/context-providers.md | 14 ++++++++++++++ extensions/vscode/config_schema.json | 8 +++----- 6 files changed, 25 insertions(+), 7 deletions(-) diff --git a/core/context/providers/ClipboardContextProvider.ts b/core/context/providers/ClipboardContextProvider.ts index eb128ea7f7..86e64d619b 100644 --- a/core/context/providers/ClipboardContextProvider.ts +++ b/core/context/providers/ClipboardContextProvider.ts @@ -14,7 +14,7 @@ class ClipboardContextProvider extends BaseContextProvider { static description: ContextProviderDescription = { title: "clipboard", displayTitle: "Clipboard", - description: "Use recent copies", + description: "Recent copies", type: "submenu", }; @@ -43,7 +43,7 @@ class ClipboardContextProvider extends BaseContextProvider { args: LoadSubmenuItemsArgs, ): Promise { const recentClipboardItems = clipboardCache.getNItems(MAX_CLIPBOARD_ITEMS); - + console.log(recentClipboardItems); return recentClipboardItems.map((item, index) => { return { id: item.id, diff --git a/core/context/providers/index.ts b/core/context/providers/index.ts index f5656832c4..3d1d7cc556 100644 --- a/core/context/providers/index.ts +++ b/core/context/providers/index.ts @@ -1,5 +1,6 @@ import { BaseContextProvider } from "../"; import { ContextProviderName } from "../../"; +import ClipboardContextProvider from "./ClipboardContextProvider"; import CodeContextProvider from "./CodeContextProvider"; import ContinueProxyContextProvider from "./ContinueProxyContextProvider"; @@ -62,6 +63,7 @@ export const Providers: (typeof BaseContextProvider)[] = [ GreptileContextProvider, WebContextProvider, MCPContextProvider, + ClipboardContextProvider, ]; export function contextProviderClassFromName( diff --git a/core/core.ts b/core/core.ts index 90a6246c3f..8718560381 100644 --- a/core/core.ts +++ b/core/core.ts @@ -295,6 +295,7 @@ export class Core { }); on("context/loadSubmenuItems", async (msg) => { + console.log("Load SUMBENU ITEMS", msg); const config = await this.config(); if (!config) { return []; @@ -379,7 +380,9 @@ export class Core { }); on("clipboardCache/add", (msg) => { + console.log("ADD TO CLIPBOARD", msg); clipboardCache.add(uuidv4(), msg.data.content); + this.messenger.send("refreshSubmenuItems", undefined); }); async function* llmStreamChat( diff --git a/core/index.d.ts b/core/index.d.ts index d916a19f11..c21040e3c9 100644 --- a/core/index.d.ts +++ b/core/index.d.ts @@ -751,6 +751,7 @@ export type ContextProviderName = | "issue" | "repo-map" | "url" + | "clipboard" | string; export type TemplateType = diff --git a/docs/docs/customize/context-providers.md b/docs/docs/customize/context-providers.md index 256f4eab5c..0b48c386b2 100644 --- a/docs/docs/customize/context-providers.md +++ b/docs/docs/customize/context-providers.md @@ -576,6 +576,20 @@ The response 200 OK should be a JSON object with the following structure: } ``` +### `@Clipboard` + +Reference recent clipboard items + +```json title="config.json" +{ + "contextProviders": [ + { + "name": "clipboard" + } + ] +} +``` + ### Requesting Context Providers Not seeing what you want? Create an issue [here](https://github.com/continuedev/continue/issues/new?assignees=TyDunn&labels=enhancement&projects=&template=feature-request-%F0%9F%92%AA.md&title=) to request a new Context Provider. diff --git a/extensions/vscode/config_schema.json b/extensions/vscode/config_schema.json index f2c98642f8..d60daa4992 100644 --- a/extensions/vscode/config_schema.json +++ b/extensions/vscode/config_schema.json @@ -1864,7 +1864,8 @@ "os", "repo-map", "greptile", - "web" + "web", + "clipboard" ], "markdownEnumDescriptions": [ "Reference the contents of the current changes as given by `git diff`", @@ -2805,10 +2806,7 @@ "type": "object", "properties": { "model": { - "enum": [ - "cohere.rerank-v3-5:0", - "amazon.rerank-v1:0" - ] + "enum": ["cohere.rerank-v3-5:0", "amazon.rerank-v1:0"] } }, "required": ["model"] From 72d4ad039f0275e1a3cfc1946b009db502f7d05a Mon Sep 17 00:00:00 2001 From: Test Date: Mon, 30 Dec 2024 11:52:27 -0600 Subject: [PATCH 50/99] bugfix(JB): handle disposed editor tooltips --- .../listeners/ContinuePluginSelectionListener.kt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/extensions/intellij/src/main/kotlin/com/github/continuedev/continueintellijextension/listeners/ContinuePluginSelectionListener.kt b/extensions/intellij/src/main/kotlin/com/github/continuedev/continueintellijextension/listeners/ContinuePluginSelectionListener.kt index 4676cae33d..24d9787f5a 100644 --- a/extensions/intellij/src/main/kotlin/com/github/continuedev/continueintellijextension/listeners/ContinuePluginSelectionListener.kt +++ b/extensions/intellij/src/main/kotlin/com/github/continuedev/continueintellijextension/listeners/ContinuePluginSelectionListener.kt @@ -28,6 +28,10 @@ class ContinuePluginSelectionListener( private var lastActiveEditor: Editor? = null override fun selectionChanged(e: SelectionEvent) { + if (e.editor.isDisposed || e.editor.project?.isDisposed == true) { + return + } + debouncer.debounce { handleSelection(e) } } From ddf19053b1b4afb37fc18a60ddd7b6abc86e27c8 Mon Sep 17 00:00:00 2001 From: Test Date: Mon, 30 Dec 2024 16:47:34 -0600 Subject: [PATCH 51/99] bugfix(GUI): broken Chinese input w/ codeblock --- gui/src/components/mainInput/CodeBlockComponent.tsx | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/gui/src/components/mainInput/CodeBlockComponent.tsx b/gui/src/components/mainInput/CodeBlockComponent.tsx index f8c359e146..0a0e7996b6 100644 --- a/gui/src/components/mainInput/CodeBlockComponent.tsx +++ b/gui/src/components/mainInput/CodeBlockComponent.tsx @@ -1,4 +1,4 @@ -import { NodeViewWrapper } from "@tiptap/react"; +import { NodeViewWrapper, NodeViewWrapperProps } from "@tiptap/react"; import { ContextItemWithId } from "core"; import { vscBadgeBackground } from ".."; import CodeSnippetPreview from "../markdown/CodeSnippetPreview"; @@ -13,8 +13,15 @@ export const CodeBlockComponent = (props: any) => { // const isFirstContextItem = item.id === contextItems[0]?.id; const isFirstContextItem = false; // TODO: fix this, decided not worth the insane renders for now + // Not setting this as a "p" will cause issues with foreign keyboards + // See https://github.com/continuedev/continue/issues/3199 + const nodeViewWrapperTag: NodeViewWrapperProps["as"] = "p"; + return ( - + Date: Mon, 30 Dec 2024 17:19:56 -0600 Subject: [PATCH 52/99] bugfix(GUI): increase z-index on tippy mentionlist --- gui/src/components/mainInput/TipTapEditor.tsx | 17 +++++++---------- gui/src/components/mainInput/getSuggestion.ts | 4 +++- 2 files changed, 10 insertions(+), 11 deletions(-) diff --git a/gui/src/components/mainInput/TipTapEditor.tsx b/gui/src/components/mainInput/TipTapEditor.tsx index bcd7328a2c..0312b3f539 100644 --- a/gui/src/components/mainInput/TipTapEditor.tsx +++ b/gui/src/components/mainInput/TipTapEditor.tsx @@ -175,6 +175,8 @@ interface TipTapEditorProps { historyKey: string; } +export const TIPPY_DIV_ID = "tippy-js-div"; + function TipTapEditor(props: TipTapEditorProps) { const dispatch = useAppDispatch(); @@ -976,22 +978,17 @@ function TipTapEditor(props: TipTapEditorProps) { {showDragOverMsg && modelSupportsImages( - defaultModel.provider, - defaultModel.model, - defaultModel.title, - defaultModel.capabilities, + defaultModel?.provider || "", + defaultModel?.model || "", + defaultModel?.title, + defaultModel?.capabilities, ) && ( <> Hold ⇧ to drop image )} -
+
); } diff --git a/gui/src/components/mainInput/getSuggestion.ts b/gui/src/components/mainInput/getSuggestion.ts index 41e6338c18..5ec1f778a7 100644 --- a/gui/src/components/mainInput/getSuggestion.ts +++ b/gui/src/components/mainInput/getSuggestion.ts @@ -5,6 +5,7 @@ import tippy from "tippy.js"; import { IIdeMessenger } from "../../context/IdeMessenger"; import MentionList from "./MentionList"; import { ComboBoxItem, ComboBoxItemType, ComboBoxSubAction } from "./types"; +import { TIPPY_DIV_ID } from "./TipTapEditor"; function getSuggestion( items: (props: { query: string }) => Promise, @@ -37,7 +38,8 @@ function getSuggestion( return; } - const container = document.getElementById("tippy-js-div"); + const container = document.getElementById(TIPPY_DIV_ID); + if (!container) { console.log("no container"); return; From 54e41d6de6eb7685425ec79241894a787a4260b1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Roger=20=C3=96berg?= Date: Wed, 1 Jan 2025 17:25:22 +0100 Subject: [PATCH 53/99] Update model-setup.md --- docs/docs/autocomplete/model-setup.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/docs/autocomplete/model-setup.md b/docs/docs/autocomplete/model-setup.md index 8aae88c754..221c17dddc 100644 --- a/docs/docs/autocomplete/model-setup.md +++ b/docs/docs/autocomplete/model-setup.md @@ -31,14 +31,14 @@ For those preferring local execution or self-hosting,`Qwen2.5-Coder 1.5B` offers ```json title="config.json"" { "tabAutocompleteModel": { - "title": "Qwen2.5-Coder 1.5B", - "model": "qwen2.5-coder:1.5b", + "title": "Qwen2.5-Coder 1.5B Base", + "model": "qwen2.5-coder:1.5b-base", "provider": "ollama" } } ``` -Have more compute? Use `qwen2.5-coder:7b` for potentially higher-quality suggestions. +Have more compute? Use `qwen2.5-coder:7b-base` for potentially higher-quality suggestions. :::note From 11b657a1546d2e03814205a4de0158ccdf65bf15 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Roger=20=C3=96berg?= Date: Wed, 1 Jan 2025 17:30:37 +0100 Subject: [PATCH 54/99] Update model-setup.md Fixed typo for LM studio --- docs/docs/autocomplete/model-setup.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/docs/autocomplete/model-setup.md b/docs/docs/autocomplete/model-setup.md index 221c17dddc..fc5233baa5 100644 --- a/docs/docs/autocomplete/model-setup.md +++ b/docs/docs/autocomplete/model-setup.md @@ -42,7 +42,7 @@ Have more compute? Use `qwen2.5-coder:7b-base` for potentially higher-quality su :::note -For LM Studio users, navigate to the "My Models" section, find your desired model, and copy the path (e.g., `Qwen/Qwen2.5-Coder-1.5B-Instruct-GGUF/wen2.5-coder-1.5b-instruct-q4_k_m.gguf`). Use this path as the `model` value in your configuration. +For LM Studio users, navigate to the "My Models" section, find your desired model, and copy the path (e.g., `Qwen/Qwen2.5-Coder-1.5B-Instruct-GGUF/qwen2.5-coder-1.5b-instruct-q4_k_m.gguf`). Use this path as the `model` value in your configuration. ::: From 8c6e33ad803ac5009b0ce7d6280ad05ac6dace03 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Roger=20=C3=96berg?= Date: Wed, 1 Jan 2025 17:31:32 +0100 Subject: [PATCH 55/99] Update autocomplete.md --- docs/docs/customize/model-types/autocomplete.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/docs/customize/model-types/autocomplete.md b/docs/docs/customize/model-types/autocomplete.md index d859206051..7f5abc8e21 100644 --- a/docs/docs/customize/model-types/autocomplete.md +++ b/docs/docs/customize/model-types/autocomplete.md @@ -13,4 +13,4 @@ In Continue, these models are used to display inline [Autocomplete](../../autoco If you have the ability to use any model, we recommend `Codestral` with [Mistral](../model-providers/top-level/mistral.md#autocomplete-model) or [Vertex AI](../model-providers/top-level/vertexai.md#autocomplete-model). -If you want to run a model locally, we recommend `Qwen2.5-Coder 1.5B` with [Ollama](../model-providers/top-level/ollama.md#autocomplete-model). +If you want to run a model locally, we recommend `Qwen2.5-Coder 1.5B-base` with [Ollama](../model-providers/top-level/ollama.md#autocomplete-model). From fb9db1e12883435fad5cc6610312ff3240ef6803 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Roger=20=C3=96berg?= Date: Wed, 1 Jan 2025 17:32:25 +0100 Subject: [PATCH 56/99] Update ollama.md --- docs/docs/customize/model-providers/top-level/ollama.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/docs/customize/model-providers/top-level/ollama.md b/docs/docs/customize/model-providers/top-level/ollama.md index ed96ac3854..c307a93448 100644 --- a/docs/docs/customize/model-providers/top-level/ollama.md +++ b/docs/docs/customize/model-providers/top-level/ollama.md @@ -23,14 +23,14 @@ We recommend configuring **Llama3.1 8B** as your chat model. ## Autocomplete model -We recommend configuring **Qwen2.5-Coder 1.5B** as your autocomplete model. +We recommend configuring **Qwen2.5-Coder 1.5B-base** as your autocomplete model. ```json title="config.json" { "tabAutocompleteModel": { - "title": "Qwen2.5-Coder 1.5B", + "title": "Qwen2.5-Coder 1.5B-base", "provider": "ollama", - "model": "qwen2.5-coder:1.5b" + "model": "qwen2.5-coder:1.5b-base" } } ``` From 7bfea237ec023fee6ff20293266d32d5cf130731 Mon Sep 17 00:00:00 2001 From: Dallin Romney Date: Wed, 1 Jan 2025 20:13:35 +0100 Subject: [PATCH 57/99] switch to strict null checks in gui --- gui/.eslintrc.json | 23 +- gui/package-lock.json | 355 ------------------ gui/package.json | 1 - gui/src/components/CheckDiv.tsx | 1 - .../InputToolbar/ToggleToolsButton.tsx | 4 +- gui/tsconfig.json | 3 +- gui/tsconfig.strictNullChecks.json | 31 -- 7 files changed, 10 insertions(+), 408 deletions(-) delete mode 100644 gui/tsconfig.strictNullChecks.json diff --git a/gui/.eslintrc.json b/gui/.eslintrc.json index 4962e7753d..b20127eb02 100644 --- a/gui/.eslintrc.json +++ b/gui/.eslintrc.json @@ -1,23 +1,16 @@ { "root": true, "parser": "@typescript-eslint/parser", - "plugins": [ - "@typescript-eslint", - "strict-null-checks", - "eslint-plugin-react" - ], + "plugins": ["@typescript-eslint", "eslint-plugin-react"], "parserOptions": { - "project": "gui/tsconfig.strictNullChecks.json" - }, - // "extends": [ - // "eslint:recommended", - // "plugin:@typescript-eslint/recommended", - // "plugin:@typescript-eslint/recommended-requiring-type-checking" - // "plugin:react/recommended" - // ], - "rules": { - "strict-null-checks/all": "warn" + "project": "gui/tsconfig.json" }, + "extends": [ + "eslint:recommended", + "plugin:@typescript-eslint/recommended", + "plugin:@typescript-eslint/recommended-requiring-type-checking" + // "plugin:react/recommended" + ], "overrides": [ { "files": ["src/**/*.{js,ts,jsx,tsx}"] diff --git a/gui/package-lock.json b/gui/package-lock.json index 9ff6fc72d1..23085939eb 100644 --- a/gui/package-lock.json +++ b/gui/package-lock.json @@ -85,7 +85,6 @@ "autoprefixer": "^10.4.13", "eslint": "^8", "eslint-plugin-react": "^7.37.2", - "eslint-plugin-strict-null-checks": "^0.1.3", "jsdom": "^25.0.1", "postcss": "^8.4.21", "tailwindcss": "^3.2.7", @@ -2936,12 +2935,6 @@ "integrity": "sha512-1m0bIFVc7eJWyve9S0RnuRgcQqF/Xd5QsUZAZeQFr1Q3/p9JWoQQEqmVy+DPTNpGXwhgIetAoYF8JSc33q29QA==", "dev": true }, - "node_modules/@types/json-schema": { - "version": "7.0.15", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", - "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", - "dev": true - }, "node_modules/@types/katex": { "version": "0.16.7", "resolved": "https://registry.npmjs.org/@types/katex/-/katex-0.16.7.tgz", @@ -3062,12 +3055,6 @@ "@types/node": "*" } }, - "node_modules/@types/semver": { - "version": "7.5.8", - "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.8.tgz", - "integrity": "sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ==", - "dev": true - }, "node_modules/@types/styled-components": { "version": "5.1.34", "resolved": "https://registry.npmjs.org/@types/styled-components/-/styled-components-5.1.34.tgz", @@ -3602,15 +3589,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/array-union": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", - "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", - "dev": true, - "engines": { - "node": ">=8" - } - }, "node_modules/array.prototype.findlast": { "version": "1.2.5", "resolved": "https://registry.npmjs.org/array.prototype.findlast/-/array.prototype.findlast-1.2.5.tgz", @@ -4671,18 +4649,6 @@ "integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==", "dev": true }, - "node_modules/dir-glob": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", - "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", - "dev": true, - "dependencies": { - "path-type": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/direction": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/direction/-/direction-1.0.4.tgz", @@ -5151,262 +5117,6 @@ "semver": "bin/semver.js" } }, - "node_modules/eslint-plugin-strict-null-checks": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/eslint-plugin-strict-null-checks/-/eslint-plugin-strict-null-checks-0.1.3.tgz", - "integrity": "sha512-c0bByePJytPXxZF9jBH17UQO41aCK08zThIXcAO3EnkdLhqUqIr+LVMfPstrN/9dLNzh4YKIbkbRPhRXRnNfNQ==", - "dev": true, - "dependencies": { - "@typescript-eslint/eslint-plugin": "^5.13.0", - "@typescript-eslint/experimental-utils": "^5.13.0", - "@typescript-eslint/parser": "^5.13.0", - "requireindex": "^1.2.0" - }, - "peerDependencies": { - "typescript": ">=3.9.4" - } - }, - "node_modules/eslint-plugin-strict-null-checks/node_modules/@typescript-eslint/eslint-plugin": { - "version": "5.62.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.62.0.tgz", - "integrity": "sha512-TiZzBSJja/LbhNPvk6yc0JrX9XqhQ0hdh6M2svYfsHGejaKFIAGd9MQ+ERIMzLGlN/kZoYIgdxFV0PuljTKXag==", - "dev": true, - "dependencies": { - "@eslint-community/regexpp": "^4.4.0", - "@typescript-eslint/scope-manager": "5.62.0", - "@typescript-eslint/type-utils": "5.62.0", - "@typescript-eslint/utils": "5.62.0", - "debug": "^4.3.4", - "graphemer": "^1.4.0", - "ignore": "^5.2.0", - "natural-compare-lite": "^1.4.0", - "semver": "^7.3.7", - "tsutils": "^3.21.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "@typescript-eslint/parser": "^5.0.0", - "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/eslint-plugin-strict-null-checks/node_modules/@typescript-eslint/experimental-utils": { - "version": "5.62.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-5.62.0.tgz", - "integrity": "sha512-RTXpeB3eMkpoclG3ZHft6vG/Z30azNHuqY6wKPBHlVMZFuEvrtlEDe8gMqDb+SO+9hjC/pLekeSCryf9vMZlCw==", - "dev": true, - "dependencies": { - "@typescript-eslint/utils": "5.62.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" - } - }, - "node_modules/eslint-plugin-strict-null-checks/node_modules/@typescript-eslint/parser": { - "version": "5.62.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.62.0.tgz", - "integrity": "sha512-VlJEV0fOQ7BExOsHYAGrgbEiZoi8D+Bl2+f6V2RrXerRSylnp+ZBHmPvaIa8cz0Ajx7WO7Z5RqfgYg7ED1nRhA==", - "dev": true, - "dependencies": { - "@typescript-eslint/scope-manager": "5.62.0", - "@typescript-eslint/types": "5.62.0", - "@typescript-eslint/typescript-estree": "5.62.0", - "debug": "^4.3.4" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/eslint-plugin-strict-null-checks/node_modules/@typescript-eslint/scope-manager": { - "version": "5.62.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.62.0.tgz", - "integrity": "sha512-VXuvVvZeQCQb5Zgf4HAxc04q5j+WrNAtNh9OwCsCgpKqESMTu3tF/jhZ3xG6T4NZwWl65Bg8KuS2uEvhSfLl0w==", - "dev": true, - "dependencies": { - "@typescript-eslint/types": "5.62.0", - "@typescript-eslint/visitor-keys": "5.62.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/eslint-plugin-strict-null-checks/node_modules/@typescript-eslint/type-utils": { - "version": "5.62.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.62.0.tgz", - "integrity": "sha512-xsSQreu+VnfbqQpW5vnCJdq1Z3Q0U31qiWmRhr98ONQmcp/yhiPJFPq8MXiJVLiksmOKSjIldZzkebzHuCGzew==", - "dev": true, - "dependencies": { - "@typescript-eslint/typescript-estree": "5.62.0", - "@typescript-eslint/utils": "5.62.0", - "debug": "^4.3.4", - "tsutils": "^3.21.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "*" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/eslint-plugin-strict-null-checks/node_modules/@typescript-eslint/types": { - "version": "5.62.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.62.0.tgz", - "integrity": "sha512-87NVngcbVXUahrRTqIK27gD2t5Cu1yuCXxbLcFtCzZGlfyVWWh8mLHkoxzjsB6DDNnvdL+fW8MiwPEJyGJQDgQ==", - "dev": true, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/eslint-plugin-strict-null-checks/node_modules/@typescript-eslint/typescript-estree": { - "version": "5.62.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.62.0.tgz", - "integrity": "sha512-CmcQ6uY7b9y694lKdRB8FEel7JbU/40iSAPomu++SjLMntB+2Leay2LO6i8VnJk58MtE9/nQSFIH6jpyRWyYzA==", - "dev": true, - "dependencies": { - "@typescript-eslint/types": "5.62.0", - "@typescript-eslint/visitor-keys": "5.62.0", - "debug": "^4.3.4", - "globby": "^11.1.0", - "is-glob": "^4.0.3", - "semver": "^7.3.7", - "tsutils": "^3.21.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/eslint-plugin-strict-null-checks/node_modules/@typescript-eslint/utils": { - "version": "5.62.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.62.0.tgz", - "integrity": "sha512-n8oxjeb5aIbPFEtmQxQYOLI0i9n5ySBEY/ZEHHZqKQSFnxio1rv6dthascc9dLuwrL0RC5mPCxB7vnAVGAYWAQ==", - "dev": true, - "dependencies": { - "@eslint-community/eslint-utils": "^4.2.0", - "@types/json-schema": "^7.0.9", - "@types/semver": "^7.3.12", - "@typescript-eslint/scope-manager": "5.62.0", - "@typescript-eslint/types": "5.62.0", - "@typescript-eslint/typescript-estree": "5.62.0", - "eslint-scope": "^5.1.1", - "semver": "^7.3.7" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" - } - }, - "node_modules/eslint-plugin-strict-null-checks/node_modules/@typescript-eslint/visitor-keys": { - "version": "5.62.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.62.0.tgz", - "integrity": "sha512-07ny+LHRzQXepkGg6w0mFY41fVUNBrL2Roj/++7V1txKugfjm/Ci/qSND03r2RhlJhJYMcTn9AhhSSqQp0Ysyw==", - "dev": true, - "dependencies": { - "@typescript-eslint/types": "5.62.0", - "eslint-visitor-keys": "^3.3.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/eslint-plugin-strict-null-checks/node_modules/eslint-scope": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", - "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", - "dev": true, - "dependencies": { - "esrecurse": "^4.3.0", - "estraverse": "^4.1.1" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/eslint-plugin-strict-null-checks/node_modules/eslint-scope/node_modules/estraverse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", - "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", - "dev": true, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/eslint-plugin-strict-null-checks/node_modules/eslint-visitor-keys": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", - "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", - "dev": true, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, "node_modules/eslint-scope": { "version": "7.2.2", "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", @@ -6303,26 +6013,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/globby": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", - "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", - "dev": true, - "dependencies": { - "array-union": "^2.1.0", - "dir-glob": "^3.0.1", - "fast-glob": "^3.2.9", - "ignore": "^5.2.0", - "merge2": "^1.4.1", - "slash": "^3.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/gopd": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", @@ -8916,12 +8606,6 @@ "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", "dev": true }, - "node_modules/natural-compare-lite": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare-lite/-/natural-compare-lite-1.4.0.tgz", - "integrity": "sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g==", - "dev": true - }, "node_modules/node-releases": { "version": "2.0.18", "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.18.tgz", @@ -9331,15 +9015,6 @@ "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", "dev": true }, - "node_modules/path-type": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", - "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", - "dev": true, - "engines": { - "node": ">=8" - } - }, "node_modules/pathe": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/pathe/-/pathe-1.1.2.tgz", @@ -10875,15 +10550,6 @@ "node": ">=0.10.0" } }, - "node_modules/requireindex": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/requireindex/-/requireindex-1.2.0.tgz", - "integrity": "sha512-L9jEkOi3ASd9PYit2cwRfyppc9NoABujTP8/5gFcbERmo5jUoAKovIC3fsF17pkTnGsrByysqX+Kxd2OTNI1ww==", - "dev": true, - "engines": { - "node": ">=0.10.5" - } - }, "node_modules/reselect": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/reselect/-/reselect-5.1.1.tgz", @@ -12094,27 +11760,6 @@ "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" }, - "node_modules/tsutils": { - "version": "3.21.0", - "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", - "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", - "dev": true, - "dependencies": { - "tslib": "^1.8.1" - }, - "engines": { - "node": ">= 6" - }, - "peerDependencies": { - "typescript": ">=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta" - } - }, - "node_modules/tsutils/node_modules/tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "dev": true - }, "node_modules/type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", diff --git a/gui/package.json b/gui/package.json index d0150d9997..23bde3349b 100644 --- a/gui/package.json +++ b/gui/package.json @@ -93,7 +93,6 @@ "autoprefixer": "^10.4.13", "eslint": "^8", "eslint-plugin-react": "^7.37.2", - "eslint-plugin-strict-null-checks": "^0.1.3", "jsdom": "^25.0.1", "postcss": "^8.4.21", "tailwindcss": "^3.2.7", diff --git a/gui/src/components/CheckDiv.tsx b/gui/src/components/CheckDiv.tsx index eaea0dc1a9..bb4bdaa007 100644 --- a/gui/src/components/CheckDiv.tsx +++ b/gui/src/components/CheckDiv.tsx @@ -1,4 +1,3 @@ -import { useState } from "react"; import styled from "styled-components"; import { defaultBorderRadius, vscBackground, vscForeground } from "."; import { CheckIcon } from "@heroicons/react/24/outline"; diff --git a/gui/src/components/mainInput/InputToolbar/ToggleToolsButton.tsx b/gui/src/components/mainInput/InputToolbar/ToggleToolsButton.tsx index 9dae27363c..edb82fd3d3 100644 --- a/gui/src/components/mainInput/InputToolbar/ToggleToolsButton.tsx +++ b/gui/src/components/mainInput/InputToolbar/ToggleToolsButton.tsx @@ -6,10 +6,8 @@ import { import { WrenchScrewdriverIcon as WrenchScrewdriverIconSolid } from "@heroicons/react/24/solid"; import { useEffect, useRef, useState } from "react"; import { useDispatch } from "react-redux"; -import styled from "styled-components"; -import { defaultBorderRadius, lightGray, vscForeground } from "../.."; +import { lightGray, vscForeground } from "../.."; import { toggleUseTools } from "../../../redux/slices/uiSlice"; -import { getFontSize } from "../../../util"; import InfoHover from "../../InfoHover"; import HoverItem from "./HoverItem"; import ToolDropdownItem from "./ToolDropdownItem"; diff --git a/gui/tsconfig.json b/gui/tsconfig.json index 64e59bd410..d41a6b9c31 100644 --- a/gui/tsconfig.json +++ b/gui/tsconfig.json @@ -7,7 +7,7 @@ "skipLibCheck": true, "esModuleInterop": false, "allowSyntheticDefaultImports": true, - "strict": false, + "strict": true, "forceConsistentCasingInFileNames": true, "module": "ESNext", "moduleResolution": "Node", @@ -18,7 +18,6 @@ "noEmitOnError": false, "types": ["vitest/globals"] // Disabled for now since it's causing too many errors - // "strictNullChecks": true }, "include": ["src", "../src/util/messenger.ts"], "references": [{ "path": "./tsconfig.node.json" }] diff --git a/gui/tsconfig.strictNullChecks.json b/gui/tsconfig.strictNullChecks.json deleted file mode 100644 index 2825e46eb2..0000000000 --- a/gui/tsconfig.strictNullChecks.json +++ /dev/null @@ -1,31 +0,0 @@ -{ - "compilerOptions": { - "strictNullChecks": true, - - - "target": "ESNext", - "useDefineForClassFields": true, - "lib": ["DOM", "DOM.Iterable", "ESNext"], - "allowJs": false, - "skipLibCheck": true, - "esModuleInterop": false, - "allowSyntheticDefaultImports": true, - "strict": false, - "forceConsistentCasingInFileNames": true, - "module": "ESNext", - "moduleResolution": "Node", - "resolveJsonModule": true, - "isolatedModules": true, - "noEmit": true, - "jsx": "react-jsx", - "noEmitOnError": false, - "types": ["vitest/globals"] - }, - "include": ["src/**/*.ts", "src/**/*.tsx", "src/**/*.js", "src/**/*.jsx"], - "exclude": ["dist", "node_modules"], - - - - - -} From fa18259a34a3b3940fbbe1a019136b94aa8c260f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Roger=20=C3=96berg?= Date: Wed, 1 Jan 2025 21:08:48 +0100 Subject: [PATCH 58/99] Update README.md --- core/autocomplete/README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/core/autocomplete/README.md b/core/autocomplete/README.md index 1136b54abc..ab6e9adfbd 100644 --- a/core/autocomplete/README.md +++ b/core/autocomplete/README.md @@ -7,7 +7,7 @@ Continue now provides support for tab autocomplete in [VS Code](https://marketpl We recommend setting up tab-autocomplete with a local Ollama instance. To do this, first download the latest version of Ollama from [here](https://ollama.ai). Then, run the following command to download our recommended model: ```bash -ollama run qwen2.5-coder:1.5b +ollama run qwen2.5-coder:1.5b-base ``` Once it has been downloaded, you should begin to see completions in VS Code. @@ -101,7 +101,7 @@ This object allows you to customize the behavior of tab-autocomplete. The availa "tabAutocompleteModel": { "title": "Tab Autocomplete Model", "provider": "ollama", - "model": "qwen2.5-coder:1.5b", + "model": "qwen2.5-coder:1.5b-base", "apiBase": "https://" }, "tabAutocompleteOptions": { @@ -123,9 +123,9 @@ Follow these steps to ensure that everything is set up correctly: 1. Make sure you have the "Enable Tab Autocomplete" setting checked (in VS Code, you can toggle by clicking the "Continue" button in the status bar). 2. Make sure you have downloaded Ollama. -3. Run `ollama run qwen2.5-coder:1.5b` to verify that the model is downloaded. +3. Run `ollama run qwen2.5-coder:1.5b-base` to verify that the model is downloaded. 4. Make sure that any other completion providers are disabled (e.g. Copilot), as they may interfere. -5. Make sure that you aren't also using another Ollama model for chat. This will cause Ollama to constantly load and unload the models from memory, resulting in slow responses (or none at all) for both. +5. If you are using another Ollama model for chat and your resources are limited, this will cause Ollama to constantly load and unload the models from memory, resulting in slow responses (or none at all) for both. A resolution for this could be using the same model for chat and autocomplete. 6. Check the output of the logs to find any potential errors (cmd/ctrl+shift+p -> "Toggle Developer Tools" -> "Console" tab in VS Code, ~/.continue/logs/core.log in JetBrains). 7. If you are still having issues, please let us know in our [Discord](https://discord.gg/vapESyrFmJ) and we'll help as soon as possible. From 96200c094f89141ac33e10e9daa0a78ff2ef30e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Roger=20=C3=96berg?= Date: Wed, 1 Jan 2025 21:22:10 +0100 Subject: [PATCH 59/99] removed base from title --- docs/docs/autocomplete/model-setup.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/docs/autocomplete/model-setup.md b/docs/docs/autocomplete/model-setup.md index fc5233baa5..2312123a06 100644 --- a/docs/docs/autocomplete/model-setup.md +++ b/docs/docs/autocomplete/model-setup.md @@ -31,7 +31,7 @@ For those preferring local execution or self-hosting,`Qwen2.5-Coder 1.5B` offers ```json title="config.json"" { "tabAutocompleteModel": { - "title": "Qwen2.5-Coder 1.5B Base", + "title": "Qwen2.5-Coder 1.5B", "model": "qwen2.5-coder:1.5b-base", "provider": "ollama" } From 2966c9c57d7b4317504f1e8952d2367fc3d221a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Roger=20=C3=96berg?= Date: Wed, 1 Jan 2025 21:24:21 +0100 Subject: [PATCH 60/99] removed base since its a name and not specific model --- docs/docs/customize/model-types/autocomplete.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/docs/customize/model-types/autocomplete.md b/docs/docs/customize/model-types/autocomplete.md index 7f5abc8e21..d859206051 100644 --- a/docs/docs/customize/model-types/autocomplete.md +++ b/docs/docs/customize/model-types/autocomplete.md @@ -13,4 +13,4 @@ In Continue, these models are used to display inline [Autocomplete](../../autoco If you have the ability to use any model, we recommend `Codestral` with [Mistral](../model-providers/top-level/mistral.md#autocomplete-model) or [Vertex AI](../model-providers/top-level/vertexai.md#autocomplete-model). -If you want to run a model locally, we recommend `Qwen2.5-Coder 1.5B-base` with [Ollama](../model-providers/top-level/ollama.md#autocomplete-model). +If you want to run a model locally, we recommend `Qwen2.5-Coder 1.5B` with [Ollama](../model-providers/top-level/ollama.md#autocomplete-model). From 5516ba86ff0cbed6b0777380c273805094c9ac20 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Roger=20=C3=96berg?= Date: Wed, 1 Jan 2025 21:25:26 +0100 Subject: [PATCH 61/99] removed base from title --- docs/docs/customize/model-providers/top-level/ollama.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/docs/customize/model-providers/top-level/ollama.md b/docs/docs/customize/model-providers/top-level/ollama.md index c307a93448..e1f3e53e89 100644 --- a/docs/docs/customize/model-providers/top-level/ollama.md +++ b/docs/docs/customize/model-providers/top-level/ollama.md @@ -23,12 +23,12 @@ We recommend configuring **Llama3.1 8B** as your chat model. ## Autocomplete model -We recommend configuring **Qwen2.5-Coder 1.5B-base** as your autocomplete model. +We recommend configuring **Qwen2.5-Coder 1.5B** as your autocomplete model. ```json title="config.json" { "tabAutocompleteModel": { - "title": "Qwen2.5-Coder 1.5B-base", + "title": "Qwen2.5-Coder 1.5B", "provider": "ollama", "model": "qwen2.5-coder:1.5b-base" } From abf1d89a3966498d7529ae2ca1747ac6bb1f9b2d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Roger=20=C3=96berg?= Date: Wed, 1 Jan 2025 22:24:09 +0100 Subject: [PATCH 62/99] added -base to the qwen-modelse --- docs/docs/customize/deep-dives/autocomplete.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/docs/customize/deep-dives/autocomplete.md b/docs/docs/customize/deep-dives/autocomplete.md index 4aa5f768d2..221a6d49fa 100644 --- a/docs/docs/customize/deep-dives/autocomplete.md +++ b/docs/docs/customize/deep-dives/autocomplete.md @@ -23,7 +23,7 @@ If you want to have the best autocomplete experience, we recommend using Codestr If you'd like to run your autocomplete model locally, we recommend using Ollama. To do this, first download the latest version of Ollama from [here](https://ollama.ai). Then, run the following command to download our recommended model: ```bash -ollama run qwen2.5-coder:1.5b +ollama run qwen2.5-coder:1.5b-base ``` Once it has been downloaded, you should begin to see completions in VS Code. @@ -37,7 +37,7 @@ All of the configuration options available for chat models are available to use "tabAutocompleteModel": { "title": "Tab Autocomplete Model", "provider": "ollama", - "model": "qwen2.5-coder:1.5b", + "model": "qwen2.5-coder:1.5b-base", "apiBase": "https://" }, ... @@ -76,7 +76,7 @@ This object allows you to customize the behavior of tab-autocomplete. The availa "tabAutocompleteModel": { "title": "Tab Autocomplete Model", "provider": "ollama", - "model": "qwen2.5-coder:1.5b", + "model": "qwen2.5-coder:1.5b-base", "apiBase": "https://" }, "tabAutocompleteOptions": { @@ -99,7 +99,7 @@ Follow these steps to ensure that everything is set up correctly: 1. Make sure you have the "Enable Tab Autocomplete" setting checked (in VS Code, you can toggle by clicking the "Continue" button in the status bar, and in JetBrains by going to Settings -> Tools -> Continue). 2. Make sure you have downloaded Ollama. -3. Run `ollama run qwen2.5-coder:1.5b` to verify that the model is downloaded. +3. Run `ollama run qwen2.5-coder:1.5b-base` to verify that the model is downloaded. 4. Make sure that any other completion providers are disabled (e.g. Copilot), as they may interfere. 5. Check the output of the logs to find any potential errors: cmd/ctrl + shift + P -> "Toggle Developer Tools" -> "Console" tab in VS Code, ~/.continue/logs/core.log in JetBrains. 6. Check VS Code settings to make sure that `"editor.inlineSuggest.enabled"` is set to `true` (use cmd/ctrl + , then search for this and check the box) From abfffd831abe960b4881472b2c7c3ca94f187a81 Mon Sep 17 00:00:00 2001 From: "Johnny.H" Date: Thu, 2 Jan 2025 15:53:12 +0800 Subject: [PATCH 63/99] fix `renderInlineAs` not working in `HttpContextProvider` --- core/context/providers/HttpContextProvider.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/core/context/providers/HttpContextProvider.ts b/core/context/providers/HttpContextProvider.ts index 05a50e15da..7b7228eee5 100644 --- a/core/context/providers/HttpContextProvider.ts +++ b/core/context/providers/HttpContextProvider.ts @@ -22,6 +22,9 @@ class HttpContextProvider extends BaseContextProvider { this.options.description || "Retrieve a context item from a custom server", type: "normal", + renderInlineAs: + this.options.renderInlineAs || + HttpContextProvider.description.renderInlineAs, }; } From 0001e275e14b26aaabadbeb30ac4fdeb843d6122 Mon Sep 17 00:00:00 2001 From: Nate Date: Thu, 2 Jan 2025 15:29:12 -0500 Subject: [PATCH 64/99] update reference --- docs/docs/reference.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/docs/reference.md b/docs/docs/reference.md index 5278205567..b61357649c 100644 --- a/docs/docs/reference.md +++ b/docs/docs/reference.md @@ -88,7 +88,7 @@ Specifies options for tab autocompletion behavior. - `debounceDelay`: Delay (in ms) before triggering autocomplete. - `maxSuffixPercentage`: Maximum percentage of prompt for suffix. - `prefixPercentage`: Percentage of input for prefix. -- `template`: Template string for autocomplete, using Mustache templating. +- `template`: Template string for autocomplete, using Mustache templating. You can use the `{{{ prefix }}}`, `{{{ suffix }}}`, `{{{ filename }}}`, `{{{ reponame }}}`, and `{{{ language }}}` variables. - `multilineCompletions`: Controls multiline completions (`"always"`, `"never"`, or `"auto"`). - `useCache`: If `true`, caches completions. - `onlyMyCode`: If `true`, only includes code within the repository. From 9cc844fb178a77667a4cd49b0d671f9631af67af Mon Sep 17 00:00:00 2001 From: Nate Date: Thu, 2 Jan 2025 15:33:08 -0500 Subject: [PATCH 65/99] update config_schema.json --- extensions/vscode/config_schema.json | 108 +++++++++++++-------------- 1 file changed, 54 insertions(+), 54 deletions(-) diff --git a/extensions/vscode/config_schema.json b/extensions/vscode/config_schema.json index f2c98642f8..c9df4e9769 100644 --- a/extensions/vscode/config_schema.json +++ b/extensions/vscode/config_schema.json @@ -2805,10 +2805,7 @@ "type": "object", "properties": { "model": { - "enum": [ - "cohere.rerank-v3-5:0", - "amazon.rerank-v1:0" - ] + "enum": ["cohere.rerank-v3-5:0", "amazon.rerank-v1:0"] } }, "required": ["model"] @@ -3132,62 +3129,65 @@ "description": "Allow tool use. Currently only supported with Claude 3.5 Sonnet", "default": true }, - "modelContextProtocolServer": { - "type": "object", - "properties": { - "transport": { - "oneOf": [ - { - "type": "object", - "properties": { - "type": { - "type": "string", - "enum": ["stdio"] - }, - "command": { - "type": "string" - }, - "args": { - "type": "array", - "items": { + "modelContextProtocolServers": { + "type": "array", + "items": { + "type": "object", + "properties": { + "transport": { + "oneOf": [ + { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": ["stdio"] + }, + "command": { "type": "string" + }, + "args": { + "type": "array", + "items": { + "type": "string" + } } - } - }, - "required": ["type", "command", "args"] - }, - { - "type": "object", - "properties": { - "type": { - "type": "string", - "enum": ["websocket"] }, - "url": { - "type": "string", - "format": "uri" - } + "required": ["type", "command", "args"] }, - "required": ["type", "url"] - }, - { - "type": "object", - "properties": { - "type": { - "type": "string", - "enum": ["sse"] + { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": ["websocket"] + }, + "url": { + "type": "string", + "format": "uri" + } }, - "url": { - "type": "string", - "format": "uri" - } + "required": ["type", "url"] }, - "required": ["type", "url"] - } - ] - } - }, - "required": ["transport"] + { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": ["sse"] + }, + "url": { + "type": "string", + "format": "uri" + } + }, + "required": ["type", "url"] + } + ] + } + }, + "required": ["transport"] + } } } } From e66b79d7bd0d0408dbb5689dd45193b3b818ce5f Mon Sep 17 00:00:00 2001 From: Dallin Romney Date: Thu, 2 Jan 2025 22:42:01 +0100 Subject: [PATCH 66/99] gui strict null checks p1 --- gui/.eslintrc.json | 6 +++--- gui/package-lock.json | 17 +++++++++++++++ gui/package.json | 2 ++ .../CodeToEditCard/AddFileCombobox.tsx | 8 +++---- .../CodeToEditCard/CodeToEditCard.tsx | 14 ++++++------- .../CodeToEditCard/CodeToEditListItem.tsx | 8 +++---- .../components/BestExperienceConfigForm.tsx | 2 +- gui/src/components/mainInput/MentionList.tsx | 20 +++++++++++------- .../components/modelSelection/ModelSelect.tsx | 10 ++++----- gui/src/context/SubmenuContextProviders.tsx | 4 +--- gui/src/context/VscTheme.tsx | 4 ++-- gui/src/forms/AddModelForm.tsx | 19 +++++++++++------ gui/src/hooks/useWebviewListener.ts | 4 ++-- gui/src/pages/gui/Chat.tsx | 3 +-- gui/src/pages/gui/StreamError.tsx | 21 +++++++++++-------- gui/src/pages/gui/ToolCallDiv/ThreadDiv.tsx | 3 +-- gui/src/pages/gui/ToolCallDiv/index.tsx | 2 -- gui/src/redux/selectors/selectLastToolCall.ts | 3 +-- gui/src/redux/slices/sessionSlice.ts | 11 +++++++--- gui/src/redux/thunks/streamNormalInput.ts | 2 +- .../thunks/streamResponseAfterToolCall.ts | 5 +++++ 21 files changed, 103 insertions(+), 65 deletions(-) diff --git a/gui/.eslintrc.json b/gui/.eslintrc.json index b20127eb02..346c650fef 100644 --- a/gui/.eslintrc.json +++ b/gui/.eslintrc.json @@ -6,9 +6,9 @@ "project": "gui/tsconfig.json" }, "extends": [ - "eslint:recommended", - "plugin:@typescript-eslint/recommended", - "plugin:@typescript-eslint/recommended-requiring-type-checking" + // "eslint:recommended", + // "plugin:@typescript-eslint/recommended", + // "plugin:@typescript-eslint/recommended-requiring-type-checking" // "plugin:react/recommended" ], "overrides": [ diff --git a/gui/package-lock.json b/gui/package-lock.json index 23085939eb..4c735e3ef6 100644 --- a/gui/package-lock.json +++ b/gui/package-lock.json @@ -24,6 +24,7 @@ "@tiptap/react": "^2.1.13", "@tiptap/starter-kit": "^2.1.13", "@tiptap/suggestion": "^2.1.13", + "@types/uuid": "^10.0.0", "@types/vscode-webview": "^1.57.1", "core": "file:../core", "dompurify": "^3.0.6", @@ -70,6 +71,7 @@ "@testing-library/jest-dom": "^6.5.0", "@testing-library/react": "^16.0.1", "@testing-library/user-event": "^14.5.2", + "@types/dompurify": "^3.2.0", "@types/lodash": "^4.17.6", "@types/mustache": "^4.2.5", "@types/node": "^20.5.6", @@ -2893,6 +2895,16 @@ "@types/ms": "*" } }, + "node_modules/@types/dompurify": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/@types/dompurify/-/dompurify-3.2.0.tgz", + "integrity": "sha512-Fgg31wv9QbLDA0SpTOXO3MaxySc4DKGLi8sna4/Utjo4r3ZRPdCt4UQee8BWr+Q5z21yifghREPJGYaEOEIACg==", + "deprecated": "This is a stub types definition. dompurify provides its own type definitions, so you do not need this installed.", + "dev": true, + "dependencies": { + "dompurify": "*" + } + }, "node_modules/@types/estree": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz", @@ -3076,6 +3088,11 @@ "resolved": "https://registry.npmjs.org/@types/use-sync-external-store/-/use-sync-external-store-0.0.6.tgz", "integrity": "sha512-zFDAD+tlpf2r4asuHEj0XH6pY6i0g5NeAHPn+15wk3BV6JA69eERFXC1gyGThDkVa1zCyKr5jox1+2LbV/AMLg==" }, + "node_modules/@types/uuid": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-10.0.0.tgz", + "integrity": "sha512-7gqG38EyHgyP1S+7+xomFtL+ZNHcKv6DwNaCZmJmo1vgMugyF3TCnXVg4t1uk89mLNwnLtnY3TpOpCOyp1/xHQ==" + }, "node_modules/@types/vscode-webview": { "version": "1.57.5", "resolved": "https://registry.npmjs.org/@types/vscode-webview/-/vscode-webview-1.57.5.tgz", diff --git a/gui/package.json b/gui/package.json index 23bde3349b..58a4529d8a 100644 --- a/gui/package.json +++ b/gui/package.json @@ -32,6 +32,7 @@ "@tiptap/react": "^2.1.13", "@tiptap/starter-kit": "^2.1.13", "@tiptap/suggestion": "^2.1.13", + "@types/uuid": "^10.0.0", "@types/vscode-webview": "^1.57.1", "core": "file:../core", "dompurify": "^3.0.6", @@ -78,6 +79,7 @@ "@testing-library/jest-dom": "^6.5.0", "@testing-library/react": "^16.0.1", "@testing-library/user-event": "^14.5.2", + "@types/dompurify": "^3.2.0", "@types/lodash": "^4.17.6", "@types/mustache": "^4.2.5", "@types/node": "^20.5.6", diff --git a/gui/src/components/CodeToEditCard/AddFileCombobox.tsx b/gui/src/components/CodeToEditCard/AddFileCombobox.tsx index b79365fb1f..129c0f082b 100644 --- a/gui/src/components/CodeToEditCard/AddFileCombobox.tsx +++ b/gui/src/components/CodeToEditCard/AddFileCombobox.tsx @@ -8,8 +8,8 @@ import FileIcon from "../FileIcon"; import { useAppSelector } from "../../redux/hooks"; export interface AddFileComboboxProps { - onSelect: (filepaths: string[]) => void; - onEscape: () => void; + onSelect: (filepaths: string[]) => void | Promise; + onEscape: () => void | Promise; } export default function AddFileCombobox({ @@ -50,7 +50,7 @@ export default function AddFileCombobox({ value={selectedFiles} onChange={(files) => { setSelectedFiles(files); - onSelect(files.map((file) => file.id)); + void onSelect(files.map((file) => file.id)); buttonRef.current?.click(); }} > @@ -72,7 +72,7 @@ export default function AddFileCombobox({ placeholder="Type to search files..." onKeyDown={(e) => { if (e.key === "Escape" && !open) { - onEscape(); + void onEscape(); } }} /> diff --git a/gui/src/components/CodeToEditCard/CodeToEditCard.tsx b/gui/src/components/CodeToEditCard/CodeToEditCard.tsx index 7bd469c240..99149ba5fc 100644 --- a/gui/src/components/CodeToEditCard/CodeToEditCard.tsx +++ b/gui/src/components/CodeToEditCard/CodeToEditCard.tsx @@ -2,7 +2,7 @@ import { useContext, useState } from "react"; import { useDispatch } from "react-redux"; import { IdeMessengerContext } from "../../context/IdeMessenger"; import CodeToEditListItem from "./CodeToEditListItem"; -import type { CodeToEdit, RangeInFileWithContents } from "core"; +import type { CodeToEdit } from "core"; import AddFileButton from "./AddFileButton"; import AddFileCombobox from "./AddFileCombobox"; import { PlusIcon, XMarkIcon } from "@heroicons/react/24/outline"; @@ -25,19 +25,19 @@ export default function CodeToEditCard() { ? "Code to edit (1 item)" : `Code to edit (${codeToEdit.length} items)`; - function onDelete(rif: RangeInFileWithContents) { + function onDelete(rif: CodeToEdit) { dispatch(removeCodeToEdit(rif)); } - function onClickFilename(code: CodeToEdit) { + async function onClickFilename(code: CodeToEdit) { if ("range" in code) { - ideMessenger.ide.showLines( + await ideMessenger.ide.showLines( code.filepath, code.range.start.line, code.range.end.line, ); } else { - ideMessenger.ide.openFile(code.filepath); + await ideMessenger.ide.openFile(code.filepath); } } @@ -93,10 +93,10 @@ export default function CodeToEditCard() { />
{ + onClick={() => { setShowAddFileCombobox(false); }} - className="text-lightgray hover:bg-lightgray hover:text-vsc-foreground mb-2 h-3.5 w-3.5 cursor-pointer rounded-md rounded-sm p-0.5 hover:bg-opacity-20" + className="text-lightgray hover:bg-lightgray hover:text-vsc-foreground mb-2 h-3.5 w-3.5 cursor-pointer rounded-sm p-0.5 hover:bg-opacity-20" />
)} diff --git a/gui/src/components/CodeToEditCard/CodeToEditListItem.tsx b/gui/src/components/CodeToEditCard/CodeToEditListItem.tsx index 46380f0843..f915f27d73 100644 --- a/gui/src/components/CodeToEditCard/CodeToEditListItem.tsx +++ b/gui/src/components/CodeToEditCard/CodeToEditListItem.tsx @@ -16,8 +16,8 @@ import { export interface CodeToEditListItemProps { code: CodeToEdit; - onDelete: (codeToEdit: CodeToEdit) => void; - onClickFilename: (codeToEdit: CodeToEdit) => void; + onDelete: (codeToEdit: CodeToEdit) => void | Promise; + onClickFilename: (codeToEdit: CodeToEdit) => void | Promise; } const NoPaddingWrapper = styled.div` @@ -87,7 +87,7 @@ export default function CodeToEditListItem({ className="flex-shrink-0 text-xs hover:underline" onClick={(e) => { e.stopPropagation(); - onClickFilename(code); + void onClickFilename(code); }} > {title} @@ -122,7 +122,7 @@ export default function CodeToEditListItem({ { e.stopPropagation(); - onDelete(code); + void onDelete(code); }} className="text-lightgray hover:bg-lightgray hover:text-vsc-foreground h-3.5 w-3.5 cursor-pointer rounded-sm p-0.5 hover:bg-opacity-20" /> diff --git a/gui/src/components/OnboardingCard/components/BestExperienceConfigForm.tsx b/gui/src/components/OnboardingCard/components/BestExperienceConfigForm.tsx index a76f6b57b2..7e81bdf040 100644 --- a/gui/src/components/OnboardingCard/components/BestExperienceConfigForm.tsx +++ b/gui/src/components/OnboardingCard/components/BestExperienceConfigForm.tsx @@ -83,7 +83,7 @@ function BestExperienceConfigForm({
diff --git a/gui/src/components/mainInput/MentionList.tsx b/gui/src/components/mainInput/MentionList.tsx index f0f328a8d3..754d436fe4 100644 --- a/gui/src/components/mainInput/MentionList.tsx +++ b/gui/src/components/mainInput/MentionList.tsx @@ -66,11 +66,12 @@ const ICONS_FOR_DROPDOWN: { [key: string]: any } = { "/cmd": CommandLineIcon, }; -export function getIconFromDropdownItem(id: string, type: ComboBoxItemType) { - return ( - ICONS_FOR_DROPDOWN[id] ?? - (type === "contextProvider" ? AtSymbolIcon : BoltIcon) - ); +export function getIconFromDropdownItem( + id: string | undefined, + type: ComboBoxItemType, +) { + const typeIcon = type === "contextProvider" ? AtSymbolIcon : BoltIcon; + return id ? (ICONS_FOR_DROPDOWN[id] ?? typeIcon) : typeIcon; } function DropdownIcon(props: { className?: string; item: ComboBoxItem }) { @@ -238,7 +239,7 @@ const MentionList = forwardRef((props: MentionListProps, ref) => { } }, [querySubmenuItem]); - const selectItem = (index) => { + const selectItem = (index: number) => { const item = allItems[index]; if (item.type === "action" && item.action) { @@ -251,7 +252,9 @@ const MentionList = forwardRef((props: MentionListProps, ref) => { item.contextProvider?.type === "submenu" ) { setSubMenuTitle(item.description); - props.enterSubmenu(props.editor, item.id); + if (item.id) { + props.enterSubmenu?.(props.editor, item.id); + } return; } @@ -359,6 +362,9 @@ const MentionList = forwardRef((props: MentionListProps, ref) => { ref={queryInputRef} placeholder={querySubmenuItem.description} onKeyDown={(e) => { + if (!queryInputRef.current) { + return; + } if (e.key === "Enter") { if (e.shiftKey) { queryInputRef.current.innerText += "\n"; diff --git a/gui/src/components/modelSelection/ModelSelect.tsx b/gui/src/components/modelSelection/ModelSelect.tsx index c5edb5d7b8..93047be18c 100644 --- a/gui/src/components/modelSelection/ModelSelect.tsx +++ b/gui/src/components/modelSelection/ModelSelect.tsx @@ -6,7 +6,7 @@ import { PlusIcon, TrashIcon, } from "@heroicons/react/24/outline"; -import { useContext, useEffect, useRef, useState } from "react"; +import { MouseEvent, useContext, useEffect, useRef, useState } from "react"; import { useDispatch, useSelector } from "react-redux"; import styled from "styled-components"; import { @@ -144,7 +144,7 @@ function ModelOption({ const dispatch = useDispatch(); const [hovered, setHovered] = useState(false); - function onClickDelete(e) { + function onClickDelete(e: MouseEvent) { e.stopPropagation(); e.preventDefault(); @@ -164,7 +164,7 @@ function ModelOption({ ); } - function onClickGear(e) { + function onClickGear(e: MouseEvent) { e.stopPropagation(); e.preventDefault(); @@ -173,7 +173,7 @@ function ModelOption({ }); } - function handleOptionClick(e) { + function handleOptionClick(e: MouseEvent) { if (showMissingApiKeyMsg) { e.preventDefault(); e.stopPropagation(); @@ -287,7 +287,7 @@ function ModelSelect() { setShowAbove(spaceBelow < dropdownHeight && spaceAbove > spaceBelow); } - function onClickAddModel(e) { + function onClickAddModel(e: MouseEvent) { e.stopPropagation(); e.preventDefault(); diff --git a/gui/src/context/SubmenuContextProviders.tsx b/gui/src/context/SubmenuContextProviders.tsx index c8ba17c774..673caec1c2 100644 --- a/gui/src/context/SubmenuContextProviders.tsx +++ b/gui/src/context/SubmenuContextProviders.tsx @@ -82,8 +82,6 @@ export const SubmenuContextProvidersProvider = ({ const [isLoading, setIsLoading] = useState(false); const [autoLoadTriggered, setAutoLoadTriggered] = useState(false); - const config = useAppSelector((store) => store.config.config); - const ideMessenger = useContext(IdeMessengerContext); const getOpenFilesItems = useCallback(async () => { @@ -102,7 +100,7 @@ export const SubmenuContextProvidersProvider = ({ })); }, [ideMessenger]); - useWebviewListener("refreshSubmenuItems", async (data) => { + useWebviewListener("refreshSubmenuItems", async () => { if (!isLoading) { setInitialLoadComplete(false); setAutoLoadTriggered((prev) => !prev); // Toggle to trigger effect diff --git a/gui/src/context/VscTheme.tsx b/gui/src/context/VscTheme.tsx index c376ee2b4a..2ca1dac59e 100644 --- a/gui/src/context/VscTheme.tsx +++ b/gui/src/context/VscTheme.tsx @@ -58,7 +58,7 @@ function constructTheme( ): Record { const rules = tmTheme?.["rules"] || []; - const tokenToForeground = {}; + const tokenToForeground: Record = {}; rules.forEach(({ token, foreground }) => { if (!foreground || !token) { return; @@ -66,7 +66,7 @@ function constructTheme( tokenToForeground[token] = foreground; }); - const theme = {}; + const theme: Record = {}; Object.keys(hljsToTextMate).forEach((className) => { const tokens = hljsToTextMate[className]; for (const scope of tokens) { diff --git a/gui/src/forms/AddModelForm.tsx b/gui/src/forms/AddModelForm.tsx index 8753b4010e..572251fd6a 100644 --- a/gui/src/forms/AddModelForm.tsx +++ b/gui/src/forms/AddModelForm.tsx @@ -52,7 +52,8 @@ function AddModelForm({ const allProviders = Object.entries(providers) .filter(([key]) => !["freetrial", "openai-aiohttp"].includes(key)) - .map(([, provider]) => provider); + .map(([, provider]) => provider) + .filter((provider) => !!provider); const popularProviders = allProviders .filter((provider) => popularProviderTitles.includes(provider.title)) @@ -187,8 +188,9 @@ function AddModelForm({ setSelectedProvider={setSelectedModel} otherOptions={ Object.entries(providers).find( - ([, provider]) => provider.title === selectedProvider.title, - )?.[1].packages + ([, provider]) => + provider?.title === selectedProvider.title, + )?.[1]?.packages } />
@@ -220,9 +222,14 @@ function AddModelForm({ - ideMessenger.post("openUrl", selectedProviderApiKeyUrl) - } + onClick={() => { + if (selectedProviderApiKeyUrl) { + ideMessenger.post( + "openUrl", + selectedProviderApiKeyUrl, + ); + } + }} > Click here {" "} diff --git a/gui/src/hooks/useWebviewListener.ts b/gui/src/hooks/useWebviewListener.ts index 568084a2dd..4bdd9f5dc0 100644 --- a/gui/src/hooks/useWebviewListener.ts +++ b/gui/src/hooks/useWebviewListener.ts @@ -13,10 +13,10 @@ export function useWebviewListener( useEffect( () => { - let listener; + let listener: (event: { data: Message }) => Promise; if (!skip) { - listener = async (event: { data: Message }) => { + listener = async (event) => { if (event.data.messageType === messageType) { const result = await handler(event.data.data); ideMessenger.respond(messageType, result, event.data.messageId); diff --git a/gui/src/pages/gui/Chat.tsx b/gui/src/pages/gui/Chat.tsx index de8453445c..0c8e5b696d 100644 --- a/gui/src/pages/gui/Chat.tsx +++ b/gui/src/pages/gui/Chat.tsx @@ -407,9 +407,8 @@ export function Chat() { return (
); diff --git a/gui/src/pages/gui/StreamError.tsx b/gui/src/pages/gui/StreamError.tsx index 1694fe4109..ca3bc04bca 100644 --- a/gui/src/pages/gui/StreamError.tsx +++ b/gui/src/pages/gui/StreamError.tsx @@ -44,15 +44,18 @@ const StreamErrorDialog = ({ error }: StreamErrorProps) => { let statusCode: undefined | number = undefined; // Attempt to get error message and status code from error - if (error && (error instanceof Error || typeof error === "object")) { - if (error["message"]) { - message = error["message"]; - const status = message?.split(" ")[0]; - if (status) { - const code = Number(status); - if (!Number.isNaN(statusCode)) { - statusCode = code; - } + if ( + error && + (error instanceof Error || typeof error === "object") && + "message" in error && + typeof error["message"] === "string" + ) { + message = error["message"]; + const status = message?.split(" ")[0]; + if (status) { + const code = Number(status); + if (!Number.isNaN(statusCode)) { + statusCode = code; } } } diff --git a/gui/src/pages/gui/ToolCallDiv/ThreadDiv.tsx b/gui/src/pages/gui/ToolCallDiv/ThreadDiv.tsx index e54345c5d3..71d61c5a82 100644 --- a/gui/src/pages/gui/ToolCallDiv/ThreadDiv.tsx +++ b/gui/src/pages/gui/ToolCallDiv/ThreadDiv.tsx @@ -8,7 +8,6 @@ interface ThreadDivProps { icon: React.ReactNode; toolCall: ToolCall; toolCallState: ToolCallState; - reactKey: string; } const Container = styled.div` @@ -48,7 +47,7 @@ export function ThreadDiv(props: ThreadDivProps) { } return ( - +
) => { - state.history.at(-1).isGatheringContext = payload; + const curMessage = state.history.at(-1); + if (curMessage) { + curMessage.isGatheringContext = payload; + } }, clearLastEmptyResponse: (state) => { if (state.history.length < 2) { @@ -472,8 +475,10 @@ export const sessionSlice = createSlice({ state, { payload }: PayloadAction<{ filepath: string; content: string }>, ) => { - state.history[state.curCheckpointIndex].checkpoint[payload.filepath] = - payload.content; + const checkpoint = state.history[state.curCheckpointIndex].checkpoint; + if (checkpoint) { + checkpoint[payload.filepath] = payload.content; + } }, updateApplyState: (state, { payload }: PayloadAction) => { const applyState = state.codeBlockApplyStates.states.find( diff --git a/gui/src/redux/thunks/streamNormalInput.ts b/gui/src/redux/thunks/streamNormalInput.ts index aec7bb38e8..6eed3ddb12 100644 --- a/gui/src/redux/thunks/streamNormalInput.ts +++ b/gui/src/redux/thunks/streamNormalInput.ts @@ -41,7 +41,7 @@ export const streamNormalInput = createAsyncThunk< (tool) => tool.function.name === toolName, ), ) - .filter(Boolean) + .filter((tool) => !!tool) : undefined, }, ); diff --git a/gui/src/redux/thunks/streamResponseAfterToolCall.ts b/gui/src/redux/thunks/streamResponseAfterToolCall.ts index fd503560ef..396ab60759 100644 --- a/gui/src/redux/thunks/streamResponseAfterToolCall.ts +++ b/gui/src/redux/thunks/streamResponseAfterToolCall.ts @@ -30,6 +30,11 @@ export const streamResponseAfterToolCall = createAsyncThunk< const initialHistory = state.session.history; const defaultModel = selectDefaultModel(state); + if (!defaultModel) { + console.error("No default model found"); + return; + } + resetStateForNewMessage(); await new Promise((resolve) => setTimeout(resolve, 0)); From 0cf152ede652d975fd774faa40494020c7514196 Mon Sep 17 00:00:00 2001 From: Test Date: Thu, 2 Jan 2025 17:41:28 -0600 Subject: [PATCH 67/99] feat: create SVG tooltip --- .../vscode/src/activation/InlineTipManager.ts | 427 ++++++++++++++++++ extensions/vscode/src/activation/activate.ts | 3 +- .../vscode/src/activation/inlineTips.ts | 122 ----- .../components/indexing/ChatIndexingPeeks.tsx | 2 +- package-lock.json | 8 + package.json | 3 + 6 files changed, 441 insertions(+), 124 deletions(-) create mode 100644 extensions/vscode/src/activation/InlineTipManager.ts delete mode 100644 extensions/vscode/src/activation/inlineTips.ts diff --git a/extensions/vscode/src/activation/InlineTipManager.ts b/extensions/vscode/src/activation/InlineTipManager.ts new file mode 100644 index 0000000000..b335cc9dd3 --- /dev/null +++ b/extensions/vscode/src/activation/InlineTipManager.ts @@ -0,0 +1,427 @@ +/* eslint-disable @typescript-eslint/naming-convention */ +import { EXTENSION_NAME } from "core/control-plane/env"; +// @ts-ignore +import svgBuilder from "svg-builder"; +import * as vscode from "vscode"; + +import { getTheme } from "../util/getTheme"; +import { getMetaKeyLabel, getMetaKeyName } from "../util/util"; + +const SVG_CONFIG = { + stroke: "#999998", + strokeWidth: 1, + shortcutColor: "#999998", + filter: "drop-shadow(0 2px 2px rgba(0,0,0,0.2))", + radius: 4, + leftMargin: 40, + debounceDelay: 500, + chatLabel: "Add to Chat", + chatShortcut: `${getMetaKeyLabel()}L`, + editLabel: "Edit", + editShortcut: `${getMetaKeyLabel()}I`, + + get fontSize() { + return Math.ceil( + (vscode.workspace.getConfiguration("editor").get("fontSize") ?? + 14) * 0.7, + ); + }, + get fontFamily() { + return ( + vscode.workspace.getConfiguration("editor").get("fontFamily") || + "helvetica" + ); + }, + get paddingY() { + return Math.ceil(this.fontSize * 0.4); + }, + get paddingX() { + return Math.ceil(this.fontSize * 0.8); + }, + get gap() { + return this.fontSize * 2.5; + }, + get tipWidth() { + return ( + this.editShortcutX + + this.getEstimatedTextWidth(this.editShortcut) + + this.paddingX + ); + }, + get tipHeight() { + return this.fontSize + this.paddingY * 2; + }, + get textY() { + return this.tipHeight / 2 + this.fontSize * 0.35; + }, + get chatLabelX() { + return this.paddingX; + }, + get chatShortcutX() { + return this.chatLabelX + this.getEstimatedTextWidth(this.chatLabel) + 4; + }, + get editLabelX() { + return this.chatShortcutX + this.gap; + }, + get editShortcutX() { + return this.editLabelX + this.getEstimatedTextWidth(this.editLabel) + 4; + }, + getEstimatedTextWidth(text: string): number { + return text.length * this.fontSize * 0.6; + }, +} as const; + +export class InlineTipManager { + private static instance: InlineTipManager; + + private readonly excludedURIPrefixes = ["output:", "vscode://inline-chat"]; + private readonly hideCommand = "continue.hideInlineTip"; + private svgTooltip: vscode.Uri | undefined = undefined; + + private debounceTimer: NodeJS.Timeout | undefined; + private lastActiveEditor?: vscode.TextEditor; + private theme = getTheme(); + private svgTooltipDecoration = this.createSvgTooltipDecoration(); + private emptyFileTooltipDecoration = this.createEmptyFileTooltipDecoration(); + + public static getInstance(): InlineTipManager { + if (!InlineTipManager.instance) { + InlineTipManager.instance = new InlineTipManager(); + } + return InlineTipManager.instance; + } + + private constructor() { + this.createSvgTooltip(); + this.setupSvgTipListeners(); + } + + public setupInlineTips(context: vscode.ExtensionContext) { + context.subscriptions.push( + vscode.window.onDidChangeTextEditorSelection((e) => { + this.handleSelectionChange(e); + }), + ); + + this.setupEmptyFileTips(context); + + context.subscriptions.push(this); + } + + public handleSelectionChange(e: vscode.TextEditorSelectionChangeEvent) { + const selection = e.selections[0]; + const editor = e.textEditor; + + if (selection.isEmpty || !this.shouldRenderTip(editor.document.uri)) { + editor.setDecorations(this.svgTooltipDecoration, []); + return; + } + + this.debouncedSelectionChange(editor, selection); + } + + public dispose() { + this.svgTooltipDecoration.dispose(); + this.emptyFileTooltipDecoration.dispose(); + + if (this.debounceTimer) { + clearTimeout(this.debounceTimer); + } + } + + private debouncedSelectionChange( + editor: vscode.TextEditor, + selection: vscode.Selection, + ) { + if (this.debounceTimer) { + clearTimeout(this.debounceTimer); + } + + this.debounceTimer = setTimeout(() => { + // Clear decoration from previous editor + if (this.lastActiveEditor && this.lastActiveEditor !== editor) { + this.lastActiveEditor.setDecorations(this.svgTooltipDecoration, []); + } + + this.lastActiveEditor = editor; + + this.updateTooltipPosition(editor, selection); + }, SVG_CONFIG.debounceDelay); + } + + private setupSvgTipListeners() { + vscode.workspace.onDidChangeConfiguration((e) => { + if (e.affectsConfiguration("workbench.colorTheme")) { + this.theme = getTheme(); + this.createSvgTooltip(); + } + }); + + vscode.workspace.onDidChangeConfiguration((e) => { + if (e.affectsConfiguration("editor.fontSize")) { + this.createSvgTooltip(); + } + }); + } + + private shouldRenderTip(uri: vscode.Uri): boolean { + const isAllowedUri = + !this.excludedURIPrefixes.some((prefix) => + uri.toString().startsWith(prefix), + ) && uri.scheme !== "comment"; + + const isEnabled = + !!vscode.workspace + .getConfiguration(EXTENSION_NAME) + .get("showInlineTip") === true; + + return isAllowedUri && isEnabled; + } + + private setupEmptyFileTips(context: vscode.ExtensionContext) { + context.subscriptions.push( + vscode.window.onDidChangeActiveTextEditor((editor) => { + if ( + editor?.document.getText() === "" && + this.shouldRenderTip(editor.document.uri) + ) { + editor.setDecorations(this.emptyFileTooltipDecoration, [ + { + range: new vscode.Range( + new vscode.Position(0, Number.MAX_VALUE), + new vscode.Position(0, Number.MAX_VALUE), + ), + }, + ]); + } + }), + ); + + context.subscriptions.push( + vscode.workspace.onDidChangeTextDocument((e) => { + if ( + e.document.getText() === "" && + this.shouldRenderTip(e.document.uri) + ) { + vscode.window.visibleTextEditors.forEach((editor) => { + editor.setDecorations(this.emptyFileTooltipDecoration, [ + { + range: new vscode.Range( + new vscode.Position(0, Number.MAX_VALUE), + new vscode.Position(0, Number.MAX_VALUE), + ), + }, + ]); + }); + } else { + vscode.window.visibleTextEditors.forEach((editor) => { + editor.setDecorations(this.emptyFileTooltipDecoration, []); + }); + } + }), + ); + } + + private createEmptyFileTooltipDecoration() { + return vscode.window.createTextEditorDecorationType({ + after: { + contentText: `Use ${getMetaKeyName()} + I to generate code`, + color: "#888", + margin: "2em 0 0 0", + fontStyle: "italic", + }, + }); + } + + private createSvgTooltipDecoration() { + return vscode.window.createTextEditorDecorationType({ + after: { + contentIconPath: this.svgTooltip, + margin: `-2.5px 0 0 ${SVG_CONFIG.leftMargin}px`, + width: `${SVG_CONFIG.tipWidth}px`, + }, + }); + } + + private createSvgTooltip() { + const baseTextConfig = { + y: SVG_CONFIG.textY, + "font-family": SVG_CONFIG.fontFamily, + "font-size": SVG_CONFIG.fontSize, + }; + + if (!this.theme) { + return; + } + + try { + const svgContent = svgBuilder + .width(SVG_CONFIG.tipWidth) + .height(SVG_CONFIG.tipHeight) + // Main rectangle + .rect({ + x: 0, + y: 0, + width: SVG_CONFIG.tipWidth, + height: SVG_CONFIG.tipHeight, + rx: SVG_CONFIG.radius, + ry: SVG_CONFIG.radius, + fill: this.theme.colors["editor.background"], + stroke: SVG_CONFIG.stroke, + "stroke-width": SVG_CONFIG.strokeWidth, + filter: SVG_CONFIG.filter, + }) + // Chat + .text( + { + ...baseTextConfig, + x: SVG_CONFIG.chatLabelX, + fill: this.theme.colors["editor.foreground"], + }, + SVG_CONFIG.chatLabel, + ) + .text( + { + ...baseTextConfig, + x: SVG_CONFIG.chatShortcutX, + fill: SVG_CONFIG.shortcutColor, + }, + SVG_CONFIG.chatShortcut, + ) + // Edit + .text( + { + ...baseTextConfig, + x: SVG_CONFIG.editLabelX, + fill: this.theme.colors["editor.foreground"], + }, + SVG_CONFIG.editLabel, + ) + .text( + { + ...baseTextConfig, + x: SVG_CONFIG.editShortcutX, + fill: SVG_CONFIG.shortcutColor, + }, + SVG_CONFIG.editShortcut, + ) + .render(); + + const dataUri = `data:image/svg+xml;base64,${Buffer.from(svgContent).toString("base64")}`; + + this.svgTooltip = vscode.Uri.parse(dataUri); + this.svgTooltipDecoration.dispose(); + this.svgTooltipDecoration = this.createSvgTooltipDecoration(); + } catch (error) { + console.error("Error creating SVG for inline tip:", error); + } + } + + private buildHideTooltipHoverMsg() { + const hoverMarkdown = new vscode.MarkdownString( + `[Disable](command:${this.hideCommand})`, + ); + + hoverMarkdown.isTrusted = true; + hoverMarkdown.supportHtml = true; + return hoverMarkdown; + } + + /** + * Calculates tooltip position using these rules: + * 1. For single-line selection: Place after the line's content + * 2. For multi-line selection: Place after the longer line between: + * - The first non-empty selected line + * - The line above the selection + * Returns null if selection is empty or contains only empty lines + */ + private calculateTooltipPosition( + editor: vscode.TextEditor, + selection: vscode.Selection, + ): vscode.Position | null { + const document = editor.document; + + // Get selection info + const startLine = selection.start.line; + const endLine = selection.end.line; + const isFullLineSelection = + selection.start.character === 0 && + (selection.end.line > selection.start.line + ? selection.end.character === 0 + : selection.end.character === + document.lineAt(selection.end.line).text.length); + + // Helper functions + const isLineEmpty = (lineNumber: number): boolean => { + return document.lineAt(lineNumber).text.trim().length === 0; + }; + + const getLineEndChar = (lineNumber: number): number => { + return document.lineAt(lineNumber).text.trimEnd().length; + }; + + // If single empty line selected and not full line selection, return null + if ( + startLine === endLine && + isLineEmpty(startLine) && + !isFullLineSelection + ) { + return null; + } + + // Find topmost non-empty line + let topNonEmptyLine = startLine; + while (topNonEmptyLine <= endLine && isLineEmpty(topNonEmptyLine)) { + topNonEmptyLine++; + } + + // If all lines empty, return null + if (topNonEmptyLine > endLine) { + return null; + } + + const OFFSET = 4; // Characters to offset from end of line + + // Single line or full line selection + if (isFullLineSelection || startLine === endLine) { + return new vscode.Position( + topNonEmptyLine, + getLineEndChar(topNonEmptyLine) + OFFSET, + ); + } + + // Check line above selection + const lineAboveSelection = Math.max(0, startLine - 1); + + // Get end positions + const topNonEmptyEndChar = getLineEndChar(topNonEmptyLine); + const lineAboveEndChar = getLineEndChar(lineAboveSelection); + + const baseEndChar = Math.max(topNonEmptyEndChar, lineAboveEndChar); + + return new vscode.Position(topNonEmptyLine, baseEndChar + OFFSET); + } + + private updateTooltipPosition( + editor: vscode.TextEditor, + selection: vscode.Selection, + ) { + const position = this.calculateTooltipPosition(editor, selection); + + if (!position) { + editor.setDecorations(this.svgTooltipDecoration, []); + return; + } + + editor.setDecorations(this.svgTooltipDecoration, [ + { + range: new vscode.Range(position, position), + hoverMessage: [this.buildHideTooltipHoverMsg()], + }, + ]); + } +} + +export default function setupInlineTips(context: vscode.ExtensionContext) { + InlineTipManager.getInstance().setupInlineTips(context); +} diff --git a/extensions/vscode/src/activation/activate.ts b/extensions/vscode/src/activation/activate.ts index 7aa0ae4a96..d40aee7402 100644 --- a/extensions/vscode/src/activation/activate.ts +++ b/extensions/vscode/src/activation/activate.ts @@ -7,7 +7,8 @@ import registerQuickFixProvider from "../lang-server/codeActions"; import { getExtensionVersion } from "../util/util"; import { VsCodeContinueApi } from "./api"; -import { setupInlineTips } from "./inlineTips"; +import setupInlineTips from "./InlineTipManager"; +// import { setupInlineTips } from "./inlineTips"; export async function activateExtension(context: vscode.ExtensionContext) { // Add necessary files diff --git a/extensions/vscode/src/activation/inlineTips.ts b/extensions/vscode/src/activation/inlineTips.ts deleted file mode 100644 index 49a8adf298..0000000000 --- a/extensions/vscode/src/activation/inlineTips.ts +++ /dev/null @@ -1,122 +0,0 @@ -import { EXTENSION_NAME } from "core/control-plane/env"; -import * as vscode from "vscode"; - -import { getMetaKeyName } from "../util/util"; - -const inlineTipDecoration = vscode.window.createTextEditorDecorationType({ - after: { - contentText: `Add to chat (${getMetaKeyName()}+L) | Edit highlighted code (${getMetaKeyName()}+I).`, - color: "#888", - margin: "0 0 0 6em", - fontWeight: "bold", - }, -}); - -function showInlineTip() { - return vscode.workspace - .getConfiguration(EXTENSION_NAME) - .get("showInlineTip"); -} - -function handleSelectionChange(e: vscode.TextEditorSelectionChangeEvent) { - const selection = e.selections[0]; - const editor = e.textEditor; - - if (editor.document.uri.toString().startsWith("output:")) { - return; - } - - if (selection.isEmpty || showInlineTip() === false) { - editor.setDecorations(inlineTipDecoration, []); - return; - } - - const line = Math.max(0, selection.start.line - 1); - - const hoverMarkdown = new vscode.MarkdownString( - `Click [here](command:continue.hideInlineTip) to hide these suggestions`, - ); - hoverMarkdown.isTrusted = true; - hoverMarkdown.supportHtml = true; - editor.setDecorations(inlineTipDecoration, [ - { - range: new vscode.Range( - new vscode.Position(line, Number.MAX_VALUE), - new vscode.Position(line, Number.MAX_VALUE), - ), - hoverMessage: [hoverMarkdown], - }, - ]); -} - -const emptyFileTooltipDecoration = vscode.window.createTextEditorDecorationType( - { - after: { - contentText: `Use ${getMetaKeyName()}+I to generate code`, - color: "#888", - margin: "2em 0 0 0", - fontStyle: "italic", - }, - }, -); - -let selectionChangeDebounceTimer: NodeJS.Timeout | undefined; - -export function setupInlineTips(context: vscode.ExtensionContext) { - context.subscriptions.push( - vscode.window.onDidChangeTextEditorSelection((e) => { - if (selectionChangeDebounceTimer) { - clearTimeout(selectionChangeDebounceTimer); - } - selectionChangeDebounceTimer = setTimeout(() => { - handleSelectionChange(e); - }, 200); - }), - ); - - context.subscriptions.push( - vscode.window.onDidChangeActiveTextEditor((editor) => { - if (editor?.document.getText() === "" && showInlineTip() === true) { - if ( - editor.document.uri.toString().startsWith("output:") || - editor.document.uri.scheme === "comment" - ) { - return; - } - - editor.setDecorations(emptyFileTooltipDecoration, [ - { - range: new vscode.Range( - new vscode.Position(0, Number.MAX_VALUE), - new vscode.Position(0, Number.MAX_VALUE), - ), - }, - ]); - } - }), - ); - - context.subscriptions.push( - vscode.workspace.onDidChangeTextDocument((e) => { - if (e.document.uri.toString().startsWith("vscode://inline-chat")) { - return; - } - if (e.document.getText() === "" && showInlineTip() === true) { - vscode.window.visibleTextEditors.forEach((editor) => { - editor.setDecorations(emptyFileTooltipDecoration, [ - { - range: new vscode.Range( - new vscode.Position(0, Number.MAX_VALUE), - new vscode.Position(0, Number.MAX_VALUE), - ), - }, - ]); - }); - } else { - vscode.window.visibleTextEditors.forEach((editor) => { - editor.setDecorations(emptyFileTooltipDecoration, []); - }); - } - }), - ); -} diff --git a/gui/src/components/indexing/ChatIndexingPeeks.tsx b/gui/src/components/indexing/ChatIndexingPeeks.tsx index 1ec1076c01..66936597de 100644 --- a/gui/src/components/indexing/ChatIndexingPeeks.tsx +++ b/gui/src/components/indexing/ChatIndexingPeeks.tsx @@ -62,7 +62,7 @@ function ChatIndexingPeek({ state }: ChatIndexingPeekProps) {
{ dispatch( setIndexingChatPeekHidden({ type: state.type, hidden: true }), diff --git a/package-lock.json b/package-lock.json index c147efa727..da32725555 100644 --- a/package-lock.json +++ b/package-lock.json @@ -4,6 +4,9 @@ "requires": true, "packages": { "": { + "dependencies": { + "svg-builder": "^2.0.0" + }, "devDependencies": { "@typescript-eslint/parser": "^7.8.0", "eslint-plugin-import": "^2.29.1", @@ -2828,6 +2831,11 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/svg-builder": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/svg-builder/-/svg-builder-2.0.0.tgz", + "integrity": "sha512-v89FptvyrOy1Gf9KPkIo2jxroCLEZLiGBRTSutL3pzqZe2xnCFltkCLnEjV8QOEJ99PzBa9NGPGEDP9l8MWIrw==" + }, "node_modules/text-table": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", diff --git a/package.json b/package.json index c093fccd9a..e3091f1983 100644 --- a/package.json +++ b/package.json @@ -11,5 +11,8 @@ "eslint-plugin-import": "^2.29.1", "prettier": "^3.3.3", "prettier-plugin-tailwindcss": "^0.6.8" + }, + "dependencies": { + "svg-builder": "^2.0.0" } } From 5a13272d375c2630be03dc97e6234389fff2d02a Mon Sep 17 00:00:00 2001 From: Test Date: Thu, 2 Jan 2025 17:56:20 -0600 Subject: [PATCH 68/99] Update InlineTipManager.ts --- extensions/vscode/src/activation/InlineTipManager.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extensions/vscode/src/activation/InlineTipManager.ts b/extensions/vscode/src/activation/InlineTipManager.ts index b335cc9dd3..954dba19d4 100644 --- a/extensions/vscode/src/activation/InlineTipManager.ts +++ b/extensions/vscode/src/activation/InlineTipManager.ts @@ -15,7 +15,7 @@ const SVG_CONFIG = { radius: 4, leftMargin: 40, debounceDelay: 500, - chatLabel: "Add to Chat", + chatLabel: "Chat", chatShortcut: `${getMetaKeyLabel()}L`, editLabel: "Edit", editShortcut: `${getMetaKeyLabel()}I`, From 02968fb1551c0a26af75340641312e1f9f5a1102 Mon Sep 17 00:00:00 2001 From: Test Date: Thu, 2 Jan 2025 20:33:23 -0600 Subject: [PATCH 69/99] fix: correct package.json update --- extensions/vscode/package-lock.json | 10 ++++++++-- extensions/vscode/package.json | 1 + extensions/vscode/src/activation/InlineTipManager.ts | 4 ++-- extensions/vscode/src/activation/activate.ts | 1 - package-lock.json | 8 -------- package.json | 3 --- 6 files changed, 11 insertions(+), 16 deletions(-) diff --git a/extensions/vscode/package-lock.json b/extensions/vscode/package-lock.json index 8f5cad8a0d..c6db7f1035 100644 --- a/extensions/vscode/package-lock.json +++ b/extensions/vscode/package-lock.json @@ -1,12 +1,12 @@ { "name": "continue", - "version": "0.9.247", + "version": "0.9.248", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "continue", - "version": "0.9.247", + "version": "0.9.248", "license": "Apache-2.0", "dependencies": { "@continuedev/fetch": "^1.0.3", @@ -48,6 +48,7 @@ "request": "^2.88.2", "socket.io-client": "^4.7.2", "strip-ansi": "^7.1.0", + "svg-builder": "^2.0.0", "systeminformation": "^5.22.10", "tailwindcss": "^3.3.2", "undici": "^6.2.0", @@ -12122,6 +12123,11 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/svg-builder": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/svg-builder/-/svg-builder-2.0.0.tgz", + "integrity": "sha512-v89FptvyrOy1Gf9KPkIo2jxroCLEZLiGBRTSutL3pzqZe2xnCFltkCLnEjV8QOEJ99PzBa9NGPGEDP9l8MWIrw==" + }, "node_modules/symbol-tree": { "version": "3.2.4", "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", diff --git a/extensions/vscode/package.json b/extensions/vscode/package.json index 6a416096cb..550ed35385 100644 --- a/extensions/vscode/package.json +++ b/extensions/vscode/package.json @@ -703,6 +703,7 @@ "request": "^2.88.2", "socket.io-client": "^4.7.2", "strip-ansi": "^7.1.0", + "svg-builder": "^2.0.0", "systeminformation": "^5.22.10", "tailwindcss": "^3.3.2", "undici": "^6.2.0", diff --git a/extensions/vscode/src/activation/InlineTipManager.ts b/extensions/vscode/src/activation/InlineTipManager.ts index 954dba19d4..0ea078c218 100644 --- a/extensions/vscode/src/activation/InlineTipManager.ts +++ b/extensions/vscode/src/activation/InlineTipManager.ts @@ -23,7 +23,7 @@ const SVG_CONFIG = { get fontSize() { return Math.ceil( (vscode.workspace.getConfiguration("editor").get("fontSize") ?? - 14) * 0.7, + 14) * 0.8, ); }, get fontFamily() { @@ -237,7 +237,7 @@ export class InlineTipManager { return vscode.window.createTextEditorDecorationType({ after: { contentIconPath: this.svgTooltip, - margin: `-2.5px 0 0 ${SVG_CONFIG.leftMargin}px`, + margin: `-5px 0 0 ${SVG_CONFIG.leftMargin}px`, width: `${SVG_CONFIG.tipWidth}px`, }, }); diff --git a/extensions/vscode/src/activation/activate.ts b/extensions/vscode/src/activation/activate.ts index d40aee7402..1bd22c7f44 100644 --- a/extensions/vscode/src/activation/activate.ts +++ b/extensions/vscode/src/activation/activate.ts @@ -8,7 +8,6 @@ import { getExtensionVersion } from "../util/util"; import { VsCodeContinueApi } from "./api"; import setupInlineTips from "./InlineTipManager"; -// import { setupInlineTips } from "./inlineTips"; export async function activateExtension(context: vscode.ExtensionContext) { // Add necessary files diff --git a/package-lock.json b/package-lock.json index da32725555..c147efa727 100644 --- a/package-lock.json +++ b/package-lock.json @@ -4,9 +4,6 @@ "requires": true, "packages": { "": { - "dependencies": { - "svg-builder": "^2.0.0" - }, "devDependencies": { "@typescript-eslint/parser": "^7.8.0", "eslint-plugin-import": "^2.29.1", @@ -2831,11 +2828,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/svg-builder": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/svg-builder/-/svg-builder-2.0.0.tgz", - "integrity": "sha512-v89FptvyrOy1Gf9KPkIo2jxroCLEZLiGBRTSutL3pzqZe2xnCFltkCLnEjV8QOEJ99PzBa9NGPGEDP9l8MWIrw==" - }, "node_modules/text-table": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", diff --git a/package.json b/package.json index e3091f1983..c093fccd9a 100644 --- a/package.json +++ b/package.json @@ -11,8 +11,5 @@ "eslint-plugin-import": "^2.29.1", "prettier": "^3.3.3", "prettier-plugin-tailwindcss": "^0.6.8" - }, - "dependencies": { - "svg-builder": "^2.0.0" } } From 92a38ccf5f0639d815684bd786f75ebede9e950c Mon Sep 17 00:00:00 2001 From: Test Date: Thu, 2 Jan 2025 23:24:32 -0600 Subject: [PATCH 70/99] bugfix: handle inline edit w/ myers diff --- .../vscode/src/diff/vertical/handler.ts | 73 ++++++++++--------- .../vscode/src/diff/vertical/manager.ts | 2 +- .../VerticalPerLineCodeLensProvider.ts | 9 --- 3 files changed, 39 insertions(+), 45 deletions(-) diff --git a/extensions/vscode/src/diff/vertical/handler.ts b/extensions/vscode/src/diff/vertical/handler.ts index a99c9c1c33..80331f8059 100644 --- a/extensions/vscode/src/diff/vertical/handler.ts +++ b/extensions/vscode/src/diff/vertical/handler.ts @@ -343,6 +343,7 @@ export class VerticalDiffHandler implements vscode.Disposable { async run(diffLineGenerator: AsyncGenerator) { let diffLines = []; + try { // As an indicator of loading this.updateIndexLineDecorations(); @@ -481,9 +482,17 @@ export class VerticalDiffHandler implements vscode.Disposable { * we have received all of the diff lines. */ async reapplyWithMeyersDiff(diffLines: DiffLine[]) { + // First, we reset the original diff by deleting any new lines and clearing decorations + for (const range of this.greenDecorationManager.getRanges()) { + await this.deleteLinesAt( + range.start.line, + range.end.line - range.start.line + 1, + ); + } + this.clearDecorations(); - // First, get our old/new file content based on the diff lines + // Then, get our old/new file content based on the original lines const oldFileContent = diffLines .filter((line) => line.type === "same" || line.type === "old") .map((line) => line.line) @@ -497,55 +506,49 @@ export class VerticalDiffHandler implements vscode.Disposable { const diffs = myersDiff(oldFileContent, newFileContent); const meyersDiffLines = diffs.map((diff) => diff.line).join("\n"); - console.log({ newFileContent }); - - // Then, we overwrite the potentially miscalcualted diff blocks with our Meyers Diff - const fullRange = new vscode.Range( - this.editor.document.positionAt(0), - this.editor.document.positionAt(this.editor.document.getText().length), - ); + // Then, we insert our diff lines await this.editor.edit((editBuilder) => { - editBuilder.replace(fullRange, meyersDiffLines); + editBuilder.replace(this.range, meyersDiffLines), + { + undoStopAfter: false, + undoStopBefore: false, + }; }); - let currentBlock = { - start: 0, - numRed: 0, - numGreen: 0, - }; + // Lastly, we apply decorations + let numRed = 0; + let numGreen = 0; - const blocks: VerticalDiffCodeLens[] = []; + const codeLensBlocks: VerticalDiffCodeLens[] = []; - // Lastly, we apply decorations diffs.forEach((diff, index) => { if (diff.type === "old") { - this.redDecorationManager.addLine(index); - currentBlock.numRed++; + this.redDecorationManager.addLine(this.startLine + index); + numRed++; } else if (diff.type === "new") { - this.greenDecorationManager.addLine(index); - currentBlock.numGreen++; - } else if ( - diff.type === "same" && - (currentBlock.numRed > 0 || currentBlock.numGreen > 0) - ) { - blocks.push({ - ...currentBlock, - start: index - currentBlock.numRed - currentBlock.numGreen, + this.greenDecorationManager.addLine(this.startLine + index); + numGreen++; + } else if (diff.type === "same" && (numRed > 0 || numGreen > 0)) { + codeLensBlocks.push({ + numRed, + numGreen, + start: this.startLine + index - numRed - numGreen, }); - currentBlock = { - start: index + 1, - numRed: 0, - numGreen: 0, - }; + numRed = 0; + numGreen = 0; } }); - if (currentBlock.numRed > 0 || currentBlock.numGreen > 0) { - blocks.push(currentBlock); + if (numRed > 0 || numGreen > 0) { + codeLensBlocks.push({ + numGreen, + numRed, + start: this.startLine + diffs.length - 1 - numRed - numGreen, + }); } - this.editorToVerticalDiffCodeLens.set(this.fileUri, blocks); + this.editorToVerticalDiffCodeLens.set(this.fileUri, codeLensBlocks); this.refreshCodeLens(); } } diff --git a/extensions/vscode/src/diff/vertical/manager.ts b/extensions/vscode/src/diff/vertical/manager.ts index ee47fd1732..1e1b41a173 100644 --- a/extensions/vscode/src/diff/vertical/manager.ts +++ b/extensions/vscode/src/diff/vertical/manager.ts @@ -3,13 +3,13 @@ import { ConfigHandler } from "core/config/ConfigHandler"; import { streamDiffLines } from "core/edit/streamDiffLines"; import { pruneLinesFromBottom, pruneLinesFromTop } from "core/llm/countTokens"; import { getMarkdownLanguageTagForFile } from "core/util"; +import * as URI from "uri-js"; import * as vscode from "vscode"; import EditDecorationManager from "../../quickEdit/EditDecorationManager"; import { VsCodeWebviewProtocol } from "../../webviewProtocol"; import { VerticalDiffHandler, VerticalDiffHandlerOptions } from "./handler"; -import * as URI from "uri-js"; export interface VerticalDiffCodeLens { start: number; diff --git a/extensions/vscode/src/lang-server/codeLens/providers/VerticalPerLineCodeLensProvider.ts b/extensions/vscode/src/lang-server/codeLens/providers/VerticalPerLineCodeLensProvider.ts index 52fd07f793..43c9015f19 100644 --- a/extensions/vscode/src/lang-server/codeLens/providers/VerticalPerLineCodeLensProvider.ts +++ b/extensions/vscode/src/lang-server/codeLens/providers/VerticalPerLineCodeLensProvider.ts @@ -53,15 +53,6 @@ export class VerticalDiffCodeLensProvider implements vscode.CodeLensProvider { arguments: [uri, i], }), ); - - if (codeLenses.length === 2) { - codeLenses.push( - new vscode.CodeLens(range, { - title: `${getMetaKeyLabel()}I to add instructions`, - command: "", - }), - ); - } } return codeLenses; From cb77d90a896918f64a62a8e5ad7d95ad7ff56b78 Mon Sep 17 00:00:00 2001 From: Test Date: Thu, 2 Jan 2025 23:26:33 -0600 Subject: [PATCH 71/99] Update handler.ts --- extensions/vscode/src/diff/vertical/handler.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extensions/vscode/src/diff/vertical/handler.ts b/extensions/vscode/src/diff/vertical/handler.ts index 80331f8059..a3a7f651fd 100644 --- a/extensions/vscode/src/diff/vertical/handler.ts +++ b/extensions/vscode/src/diff/vertical/handler.ts @@ -544,7 +544,7 @@ export class VerticalDiffHandler implements vscode.Disposable { codeLensBlocks.push({ numGreen, numRed, - start: this.startLine + diffs.length - 1 - numRed - numGreen, + start: this.startLine + diffs.length - numRed - numGreen, }); } From 2200b6eafd2773afbd7172e5fbd601464a5be906 Mon Sep 17 00:00:00 2001 From: Dallin Romney Date: Fri, 3 Jan 2025 10:10:25 +0100 Subject: [PATCH 72/99] uri util fixes --- core/util/ideUtils.ts | 18 ++--- core/util/uri.test.ts | 22 ------ core/util/uri.ts | 71 ++++++++----------- .../StepContainerPreToolbar.tsx | 4 +- 4 files changed, 42 insertions(+), 73 deletions(-) diff --git a/core/util/ideUtils.ts b/core/util/ideUtils.ts index 93cb5008da..b969b22869 100644 --- a/core/util/ideUtils.ts +++ b/core/util/ideUtils.ts @@ -1,6 +1,10 @@ import { IDE } from ".."; -import { joinPathsToUri, pathToUriPathSegment } from "./uri"; +import { + joinEncodedUriPathSegmentToUri, + joinPathsToUri, + pathToUriPathSegment, +} from "./uri"; /* This function takes a relative (to workspace) filepath @@ -51,7 +55,7 @@ export async function inferResolvedUriFromRelativePath( for (const suffix of suffixes) { const uris = dirs.map((dir) => ({ dir, - partialUri: joinPathsToUri(dir, suffix), + partialUri: joinEncodedUriPathSegmentToUri(dir, suffix), })); const promises = uris.map(async ({ partialUri, dir }) => { const exists = await ide.fileExists(partialUri); @@ -67,15 +71,13 @@ export async function inferResolvedUriFromRelativePath( // If exactly one directory matches, use it if (existingUris.length === 1) { - return joinPathsToUri(existingUris[0].dir, segments.join("/")); + return joinEncodedUriPathSegmentToUri( + existingUris[0].dir, + segments.join("/"), + ); } } // If no unique match found, use the first directory return joinPathsToUri(dirs[0], path); } - -interface ResolveResult { - resolvedUri: string; - matchedDir: string; -} diff --git a/core/util/uri.test.ts b/core/util/uri.test.ts index 9e24710542..8a915d73f0 100644 --- a/core/util/uri.test.ts +++ b/core/util/uri.test.ts @@ -1,9 +1,7 @@ // Generated by continue -import * as URI from "uri-js"; import { pathToUriPathSegment, findUriInDirs, - relativePathOrUriToUri, getUriPathBasename, getUriFileExtension, getFileExtensionFromBasename, @@ -141,26 +139,6 @@ describe("uri utils", () => { }); }); - describe("relativePathOrUriToUri", () => { - const consoleSpy = jest.spyOn(console, "trace").mockImplementation(); - - afterEach(() => { - consoleSpy.mockClear(); - }); - - it("should return original URI if scheme exists", () => { - const uri = "file:///path/to/file.txt"; - expect(relativePathOrUriToUri(uri, "file:///base")).toBe(uri); - }); - - it("should convert relative path to URI", () => { - expect(relativePathOrUriToUri("path/to/file.txt", "file:///base")).toBe( - "file:///base/path/to/file.txt", - ); - expect(consoleSpy).toHaveBeenCalledWith("Received path with no scheme"); - }); - }); - describe("getLastNUriRelativePathParts", () => { it("should get last N parts of URI relative path", () => { const dirUris = ["file:///workspace/project1"]; diff --git a/core/util/uri.ts b/core/util/uri.ts index 708408ce07..a1bcd7745f 100644 --- a/core/util/uri.ts +++ b/core/util/uri.ts @@ -5,23 +5,21 @@ import * as URI from "uri-js"; \this\is\afile.ts -> this/is/afile.ts is/already/clean -> is/already/clean **/ - export function pathToUriPathSegment(path: string) { let clean = path.replace(/[\\]/g, "/"); // backslashes -> forward slashes clean = clean.replace(/^\//, ""); // remove start slash clean = clean.replace(/\/$/, ""); // remove end slash return clean .split("/") - .map((segment) => encodeURIComponent(segment)) + .map((part) => encodeURIComponent(part)) .join("/"); } export function getCleanUriPath(uri: string) { - const path = URI.parse(uri).path; - if (!path) { - return ""; - } - return pathToUriPathSegment(path); + const path = URI.parse(uri).path ?? ""; + let clean = path.replace(/^\//, ""); // remove start slash + clean = clean.replace(/\/$/, ""); // remove end slash + return clean; } export function findUriInDirs( @@ -36,6 +34,7 @@ export function findUriInDirs( if (!uriComps.scheme) { throw new Error(`Invalid uri: ${uri}`); } + const uriPathParts = getCleanUriPath(uri).split("/"); for (const dir of dirUriCandidates) { const dirComps = URI.parse(dir); @@ -51,14 +50,7 @@ export function findUriInDirs( // file:///folder/file is not within file:///fold // At this point we break the path up and check if each dir path part matches - const dirPathParts = (dirComps.path ?? "") - .replace(/^\//, "") - .split("/") - .map((part) => encodeURIComponent(part)); - const uriPathParts = (uriComps.path ?? "") - .replace(/^\//, "") - .split("/") - .map((part) => encodeURIComponent(part)); + const dirPathParts = getCleanUriPath(dir).split("/"); if (uriPathParts.length < dirPathParts.length) { continue; @@ -70,7 +62,10 @@ export function findUriInDirs( } } if (allDirPartsMatch) { - const relativePath = uriPathParts.slice(dirPathParts.length).join("/"); + const relativePath = uriPathParts + .slice(dirPathParts.length) + .map(decodeURIComponent) + .join("/"); return { uri, relativePathOrBasename: relativePath, @@ -86,28 +81,13 @@ export function findUriInDirs( }; } -/* - To smooth out the transition from path to URI will use this function to warn when path is used - This will NOT work consistently with full OS paths like c:\blah\blah or ~/Users/etc -*/ -export function relativePathOrUriToUri( - relativePathOrUri: string, - defaultDirUri: string, -): string { - const out = URI.parse(relativePathOrUri); - if (out.scheme) { - return relativePathOrUri; - } - console.trace("Received path with no scheme"); - return joinPathsToUri(defaultDirUri, out.path ?? ""); -} - /* Returns just the file or folder name of a URI */ export function getUriPathBasename(uri: string): string { - const cleanPath = getCleanUriPath(uri); - return cleanPath.split("/")?.pop() || ""; + const path = getCleanUriPath(uri); + const basename = path.split("/").pop() || ""; + return decodeURIComponent(basename); } export function getFileExtensionFromBasename(basename: string) { @@ -142,6 +122,15 @@ export function joinPathsToUri(uri: string, ...pathSegments: string[]) { return URI.serialize(components); } +export function joinEncodedUriPathSegmentToUri( + uri: string, + pathSegment: string, +) { + const components = URI.parse(uri); + components.path = `${components.path}/${pathSegment}`; + return URI.serialize(components); +} + export function getShortestUniqueRelativeUriPaths( uris: string[], dirUriCandidates: string[], @@ -153,8 +142,7 @@ export function getShortestUniqueRelativeUriPaths( const segmentCombinationsMap = new Map(); const segmentsInfo = uris.map((uri) => { const { relativePathOrBasename } = findUriInDirs(uri, dirUriCandidates); - const cleanPath = pathToUriPathSegment(relativePathOrBasename); - const segments = cleanPath.split("/"); + const segments = relativePathOrBasename.split("/"); const suffixes: string[] = []; // Generate all possible suffix combinations, starting from the shortest (basename) @@ -168,18 +156,19 @@ export function getShortestUniqueRelativeUriPaths( ); } - return { uri, segments, suffixes, cleanPath }; + return { uri, segments, suffixes, relativePathOrBasename }; }); // Find shortest unique path for each URI - return segmentsInfo.map(({ uri, suffixes, cleanPath }) => { + return segmentsInfo.map(({ uri, suffixes, relativePathOrBasename }) => { // Since suffixes are now ordered from shortest to longest, // the first unique one we find will be the shortest - const uniqueCleanPath = + const uniquePath = suffixes.find((suffix) => segmentCombinationsMap.get(suffix) === 1) ?? - cleanPath; // fallback to full path if no unique suffix found - return { uri, uniquePath: decodeURIComponent(uniqueCleanPath) }; + relativePathOrBasename; // fallback to full path if no unique suffix found + return { uri, uniquePath }; }); } + // Only used when working with system paths and relative paths // Since doesn't account for URI segements before workspace export function getLastNPathParts(filepath: string, n: number): string { diff --git a/gui/src/components/markdown/StepContainerPreToolbar/StepContainerPreToolbar.tsx b/gui/src/components/markdown/StepContainerPreToolbar/StepContainerPreToolbar.tsx index c6a0d58f0e..398ce57476 100644 --- a/gui/src/components/markdown/StepContainerPreToolbar/StepContainerPreToolbar.tsx +++ b/gui/src/components/markdown/StepContainerPreToolbar/StepContainerPreToolbar.tsx @@ -93,12 +93,12 @@ export default function StepContainerPreToolbar( if (!defaultModel) { return; } - + const fileUri = await inferResolvedUriFromRelativePath( props.relativeFilepath, ideMessenger.ide, ); - + ideMessenger.post("applyToFile", { streamId: streamIdRef.current, filepath: fileUri, From 821c841b43e7b054a207d645df61e93cbc15baa4 Mon Sep 17 00:00:00 2001 From: Dallin Romney Date: Fri, 3 Jan 2025 10:47:33 +0100 Subject: [PATCH 73/99] fix multifile edit --- core/util/ideUtils.ts | 6 +++--- extensions/vscode/package-lock.json | 4 ++-- .../StepContainerPreToolbar.tsx | 1 + gui/src/util/getMultifileEditPrompt.ts | 18 ++++++++++++------ 4 files changed, 18 insertions(+), 11 deletions(-) diff --git a/core/util/ideUtils.ts b/core/util/ideUtils.ts index b969b22869..9ed4a47fc3 100644 --- a/core/util/ideUtils.ts +++ b/core/util/ideUtils.ts @@ -34,7 +34,7 @@ export async function resolveRelativePathInDir( If no meaninful path match just concatenates to first dir's uri */ export async function inferResolvedUriFromRelativePath( - path: string, + relativePath: string, ide: IDE, dirCandidates?: string[], ): Promise { @@ -44,7 +44,7 @@ export async function inferResolvedUriFromRelativePath( throw new Error("inferResolvedUriFromRelativePath: no dirs provided"); } - const segments = pathToUriPathSegment(path).split("/"); + const segments = pathToUriPathSegment(relativePath).split("/"); // Generate all possible suffixes from shortest to longest const suffixes: string[] = []; for (let i = segments.length - 1; i >= 0; i--) { @@ -79,5 +79,5 @@ export async function inferResolvedUriFromRelativePath( } // If no unique match found, use the first directory - return joinPathsToUri(dirs[0], path); + return joinPathsToUri(dirs[0], relativePath); } diff --git a/extensions/vscode/package-lock.json b/extensions/vscode/package-lock.json index 8f5cad8a0d..0a2dd83304 100644 --- a/extensions/vscode/package-lock.json +++ b/extensions/vscode/package-lock.json @@ -1,12 +1,12 @@ { "name": "continue", - "version": "0.9.247", + "version": "0.9.248", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "continue", - "version": "0.9.247", + "version": "0.9.248", "license": "Apache-2.0", "dependencies": { "@continuedev/fetch": "^1.0.3", diff --git a/gui/src/components/markdown/StepContainerPreToolbar/StepContainerPreToolbar.tsx b/gui/src/components/markdown/StepContainerPreToolbar/StepContainerPreToolbar.tsx index 398ce57476..1ee03d2f58 100644 --- a/gui/src/components/markdown/StepContainerPreToolbar/StepContainerPreToolbar.tsx +++ b/gui/src/components/markdown/StepContainerPreToolbar/StepContainerPreToolbar.tsx @@ -98,6 +98,7 @@ export default function StepContainerPreToolbar( props.relativeFilepath, ideMessenger.ide, ); + console.log("HERE", props.relativeFilepath, fileUri); ideMessenger.post("applyToFile", { streamId: streamIdRef.current, diff --git a/gui/src/util/getMultifileEditPrompt.ts b/gui/src/util/getMultifileEditPrompt.ts index 8f526f1e24..13ee8ac788 100644 --- a/gui/src/util/getMultifileEditPrompt.ts +++ b/gui/src/util/getMultifileEditPrompt.ts @@ -1,16 +1,22 @@ -import { CodeToEdit } from "core"; +import { CodeToEdit, IDE } from "core"; +import { findUriInDirs } from "core/util/uri"; export default function getMultifileEditPrompt( codeToEdit: CodeToEdit[], + dirs?: string[], ): string { const codeToEditStr = codeToEdit - .map( - (code) => ` -\`\`\` ${code.filepath} + .map((code) => { + const { relativePathOrBasename } = findUriInDirs( + code.filepath, + dirs ?? window.workspacePaths ?? [], + ); + return ` +\`\`\` ${relativePathOrBasename} ${code.contents} \`\`\` - `, - ) + `; + }) .join("\n"); return ` From 02ab561b0191194fd9ddf7d47080ab896ac36b38 Mon Sep 17 00:00:00 2001 From: Dallin Romney Date: Fri, 3 Jan 2025 16:00:42 +0100 Subject: [PATCH 74/99] gui strict null checks p3 --- gui/src/components/markdown/utils.ts | 2 +- gui/src/context/IdeMessenger.ts | 12 ++++-- gui/src/context/SubmenuContextProviders.tsx | 10 +++-- gui/src/redux/slices/sessionSlice.ts | 41 +++++++++------------ gui/src/redux/store.ts | 5 ++- gui/src/redux/thunks/callTool.ts | 2 +- gui/src/redux/thunks/gatherContext.ts | 6 ++- gui/src/redux/thunks/session.ts | 2 +- gui/src/redux/thunks/streamResponse.ts | 7 +++- 9 files changed, 49 insertions(+), 38 deletions(-) diff --git a/gui/src/components/markdown/utils.ts b/gui/src/components/markdown/utils.ts index 1044066d1d..9b23293038 100644 --- a/gui/src/components/markdown/utils.ts +++ b/gui/src/components/markdown/utils.ts @@ -30,7 +30,7 @@ export function isTerminalCodeBlock( text: string, ) { return ( - terminalLanguages.includes(language) || + (language && terminalLanguages.includes(language)) || ((!language || language?.length === 0) && (text.trim().split("\n").length === 1 || commonTerminalCommands.some((c) => text.trim().startsWith(c)))) diff --git a/gui/src/context/IdeMessenger.ts b/gui/src/context/IdeMessenger.ts index 76881e9a79..46bc925386 100644 --- a/gui/src/context/IdeMessenger.ts +++ b/gui/src/context/IdeMessenger.ts @@ -164,18 +164,22 @@ export class IdeMessenger implements IIdeMessenger { messageType: T, data: FromWebviewProtocol[T][0], cancelToken?: AbortSignal, - ): AsyncGenerator { - // ): FromWebviewProtocol[T][1] { + ): AsyncGenerator { const messageId = uuidv4(); this.post(messageType, data, messageId); - const buffer: any[] = []; + const buffer: FromWebviewProtocol[T][1][] = []; let index = 0; let done = false; let returnVal = undefined; - const handler = (event: { data: Message }) => { + const handler = (event: { + data: Message<{ + done: boolean; + content: FromWebviewProtocol[T][1]; + }>; + }) => { if (event.data.messageId === messageId) { const responseData = event.data.data; if (responseData.done) { diff --git a/gui/src/context/SubmenuContextProviders.tsx b/gui/src/context/SubmenuContextProviders.tsx index 673caec1c2..f99848665f 100644 --- a/gui/src/context/SubmenuContextProviders.tsx +++ b/gui/src/context/SubmenuContextProviders.tsx @@ -144,7 +144,9 @@ export const SubmenuContextProvidersProvider = ({ [minisearches], ); - const lastOpenFilesRef = useRef([]); + const lastOpenFilesRef = useRef< + Awaited> + >([]); useEffect(() => { let isMounted = true; const refreshOpenFiles = async () => { @@ -214,12 +216,14 @@ export const SubmenuContextProvidersProvider = ({ try { const results = getSubmenuSearchResults(providerTitle, query); if (results.length === 0) { - const fallbackItems = (fallbackResults[providerTitle] ?? []) + const fallbackItems = ( + providerTitle ? (fallbackResults[providerTitle] ?? []) : [] + ) .slice(0, limit) .map((result) => { return { ...result, - providerTitle, + providerTitle: providerTitle || "unknown", }; }); diff --git a/gui/src/redux/slices/sessionSlice.ts b/gui/src/redux/slices/sessionSlice.ts index 4888d5cb1c..7cd06594b1 100644 --- a/gui/src/redux/slices/sessionSlice.ts +++ b/gui/src/redux/slices/sessionSlice.ts @@ -281,16 +281,16 @@ export const sessionSlice = createSlice({ }, streamUpdate: (state, action: PayloadAction) => { if (state.history.length) { + const lastItem = state.history[state.history.length - 1]; + const lastMessage = lastItem.message; for (const message of action.payload) { - const lastMessage = state.history[state.history.length - 1]; - if ( message.role && - (lastMessage.message.role !== message.role || + (lastMessage.role !== message.role || // This is when a tool call comes after assistant text - (lastMessage.message.content !== "" && - message.role === "assistant" && - message.toolCalls?.length)) + (lastMessage.content !== "" && + message.role === "assistant" +)) ) { // Create a new message const historyItem: ChatHistoryItemWithMessageId = { @@ -300,8 +300,8 @@ export const sessionSlice = createSlice({ }, contextItems: [], }; - - if (message.role === "assistant" && message.toolCalls) { + const toolCalls = + if (message.role === "assistant" && message.toolCalls?.[0]) { const [_, parsedArgs] = incrementalParseJson( message.toolCalls[0].function.arguments, ); @@ -316,34 +316,29 @@ export const sessionSlice = createSlice({ state.history.push(historyItem); } else { // Add to the existing message - const msg = state.history[state.history.length - 1].message; if (message.content) { - msg.content += renderChatMessage(message); + lastMessage.content += renderChatMessage(message); } else if ( message.role === "assistant" && message.toolCalls && - msg.role === "assistant" + lastMessage.role === "assistant" ) { - if (!msg.toolCalls) { - msg.toolCalls = []; + if (!lastMessage.toolCalls) { + lastMessage.toolCalls = []; } message.toolCalls.forEach((toolCall, i) => { - if (msg.toolCalls.length <= i) { - msg.toolCalls.push(toolCall); + if (lastMessage.toolCalls!.length <= i) { + lastMessage.toolCalls!.push(toolCall); } else { - msg.toolCalls[i].function.arguments += + lastMessage.toolCalls[i].function.arguments += toolCall.function.arguments; const [_, parsedArgs] = incrementalParseJson( - msg.toolCalls[i].function.arguments, + lastMessage.toolCalls[i].function.arguments, ); - state.history[ - state.history.length - 1 - ].toolCallState.parsedArgs = parsedArgs; - state.history[ - state.history.length - 1 - ].toolCallState.toolCall.function.arguments += + lastItem.toolCallState.parsedArgs = parsedArgs; + lastItem.toolCallState.toolCall.function.arguments += toolCall.function.arguments; } }); diff --git a/gui/src/redux/store.ts b/gui/src/redux/store.ts index 5ddf55ee66..8a617d2e89 100644 --- a/gui/src/redux/store.ts +++ b/gui/src/redux/store.ts @@ -63,7 +63,10 @@ const persistConfig = { migrate: createMigrate(migrations, { debug: false }), }; -const persistedReducer = persistReducer(persistConfig, rootReducer); +const persistedReducer = persistReducer>( + persistConfig, + rootReducer, +); export function setupStore() { return configureStore({ diff --git a/gui/src/redux/thunks/callTool.ts b/gui/src/redux/thunks/callTool.ts index 184ad6e6a7..7f9af5b04a 100644 --- a/gui/src/redux/thunks/callTool.ts +++ b/gui/src/redux/thunks/callTool.ts @@ -14,10 +14,10 @@ export const callTool = createAsyncThunk( const state = getState(); const toolCallState = selectCurrentToolCall(state); - console.log("calling tool", toolCallState.toolCall); if (!toolCallState) { return; } + console.log("calling tool", toolCallState.toolCall); if (toolCallState.status !== "generated") { return; diff --git a/gui/src/redux/thunks/gatherContext.ts b/gui/src/redux/thunks/gatherContext.ts index 7f59a0547f..1250124794 100644 --- a/gui/src/redux/thunks/gatherContext.ts +++ b/gui/src/redux/thunks/gatherContext.ts @@ -39,8 +39,10 @@ export const gatherContext = createAsyncThunk< state.config.config.experimental?.defaultContext ?? []; if (!state.config.defaultModelTitle) { - console.error("Failed to gather context, no model selected"); - return; + console.error( + "gatherContext thunk: Cannot gather context, no model selected", + ); + throw new Error("No chat model selected"); } // Resolve context providers and construct new history diff --git a/gui/src/redux/thunks/session.ts b/gui/src/redux/thunks/session.ts index 9e4bbddc2c..d9f35cfd6d 100644 --- a/gui/src/redux/thunks/session.ts +++ b/gui/src/redux/thunks/session.ts @@ -192,7 +192,7 @@ export const saveCurrentSession = createAsyncThunk< } // Fallbacks if above doesn't work out or getChatTitles = false if (title === NEW_SESSION_TITLE) { - title = getChatTitleFromMessage(history[0].message); + title = getChatTitleFromMessage(state.session.history[0].message); } } // More fallbacks in case of no title diff --git a/gui/src/redux/thunks/streamResponse.ts b/gui/src/redux/thunks/streamResponse.ts index 3b70ecba15..0ad5f2fbe6 100644 --- a/gui/src/redux/thunks/streamResponse.ts +++ b/gui/src/redux/thunks/streamResponse.ts @@ -66,6 +66,10 @@ export const streamResponseThunk = createAsyncThunk< const slashCommands = state.config.config.slashCommands || []; const inputIndex = index ?? state.session.history.length; + if (!defaultModel) { + throw new Error("No chat model selected"); + } + dispatch(submitEditorAndInitAtIndex({ index, editorState })); resetStateForNewMessage(); @@ -115,7 +119,6 @@ export const streamResponseThunk = createAsyncThunk< unwrapResult(await dispatch(streamNormalInput(messages))); } else { const [slashCommand, commandInput] = commandAndInput; - let updatedContextItems = []; posthog.capture("step run", { step_name: slashCommand.name, params: {}, @@ -132,7 +135,7 @@ export const streamResponseThunk = createAsyncThunk< input: commandInput, historyIndex: inputIndex, selectedCode, - contextItems: updatedContextItems, + contextItems: [], }), ); } From c7ed2aa62e2b142f5e80448afc264bdc18d44c79 Mon Sep 17 00:00:00 2001 From: Dallin Romney Date: Fri, 3 Jan 2025 16:41:14 +0100 Subject: [PATCH 75/99] gui strict null checks continued --- .../StepContainer/UndoAndRedoButtons.tsx | 13 +++++----- gui/src/components/mainInput/resolveInput.ts | 18 ++++++------- .../StepContainerPreActionButtons.tsx | 2 +- .../StepContainerPreToolbar.tsx | 6 ++--- .../markdown/StyledMarkdownPreview.tsx | 6 ++--- gui/src/components/markdown/utils.ts | 2 +- gui/src/redux/slices/sessionSlice.ts | 26 +++++++++---------- 7 files changed, 37 insertions(+), 36 deletions(-) diff --git a/gui/src/components/StepContainer/UndoAndRedoButtons.tsx b/gui/src/components/StepContainer/UndoAndRedoButtons.tsx index a57b29bd2a..e0e57c3e17 100644 --- a/gui/src/components/StepContainer/UndoAndRedoButtons.tsx +++ b/gui/src/components/StepContainer/UndoAndRedoButtons.tsx @@ -12,9 +12,7 @@ export default function UndoAndRedoButtons() { const dispatch = useDispatch(); const ideMessenger = useContext(IdeMessengerContext); - const checkpoints = useAppSelector( - (store) => store.session.history.at(-1).checkpoint, - ); + const history = useAppSelector((store) => store.session.history); const curCheckpointIndex = useAppSelector( (store) => store.session.curCheckpointIndex, @@ -28,10 +26,13 @@ export default function UndoAndRedoButtons() { type === "undo" ? curCheckpointIndex - 1 : curCheckpointIndex + 1, ); - const checkpoint = checkpoints[checkpointIndex]; + const checkpoint = history[checkpointIndex]?.checkpoint ?? {}; - for (const [filepath, prevFileContent] of Object.entries(checkpoint)) { - ideMessenger.post("overwriteFile", { filepath, prevFileContent }); + for (const [filepath, cachedFileContent] of Object.entries(checkpoint)) { + ideMessenger.post("overwriteFile", { + filepath, + prevFileContent: cachedFileContent, + }); } dispatch(setCurCheckpointIndex(checkpointIndex)); diff --git a/gui/src/components/mainInput/resolveInput.ts b/gui/src/components/mainInput/resolveInput.ts index 9c5107d1bb..535a3074f3 100644 --- a/gui/src/components/mainInput/resolveInput.ts +++ b/gui/src/components/mainInput/resolveInput.ts @@ -122,8 +122,8 @@ async function resolveEditorContent({ let contextItems: ContextItemWithId[] = []; for (const item of contextItemAttrs) { const result = await ideMessenger.request("context/getContextItems", { - name: item.itemType === "contextProvider" ? item.id : item.itemType, - query: item.query, + name: item.itemType === "contextProvider" ? item.id : item.itemType!, + query: item.query ?? "", fullInput: stripImages(parts), selectedCode, selectedModelTitle, @@ -211,22 +211,22 @@ function resolveParagraph( p: JSONContent, ): [string, MentionAttrs[], string | undefined] { let text = ""; - const contextItems = []; + const contextItems: MentionAttrs[] = []; let slashCommand: string | undefined = undefined; for (const child of p.content || []) { if (child.type === "text") { - text += text === "" ? child.text.trimStart() : child.text; + text += text === "" ? child.text?.trimStart() : child.text; } else if (child.type === "mention") { text += - typeof child.attrs.renderInlineAs === "string" + typeof child.attrs?.renderInlineAs === "string" ? child.attrs.renderInlineAs - : child.attrs.label; - contextItems.push(child.attrs); + : child.attrs?.label; + contextItems.push(child.attrs as MentionAttrs); } else if (child.type === "slashcommand") { if (typeof slashCommand === "undefined") { - slashCommand = child.attrs.id; + slashCommand = child.attrs?.id; } else { - text += child.attrs.label; + text += child.attrs?.label; } } else { console.warn("Unexpected child type", child.type); diff --git a/gui/src/components/markdown/StepContainerPreActionButtons.tsx b/gui/src/components/markdown/StepContainerPreActionButtons.tsx index 1afbe2a277..4ecce83154 100644 --- a/gui/src/components/markdown/StepContainerPreActionButtons.tsx +++ b/gui/src/components/markdown/StepContainerPreActionButtons.tsx @@ -50,7 +50,7 @@ const InnerHoverDiv = styled.div<{ isBottomToolbarPosition: boolean }>` `; interface StepContainerPreActionButtonsProps { - language: string; + language: string | null; codeBlockContent: string; codeBlockIndex: number; children: any; diff --git a/gui/src/components/markdown/StepContainerPreToolbar/StepContainerPreToolbar.tsx b/gui/src/components/markdown/StepContainerPreToolbar/StepContainerPreToolbar.tsx index c6a0d58f0e..45b55f10b7 100644 --- a/gui/src/components/markdown/StepContainerPreToolbar/StepContainerPreToolbar.tsx +++ b/gui/src/components/markdown/StepContainerPreToolbar/StepContainerPreToolbar.tsx @@ -44,7 +44,7 @@ const ToolbarDiv = styled.div<{ isExpanded: boolean }>` export interface StepContainerPreToolbarProps { codeBlockContent: string; - language: string; + language: string | null; relativeFilepath: string; isGeneratingCodeBlock: boolean; codeBlockIndex: number; // To track which codeblock we are applying @@ -93,12 +93,12 @@ export default function StepContainerPreToolbar( if (!defaultModel) { return; } - + const fileUri = await inferResolvedUriFromRelativePath( props.relativeFilepath, ideMessenger.ide, ); - + ideMessenger.post("applyToFile", { streamId: streamIdRef.current, filepath: fileUri, diff --git a/gui/src/components/markdown/StyledMarkdownPreview.tsx b/gui/src/components/markdown/StyledMarkdownPreview.tsx index a6eb2c5696..5ad8c52c2c 100644 --- a/gui/src/components/markdown/StyledMarkdownPreview.tsx +++ b/gui/src/components/markdown/StyledMarkdownPreview.tsx @@ -228,7 +228,7 @@ const StyledMarkdownPreview = memo(function StyledMarkdownPreview( ], rehypeReactOptions: { components: { - a: ({ node, ...aProps }) => { + a: ({ ...aProps }) => { const tooltipId = uuidv4(); return ( @@ -247,7 +247,7 @@ const StyledMarkdownPreview = memo(function StyledMarkdownPreview( ); }, - pre: ({ node, ...preProps }) => { + pre: ({ ...preProps }) => { const codeBlockIndex = preProps["data-codeblockindex"]; const preChildProps = preProps?.children?.[0]?.props ?? {}; @@ -293,7 +293,7 @@ const StyledMarkdownPreview = memo(function StyledMarkdownPreview( ); }, - code: ({ node, ...codeProps }) => { + code: ({ ...codeProps }) => { const content = getCodeChildrenContent(codeProps.children); if (content && previousFileContextItemsRef.current) { diff --git a/gui/src/components/markdown/utils.ts b/gui/src/components/markdown/utils.ts index 9b23293038..9435abe472 100644 --- a/gui/src/components/markdown/utils.ts +++ b/gui/src/components/markdown/utils.ts @@ -26,7 +26,7 @@ export function getTerminalCommand(text: string): string { } export function isTerminalCodeBlock( - language: string | undefined, + language: string | undefined | null, text: string, ) { return ( diff --git a/gui/src/redux/slices/sessionSlice.ts b/gui/src/redux/slices/sessionSlice.ts index 7cd06594b1..2ad3ee89e0 100644 --- a/gui/src/redux/slices/sessionSlice.ts +++ b/gui/src/redux/slices/sessionSlice.ts @@ -288,9 +288,7 @@ export const sessionSlice = createSlice({ message.role && (lastMessage.role !== message.role || // This is when a tool call comes after assistant text - (lastMessage.content !== "" && - message.role === "assistant" -)) + (lastMessage.content !== "" && message.role === "assistant")) ) { // Create a new message const historyItem: ChatHistoryItemWithMessageId = { @@ -300,17 +298,19 @@ export const sessionSlice = createSlice({ }, contextItems: [], }; - const toolCalls = if (message.role === "assistant" && message.toolCalls?.[0]) { - const [_, parsedArgs] = incrementalParseJson( - message.toolCalls[0].function.arguments, - ); - historyItem.toolCallState = { - status: "generating", - toolCall: message.toolCalls[0] as ToolCall, - toolCallId: message.toolCalls[0].id, - parsedArgs, - }; + const toolCalls = message.toolCalls?.[0]; + if (toolCalls) { + const [_, parsedArgs] = incrementalParseJson( + message.toolCalls[0].function.arguments, + ); + historyItem.toolCallState = { + status: "generating", + toolCall: message.toolCalls[0] as ToolCall, + toolCallId: message.toolCalls[0].id, + parsedArgs, + }; + } } state.history.push(historyItem); From 877f1cf812f4bb22ef4b2cc9c3d8915bd66de73f Mon Sep 17 00:00:00 2001 From: Dallin Romney Date: Fri, 3 Jan 2025 17:20:28 +0100 Subject: [PATCH 76/99] null checks p4 --- .../components/mainInput/FillerExtension.tsx | 20 +++++ gui/src/components/mainInput/MentionList.tsx | 2 +- gui/src/components/mainInput/TipTapEditor.tsx | 78 ++++++++++++++----- .../modelSelection/ModelSelectionListbox.tsx | 7 +- gui/src/hooks/useSetup.ts | 10 ++- gui/src/pages/AddNewModel/AddNewModel.tsx | 39 ++++++---- 6 files changed, 107 insertions(+), 49 deletions(-) create mode 100644 gui/src/components/mainInput/FillerExtension.tsx diff --git a/gui/src/components/mainInput/FillerExtension.tsx b/gui/src/components/mainInput/FillerExtension.tsx new file mode 100644 index 0000000000..9b8baac86e --- /dev/null +++ b/gui/src/components/mainInput/FillerExtension.tsx @@ -0,0 +1,20 @@ +import { Node } from "@tiptap/core"; + +export const MockExtension = Node.create({ + name: "mockExtension", + + addOptions() { + return { + enabled: false, + }; + }, + addCommands() { + return {}; + }, + addKeyboardShortcuts() { + return {}; + }, + addProseMirrorPlugins() { + return []; + }, +}); diff --git a/gui/src/components/mainInput/MentionList.tsx b/gui/src/components/mainInput/MentionList.tsx index 754d436fe4..8143fe5072 100644 --- a/gui/src/components/mainInput/MentionList.tsx +++ b/gui/src/components/mainInput/MentionList.tsx @@ -311,7 +311,7 @@ const MentionList = forwardRef((props: MentionListProps, ref) => { useEffect(() => setSelectedIndex(0), [allItems]); useImperativeHandle(ref, () => ({ - onKeyDown: ({ event }) => { + onKeyDown: ({ event }: { event: KeyboardEvent }) => { if (event.key === "ArrowUp") { upHandler(); return true; diff --git a/gui/src/components/mainInput/TipTapEditor.tsx b/gui/src/components/mainInput/TipTapEditor.tsx index 00838ad91d..8b360457d1 100644 --- a/gui/src/components/mainInput/TipTapEditor.tsx +++ b/gui/src/components/mainInput/TipTapEditor.tsx @@ -5,7 +5,13 @@ import Paragraph from "@tiptap/extension-paragraph"; import Placeholder from "@tiptap/extension-placeholder"; import Text from "@tiptap/extension-text"; import { Plugin } from "@tiptap/pm/state"; -import { Editor, EditorContent, JSONContent, useEditor } from "@tiptap/react"; +import { + AnyExtension, + Editor, + EditorContent, + JSONContent, + useEditor, +} from "@tiptap/react"; import { ContextProviderDescription, InputModifiers } from "core"; import { rifWithContentsToContextItem } from "core/commands/util"; import { modelSupportsImages } from "core/llm/autodetect"; @@ -71,6 +77,7 @@ import { handleVSCMetaKeyIssues, } from "./handleMetaKeyIssues"; import { ComboBoxItem } from "./types"; +import { MockExtension } from "./FillerExtension"; const InputBoxDiv = styled.div<{ border?: string }>` resize: none; @@ -301,7 +308,7 @@ function TipTapEditor(props: TipTapEditorProps) { const { prevRef, nextRef, addRef } = useInputHistory(props.historyKey); - const editor: Editor = useEditor({ + const editor: Editor | null = useEditor({ extensions: [ Document, History, @@ -392,6 +399,7 @@ function TipTapEditor(props: TipTapEditorProps) { if (isStreamingRef.current) { return true; } + return false; }, "Shift-Enter": () => this.editor.commands.first(({ commands }) => [ @@ -417,6 +425,7 @@ function TipTapEditor(props: TipTapEditorProps) { }, 0); return true; } + return false; }, Escape: () => { if (inDropdownRef.current || !isInEditModeRef.current) { @@ -449,6 +458,7 @@ function TipTapEditor(props: TipTapEditorProps) { }, 0); return true; } + return false; }, }; }, @@ -530,7 +540,7 @@ function TipTapEditor(props: TipTapEditorProps) { return props.node.attrs.label; }, }) - : undefined, + : MockExtension, CodeBlockExtension, ], editorProps: { @@ -562,6 +572,9 @@ function TipTapEditor(props: TipTapEditorProps) { } useEffect(() => { + if (!editor) { + return; + } const placeholder = getPlaceholderText( props.placeholder, historyLengthRef.current, @@ -576,9 +589,9 @@ function TipTapEditor(props: TipTapEditorProps) { useEffect(() => { if (props.isMainInput) { - editor.commands.clearContent(true); + editor?.commands.clearContent(true); } - }, [isInEditMode, props.isMainInput]); + }, [editor, isInEditMode, props.isMainInput]); useEffect(() => { if (editor) { @@ -615,6 +628,10 @@ function TipTapEditor(props: TipTapEditorProps) { * with those key actions. */ const handleKeyDown = async (e: KeyboardEvent) => { + if (!editor) { + return; + } + setActiveKey(e.key); if (!editorFocusedRef?.current || !isMetaEquivalentKeyPressed(e)) return; @@ -632,6 +649,9 @@ function TipTapEditor(props: TipTapEditorProps) { const onEnterRef = useUpdatingRef( (modifiers: InputModifiers) => { + if (!editor) { + return; + } if (isStreaming || isEditModeAndNoCodeToEdit) { return; } @@ -659,7 +679,7 @@ function TipTapEditor(props: TipTapEditorProps) { * commands are redundant, especially the ones inside * useTimeout. */ - editor.commands.focus(); + editor?.commands.focus(); } }, [props.isMainInput, editor]); @@ -679,7 +699,7 @@ function TipTapEditor(props: TipTapEditorProps) { return; } queueMicrotask(() => { - editor.commands.setContent(mainInputContentTrigger); + editor?.commands.setContent(mainInputContentTrigger); }); dispatch(setMainEditorContentTrigger(undefined)); }, [editor, props.isMainInput, mainInputContentTrigger]); @@ -852,7 +872,7 @@ function TipTapEditor(props: TipTapEditorProps) { useWebviewListener( "isContinueInputFocused", async () => { - return props.isMainInput && editorFocusedRef.current; + return props.isMainInput && !!editorFocusedRef.current; }, [editorFocusedRef, props.isMainInput], !props.isMainInput, @@ -902,6 +922,9 @@ function TipTapEditor(props: TipTapEditorProps) { const insertCharacterWithWhitespace = useCallback( (char: string) => { + if (!editor) { + return; + } const text = editor.getText(); if (!text.endsWith(char)) { if (text.length > 0 && !text.endsWith(" ")) { @@ -921,7 +944,7 @@ function TipTapEditor(props: TipTapEditorProps) { onKeyUp={handleKeyUp} className="cursor-text" onClick={() => { - editor && editor.commands.focus(); + editor?.commands.focus(); }} onDragOver={(event) => { event.preventDefault(); @@ -941,6 +964,7 @@ function TipTapEditor(props: TipTapEditorProps) { }} onDrop={(event) => { if ( + !defaultModel || !modelSupportsImages( defaultModel.provider, defaultModel.model, @@ -952,11 +976,17 @@ function TipTapEditor(props: TipTapEditorProps) { } setShowDragOverMsg(false); let file = event.dataTransfer.files[0]; - handleImageFile(file).then(([img, dataUrl]) => { - const { schema } = editor.state; - const node = schema.nodes.image.create({ src: dataUrl }); - const tr = editor.state.tr.insert(0, node); - editor.view.dispatch(tr); + handleImageFile(file).then((result) => { + if (!editor) { + return; + } + if (result) { + const [_, dataUrl] = result; + const { schema } = editor.state; + const node = schema.nodes.image.create({ src: dataUrl }); + const tr = editor.state.tr.insert(0, node); + editor.view.dispatch(tr); + } }); event.preventDefault(); }} @@ -977,13 +1007,19 @@ function TipTapEditor(props: TipTapEditorProps) { onAddSlashCommand={() => insertCharacterWithWhitespace("/")} onEnter={onEnterRef.current} onImageFileSelected={(file) => { - handleImageFile(file).then(([img, dataUrl]) => { - const { schema } = editor.state; - const node = schema.nodes.image.create({ src: dataUrl }); - editor.commands.command(({ tr }) => { - tr.insert(0, node); - return true; - }); + handleImageFile(file).then((result) => { + if (!editor) { + return; + } + if (result) { + const [_, dataUrl] = result; + const { schema } = editor.state; + const node = schema.nodes.image.create({ src: dataUrl }); + editor.commands.command(({ tr }) => { + tr.insert(0, node); + return true; + }); + } }); }} disabled={isStreaming} diff --git a/gui/src/components/modelSelection/ModelSelectionListbox.tsx b/gui/src/components/modelSelection/ModelSelectionListbox.tsx index d043632b98..306f48ede6 100644 --- a/gui/src/components/modelSelection/ModelSelectionListbox.tsx +++ b/gui/src/components/modelSelection/ModelSelectionListbox.tsx @@ -17,7 +17,7 @@ import { vscListActiveBackground, vscListActiveForeground, } from ".."; -import { providers } from "../../pages/AddNewModel/configs/providers"; +import { DisplayInfo } from "../../pages/AddNewModel/configs/models"; export const StyledListbox = styled(Listbox)` background-color: ${vscBackground}; @@ -98,11 +98,6 @@ export const StyledListboxOption = styled(Listbox.Option)<{ } `; -interface DisplayInfo { - title: string; - icon?: string; -} - interface ModelSelectionListboxProps { selectedProvider: DisplayInfo; setSelectedProvider: Dispatch>; diff --git a/gui/src/hooks/useSetup.ts b/gui/src/hooks/useSetup.ts index 65d72557c5..ee8ced33f1 100644 --- a/gui/src/hooks/useSetup.ts +++ b/gui/src/hooks/useSetup.ts @@ -119,10 +119,12 @@ function useSetup() { if (isJetBrains()) { // Save theme colors to local storage for immediate loading in JetBrains ideMessenger.request("jetbrains/getColors", undefined).then((result) => { - Object.keys(result).forEach((key) => { - document.body.style.setProperty(key, result[key]); - document.documentElement.style.setProperty(key, result[key]); - }); + if (result.status === "success") { + Object.entries(result.content).forEach(([key, value]) => { + document.body.style.setProperty(key, value); + document.documentElement.style.setProperty(key, value); + }); + } }); // Tell JetBrains the webview is ready diff --git a/gui/src/pages/AddNewModel/AddNewModel.tsx b/gui/src/pages/AddNewModel/AddNewModel.tsx index 89212e9e39..5b9687537c 100644 --- a/gui/src/pages/AddNewModel/AddNewModel.tsx +++ b/gui/src/pages/AddNewModel/AddNewModel.tsx @@ -122,22 +122,24 @@ function AddNewModel() {
- {Object.entries(providers).map(([providerName, modelInfo], i) => ( - { - console.log(`/addModel/provider/${providerName}`); - navigate(`/addModel/provider/${providerName}`); - }} - /> - ))} + {Object.entries(providers).map(([providerName, modelInfo], i) => + modelInfo ? ( + { + console.log(`/addModel/provider/${providerName}`); + navigate(`/addModel/provider/${providerName}`); + }} + /> + ) : null, + )} ) : ( @@ -176,6 +178,9 @@ function AddNewModel() { dimensions={config.dimensions} providerOptions={config.providerOptions} onClick={(e, dimensionChoices, selectedProvider) => { + if (!selectedProvider) { + return; + } const model = { ...config.params, ..._.merge( @@ -187,7 +192,7 @@ function AddNewModel() { }; }) || []), ), - provider: providers[selectedProvider].provider, + provider: providers[selectedProvider]?.provider, }; ideMessenger.post("config/addModel", { model }); dispatch( From 49bda15a2f7063b94392206fa2b25530a9ad1726 Mon Sep 17 00:00:00 2001 From: Dallin Romney Date: Fri, 3 Jan 2025 17:36:37 +0100 Subject: [PATCH 77/99] guinull-chekc-continued --- gui/src/components/FileIcon.tsx | 1 + .../components/BestExperienceConfigForm.tsx | 6 ++-- .../components/StepContainer/EditActions.tsx | 2 +- .../ToolbarButtonWithTooltip.tsx | 11 +++++-- gui/src/forms/AddModelForm.tsx | 8 ++--- gui/src/pages/AddNewModel/configs/models.ts | 29 ++++++++++--------- .../FunctionSpecificToolCallDiv.tsx | 6 ++-- gui/src/pages/gui/ToolCallDiv/ThreadDiv.tsx | 6 ++-- gui/src/pages/gui/ToolCallDiv/index.tsx | 4 +-- gui/src/redux/thunks/streamNormalInput.ts | 12 ++++---- 10 files changed, 47 insertions(+), 38 deletions(-) diff --git a/gui/src/components/FileIcon.tsx b/gui/src/components/FileIcon.tsx index 995b59be10..6f96e498dc 100644 --- a/gui/src/components/FileIcon.tsx +++ b/gui/src/components/FileIcon.tsx @@ -1,3 +1,4 @@ +// @ts-ignore import DOMPurify from "dompurify"; import { useMemo } from "react"; import { themeIcons } from "seti-file-icons"; diff --git a/gui/src/components/OnboardingCard/components/BestExperienceConfigForm.tsx b/gui/src/components/OnboardingCard/components/BestExperienceConfigForm.tsx index 7e81bdf040..575e1e8a33 100644 --- a/gui/src/components/OnboardingCard/components/BestExperienceConfigForm.tsx +++ b/gui/src/components/OnboardingCard/components/BestExperienceConfigForm.tsx @@ -1,6 +1,6 @@ import { CubeIcon } from "@heroicons/react/24/outline"; import { DEFAULT_CHAT_MODEL_CONFIG } from "core/config/default"; -import { useContext, useState } from "react"; +import { FormEventHandler, useContext, useState } from "react"; import { useDispatch } from "react-redux"; import { Button, Input, InputSubtext, lightGray } from "../.."; import { IdeMessengerContext } from "../../../context/IdeMessenger"; @@ -33,7 +33,7 @@ function BestExperienceConfigForm({ const [autocompleteApiKey, setAutocompleteApiKey] = useState(""); const [chatApiKey, setChatApiKey] = useState(""); - async function handleSubmit(e) { + const handleSubmit: FormEventHandler = async (e) => { e.preventDefault(); const chatModelConfig = { @@ -74,7 +74,7 @@ function BestExperienceConfigForm({ } onComplete(); - } + }; return (
diff --git a/gui/src/components/StepContainer/EditActions.tsx b/gui/src/components/StepContainer/EditActions.tsx index bd1f1f5d9f..d40ed72b9e 100644 --- a/gui/src/components/StepContainer/EditActions.tsx +++ b/gui/src/components/StepContainer/EditActions.tsx @@ -36,7 +36,7 @@ export default function EditActions({ index, item }: EditActionsProps) { // const isCurCheckpoint = Math.floor(index / 2) === curCheckpointIndex; const hasPendingApplies = pendingApplyStates.length > 0; - if (isStreaming) return; + if (isStreaming) return null; return (
void; + children: ReactNode; + tooltipContent: string; +} + export function ToolbarButtonWithTooltip({ onClick, children, tooltipContent, -}) { +}: ToolbarButtonWithTooltipProps) { const tooltipId = useMemo( () => `tooltip-${Math.random().toString(36).slice(2, 11)}`, [], diff --git a/gui/src/forms/AddModelForm.tsx b/gui/src/forms/AddModelForm.tsx index 572251fd6a..021014a7bd 100644 --- a/gui/src/forms/AddModelForm.tsx +++ b/gui/src/forms/AddModelForm.tsx @@ -78,13 +78,13 @@ function AddModelForm({ } const required = selectedProvider.collectInputFor - .filter((input) => input.required) + ?.filter((input) => input.required) .map((input) => { const value = formMethods.watch(input.key); return value; }); - return !required.every((value) => value !== undefined && value.length > 0); + return !required?.every((value) => value !== undefined && value.length > 0); } useEffect(() => { @@ -94,8 +94,8 @@ function AddModelForm({ function onSubmit() { const apiKey = formMethods.watch("apiKey"); const hasValidApiKey = apiKey !== undefined && apiKey !== ""; - const reqInputFields = {}; - for (let input of selectedProvider.collectInputFor) { + const reqInputFields: Record = {}; + for (let input of selectedProvider.collectInputFor ?? []) { reqInputFields[input.key] = formMethods.watch(input.key); } diff --git a/gui/src/pages/AddNewModel/configs/models.ts b/gui/src/pages/AddNewModel/configs/models.ts index a9802ef12f..d1ce4e0712 100644 --- a/gui/src/pages/AddNewModel/configs/models.ts +++ b/gui/src/pages/AddNewModel/configs/models.ts @@ -9,9 +9,13 @@ export interface PackageDimension { description: string; options: { [key: string]: { [key: string]: any } }; } -export interface ModelPackage { + +export interface DisplayInfo { title: string; icon?: string; +} + +export interface ModelPackage extends DisplayInfo { collectInputFor?: InputDescriptor[]; description: string; refUrl?: string; @@ -70,7 +74,7 @@ export const models: { [key: string]: ModelPackage } = { "sambanova", "cerebras", "nebius", - "scaleway" + "scaleway", ], isOpenSource: true, }, @@ -1085,8 +1089,7 @@ export const models: { [key: string]: ModelPackage } = { }, asksagegpt35gov: { title: "GPT-3.5-Turbo gov", - description: - "U.S. Government. Inexpensive and good ROI.", + description: "U.S. Government. Inexpensive and good ROI.", params: { model: "gpt-gov", contextLength: 8096, @@ -1122,7 +1125,7 @@ export const models: { [key: string]: ModelPackage } = { contextLength: 8_192, title: "GPT-4", }, - providerOptions: ["openai",], + providerOptions: ["openai"], icon: "openai.png", isOpenSource: false, }, @@ -1135,7 +1138,7 @@ export const models: { [key: string]: ModelPackage } = { contextLength: 32_768, title: "GPT-4-32k", }, - providerOptions: ["openai",], + providerOptions: ["openai"], icon: "openai.png", isOpenSource: false, }, @@ -1148,7 +1151,7 @@ export const models: { [key: string]: ModelPackage } = { contextLength: 128_000, title: "GPT-o1", systemMessage: - "You are an expert software developer. You give helpful and concise responses.", + "You are an expert software developer. You give helpful and concise responses.", }, providerOptions: ["askSage"], icon: "openai.png", @@ -1163,7 +1166,7 @@ export const models: { [key: string]: ModelPackage } = { contextLength: 128_000, title: "GPT-o1-mini", systemMessage: - "You are an expert software developer. You give helpful and concise responses.", + "You are an expert software developer. You give helpful and concise responses.", }, providerOptions: ["askSage"], icon: "openai.png", @@ -1178,7 +1181,7 @@ export const models: { [key: string]: ModelPackage } = { contextLength: 200_000, title: "Claude 3.5 Sonnet gov", systemMessage: - "You are an expert software developer. You give helpful and concise responses.", + "You are an expert software developer. You give helpful and concise responses.", }, providerOptions: ["askSage"], icon: "anthropic.png", @@ -1186,8 +1189,7 @@ export const models: { [key: string]: ModelPackage } = { }, asksagegroqllama33: { title: "Llama 3.3", - description: - "Llama-3.3 is a large language model customized by Groq.", + description: "Llama-3.3 is a large language model customized by Groq.", params: { title: "Llama 3.3", model: "groq-llama33", @@ -1198,8 +1200,7 @@ export const models: { [key: string]: ModelPackage } = { }, asksagegroq70b: { title: "Groq-70B", - description: - "A large language model customized by Groq.", + description: "A large language model customized by Groq.", params: { title: "Groq-70B", model: "groq-70b", @@ -1244,7 +1245,7 @@ export const models: { [key: string]: ModelPackage } = { }, icon: "qwen.png", providerOptions: ["scaleway"], - isOpenSource: true, + isOpenSource: true, }, grokBeta: { title: "Grok Beta", diff --git a/gui/src/pages/gui/ToolCallDiv/FunctionSpecificToolCallDiv.tsx b/gui/src/pages/gui/ToolCallDiv/FunctionSpecificToolCallDiv.tsx index be4905398c..52b41f5050 100644 --- a/gui/src/pages/gui/ToolCallDiv/FunctionSpecificToolCallDiv.tsx +++ b/gui/src/pages/gui/ToolCallDiv/FunctionSpecificToolCallDiv.tsx @@ -1,4 +1,4 @@ -import { ToolCall, ToolCallState } from "core"; +import { ToolCall, ToolCallDelta, ToolCallState } from "core"; import { CreateFile } from "./CreateFile"; import { RunTerminalCommand } from "./RunTerminalCommand"; @@ -6,12 +6,12 @@ function FunctionSpecificToolCallDiv({ toolCall, toolCallState, }: { - toolCall: ToolCall; + toolCall: ToolCallDelta; toolCallState: ToolCallState; }) { const args = toolCallState.parsedArgs; - switch (toolCall.function.name) { + switch (toolCall.function?.name) { case "builtin_create_new_file": return ( props.toolCall.function.name === tool.function.name, + (tool) => props.toolCall.function?.name === tool.function.name, ), props.toolCallState, )} diff --git a/gui/src/pages/gui/ToolCallDiv/index.tsx b/gui/src/pages/gui/ToolCallDiv/index.tsx index fdc16f6ae1..9e3e1fc0f8 100644 --- a/gui/src/pages/gui/ToolCallDiv/index.tsx +++ b/gui/src/pages/gui/ToolCallDiv/index.tsx @@ -3,14 +3,14 @@ import { CheckIcon, XMarkIcon, } from "@heroicons/react/24/outline"; -import { ToolCall, ToolCallState, ToolStatus } from "core"; +import { ToolCall, ToolCallDelta, ToolCallState, ToolStatus } from "core"; import { vscButtonBackground } from "../../../components"; import Spinner from "../../../components/gui/Spinner"; import FunctionSpecificToolCallDiv from "./FunctionSpecificToolCallDiv"; import { ThreadDiv } from "./ThreadDiv"; interface ToolCallDivProps { - toolCall: ToolCall; + toolCall: ToolCallDelta; toolCallState: ToolCallState; } diff --git a/gui/src/redux/thunks/streamNormalInput.ts b/gui/src/redux/thunks/streamNormalInput.ts index 6eed3ddb12..ee1d8167a0 100644 --- a/gui/src/redux/thunks/streamNormalInput.ts +++ b/gui/src/redux/thunks/streamNormalInput.ts @@ -32,18 +32,18 @@ export const streamNormalInput = createAsyncThunk< defaultModel.title, streamAborter.signal, messages, - { - tools: useTools - ? Object.keys(toolSettings) + useTools + ? { + tools: Object.keys(toolSettings) .filter((tool) => toolSettings[tool] !== "disabled") .map((toolName) => state.config.config.tools.find( (tool) => tool.function.name === toolName, ), ) - .filter((tool) => !!tool) - : undefined, - }, + .filter((tool) => !!tool), + } + : {}, ); // Stream response From eccff7ba08fadc21e6e25f7f9463f74915fe1853 Mon Sep 17 00:00:00 2001 From: Dallin Romney Date: Fri, 3 Jan 2025 18:21:05 +0100 Subject: [PATCH 78/99] gui strict null checks part 17 --- gui/src/components/markdown/utils.ts | 2 +- .../modelSelection/ModelSelectionListbox.tsx | 12 +++++++++--- gui/src/forms/AddModelForm.tsx | 3 ++- gui/src/pages/AddNewModel/configs/models.ts | 4 +++- gui/src/pages/AddNewModel/configs/providers.ts | 3 ++- gui/src/redux/slices/sessionSlice.ts | 2 +- gui/src/redux/thunks/streamNormalInput.ts | 3 ++- 7 files changed, 20 insertions(+), 9 deletions(-) diff --git a/gui/src/components/markdown/utils.ts b/gui/src/components/markdown/utils.ts index 9435abe472..d34044089a 100644 --- a/gui/src/components/markdown/utils.ts +++ b/gui/src/components/markdown/utils.ts @@ -37,7 +37,7 @@ export function isTerminalCodeBlock( ); } -function childToText(child: any) { +function childToText(child: any): string { if (typeof child === "string") { return child; } else if (child?.props) { diff --git a/gui/src/components/modelSelection/ModelSelectionListbox.tsx b/gui/src/components/modelSelection/ModelSelectionListbox.tsx index 306f48ede6..0fc8e7deeb 100644 --- a/gui/src/components/modelSelection/ModelSelectionListbox.tsx +++ b/gui/src/components/modelSelection/ModelSelectionListbox.tsx @@ -17,7 +17,11 @@ import { vscListActiveBackground, vscListActiveForeground, } from ".."; -import { DisplayInfo } from "../../pages/AddNewModel/configs/models"; +import { + DisplayInfo, + ModelPackage, +} from "../../pages/AddNewModel/configs/models"; +import { ProviderInfo } from "../../pages/AddNewModel/configs/providers"; export const StyledListbox = styled(Listbox)` background-color: ${vscBackground}; @@ -99,8 +103,10 @@ export const StyledListboxOption = styled(Listbox.Option)<{ `; interface ModelSelectionListboxProps { - selectedProvider: DisplayInfo; - setSelectedProvider: Dispatch>; + selectedProvider: DisplayInfo | ProviderInfo | ModelPackage; + setSelectedProvider: Dispatch< + SetStateAction + >; topOptions?: DisplayInfo[]; otherOptions?: DisplayInfo[]; } diff --git a/gui/src/forms/AddModelForm.tsx b/gui/src/forms/AddModelForm.tsx index 021014a7bd..ac535d8624 100644 --- a/gui/src/forms/AddModelForm.tsx +++ b/gui/src/forms/AddModelForm.tsx @@ -53,7 +53,8 @@ function AddModelForm({ const allProviders = Object.entries(providers) .filter(([key]) => !["freetrial", "openai-aiohttp"].includes(key)) .map(([, provider]) => provider) - .filter((provider) => !!provider); + .filter((provider) => !!provider) + .map((provider) => provider!); // for type checking const popularProviders = allProviders .filter((provider) => popularProviderTitles.includes(provider.title)) diff --git a/gui/src/pages/AddNewModel/configs/models.ts b/gui/src/pages/AddNewModel/configs/models.ts index d1ce4e0712..ab469f1300 100644 --- a/gui/src/pages/AddNewModel/configs/models.ts +++ b/gui/src/pages/AddNewModel/configs/models.ts @@ -15,7 +15,9 @@ export interface DisplayInfo { icon?: string; } -export interface ModelPackage extends DisplayInfo { +export interface ModelPackage { + title: string; + icon?: string; collectInputFor?: InputDescriptor[]; description: string; refUrl?: string; diff --git a/gui/src/pages/AddNewModel/configs/providers.ts b/gui/src/pages/AddNewModel/configs/providers.ts index f3d3a9ccb0..94da86ef22 100644 --- a/gui/src/pages/AddNewModel/configs/providers.ts +++ b/gui/src/pages/AddNewModel/configs/providers.ts @@ -165,7 +165,8 @@ export const providers: Partial> = { title: "Scaleway", provider: "scaleway", refPage: "scaleway", - description: "Use the Scaleway Generative APIs to instantly access leading open models", + description: + "Use the Scaleway Generative APIs to instantly access leading open models", longDescription: `Hosted in European data centers, ideal for developers requiring low latency, full data privacy, and compliance with EU AI Act. You can generate your API key in [Scaleway's console](https://console.scaleway.com/generative-api/models). Get started:\n1. Create an API key [here](https://console.scaleway.com/iam/api-keys/)\n2. Paste below\n3. Select a model preset`, params: { apiKey: "", diff --git a/gui/src/redux/slices/sessionSlice.ts b/gui/src/redux/slices/sessionSlice.ts index 2ad3ee89e0..04b4ed9f4c 100644 --- a/gui/src/redux/slices/sessionSlice.ts +++ b/gui/src/redux/slices/sessionSlice.ts @@ -306,7 +306,7 @@ export const sessionSlice = createSlice({ ); historyItem.toolCallState = { status: "generating", - toolCall: message.toolCalls[0] as ToolCall, + toolCall: message.toolCalls[0], toolCallId: message.toolCalls[0].id, parsedArgs, }; diff --git a/gui/src/redux/thunks/streamNormalInput.ts b/gui/src/redux/thunks/streamNormalInput.ts index ee1d8167a0..d37575ce26 100644 --- a/gui/src/redux/thunks/streamNormalInput.ts +++ b/gui/src/redux/thunks/streamNormalInput.ts @@ -41,7 +41,8 @@ export const streamNormalInput = createAsyncThunk< (tool) => tool.function.name === toolName, ), ) - .filter((tool) => !!tool), + .filter((tool) => !!tool) + .map((tool) => tool!), // for type safety } : {}, ); From 9d59ebdc7bf70484e03910902cc70d88cea8813d Mon Sep 17 00:00:00 2001 From: Dallin Romney Date: Fri, 3 Jan 2025 18:21:20 +0100 Subject: [PATCH 79/99] almost there --- core/protocol/ideWebview.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/protocol/ideWebview.ts b/core/protocol/ideWebview.ts index 4ca7b05747..586afedf4a 100644 --- a/core/protocol/ideWebview.ts +++ b/core/protocol/ideWebview.ts @@ -43,7 +43,7 @@ export type ToIdeFromWebviewProtocol = ToIdeFromWebviewOrCoreProtocol & { vscMediaUrl: string; }, ]; - "jetbrains/getColors": [undefined, void]; + "jetbrains/getColors": [undefined, Record]; "vscode/openMoveRightMarkdown": [undefined, void]; setGitHubAuthToken: [{ token: string }, void]; acceptDiff: [{ filepath: string; streamId?: string }, void]; From f2820feca92490a4eff42aa6773dec2f6de471bd Mon Sep 17 00:00:00 2001 From: Nate Date: Fri, 3 Jan 2025 13:45:24 -0500 Subject: [PATCH 80/99] add env option to MCP --- core/context/mcp/index.ts | 1 + core/index.d.ts | 1 + extensions/vscode/config_schema.json | 7 +++++++ 3 files changed, 9 insertions(+) diff --git a/core/context/mcp/index.ts b/core/context/mcp/index.ts index e214b1e026..26be2f471c 100644 --- a/core/context/mcp/index.ts +++ b/core/context/mcp/index.ts @@ -72,6 +72,7 @@ class MCPConnection { return new StdioClientTransport({ command: options.transport.command, args: options.transport.args, + env: options.transport.env, }); case "websocket": return new WebSocketClientTransport(new URL(options.transport.url)); diff --git a/core/index.d.ts b/core/index.d.ts index d916a19f11..2d7622895d 100644 --- a/core/index.d.ts +++ b/core/index.d.ts @@ -940,6 +940,7 @@ export interface StdioOptions { type: "stdio"; command: string; args: string[]; + env?: Record; } export interface WebSocketOptions { diff --git a/extensions/vscode/config_schema.json b/extensions/vscode/config_schema.json index c9df4e9769..c0ffddd3ae 100644 --- a/extensions/vscode/config_schema.json +++ b/extensions/vscode/config_schema.json @@ -3151,6 +3151,13 @@ "items": { "type": "string" } + }, + "env": { + "type": "object", + "additionalProperties": { + "type": "string" + }, + "description": "Environment variables to pass to the command" } }, "required": ["type", "command", "args"] From efd3b7bf715f16cd29f2825e0e3d84f871829e63 Mon Sep 17 00:00:00 2001 From: Nate Date: Fri, 3 Jan 2025 13:55:06 -0500 Subject: [PATCH 81/99] fix merge conflicts --- core/indexing/docs/DocsService.ts | 2 -- gui/src/components/dialogs/AddDocsDialog.tsx | 12 ++++++------ gui/src/redux/slices/configSlice.ts | 14 ++++++++++++-- 3 files changed, 18 insertions(+), 10 deletions(-) diff --git a/core/indexing/docs/DocsService.ts b/core/indexing/docs/DocsService.ts index 3d9cb0fd7c..1696466dc2 100644 --- a/core/indexing/docs/DocsService.ts +++ b/core/indexing/docs/DocsService.ts @@ -18,8 +18,6 @@ import TransformersJsEmbeddingsProvider from "../../llm/llms/TransformersJsEmbed import { FromCoreProtocol, ToCoreProtocol } from "../../protocol"; import { IMessenger } from "../../protocol/messenger"; import { fetchFavicon, getFaviconBase64 } from "../../util/fetchFavicon"; -import { IMessenger } from "../../protocol/messenger"; -import { GlobalContext } from "../../util/GlobalContext"; import { editConfigJson, getDocsSqlitePath, diff --git a/gui/src/components/dialogs/AddDocsDialog.tsx b/gui/src/components/dialogs/AddDocsDialog.tsx index f816369c8d..01f9244255 100644 --- a/gui/src/components/dialogs/AddDocsDialog.tsx +++ b/gui/src/components/dialogs/AddDocsDialog.tsx @@ -5,19 +5,19 @@ import { PlusIcon, } from "@heroicons/react/24/outline"; import { IndexingStatus, PackageDocsResult, SiteIndexingConfig } from "core"; +import preIndexedDocs from "core/indexing/docs/preIndexedDocs"; import { usePostHog } from "posthog-js/react"; import { useContext, useLayoutEffect, useMemo, useRef, useState } from "react"; import { useDispatch } from "react-redux"; import { Input, SecondaryButton } from ".."; import { IdeMessengerContext } from "../../context/IdeMessenger"; +import { useAppSelector } from "../../redux/hooks"; +import { updateConfig } from "../../redux/slices/configSlice"; +import { updateIndexingStatus } from "../../redux/slices/indexingSlice"; import { setDialogMessage, setShowDialog } from "../../redux/slices/uiSlice"; -import { ToolTip } from "../gui/Tooltip"; import FileIcon from "../FileIcon"; +import { ToolTip } from "../gui/Tooltip"; import DocsIndexingPeeks from "../indexing/DocsIndexingPeeks"; -import preIndexedDocs from "core/indexing/docs/preIndexedDocs"; -import { updateIndexingStatus } from "../../redux/slices/indexingSlice"; -import { useAppSelector } from "../../redux/hooks"; -import { setConfig } from "../../redux/slices/configSlice"; function AddDocsDialog() { const config = useAppSelector((store) => store.config.config); @@ -146,7 +146,7 @@ function AddDocsDialog() { // Optimistic status update dispatch( - setConfig({ + updateConfig({ ...config, docs: [ ...(config.docs?.filter( diff --git a/gui/src/redux/slices/configSlice.ts b/gui/src/redux/slices/configSlice.ts index 35e530e474..99401fe05d 100644 --- a/gui/src/redux/slices/configSlice.ts +++ b/gui/src/redux/slices/configSlice.ts @@ -55,6 +55,12 @@ export const configSlice = createSlice({ state.config = config; state.defaultModelTitle = defaultModelTitle; }, + updateConfig: ( + state, + { payload: config }: PayloadAction, + ) => { + state.config = config; + }, setConfigError: ( state, { payload: error }: PayloadAction, @@ -93,8 +99,12 @@ export const configSlice = createSlice({ }, }); -export const { setDefaultModel, setConfigResult, setConfigError } = - configSlice.actions; +export const { + setDefaultModel, + updateConfig, + setConfigResult, + setConfigError, +} = configSlice.actions; export const { selectDefaultModel, From 9fdd9a53484e73b6cf72da98b39365c348f51800 Mon Sep 17 00:00:00 2001 From: Nate Date: Fri, 3 Jan 2025 14:28:29 -0500 Subject: [PATCH 82/99] bump --- extensions/vscode/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extensions/vscode/package.json b/extensions/vscode/package.json index 550ed35385..08cb25e3ad 100644 --- a/extensions/vscode/package.json +++ b/extensions/vscode/package.json @@ -2,7 +2,7 @@ "name": "continue", "icon": "media/icon.png", "author": "Continue Dev, Inc", - "version": "0.9.248", + "version": "0.9.249", "repository": { "type": "git", "url": "https://github.com/continuedev/continue" From a0eb5bba93933dd112f9e38b1310d8ddca92b7b1 Mon Sep 17 00:00:00 2001 From: Nate Date: Fri, 3 Jan 2025 15:38:07 -0500 Subject: [PATCH 83/99] remove authentication provider registration to avoid inability to disable extension per workspace --- extensions/vscode/package.json | 6 ------ 1 file changed, 6 deletions(-) diff --git a/extensions/vscode/package.json b/extensions/vscode/package.json index 08cb25e3ad..0d37aa237c 100644 --- a/extensions/vscode/package.json +++ b/extensions/vscode/package.json @@ -57,12 +57,6 @@ ], "main": "./out/extension.js", "contributes": { - "authentication": [ - { - "id": "continue", - "label": "Continue" - } - ], "languages": [ { "filenames": [ From 064749116463f63bac47223e18eda0a13b750775 Mon Sep 17 00:00:00 2001 From: Nate Date: Fri, 3 Jan 2025 22:54:03 -0500 Subject: [PATCH 84/99] don't show all webview message errors --- extensions/vscode/src/webviewProtocol.ts | 21 +-------------------- 1 file changed, 1 insertion(+), 20 deletions(-) diff --git a/extensions/vscode/src/webviewProtocol.ts b/extensions/vscode/src/webviewProtocol.ts index 0632d44b9e..fea6a3e8eb 100644 --- a/extensions/vscode/src/webviewProtocol.ts +++ b/extensions/vscode/src/webviewProtocol.ts @@ -1,7 +1,7 @@ import { FromWebviewProtocol, ToWebviewProtocol } from "core/protocol"; +import { Message } from "core/protocol/messenger"; import { WebviewMessengerResult } from "core/protocol/util"; import { extractMinimalStackTraceInfo } from "core/util/extractMinimalStackTraceInfo"; -import { Message } from "core/protocol/messenger"; import { Telemetry } from "core/util/posthog"; import { v4 as uuidv4 } from "uuid"; import * as vscode from "vscode"; @@ -140,25 +140,6 @@ export class VsCodeWebviewProtocol }, false, ); - vscode.window - .showErrorMessage( - message.split("\n\n")[0], - "Show Logs", - "Troubleshooting", - ) - .then((selection) => { - if (selection === "Show Logs") { - vscode.commands.executeCommand( - "workbench.action.toggleDevTools", - ); - } else if (selection === "Troubleshooting") { - vscode.env.openExternal( - vscode.Uri.parse( - "https://docs.continue.dev/troubleshooting", - ), - ); - } - }); } } } From 723a75ca00e641a9ed6758f55ec6d0ef9ea6fe34 Mon Sep 17 00:00:00 2001 From: Nate Date: Fri, 3 Jan 2025 22:55:16 -0500 Subject: [PATCH 85/99] bump --- extensions/vscode/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extensions/vscode/package.json b/extensions/vscode/package.json index 08cb25e3ad..4d4b2b8c7d 100644 --- a/extensions/vscode/package.json +++ b/extensions/vscode/package.json @@ -2,7 +2,7 @@ "name": "continue", "icon": "media/icon.png", "author": "Continue Dev, Inc", - "version": "0.9.249", + "version": "0.9.250", "repository": { "type": "git", "url": "https://github.com/continuedev/continue" From 04a3b5c3715e15c95a1c3eaa9a3c4d0f6acf3f70 Mon Sep 17 00:00:00 2001 From: Akshay Gulabrao Date: Sat, 4 Jan 2025 01:20:21 -0800 Subject: [PATCH 86/99] Update cohere.md for reranking, json is expecting name not provider --- docs/docs/customize/model-providers/more/cohere.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/docs/customize/model-providers/more/cohere.md b/docs/docs/customize/model-providers/more/cohere.md index 24f2a7f88a..8a1f069a65 100644 --- a/docs/docs/customize/model-providers/more/cohere.md +++ b/docs/docs/customize/model-providers/more/cohere.md @@ -46,7 +46,7 @@ We recommend configuring **rerank-english-v3.0** as your reranking model. ```json title="config.json" { "reranker": { - "provider": "cohere", + "name": "cohere", "params": { "model": "rerank-english-v3.0", "apiKey": "" From 41357cd00c72b61cd54b80aa3963d047c0bb4184 Mon Sep 17 00:00:00 2001 From: liuk Date: Sat, 4 Jan 2025 19:38:39 +0800 Subject: [PATCH 87/99] fix: Clear all diff blocks before streaming and ensure dispose is run on EDT - Clear all diff blocks before running the diff stream. - Ensure that inlay executes dispose on the Event Dispatch Thread (EDT). --- .../continue/IdeProtocolClient.kt | 5 ++++- .../editor/VerticalDiffBlock.kt | 11 ++++++++++- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/extensions/intellij/src/main/kotlin/com/github/continuedev/continueintellijextension/continue/IdeProtocolClient.kt b/extensions/intellij/src/main/kotlin/com/github/continuedev/continueintellijextension/continue/IdeProtocolClient.kt index b4078beeac..5921300cf5 100644 --- a/extensions/intellij/src/main/kotlin/com/github/continuedev/continueintellijextension/continue/IdeProtocolClient.kt +++ b/extensions/intellij/src/main/kotlin/com/github/continuedev/continueintellijextension/continue/IdeProtocolClient.kt @@ -469,6 +469,10 @@ class IdeProtocolClient( } + val diffStreamService = project.service() + // Clear all diff blocks before running the diff stream + diffStreamService.reject(editor) + val llmTitle = (llm as? Map<*, *>)?.get("title") as? String ?: "" val prompt = @@ -501,7 +505,6 @@ class IdeProtocolClient( rif?.range?.end?.line ?: (editor.document.lineCount - 1), {}, {}) - val diffStreamService = project.service() diffStreamService.register(diffStreamHandler, editor) diffStreamHandler.streamDiffLinesToEditor( diff --git a/extensions/intellij/src/main/kotlin/com/github/continuedev/continueintellijextension/editor/VerticalDiffBlock.kt b/extensions/intellij/src/main/kotlin/com/github/continuedev/continueintellijextension/editor/VerticalDiffBlock.kt index 64f1d1bb77..6f311c29be 100644 --- a/extensions/intellij/src/main/kotlin/com/github/continuedev/continueintellijextension/editor/VerticalDiffBlock.kt +++ b/extensions/intellij/src/main/kotlin/com/github/continuedev/continueintellijextension/editor/VerticalDiffBlock.kt @@ -3,6 +3,7 @@ package com.github.continuedev.continueintellijextension.editor import com.github.continuedev.continueintellijextension.utils.getAltKeyLabel import com.github.continuedev.continueintellijextension.utils.getShiftKeyLabel import com.intellij.openapi.Disposable +import com.intellij.openapi.application.invokeLater import com.intellij.openapi.command.WriteCommandAction import com.intellij.openapi.editor.Editor import com.intellij.openapi.editor.LogicalPosition @@ -11,6 +12,7 @@ import com.intellij.openapi.editor.markup.HighlighterLayer import com.intellij.openapi.project.Project import com.intellij.openapi.util.TextRange import com.intellij.ui.JBColor +import com.intellij.util.application import java.awt.* import javax.swing.BorderFactory import javax.swing.JButton @@ -41,7 +43,14 @@ class VerticalDiffBlock( } fun clearEditorUI() { - deletionInlay?.dispose() + deletionInlay?.let { + // Ensure that dispose is executed on EDT + if (application.isDispatchThread) { + it.dispose() + } else { + invokeLater { it.dispose() } + } + } removeGreenHighlighters() removeButtons() } From 1fb430ddb3c1685ee8608981c9a3aea6bacf8eef Mon Sep 17 00:00:00 2001 From: Dallin Romney Date: Sat, 4 Jan 2025 18:59:17 +0100 Subject: [PATCH 88/99] convert config json to file url for local config --- core/config/ConfigHandler.ts | 3 ++- core/tools/implementations/createNewFile.ts | 8 ++++---- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/core/config/ConfigHandler.ts b/core/config/ConfigHandler.ts index 9ef3cc8d29..2c91148ba0 100644 --- a/core/config/ConfigHandler.ts +++ b/core/config/ConfigHandler.ts @@ -25,6 +25,7 @@ import { ProfileDescription, ProfileLifecycleManager, } from "./ProfileLifecycleManager.js"; +import { pathToFileURL } from "url"; export type { ProfileDescription }; @@ -88,7 +89,7 @@ export class ConfigHandler { async openConfigProfile(profileId?: string) { let openProfileId = profileId || this.selectedProfileId; if (openProfileId === "local") { - await this.ide.openFile(getConfigJsonPath()); + await this.ide.openFile(pathToFileURL(getConfigJsonPath()).toString()); } else { await this.ide.openUrl( "https://app.continue.dev/", diff --git a/core/tools/implementations/createNewFile.ts b/core/tools/implementations/createNewFile.ts index 1b733da010..b5d5e6c1b9 100644 --- a/core/tools/implementations/createNewFile.ts +++ b/core/tools/implementations/createNewFile.ts @@ -3,13 +3,13 @@ import { inferResolvedUriFromRelativePath } from "../../util/ideUtils"; import { ToolImpl } from "."; export const createNewFileImpl: ToolImpl = async (args, extras) => { - const resolvedFilepath = await inferResolvedUriFromRelativePath( + const resolvedFileUri = await inferResolvedUriFromRelativePath( args.filepath, extras.ide, ); - if (resolvedFilepath) { - await extras.ide.writeFile(resolvedFilepath, args.contents); - await extras.ide.openFile(resolvedFilepath); + if (resolvedFileUri) { + await extras.ide.writeFile(resolvedFileUri, args.contents); + await extras.ide.openFile(resolvedFileUri); } return []; }; From 9a9e43d0846121d7b5716895e766571a698a51ce Mon Sep 17 00:00:00 2001 From: Dallin Romney Date: Sun, 5 Jan 2025 14:51:17 +0100 Subject: [PATCH 89/99] revert idemessenger --- extensions/vscode/package-lock.json | 4 ++-- gui/src/context/IdeMessenger.ts | 12 ++++-------- gui/src/forms/AddModelForm.tsx | 2 +- 3 files changed, 7 insertions(+), 11 deletions(-) diff --git a/extensions/vscode/package-lock.json b/extensions/vscode/package-lock.json index c6db7f1035..389aeae957 100644 --- a/extensions/vscode/package-lock.json +++ b/extensions/vscode/package-lock.json @@ -1,12 +1,12 @@ { "name": "continue", - "version": "0.9.248", + "version": "0.9.250", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "continue", - "version": "0.9.248", + "version": "0.9.250", "license": "Apache-2.0", "dependencies": { "@continuedev/fetch": "^1.0.3", diff --git a/gui/src/context/IdeMessenger.ts b/gui/src/context/IdeMessenger.ts index 46bc925386..76881e9a79 100644 --- a/gui/src/context/IdeMessenger.ts +++ b/gui/src/context/IdeMessenger.ts @@ -164,22 +164,18 @@ export class IdeMessenger implements IIdeMessenger { messageType: T, data: FromWebviewProtocol[T][0], cancelToken?: AbortSignal, - ): AsyncGenerator { + ): AsyncGenerator { + // ): FromWebviewProtocol[T][1] { const messageId = uuidv4(); this.post(messageType, data, messageId); - const buffer: FromWebviewProtocol[T][1][] = []; + const buffer: any[] = []; let index = 0; let done = false; let returnVal = undefined; - const handler = (event: { - data: Message<{ - done: boolean; - content: FromWebviewProtocol[T][1]; - }>; - }) => { + const handler = (event: { data: Message }) => { if (event.data.messageId === messageId) { const responseData = event.data.data; if (responseData.done) { diff --git a/gui/src/forms/AddModelForm.tsx b/gui/src/forms/AddModelForm.tsx index ac535d8624..7b25e59975 100644 --- a/gui/src/forms/AddModelForm.tsx +++ b/gui/src/forms/AddModelForm.tsx @@ -186,7 +186,7 @@ function AddModelForm({ From 2d3af82ad742c98030bb7f451bb149c467a09c6f Mon Sep 17 00:00:00 2001 From: Carl Gerlach Date: Mon, 6 Jan 2025 02:04:16 +0800 Subject: [PATCH 90/99] Active file is alt+enter instead of ctrl+alt+enter. Active file is alt+enter instead of ctrl+alt+enter. --- docs/docs/chat/context-selection.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/docs/chat/context-selection.md b/docs/docs/chat/context-selection.md index 7bd8fe9270..4ec425e814 100644 --- a/docs/docs/chat/context-selection.md +++ b/docs/docs/chat/context-selection.md @@ -15,7 +15,7 @@ The highlighted code you’ve selected by pressing cmd/ctrl + L< ## Active file -You can include the currently open file as context by pressing cmd + opt + enter (Mac) or ctrl + alt + enter (Windows) when you send your request. +You can include the currently open file as context by pressing cmd + opt + enter (Mac) or alt + enter (Windows) when you send your request at Chat window(Prompt can't be empty). ## Specific file From 98c2b7a39fb0b34fab9ca5b5f265d6ac6da8500e Mon Sep 17 00:00:00 2001 From: Dallin Romney Date: Sun, 5 Jan 2025 22:00:36 +0100 Subject: [PATCH 91/99] gui strict null checks finish up --- core/core.ts | 1 - core/protocol/core.ts | 20 ++++++++- core/protocol/messenger/index.ts | 2 +- .../modelSelection/ModelSelectionListbox.tsx | 6 +-- gui/src/context/IdeMessenger.ts | 22 ++++++---- gui/src/forms/AddModelForm.tsx | 24 ++++++++++- gui/src/hooks/useWebviewListener.ts | 2 +- gui/src/pages/gui/Chat.tsx | 2 +- gui/src/redux/slices/sessionSlice.ts | 42 ++++++++++++------- gui/src/redux/thunks/streamNormalInput.ts | 7 ++-- 10 files changed, 91 insertions(+), 37 deletions(-) diff --git a/core/core.ts b/core/core.ts index 4c14fff021..415b621bd2 100644 --- a/core/core.ts +++ b/core/core.ts @@ -421,7 +421,6 @@ export class Core { const chunk = next.value; - // @ts-ignore yield { content: chunk }; next = await gen.next(); } diff --git a/core/protocol/core.ts b/core/protocol/core.ts index 3849f27026..19f1942fe1 100644 --- a/core/protocol/core.ts +++ b/core/protocol/core.ts @@ -23,10 +23,26 @@ import type { } from "../"; import { ConfigResult } from "../config/load"; -export type ProtocolGeneratorType = AsyncGenerator<{ +export type ProtocolGeneratorYield = { done?: boolean; content: T; -}>; +}; +export type ProtocolGeneratorType = AsyncGenerator< + ProtocolGeneratorYield +>; + +export type AsyncGeneratorYieldType = + T extends AsyncGenerator + ? Y extends ProtocolGeneratorYield + ? PR + : never + : never; +// export type AsyncGeneratorReturnType = +// T extends AsyncGenerator +// ? R extends ProtocolGeneratorYield +// ? PR +// : never +// : never; export type OnboardingModes = | "Local" diff --git a/core/protocol/messenger/index.ts b/core/protocol/messenger/index.ts index 8b7245224d..dd5519b7ff 100644 --- a/core/protocol/messenger/index.ts +++ b/core/protocol/messenger/index.ts @@ -2,7 +2,7 @@ import { v4 as uuidv4 } from "uuid"; import type { IProtocol } from "../index"; -export interface Message { +export interface Message { messageType: string; messageId: string; data: T; diff --git a/gui/src/components/modelSelection/ModelSelectionListbox.tsx b/gui/src/components/modelSelection/ModelSelectionListbox.tsx index 0fc8e7deeb..aadf4319c9 100644 --- a/gui/src/components/modelSelection/ModelSelectionListbox.tsx +++ b/gui/src/components/modelSelection/ModelSelectionListbox.tsx @@ -103,10 +103,8 @@ export const StyledListboxOption = styled(Listbox.Option)<{ `; interface ModelSelectionListboxProps { - selectedProvider: DisplayInfo | ProviderInfo | ModelPackage; - setSelectedProvider: Dispatch< - SetStateAction - >; + selectedProvider: DisplayInfo; + setSelectedProvider: (val: DisplayInfo) => void; topOptions?: DisplayInfo[]; otherOptions?: DisplayInfo[]; } diff --git a/gui/src/context/IdeMessenger.ts b/gui/src/context/IdeMessenger.ts index 76881e9a79..1955f4f4c4 100644 --- a/gui/src/context/IdeMessenger.ts +++ b/gui/src/context/IdeMessenger.ts @@ -7,6 +7,10 @@ import { createContext } from "react"; import { v4 as uuidv4 } from "uuid"; import "vscode-webview"; import { isJetBrains } from "../util"; +import { + AsyncGeneratorYieldType, + ProtocolGeneratorYield, +} from "core/protocol/core"; interface vscode { postMessage(message: any): vscode; @@ -164,24 +168,28 @@ export class IdeMessenger implements IIdeMessenger { messageType: T, data: FromWebviewProtocol[T][0], cancelToken?: AbortSignal, - ): AsyncGenerator { - // ): FromWebviewProtocol[T][1] { + ): AsyncGenerator[], any> { + type GenYield = AsyncGeneratorYieldType; + // type GenReturn = AsyncGeneratorReturnType; + const messageId = uuidv4(); this.post(messageType, data, messageId); - const buffer: any[] = []; + const buffer: GenYield[] = []; let index = 0; let done = false; let returnVal = undefined; - const handler = (event: { data: Message }) => { + const handler = (event: { + data: Message>; + }) => { if (event.data.messageId === messageId) { const responseData = event.data.data; if (responseData.done) { window.removeEventListener("message", handler); done = true; - returnVal = responseData; + returnVal = responseData.content; } else { buffer.push(responseData.content); } @@ -239,8 +247,8 @@ export class IdeMessenger implements IIdeMessenger { next = await gen.next(); } - if (next.value.error) { - throw new Error(next.value.error); + if (!next.done) { + throw new Error(); } return { diff --git a/gui/src/forms/AddModelForm.tsx b/gui/src/forms/AddModelForm.tsx index 7b25e59975..e96349f1b5 100644 --- a/gui/src/forms/AddModelForm.tsx +++ b/gui/src/forms/AddModelForm.tsx @@ -14,6 +14,7 @@ import { import { FREE_TRIAL_LIMIT_REQUESTS, hasPassedFTL } from "../util/freeTrial"; import { completionParamsInputs } from "../pages/AddNewModel/configs/completionParamsInputs"; import { setDefaultModel } from "../redux/slices/configSlice"; +import { DisplayInfo } from "../pages/AddNewModel/configs/models"; interface QuickModelSetupProps { onDone: () => void; @@ -149,7 +150,14 @@ function AddModelForm({ { + const match = [...popularProviders, ...otherProviders].find( + (provider) => provider.title === val.title, + ); + if (match) { + setSelectedProvider(match); + } + }} topOptions={popularProviders} otherOptions={otherProviders} /> @@ -186,7 +194,19 @@ function AddModelForm({ { + const options = + Object.entries(providers).find( + ([, provider]) => + provider?.title === selectedProvider.title, + )?.[1]?.packages ?? []; + const match = options.find( + (option) => option.title === val.title, + ); + if (match) { + setSelectedModel(match); + } + }} otherOptions={ Object.entries(providers).find( ([, provider]) => diff --git a/gui/src/hooks/useWebviewListener.ts b/gui/src/hooks/useWebviewListener.ts index 4bdd9f5dc0..797df3e611 100644 --- a/gui/src/hooks/useWebviewListener.ts +++ b/gui/src/hooks/useWebviewListener.ts @@ -13,7 +13,7 @@ export function useWebviewListener( useEffect( () => { - let listener: (event: { data: Message }) => Promise; + let listener: (event: { data: Message}) => Promise; if (!skip) { listener = async (event) => { diff --git a/gui/src/pages/gui/Chat.tsx b/gui/src/pages/gui/Chat.tsx index 0c8e5b696d..99c61a71c3 100644 --- a/gui/src/pages/gui/Chat.tsx +++ b/gui/src/pages/gui/Chat.tsx @@ -407,7 +407,7 @@ export function Chat() { return (
diff --git a/gui/src/redux/slices/sessionSlice.ts b/gui/src/redux/slices/sessionSlice.ts index 04b4ed9f4c..6fcb0a6225 100644 --- a/gui/src/redux/slices/sessionSlice.ts +++ b/gui/src/redux/slices/sessionSlice.ts @@ -288,7 +288,9 @@ export const sessionSlice = createSlice({ message.role && (lastMessage.role !== message.role || // This is when a tool call comes after assistant text - (lastMessage.content !== "" && message.role === "assistant")) + (lastMessage.content !== "" && + message.role === "assistant" && + message.toolCalls?.length)) ) { // Create a new message const historyItem: ChatHistoryItemWithMessageId = { @@ -302,12 +304,12 @@ export const sessionSlice = createSlice({ const toolCalls = message.toolCalls?.[0]; if (toolCalls) { const [_, parsedArgs] = incrementalParseJson( - message.toolCalls[0].function.arguments, + message.toolCalls[0].function?.arguments ?? "{}", ); historyItem.toolCallState = { status: "generating", - toolCall: message.toolCalls[0], - toolCallId: message.toolCalls[0].id, + toolCall: message.toolCalls[0] as ToolCall, + toolCallId: message.toolCalls[0].id as ToolCall["id"], parsedArgs, }; } @@ -330,16 +332,28 @@ export const sessionSlice = createSlice({ if (lastMessage.toolCalls!.length <= i) { lastMessage.toolCalls!.push(toolCall); } else { - lastMessage.toolCalls[i].function.arguments += - toolCall.function.arguments; - - const [_, parsedArgs] = incrementalParseJson( - lastMessage.toolCalls[i].function.arguments, - ); - - lastItem.toolCallState.parsedArgs = parsedArgs; - lastItem.toolCallState.toolCall.function.arguments += - toolCall.function.arguments; + if ( + toolCall?.function?.arguments && + lastMessage?.toolCalls?.[i]?.function?.arguments && + lastItem.toolCallState + ) { + lastMessage.toolCalls[i].function!.arguments += + toolCall.function.arguments; + + const [_, parsedArgs] = incrementalParseJson( + lastMessage.toolCalls[i].function!.arguments!, + ); + + lastItem.toolCallState.parsedArgs = parsedArgs; + lastItem.toolCallState.toolCall.function.arguments += + toolCall.function.arguments; + } else { + console.error( + "Unexpected tool call format received - this message added during gui strict null checks", + message, + lastMessage, + ); + } } }); } diff --git a/gui/src/redux/thunks/streamNormalInput.ts b/gui/src/redux/thunks/streamNormalInput.ts index d37575ce26..d09a59fa22 100644 --- a/gui/src/redux/thunks/streamNormalInput.ts +++ b/gui/src/redux/thunks/streamNormalInput.ts @@ -55,15 +55,14 @@ export const streamNormalInput = createAsyncThunk< break; } - const updates = next.value as ChatMessage[]; + const updates = next.value; dispatch(streamUpdate(updates)); next = await gen.next(); } // Attach prompt log - let returnVal = next.value as PromptLog; - if (returnVal) { - dispatch(addPromptCompletionPair([returnVal])); + if (next.done) { + dispatch(addPromptCompletionPair([next.value])); } // If it's a tool call that is automatically accepted, we should call it From 0a3fb194763a55d8e1543dbcbc9fe159e950a703 Mon Sep 17 00:00:00 2001 From: Dallin Romney Date: Mon, 6 Jan 2025 12:50:14 +0100 Subject: [PATCH 92/99] revert to message any type --- core/protocol/messenger/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/protocol/messenger/index.ts b/core/protocol/messenger/index.ts index dd5519b7ff..8b7245224d 100644 --- a/core/protocol/messenger/index.ts +++ b/core/protocol/messenger/index.ts @@ -2,7 +2,7 @@ import { v4 as uuidv4 } from "uuid"; import type { IProtocol } from "../index"; -export interface Message { +export interface Message { messageType: string; messageId: string; data: T; From c61012c2b9fe795d30f0ff122ca662b9b922e074 Mon Sep 17 00:00:00 2001 From: hbernie Date: Mon, 6 Jan 2025 12:02:59 -0800 Subject: [PATCH 93/99] Update docs to remove deepseek coder. --- docs/docs/customize/model-providers/more/kindo.md | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/docs/docs/customize/model-providers/more/kindo.md b/docs/docs/customize/model-providers/more/kindo.md index 4915a1ee2d..9e205c66ed 100644 --- a/docs/docs/customize/model-providers/more/kindo.md +++ b/docs/docs/customize/model-providers/more/kindo.md @@ -1,6 +1,6 @@ # Kindo -Kindo offers centralized control over your organization's AI operations, ensuring data protection and compliance with internal policies while supporting various commercial and open-source models. To get started, sign up [here](https://app.kindo.ai/), create your API key on the [API keys page](https://app.kindo.ai/settings/api), and choose a model from the list of supported models in the [plugins tab](https://app.kindo.ai/plugins). +Kindo offers centralized control over your organization's AI operations, ensuring data protection and compliance with internal policies while supporting various commercial and open-source models. To get started, sign up [here](https://app.kindo.ai/), create an API key in [Settings > API > API Keys](https://app.kindo.ai/settings/api), and choose a model from the list of supported models in the "Available Models" tab or copy and paste the config in [Plugins > Your Configuration](https://app.kindo.ai/plugins). ## Config Example @@ -10,7 +10,7 @@ Kindo offers centralized control over your organization's AI operations, ensurin { "title": "Claude 3.5 Sonnet", "provider": "kindo", - "model": "claude-3-5-sonnet-20240620", + "model": "claude-3-5-sonnet", "apiKey": "" } ] @@ -26,13 +26,8 @@ Kindo offers centralized control over your organization's AI operations, ensurin "title": "WhiteRabbitNeo", "provider": "kindo", "model": "/models/WhiteRabbitNeo-33B-DeepSeekCoder", - "apiKey": "" - }, - { - "title": "DeepSeek", - "provider": "kindo", - "model": "deepseek-ai/deepseek-coder-33b-instruct", - "apiKey": "" + "apiKey": "", + "template": "none" } ] } From 6216c06153394014cac017a4f9758fce39c3686e Mon Sep 17 00:00:00 2001 From: Dallin Romney Date: Tue, 7 Jan 2025 01:33:01 +0100 Subject: [PATCH 94/99] split up docs sqlite migrations --- core/indexing/docs/DocsService.ts | 19 +++++++++++++++++-- core/indexing/docs/migrations.ts | 24 ++++++++++++++++++++++-- 2 files changed, 39 insertions(+), 4 deletions(-) diff --git a/core/indexing/docs/DocsService.ts b/core/indexing/docs/DocsService.ts index 1696466dc2..1f56de4955 100644 --- a/core/indexing/docs/DocsService.ts +++ b/core/indexing/docs/DocsService.ts @@ -743,10 +743,17 @@ export default class DocsService { } async getFavicon(startUrl: string) { + if (!this.config.embeddingsProvider) { + console.warn( + "Attempting to get favicon without embeddings provider specified", + ); + return; + } const db = await this.getOrCreateSqliteDb(); const result = await db.get( - `SELECT favicon FROM ${DocsService.sqlitebTableName} WHERE startUrl = ?`, + `SELECT favicon FROM ${DocsService.sqlitebTableName} WHERE startUrl = ? AND embeddingsProviderId = ?`, startUrl, + this.config.embeddingsProvider.embeddingId, ); if (!result) { @@ -1011,10 +1018,18 @@ export default class DocsService { } private async deleteMetadataFromSqlite(startUrl: string) { + if (!this.config.embeddingsProvider) { + console.warn( + `Attempting to delete metadata for ${startUrl} without embeddings provider specified`, + ); + return; + } const db = await this.getOrCreateSqliteDb(); + await db.run( - `DELETE FROM ${DocsService.sqlitebTableName} WHERE startUrl = ?`, + `DELETE FROM ${DocsService.sqlitebTableName} WHERE startUrl = ? AND embeddingsProviderId = ?`, startUrl, + this.config.embeddingsProvider.embeddingId, ); } diff --git a/core/indexing/docs/migrations.ts b/core/indexing/docs/migrations.ts index b84b0b842b..1a09b8b5a6 100644 --- a/core/indexing/docs/migrations.ts +++ b/core/indexing/docs/migrations.ts @@ -27,7 +27,7 @@ export async function runLanceMigrations(table: Table) { export async function runSqliteMigrations(db: Database) { await new Promise((resolve) => { - void migrate( + await migrate( "sqlite_modify_docs_columns_and_copy_to_config", async () => { try { @@ -52,7 +52,7 @@ export async function runSqliteMigrations(db: Database) { `ALTER TABLE ${DocsService.sqlitebTableName} RENAME COLUMN baseUrl TO startUrl;`, ); } - + const hasEmbeddingsProviderColumn = pragma.some( (pragma) => pragma.name === "embeddingsProviderId", ); @@ -77,5 +77,25 @@ export async function runSqliteMigrations(db: Database) { }, () => resolve(undefined), ); + await migrate( + "sqlite_delete_docs_with_no_embeddingsProviderId", + async () => { + try { + const pragma = await db.all( + `PRAGMA table_info(${DocsService.sqlitebTableName});`, + ); + const hasEmbeddingsProviderColumn = pragma.some( + (pragma) => pragma.name === "embeddingsProviderId", + ); + if (!hasEmbeddingsProviderColumn) { + // gotta just delete in this case since old docs will be unusable anyway + await db.exec(`DROP TABLE ${DocsService.sqlitebTableName};`); + } + } finally { + resolve(undefined); + } + }, + () => resolve(undefined), + ); }); } From 630d0fb76d16a2b23ececc6678778be371c3086a Mon Sep 17 00:00:00 2001 From: Dallin Romney Date: Tue, 7 Jan 2025 01:49:13 +0100 Subject: [PATCH 95/99] fix migration async logic sqlite docs --- core/indexing/docs/migrations.ts | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/core/indexing/docs/migrations.ts b/core/indexing/docs/migrations.ts index 1a09b8b5a6..baa6589408 100644 --- a/core/indexing/docs/migrations.ts +++ b/core/indexing/docs/migrations.ts @@ -27,7 +27,7 @@ export async function runLanceMigrations(table: Table) { export async function runSqliteMigrations(db: Database) { await new Promise((resolve) => { - await migrate( + void migrate( "sqlite_modify_docs_columns_and_copy_to_config", async () => { try { @@ -52,7 +52,7 @@ export async function runSqliteMigrations(db: Database) { `ALTER TABLE ${DocsService.sqlitebTableName} RENAME COLUMN baseUrl TO startUrl;`, ); } - + const hasEmbeddingsProviderColumn = pragma.some( (pragma) => pragma.name === "embeddingsProviderId", ); @@ -77,7 +77,10 @@ export async function runSqliteMigrations(db: Database) { }, () => resolve(undefined), ); - await migrate( + }); + + await new Promise((resolve) => { + void migrate( "sqlite_delete_docs_with_no_embeddingsProviderId", async () => { try { From c3bd2ca6c2cd5a006bae9c53c1ffb0d87a429601 Mon Sep 17 00:00:00 2001 From: Dallin Romney Date: Tue, 7 Jan 2025 01:50:12 +0100 Subject: [PATCH 96/99] docs sqlite migrations fix p3 --- core/indexing/docs/migrations.ts | 8 -------- 1 file changed, 8 deletions(-) diff --git a/core/indexing/docs/migrations.ts b/core/indexing/docs/migrations.ts index baa6589408..38aa987b35 100644 --- a/core/indexing/docs/migrations.ts +++ b/core/indexing/docs/migrations.ts @@ -53,14 +53,6 @@ export async function runSqliteMigrations(db: Database) { ); } - const hasEmbeddingsProviderColumn = pragma.some( - (pragma) => pragma.name === "embeddingsProviderId", - ); - if (!hasEmbeddingsProviderColumn) { - // gotta just delete in this case since old docs will be unusable anyway - await db.exec(`DROP TABLE ${DocsService.sqlitebTableName};`); - } - const needsToUpdateConfig = !hasFaviconCol || hasBaseUrlCol; if (needsToUpdateConfig) { const sqliteDocs = await db.all< From c17c3a2f891412dac212919fc405f196f00bcc1b Mon Sep 17 00:00:00 2001 From: Dallin Romney Date: Tue, 7 Jan 2025 02:27:18 +0100 Subject: [PATCH 97/99] filenameLink: endswith detection --- .../markdown/StyledMarkdownPreview.tsx | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/gui/src/components/markdown/StyledMarkdownPreview.tsx b/gui/src/components/markdown/StyledMarkdownPreview.tsx index a6eb2c5696..66ceb2395d 100644 --- a/gui/src/components/markdown/StyledMarkdownPreview.tsx +++ b/gui/src/components/markdown/StyledMarkdownPreview.tsx @@ -296,14 +296,16 @@ const StyledMarkdownPreview = memo(function StyledMarkdownPreview( code: ({ node, ...codeProps }) => { const content = getCodeChildrenContent(codeProps.children); - if (content && previousFileContextItemsRef.current) { + if (content) { // Insert file links for matching previous context items - const ctxItem = previousFileContextItemsRef.current.find((item) => - item.uri!.value!.includes(content), - ); - if (ctxItem) { - const rif = ctxItemToRifWithContents(ctxItem); - return ; + if (previousFileContextItemsRef.current?.length) { + const ctxItem = previousFileContextItemsRef.current.find((item) => + item.uri!.value!.endsWith(content), + ); + if (ctxItem) { + const rif = ctxItemToRifWithContents(ctxItem); + return ; + } } // Insert symbols for exact matches From c97c88f16af2d04c907fed4eead1dbb28755d845 Mon Sep 17 00:00:00 2001 From: Dallin Romney Date: Tue, 7 Jan 2025 02:36:02 +0100 Subject: [PATCH 98/99] filename link: exact match last uri segment --- .../components/markdown/StyledMarkdownPreview.tsx | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/gui/src/components/markdown/StyledMarkdownPreview.tsx b/gui/src/components/markdown/StyledMarkdownPreview.tsx index 66ceb2395d..acac40ae3b 100644 --- a/gui/src/components/markdown/StyledMarkdownPreview.tsx +++ b/gui/src/components/markdown/StyledMarkdownPreview.tsx @@ -298,10 +298,16 @@ const StyledMarkdownPreview = memo(function StyledMarkdownPreview( if (content) { // Insert file links for matching previous context items - if (previousFileContextItemsRef.current?.length) { - const ctxItem = previousFileContextItemsRef.current.find((item) => - item.uri!.value!.endsWith(content), + // With some reasonable limitations on what might be a filename + if ( + previousFileContextItemsRef.current?.length && + content.includes(".") && + content.length > 2 + ) { + const ctxItem = previousFileContextItemsRef.current.find( + (item) => item.uri!.value!.split("/").pop() === content, // Exact match for last segment of URI ); + if (ctxItem) { const rif = ctxItemToRifWithContents(ctxItem); return ; From 184848b5eef68ebb249e494f55f010ba459c5853 Mon Sep 17 00:00:00 2001 From: Test Date: Tue, 7 Jan 2025 10:52:38 -0800 Subject: [PATCH 99/99] Update commit.ts --- core/commands/slash/commit.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/commands/slash/commit.ts b/core/commands/slash/commit.ts index c2d09adba9..8fc1ae27bb 100644 --- a/core/commands/slash/commit.ts +++ b/core/commands/slash/commit.ts @@ -5,7 +5,7 @@ const CommitMessageCommand: SlashCommand = { name: "commit", description: "Generate a commit message for current changes", run: async function* ({ ide, llm, params }) { - const includeUnstaged = params?.includeUnstaged ?? true; + const includeUnstaged = params?.includeUnstaged ?? false; const diff = await ide.getDiff(includeUnstaged); if (diff.length === 0) {