diff --git a/core/config/ConfigHandler.ts b/core/config/ConfigHandler.ts index 6b37539e45..e01b41e0e8 100644 --- a/core/config/ConfigHandler.ts +++ b/core/config/ConfigHandler.ts @@ -188,6 +188,13 @@ export class ConfigHandler { this.selectedProfileId = profileId; const newConfig = await this.loadConfig(); this.notifyConfigListerners(newConfig); + const selectedProfiles = + this.globalContext.get("lastSelectedProfileForWorkspace") ?? {}; + selectedProfiles[await this.getWorkspaceId()] = profileId; + this.globalContext.update( + "lastSelectedProfileForWorkspace", + selectedProfiles, + ); } // A unique ID for the current workspace, built from folder names diff --git a/core/config/onboarding.ts b/core/config/onboarding.ts index c1062e4211..5b727c34fc 100644 --- a/core/config/onboarding.ts +++ b/core/config/onboarding.ts @@ -10,11 +10,6 @@ export function setupApiKeysMode( return { ...config, models: config.models.filter((model) => model.provider !== "free-trial"), - tabAutocompleteModel: { - title: "Tab Autocomplete", - provider: "free-trial", - model: TRIAL_FIM_MODEL, - }, embeddingsProvider: { provider: "free-trial", }, diff --git a/core/llm/index.ts b/core/llm/index.ts index cddf6c7f6a..4da78f6901 100644 --- a/core/llm/index.ts +++ b/core/llm/index.ts @@ -1,3 +1,4 @@ +import { findLlmInfo } from "@continuedev/llm-info"; import Handlebars from "handlebars"; import { ChatMessage, @@ -114,14 +115,17 @@ export abstract class BaseLLM implements ILLM { ..._options, }; + this.model = options.model; + const llmInfo = findLlmInfo(this.model); + const templateType = options.template ?? autodetectTemplateType(options.model); this.title = options.title; this.uniqueId = options.uniqueId ?? "None"; - this.model = options.model; this.systemMessage = options.systemMessage; - this.contextLength = options.contextLength ?? DEFAULT_CONTEXT_LENGTH; + this.contextLength = + options.contextLength ?? llmInfo?.contextLength ?? DEFAULT_CONTEXT_LENGTH; this.completionOptions = { ...options.completionOptions, model: options.model || "gpt-4", diff --git a/core/llm/templates/edit.ts b/core/llm/templates/edit.ts index f6bf1e848e..fd288a48ea 100644 --- a/core/llm/templates/edit.ts +++ b/core/llm/templates/edit.ts @@ -95,7 +95,7 @@ ${otherData.codeToEdit} ${suffixTag} \`\`\` -Please rewrite the entire code block above in order to satisfy the following request: "${otherData.userInput}".${suffixExplanation}`, +Please rewrite the entire code block above in order to satisfy the following request: "${otherData.userInput}". You should rewrite the entire code block without leaving placeholders, even if the code is the same as before.${suffixExplanation}`, }, { role: "assistant", @@ -115,7 +115,7 @@ ${otherData.codeToEdit} ${suffixTag} \`\`\` -Please rewrite the entire code block above, editing the portion below "${START_TAG}" in order to satisfy the following request: "${otherData.userInput}".${suffixExplanation} +Please rewrite the entire code block above, editing the portion below "${START_TAG}" in order to satisfy the following request: "${otherData.userInput}". You should rewrite the entire code block without leaving placeholders, even if the code is the same as before.${suffixExplanation} `, }, { diff --git a/core/package-lock.json b/core/package-lock.json index 9be2bdb6d5..cc2167bf13 100644 --- a/core/package-lock.json +++ b/core/package-lock.json @@ -12,6 +12,7 @@ "@aws-sdk/client-bedrock-runtime": "^3.574.0", "@aws-sdk/credential-providers": "^3.596.0", "@continuedev/config-types": "^1.0.6", + "@continuedev/llm-info": "^1.0.1", "@mozilla/readability": "^0.5.0", "@octokit/rest": "^20.0.2", "@types/jsdom": "^21.1.6", @@ -3937,6 +3938,11 @@ "zod": "^3.23.8" } }, + "node_modules/@continuedev/llm-info": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@continuedev/llm-info/-/llm-info-1.0.1.tgz", + "integrity": "sha512-fmRXuOSwJ9ogBPSDduoedHF3WVg1rge3o8SRRg1hkUagRnxwZqtQj1sS5SjQyAqjQv6VPMGPDEtD77Za/hc4Jg==" + }, "node_modules/@esbuild/android-arm": { "version": "0.17.19", "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.17.19.tgz", diff --git a/core/package.json b/core/package.json index e057ba1691..cfd12929f4 100644 --- a/core/package.json +++ b/core/package.json @@ -32,6 +32,7 @@ "@aws-sdk/client-bedrock-runtime": "^3.574.0", "@aws-sdk/credential-providers": "^3.596.0", "@continuedev/config-types": "^1.0.6", + "@continuedev/llm-info": "^1.0.1", "@mozilla/readability": "^0.5.0", "@octokit/rest": "^20.0.2", "@types/jsdom": "^21.1.6", diff --git a/core/util/paths.ts b/core/util/paths.ts index 2764fa8be4..fedeac3136 100644 --- a/core/util/paths.ts +++ b/core/util/paths.ts @@ -177,16 +177,17 @@ function getMigrationsFolderPath(): string { return migrationsPath; } -export function migrate( +export async function migrate( id: string, - callback: () => void, + callback: () => void | Promise, onAlreadyComplete?: () => void, ) { const migrationsPath = getMigrationsFolderPath(); const migrationPath = path.join(migrationsPath, id); + if (!fs.existsSync(migrationPath)) { try { - callback(); + await Promise.resolve(callback()); fs.writeFileSync(migrationPath, ""); } catch (e) { console.error(`Migration ${id} failed`, e); diff --git a/docs/docs/customization/context-providers.md b/docs/docs/customization/context-providers.md index c9f3f144ef..66aacea537 100644 --- a/docs/docs/customization/context-providers.md +++ b/docs/docs/customization/context-providers.md @@ -16,6 +16,14 @@ As an example, say you are working on solving a new GitHub Issue. You type '@iss To use any of the built-in context providers, open `~/.continue/config.json` and add it to the `contextProviders` list. +### Files + +Type '@file' to reference any file in your current workspace. + +```json +{ "name": "file" } +``` + ### Code Type '@code' to reference specific functions or classes from throughout your project. @@ -498,9 +506,7 @@ Continue exposes an API for registering context providers from a 3rd party VSCod ```json { - "extensionDependencies": [ - "continue.continue" - ], + "extensionDependencies": ["continue.continue"] } ``` @@ -513,7 +519,6 @@ Here is an example: import * as vscode from "vscode"; class MyCustomProvider implements IContextProvider { - get description(): ContextProviderDescription { return { title: "custom", @@ -525,7 +530,7 @@ class MyCustomProvider implements IContextProvider { async getContextItems( query: string, - extras: ContextProviderExtras + extras: ContextProviderExtras, ): Promise { return [ { @@ -537,7 +542,7 @@ class MyCustomProvider implements IContextProvider { } async loadSubmenuItems( - args: LoadSubmenuItemsArgs + args: LoadSubmenuItemsArgs, ): Promise { return []; } @@ -554,5 +559,4 @@ const continueApi = continueExt?.exports; // register your custom provider continueApi?.registerCustomContextProvider(customProvider); - -``` \ No newline at end of file +``` diff --git a/extensions/intellij/gradle.properties b/extensions/intellij/gradle.properties index f9e9a3446f..755cf55fff 100644 --- a/extensions/intellij/gradle.properties +++ b/extensions/intellij/gradle.properties @@ -4,7 +4,7 @@ pluginGroup = com.github.continuedev.continueintellijextension pluginName = continue-intellij-extension pluginRepositoryUrl = https://github.com/continuedev/continue # SemVer format -> https://semver.org -pluginVersion = 0.0.53 +pluginVersion = 0.0.54 # Supported build number ranges and IntelliJ Platform versions -> https://plugins.jetbrains.com/docs/intellij/build-number-ranges.html pluginSinceBuild = 223 diff --git a/extensions/intellij/src/main/kotlin/com/github/continuedev/continueintellijextension/autocomplete/AutocompleteActionGroup.kt b/extensions/intellij/src/main/kotlin/com/github/continuedev/continueintellijextension/autocomplete/AutocompleteActionGroup.kt new file mode 100644 index 0000000000..bae9a8fe03 --- /dev/null +++ b/extensions/intellij/src/main/kotlin/com/github/continuedev/continueintellijextension/autocomplete/AutocompleteActionGroup.kt @@ -0,0 +1,29 @@ +package com.github.continuedev.continueintellijextension.autocomplete + +import com.github.continuedev.continueintellijextension.services.ContinueExtensionSettings +import com.intellij.openapi.actionSystem.ActionUpdateThread +import com.intellij.openapi.actionSystem.AnActionEvent +import com.intellij.openapi.actionSystem.DefaultActionGroup +import com.intellij.openapi.components.service + +class AutocompleteActionGroup : DefaultActionGroup() { + override fun getActionUpdateThread(): ActionUpdateThread { + return ActionUpdateThread.EDT + } + + override fun update(e: AnActionEvent) { + super.update(e) + removeAll() + + val continueSettingsService = service() + if (continueSettingsService.continueState.enableTabAutocomplete) { + addAll( + DisableTabAutocompleteAction(), + ) + } else { + addAll( + EnableTabAutocompleteAction(), + ) + } + } +} \ No newline at end of file diff --git a/extensions/intellij/src/main/kotlin/com/github/continuedev/continueintellijextension/autocomplete/AutocompleteEditorListener.kt b/extensions/intellij/src/main/kotlin/com/github/continuedev/continueintellijextension/autocomplete/AutocompleteEditorListener.kt index 9320705f8b..a2ef465a35 100644 --- a/extensions/intellij/src/main/kotlin/com/github/continuedev/continueintellijextension/autocomplete/AutocompleteEditorListener.kt +++ b/extensions/intellij/src/main/kotlin/com/github/continuedev/continueintellijextension/autocomplete/AutocompleteEditorListener.kt @@ -12,10 +12,15 @@ import com.intellij.openapi.util.TextRange class AutocompleteCaretListener: CaretListener { override fun caretPositionChanged(event: CaretEvent) { val caret = event.caret ?: return - val oldPosition = event.oldPosition val offset = caret.offset val editor = caret.editor val autocompleteService = editor.project?.service() ?: return + + if (autocompleteService.lastChangeWasPartialAccept) { + autocompleteService.lastChangeWasPartialAccept = false + return + } + val pending = autocompleteService.pendingCompletion; if (pending != null && pending.editor == editor && pending.offset == offset) { return @@ -29,10 +34,16 @@ class AutocompleteDocumentListener(private val editorManager: FileEditorManager, if (editor != editorManager.selectedTextEditor) { return } + + val service = editor.project?.service() ?: return + if (service.lastChangeWasPartialAccept) { + return + } + // Invoke later is important, otherwise the completion will be triggered before the document is updated // causing the old caret offset to be used invokeLater { - editor.project?.service()?.triggerCompletion(editor) + service.triggerCompletion(editor) } } } 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 6d3691ad6a..8731f7cacc 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 @@ -14,18 +14,29 @@ import com.intellij.openapi.editor.Editor import com.intellij.openapi.editor.InlayProperties import com.intellij.openapi.fileEditor.FileDocumentManager import com.intellij.openapi.project.Project +import com.intellij.openapi.wm.WindowManager data class PendingCompletion ( - val editor: Editor, - val offset: Int, - val completionId: String, - val text: String? + val editor: Editor, + var offset: Int, + val completionId: String, + var text: String? ) @Service(Service.Level.PROJECT) class AutocompleteService(private val project: Project) { var pendingCompletion: PendingCompletion? = null; private val autocompleteLookupListener = project.service() + private var widget: AutocompleteSpinnerWidget? = null + + // To avoid triggering another completion on partial acceptance, + // we need to keep track of whether the last change was a partial accept + var lastChangeWasPartialAccept = false + + init { + val statusBar = WindowManager.getInstance().getStatusBar(project) + widget = statusBar.getWidget("AutocompleteSpinnerWidget") as? AutocompleteSpinnerWidget + } fun triggerCompletion(editor: Editor) { val settings = @@ -42,6 +53,7 @@ class AutocompleteService(private val project: Project) { val completionId = uuid() val offset = editor.caretModel.primaryCaret.offset pendingCompletion = PendingCompletion(editor, offset, completionId, null) + widget?.setLoading(true) // Request a completion from the core val virtualFile = FileDocumentManager.getInstance().getFile(editor.document) @@ -63,11 +75,13 @@ class AutocompleteService(private val project: Project) { val lineLength = lineEnd - lineStart project.service().coreMessenger?.request("autocomplete/complete", input, null, ({ response -> + widget?.setLoading(false) + val completions = response as List<*> if (completions.isNotEmpty()) { val completion = completions[0].toString() - if (completion.lines().size === 1 || column >= lineLength) { + if (completion.isNotEmpty() && (completion.lines().size === 1 || column >= lineLength)) { // Do not render if completion is multi-line and caret is in middle of line renderCompletion(editor, offset, completion) pendingCompletion = pendingCompletion?.copy(text = completion) @@ -80,12 +94,18 @@ class AutocompleteService(private val project: Project) { } private fun renderCompletion(editor: Editor, offset: Int, text: String) { + if (text.isEmpty()) { + return + } // Don't render completions when code completion dropdown is visible if (!autocompleteLookupListener.isLookupEmpty()) { return } ApplicationManager.getApplication().invokeLater { WriteAction.run { + // Clear existing completions + hideCompletions(editor) + val properties = InlayProperties() properties.relatesToPrecedingText(true) properties.disableSoftWrapping(true) @@ -120,8 +140,57 @@ class AutocompleteService(private val project: Project) { } } + private fun splitKeepingDelimiters(input: String, delimiterPattern: String = "\\s+"): List { + val initialSplit = input.split("(?<=$delimiterPattern)|(?=$delimiterPattern)".toRegex()) + .filter { it.isNotEmpty() } + + val result = mutableListOf() + var currentDelimiter = "" + + for (part in initialSplit) { + if (part.matches(delimiterPattern.toRegex())) { + currentDelimiter += part + } else { + if (currentDelimiter.isNotEmpty()) { + result.add(currentDelimiter) + currentDelimiter = "" + } + result.add(part) + } + } + + if (currentDelimiter.isNotEmpty()) { + result.add(currentDelimiter) + } + + return result +} + + fun partialAccept() { + val completion = pendingCompletion ?: return + val text = completion.text ?: return + val editor = completion.editor + val offset = completion.offset + + lastChangeWasPartialAccept = true + + // Split the text into words, keeping delimiters + val words = splitKeepingDelimiters(text) + println(words) + val word = words[0] + editor.document.insertString(offset, word) + editor.caretModel.moveToOffset(offset + word.length) + + // Remove the completion and re-display it + hideCompletions(editor) + completion.text = text.substring(word.length) + completion.offset += word.length + renderCompletion(editor, completion.offset, completion.text!!) + } + private fun cancelCompletion(completion: PendingCompletion) { // Send cancellation message to core + widget?.setLoading(false) project.service().coreMessenger?.request("autocomplete/cancel", null,null, ({})) } diff --git a/extensions/intellij/src/main/kotlin/com/github/continuedev/continueintellijextension/autocomplete/AutocompleteSpinnerWidgetFactory.kt b/extensions/intellij/src/main/kotlin/com/github/continuedev/continueintellijextension/autocomplete/AutocompleteSpinnerWidgetFactory.kt new file mode 100644 index 0000000000..8408a4964e --- /dev/null +++ b/extensions/intellij/src/main/kotlin/com/github/continuedev/continueintellijextension/autocomplete/AutocompleteSpinnerWidgetFactory.kt @@ -0,0 +1,111 @@ +package com.github.continuedev.continueintellijextension.autocomplete + +import com.github.continuedev.continueintellijextension.services.ContinueExtensionSettings +import com.intellij.openapi.Disposable +import com.intellij.openapi.components.service +import com.intellij.openapi.project.Project +import com.intellij.openapi.util.Disposer +import com.intellij.openapi.wm.StatusBar +import com.intellij.openapi.wm.StatusBarWidget +import com.intellij.openapi.util.IconLoader +import com.intellij.openapi.wm.StatusBarWidgetFactory +import com.intellij.openapi.wm.WindowManager +import com.intellij.openapi.wm.impl.status.EditorBasedWidget +import com.intellij.ui.AnimatedIcon +import com.intellij.util.Consumer +import java.awt.event.MouseEvent +import javax.swing.Icon +import javax.swing.JLabel + +class AutocompleteSpinnerWidget(project: Project): EditorBasedWidget(project), StatusBarWidget.IconPresentation, Disposable { + private val iconLabel = JLabel() + private var isLoading = false + + private val animatedIcon = AnimatedIcon( + 100, + IconLoader.getIcon("/icons/AnimationLoadingIcon/AnimationLoading1(RiderLight).svg", javaClass), + IconLoader.getIcon("/icons/AnimationLoadingIcon/AnimationLoading2(RiderLight).svg", javaClass), + IconLoader.getIcon("/icons/AnimationLoadingIcon/AnimationLoading3(RiderLight).svg", javaClass), + IconLoader.getIcon("/icons/AnimationLoadingIcon/AnimationLoading4(RiderLight).svg", javaClass), + IconLoader.getIcon("/icons/AnimationLoadingIcon/AnimationLoading5(RiderLight).svg", javaClass), + IconLoader.getIcon("/icons/AnimationLoadingIcon/AnimationLoading6(RiderLight).svg", javaClass), + IconLoader.getIcon("/icons/AnimationLoadingIcon/AnimationLoading7(RiderLight).svg", javaClass), + IconLoader.getIcon("/icons/AnimationLoadingIcon/AnimationLoading8(RiderLight).svg", javaClass), + ) + + init { + updateIcon() + } + + fun show() { + println("Showing autocomplete spinner widget") + } + + override fun dispose() {} + + override fun ID(): String { + return "AutocompleteSpinnerWidget" + } + + override fun getTooltipText(): String? { + val enabled = service().state.enableTabAutocomplete + return if (enabled) "Continue Autocomplete Enabled" else "Continue Autocomplete Disabled" + } + + override fun getClickConsumer(): Consumer? { + return null + } + + override fun getIcon(): Icon = if (isLoading) animatedIcon else + IconLoader.getIcon("/icons/continue.svg", javaClass) + + fun setLoading(loading: Boolean) { + isLoading = loading + updateIcon() + } + + private fun updateIcon() { + iconLabel.icon = getIcon() + + + // Update the widget + val statusBar = WindowManager.getInstance().getStatusBar(project) + statusBar.updateWidget(ID()) + } + + override fun install(statusBar: StatusBar) { + updateIcon() + } + + override fun getPresentation(): StatusBarWidget.WidgetPresentation? { + return this + } +} + +class AutocompleteSpinnerWidgetFactory: StatusBarWidgetFactory { + fun create(project: Project): AutocompleteSpinnerWidget { + return AutocompleteSpinnerWidget(project) + } + + override fun getId(): String { + return "AutocompleteSpinnerWidget" + } + + override fun getDisplayName(): String { + return "Continue Autocomplete" + } + + override fun isAvailable(p0: Project): Boolean { + return true + } + + override fun createWidget(project: Project): StatusBarWidget { + return AutocompleteSpinnerWidget(project) + } + + override fun disposeWidget(p0: StatusBarWidget) { + Disposer.dispose(p0) + } + + override fun canBeEnabledOn(p0: StatusBar): Boolean = true +} \ No newline at end of file diff --git a/extensions/intellij/src/main/kotlin/com/github/continuedev/continueintellijextension/autocomplete/ContinueCustomElementRenderer.kt b/extensions/intellij/src/main/kotlin/com/github/continuedev/continueintellijextension/autocomplete/ContinueCustomElementRenderer.kt index 3b8c8fe761..b151238c38 100644 --- a/extensions/intellij/src/main/kotlin/com/github/continuedev/continueintellijextension/autocomplete/ContinueCustomElementRenderer.kt +++ b/extensions/intellij/src/main/kotlin/com/github/continuedev/continueintellijextension/autocomplete/ContinueCustomElementRenderer.kt @@ -19,7 +19,8 @@ class ContinueCustomElementRenderer ( val text: String, ) : EditorCustomElementRenderer { override fun calcWidthInPixels(inlay: Inlay<*>): Int { - return (inlay.editor as EditorImpl).getFontMetrics(Font.PLAIN).stringWidth(this.text) + val width = (inlay.editor as EditorImpl).getFontMetrics(Font.PLAIN).stringWidth(this.text) + return width } private fun font(editor: Editor): Font { diff --git a/extensions/intellij/src/main/kotlin/com/github/continuedev/continueintellijextension/autocomplete/ContinueMultilineCustomElementRenderer.kt b/extensions/intellij/src/main/kotlin/com/github/continuedev/continueintellijextension/autocomplete/ContinueMultilineCustomElementRenderer.kt index 989519cd67..38ecb5766b 100644 --- a/extensions/intellij/src/main/kotlin/com/github/continuedev/continueintellijextension/autocomplete/ContinueMultilineCustomElementRenderer.kt +++ b/extensions/intellij/src/main/kotlin/com/github/continuedev/continueintellijextension/autocomplete/ContinueMultilineCustomElementRenderer.kt @@ -7,9 +7,8 @@ import com.intellij.openapi.editor.colors.EditorFontType import com.intellij.openapi.editor.impl.EditorImpl import com.intellij.openapi.editor.impl.FontInfo import com.intellij.openapi.editor.markup.TextAttributes -import com.intellij.ui.Gray import com.intellij.ui.JBColor -import java.awt.Color +import com.intellij.util.ui.UIUtil import java.awt.Font import java.awt.Graphics import java.awt.Rectangle @@ -39,7 +38,7 @@ class ContinueMultilineCustomElementRenderer ( protected val font: Font get() { val editorFont = editor.colorsScheme.getFont(EditorFontType.PLAIN) - return editorFont.deriveFont(Font.PLAIN) ?: editorFont + return UIUtil.getFontWithFallbackIfNeeded(editorFont, text).deriveFont(editor.colorsScheme.editorFontSize) } private fun offsetY(): Int { @@ -58,7 +57,9 @@ class ContinueMultilineCustomElementRenderer ( FontInfo.getFontMetrics(font, FontInfo.getFontRenderContext(editor.contentComponent)) val fontWidth = font.createGlyphVector(metrics.fontRenderContext, text).visualBounds.width - val widthBeforeCaret = (editor as EditorImpl).getFontMetrics(Font.PLAIN).stringWidth(text.substring(0, currentColumn)) + val widthBeforeCaret = (editor as EditorImpl).getFontMetrics(Font.PLAIN).stringWidth( + text.substring(0, minOf(currentColumn, text.length)) + ) return max(0, widthBeforeCaret - (editor as EditorImpl).scrollingModel.horizontalScrollOffset) } @@ -68,7 +69,7 @@ class ContinueMultilineCustomElementRenderer ( var additionalYOffset = -editor.lineHeight; var isFirstLine = true for (line in text.lines()) { - g.drawString(line, if (isFirstLine) targetRegion.x + offsetX() else targetRegion.x, targetRegion.y + offsetY() + additionalYOffset) + g.drawString(line, if (isFirstLine) targetRegion.x + offsetX() else targetRegion.x, targetRegion.y + inlay.editor.ascent + additionalYOffset) additionalYOffset += editor.lineHeight isFirstLine = false } diff --git a/extensions/intellij/src/main/kotlin/com/github/continuedev/continueintellijextension/autocomplete/DisableTabAutocompleteAction.kt b/extensions/intellij/src/main/kotlin/com/github/continuedev/continueintellijextension/autocomplete/DisableTabAutocompleteAction.kt new file mode 100644 index 0000000000..d7e1f3b133 --- /dev/null +++ b/extensions/intellij/src/main/kotlin/com/github/continuedev/continueintellijextension/autocomplete/DisableTabAutocompleteAction.kt @@ -0,0 +1,12 @@ +package com.github.continuedev.continueintellijextension.autocomplete + +import com.github.continuedev.continueintellijextension.services.ContinueExtensionSettings +import com.intellij.openapi.actionSystem.AnAction +import com.intellij.openapi.actionSystem.AnActionEvent +import com.intellij.openapi.components.service +class DisableTabAutocompleteAction : AnAction() { + override fun actionPerformed(e: AnActionEvent) { + val continueSettingsService = service() + continueSettingsService.continueState.enableTabAutocomplete = true + } +} diff --git a/extensions/intellij/src/main/kotlin/com/github/continuedev/continueintellijextension/autocomplete/EnableTabAutocompleteAction.kt b/extensions/intellij/src/main/kotlin/com/github/continuedev/continueintellijextension/autocomplete/EnableTabAutocompleteAction.kt new file mode 100644 index 0000000000..b14aa2190e --- /dev/null +++ b/extensions/intellij/src/main/kotlin/com/github/continuedev/continueintellijextension/autocomplete/EnableTabAutocompleteAction.kt @@ -0,0 +1,12 @@ +package com.github.continuedev.continueintellijextension.autocomplete + +import com.github.continuedev.continueintellijextension.services.ContinueExtensionSettings +import com.intellij.openapi.actionSystem.AnAction +import com.intellij.openapi.actionSystem.AnActionEvent +import com.intellij.openapi.components.service +class EnableTabAutocompleteAction : AnAction() { + override fun actionPerformed(e: AnActionEvent) { + val continueSettingsService = service() + continueSettingsService.continueState.enableTabAutocomplete = true + } +} diff --git a/extensions/intellij/src/main/kotlin/com/github/continuedev/continueintellijextension/autocomplete/PartialAcceptAutocompleteAction.kt b/extensions/intellij/src/main/kotlin/com/github/continuedev/continueintellijextension/autocomplete/PartialAcceptAutocompleteAction.kt index 1cd223df91..93c94ee0ab 100644 --- a/extensions/intellij/src/main/kotlin/com/github/continuedev/continueintellijextension/autocomplete/PartialAcceptAutocompleteAction.kt +++ b/extensions/intellij/src/main/kotlin/com/github/continuedev/continueintellijextension/autocomplete/PartialAcceptAutocompleteAction.kt @@ -11,8 +11,7 @@ import com.intellij.openapi.editor.actionSystem.EditorActionHandler class PartialAcceptAutocompleteAction: EditorAction(object : EditorActionHandler() { override fun doExecute(editor: Editor, caret: Caret?, dataContext: DataContext?) { ApplicationManager.getApplication().runWriteAction { - // TODO -// editor.project?.service()?.accept() + editor.project?.service()?.partialAccept() } } diff --git a/extensions/intellij/src/main/kotlin/com/github/continuedev/continueintellijextension/editor/InlineEditAction.kt b/extensions/intellij/src/main/kotlin/com/github/continuedev/continueintellijextension/editor/InlineEditAction.kt index 73a48e7bae..e974cf8fd8 100644 --- a/extensions/intellij/src/main/kotlin/com/github/continuedev/continueintellijextension/editor/InlineEditAction.kt +++ b/extensions/intellij/src/main/kotlin/com/github/continuedev/continueintellijextension/editor/InlineEditAction.kt @@ -66,7 +66,7 @@ class InlineEditAction : AnAction(), DumbAware { val modelTitles = mutableListOf() continuePluginService.coreMessenger?.request("config/getSerializedProfileInfo", null, null) { response -> val config = response as Map - val models = config["models"] as List> + val models = (config["config"] as Map)["models"] as List> modelTitles.addAll(models.map { it["title"] as String }) } val maxWaitTime = 200 diff --git a/extensions/intellij/src/main/resources/META-INF/plugin.xml b/extensions/intellij/src/main/resources/META-INF/plugin.xml index 2ff6bbd895..50cab8fdfb 100644 --- a/extensions/intellij/src/main/resources/META-INF/plugin.xml +++ b/extensions/intellij/src/main/resources/META-INF/plugin.xml @@ -22,6 +22,8 @@ + messages.MyBundle @@ -108,10 +110,6 @@ text="Start New Continue Session" icon="AllIcons.General.Add" description="Start New Continue Session"> - - + + + + diff --git a/extensions/intellij/src/main/resources/webview/continue-dev-square.png b/extensions/intellij/src/main/resources/webview/continue-dev-square.png deleted file mode 100644 index e4b625568c..0000000000 Binary files a/extensions/intellij/src/main/resources/webview/continue-dev-square.png and /dev/null differ diff --git a/extensions/intellij/src/main/resources/webview/continue.gif b/extensions/intellij/src/main/resources/webview/continue.gif deleted file mode 100644 index daed66637a..0000000000 Binary files a/extensions/intellij/src/main/resources/webview/continue.gif and /dev/null differ diff --git a/extensions/intellij/src/main/resources/webview/play_button.png b/extensions/intellij/src/main/resources/webview/play_button.png deleted file mode 100644 index af37937584..0000000000 Binary files a/extensions/intellij/src/main/resources/webview/play_button.png and /dev/null differ diff --git a/extensions/vscode/package-lock.json b/extensions/vscode/package-lock.json index 658636fe76..5a407bc09b 100644 --- a/extensions/vscode/package-lock.json +++ b/extensions/vscode/package-lock.json @@ -1,12 +1,12 @@ { "name": "continue", - "version": "0.9.181", + "version": "0.9.182", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "continue", - "version": "0.9.181", + "version": "0.9.182", "license": "Apache-2.0", "dependencies": { "@electron/rebuild": "^3.2.10", @@ -98,6 +98,7 @@ "@aws-sdk/client-bedrock-runtime": "^3.574.0", "@aws-sdk/credential-providers": "^3.596.0", "@continuedev/config-types": "^1.0.6", + "@continuedev/llm-info": "^1.0.1", "@mozilla/readability": "^0.5.0", "@octokit/rest": "^20.0.2", "@types/jsdom": "^21.1.6", diff --git a/gui/package-lock.json b/gui/package-lock.json index 7de4826405..0d43acf64f 100644 --- a/gui/package-lock.json +++ b/gui/package-lock.json @@ -90,6 +90,7 @@ "@aws-sdk/client-bedrock-runtime": "^3.574.0", "@aws-sdk/credential-providers": "^3.596.0", "@continuedev/config-types": "^1.0.6", + "@continuedev/llm-info": "^1.0.1", "@mozilla/readability": "^0.5.0", "@octokit/rest": "^20.0.2", "@types/jsdom": "^21.1.6", diff --git a/gui/public/continue-dev-square.png b/gui/public/continue-dev-square.png deleted file mode 100644 index e4b625568c..0000000000 Binary files a/gui/public/continue-dev-square.png and /dev/null differ diff --git a/gui/public/continue.gif b/gui/public/continue.gif deleted file mode 100644 index daed66637a..0000000000 Binary files a/gui/public/continue.gif and /dev/null differ diff --git a/gui/public/play_button.png b/gui/public/play_button.png deleted file mode 100644 index af37937584..0000000000 Binary files a/gui/public/play_button.png and /dev/null differ diff --git a/gui/src/components/dialogs/ConfirmationDialog.tsx b/gui/src/components/dialogs/ConfirmationDialog.tsx index 6033e9b3de..6cb4dd18fb 100644 --- a/gui/src/components/dialogs/ConfirmationDialog.tsx +++ b/gui/src/components/dialogs/ConfirmationDialog.tsx @@ -11,6 +11,14 @@ const GridDiv = styled.div` grid-template-columns: 1fr 1fr; grid-gap: 8px; align-items: center; + + > * { + grid-column: 2; + } + + > :nth-last-child(2):first-child { + grid-column: 1; + } `; interface ConfirmationDialogProps { @@ -18,6 +26,8 @@ interface ConfirmationDialogProps { onCancel?: () => void; text: string; title?: string; + hideCancelButton?: boolean; + confirmText?: string; } function ConfirmationDialog(props: ConfirmationDialogProps) { @@ -29,15 +39,17 @@ function ConfirmationDialog(props: ConfirmationDialogProps) {

{props.text}

- { - props.onCancel?.(); - dispatch(setShowDialog(false)); - dispatch(setDialogMessage(undefined)); - }} - > - Cancel - + {!!props.hideCancelButton || ( + { + props.onCancel?.(); + dispatch(setShowDialog(false)); + dispatch(setDialogMessage(undefined)); + }} + > + Cancel + + )} diff --git a/gui/src/components/dialogs/index.tsx b/gui/src/components/dialogs/index.tsx index 50fc3ca078..6f5b450d5a 100644 --- a/gui/src/components/dialogs/index.tsx +++ b/gui/src/components/dialogs/index.tsx @@ -4,9 +4,9 @@ import styled from "styled-components"; import { VSC_BACKGROUND_VAR, defaultBorderRadius, + lightGray, parseColorForHex, vscBackground, - vscFocusBorder, vscForeground, } from ".."; @@ -32,7 +32,7 @@ const Dialog = styled.div` border-radius: ${defaultBorderRadius}; display: flex; flex-direction: column; - border: 1px solid ${vscFocusBorder}; + border: 1px solid ${lightGray}; margin: auto; word-wrap: break-word; // overflow: hidden; diff --git a/gui/src/components/mainInput/ContextItemsPeek.tsx b/gui/src/components/mainInput/ContextItemsPeek.tsx index 48a3422e1d..47e45b69cb 100644 --- a/gui/src/components/mainInput/ContextItemsPeek.tsx +++ b/gui/src/components/mainInput/ContextItemsPeek.tsx @@ -15,15 +15,16 @@ import FileIcon from "../FileIcon"; const ContextItemDiv = styled.div` cursor: pointer; - padding-left: 6px; - padding-right: 10px; - padding-top: 6px; - padding-bottom: 6px; + padding: 6px 10px 6px 6px; margin-left: 4px; display: flex; align-items: center; border-radius: ${defaultBorderRadius}; - width: fit-content; + font-size: ${getFontSize()}; + max-width: 100%; + overflow: hidden; + white-space: nowrap; + text-overflow: ellipsis; &:hover { background-color: #fff1; diff --git a/gui/src/components/modelSelection/ModelSelect.tsx b/gui/src/components/modelSelection/ModelSelect.tsx index 570badba00..4bd4b6877e 100644 --- a/gui/src/components/modelSelection/ModelSelect.tsx +++ b/gui/src/components/modelSelection/ModelSelect.tsx @@ -170,6 +170,10 @@ function ModelSelect() { const [options, setOptions] = useState([]); + const selectedProfileId = useSelector( + (store: RootState) => store.state.selectedProfileId, + ); + useEffect(() => { setOptions( allModels.map((model) => { @@ -227,22 +231,26 @@ function ModelSelect() { /> ))} - + {selectedProfileId === "local" && ( + <> + - { - e.stopPropagation(); - e.preventDefault(); - navigate("/addModel"); - }} - value={"addModel" as any} - > -
- - Add Model -
-
+ { + e.stopPropagation(); + e.preventDefault(); + navigate("/addModel"); + }} + value={"addModel" as any} + > +
+ + Add Model +
+
+ + )} diff --git a/gui/src/hooks/useAuth.tsx b/gui/src/hooks/useAuth.tsx index db69bf891f..cd0919a3cf 100644 --- a/gui/src/hooks/useAuth.tsx +++ b/gui/src/hooks/useAuth.tsx @@ -4,6 +4,7 @@ import { useDispatch } from "react-redux"; import ConfirmationDialog from "../components/dialogs/ConfirmationDialog"; import { IdeMessengerContext } from "../context/IdeMessenger"; import { setDialogMessage, setShowDialog } from "../redux/slices/uiStateSlice"; +import { getLocalStorage, setLocalStorage } from "../util/localStorage"; import { useWebviewListener } from "./useWebviewListener"; export function useAuth(): { @@ -31,7 +32,27 @@ export function useAuth(): { console.log("login"); ideMessenger .request("getControlPlaneSessionInfo", { silent: false }) - .then(setSession); + .then((session) => { + setSession(session); + + // If this is the first time the user has logged in, explain how profiles work + if (!getLocalStorage("shownProfilesIntroduction")) { + dispatch(setShowDialog(true)); + dispatch( + setDialogMessage( + {}} + />, + ), + ); + setLocalStorage("shownProfilesIntroduction", true); + } + }); }; const logout = () => { @@ -39,6 +60,8 @@ export function useAuth(): { dispatch( setDialogMessage( {}} />, diff --git a/gui/src/pages/gui.tsx b/gui/src/pages/gui.tsx index 8837e001e5..08ec5fc6e6 100644 --- a/gui/src/pages/gui.tsx +++ b/gui/src/pages/gui.tsx @@ -26,10 +26,12 @@ import { vscBackground, vscForeground, } from "../components"; +import { ChatScrollAnchor } from "../components/ChatScrollAnchor"; import StepContainer from "../components/gui/StepContainer"; import TimelineItem from "../components/gui/TimelineItem"; import ContinueInputBox from "../components/mainInput/ContinueInputBox"; import { defaultInputModifiers } from "../components/mainInput/inputModifiers"; +import { TutorialCard } from "../components/mainInput/TutorialCard"; import { IdeMessengerContext } from "../context/IdeMessenger"; import useChatHandler from "../hooks/useChatHandler"; import useHistory from "../hooks/useHistory"; @@ -52,10 +54,8 @@ import { isJetBrains, isMetaEquivalentKeyPressed, } from "../util"; -import { getLocalStorage, setLocalStorage } from "../util/localStorage"; import { FREE_TRIAL_LIMIT_REQUESTS } from "../util/freeTrial"; -import { ChatScrollAnchor } from "../components/ChatScrollAnchor"; -import { TutorialCard } from "../components/mainInput/TutorialCard"; +import { getLocalStorage, setLocalStorage } from "../util/localStorage"; const TopGuiDiv = styled.div` overflow-y: scroll; diff --git a/gui/src/util/localStorage.ts b/gui/src/util/localStorage.ts index ca26b6ce41..21ae4499f2 100644 --- a/gui/src/util/localStorage.ts +++ b/gui/src/util/localStorage.ts @@ -15,6 +15,7 @@ type LocalStorageTypes = { signedInToGh: boolean; isOnboardingInProgress: boolean; showTutorialCard: boolean; + shownProfilesIntroduction: boolean; }; export function getLocalStorage( diff --git a/packages/control-plane-types/package-lock.json b/packages/control-plane-types/package-lock.json deleted file mode 100644 index 0df9c60ee5..0000000000 --- a/packages/control-plane-types/package-lock.json +++ /dev/null @@ -1,29 +0,0 @@ -{ - "name": "control-plane-types", - "version": "1.0.0", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "name": "control-plane-types", - "version": "1.0.0", - "license": "Apache-2.0", - "devDependencies": { - "typescript": "^5.5.2" - } - }, - "node_modules/typescript": { - "version": "5.5.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.5.2.tgz", - "integrity": "sha512-NcRtPEOsPFFWjobJEtfihkLCZCXZt/os3zf8nTxjVH3RvTSxjrCamJpbExGvYOF+tFHc3pA65qpdwPbzjohhew==", - "dev": true, - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=14.17" - } - } - } -} diff --git a/packages/control-plane-types/src/index.d.ts b/packages/control-plane-types/src/index.d.ts deleted file mode 100644 index d716289a40..0000000000 --- a/packages/control-plane-types/src/index.d.ts +++ /dev/null @@ -1,4 +0,0 @@ -export interface Test { - a: string; - b: number; -} diff --git a/packages/control-plane-types/tsconfig.json b/packages/control-plane-types/tsconfig.json deleted file mode 100644 index 8bb6097f80..0000000000 --- a/packages/control-plane-types/tsconfig.json +++ /dev/null @@ -1,108 +0,0 @@ -{ - "compilerOptions": { - /* Visit https://aka.ms/tsconfig to read more about this file */ - - /* Projects */ - // "incremental": true, /* Save .tsbuildinfo files to allow for incremental compilation of projects. */ - // "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */ - // "tsBuildInfoFile": "./.tsbuildinfo", /* Specify the path to .tsbuildinfo incremental compilation file. */ - // "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects. */ - // "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */ - // "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */ - - /* Language and Environment */ - "target": "es2016", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */ - // "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */ - // "jsx": "preserve", /* Specify what JSX code is generated. */ - // "experimentalDecorators": true, /* Enable experimental support for legacy experimental decorators. */ - // "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */ - // "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h'. */ - // "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */ - // "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using 'jsx: react-jsx*'. */ - // "reactNamespace": "", /* Specify the object invoked for 'createElement'. This only applies when targeting 'react' JSX emit. */ - // "noLib": true, /* Disable including any library files, including the default lib.d.ts. */ - // "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */ - // "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */ - - /* Modules */ - "module": "commonjs", /* Specify what module code is generated. */ - // "rootDir": "./", /* Specify the root folder within your source files. */ - // "moduleResolution": "node10", /* Specify how TypeScript looks up a file from a given module specifier. */ - // "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */ - // "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */ - // "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */ - // "typeRoots": [], /* Specify multiple folders that act like './node_modules/@types'. */ - // "types": [], /* Specify type package names to be included without being referenced in a source file. */ - // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ - // "moduleSuffixes": [], /* List of file name suffixes to search when resolving a module. */ - // "allowImportingTsExtensions": true, /* Allow imports to include TypeScript file extensions. Requires '--moduleResolution bundler' and either '--noEmit' or '--emitDeclarationOnly' to be set. */ - // "resolvePackageJsonExports": true, /* Use the package.json 'exports' field when resolving package imports. */ - // "resolvePackageJsonImports": true, /* Use the package.json 'imports' field when resolving imports. */ - // "customConditions": [], /* Conditions to set in addition to the resolver-specific defaults when resolving imports. */ - // "resolveJsonModule": true, /* Enable importing .json files. */ - // "allowArbitraryExtensions": true, /* Enable importing files with any extension, provided a declaration file is present. */ - // "noResolve": true, /* Disallow 'import's, 'require's or ''s from expanding the number of files TypeScript should add to a project. */ - - /* JavaScript Support */ - // "allowJs": true, /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */ - // "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */ - // "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from 'node_modules'. Only applicable with 'allowJs'. */ - - /* Emit */ - // "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */ - // "declarationMap": true, /* Create sourcemaps for d.ts files. */ - // "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */ - // "sourceMap": true, /* Create source map files for emitted JavaScript files. */ - // "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */ - // "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If 'declaration' is true, also designates a file that bundles all .d.ts output. */ - // "outDir": "./", /* Specify an output folder for all emitted files. */ - // "removeComments": true, /* Disable emitting comments. */ - // "noEmit": true, /* Disable emitting files from a compilation. */ - // "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */ - // "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */ - // "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */ - // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ - // "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */ - // "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */ - // "newLine": "crlf", /* Set the newline character for emitting files. */ - // "stripInternal": true, /* Disable emitting declarations that have '@internal' in their JSDoc comments. */ - // "noEmitHelpers": true, /* Disable generating custom helper functions like '__extends' in compiled output. */ - // "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */ - // "preserveConstEnums": true, /* Disable erasing 'const enum' declarations in generated code. */ - // "declarationDir": "./", /* Specify the output directory for generated declaration files. */ - - /* Interop Constraints */ - // "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */ - // "verbatimModuleSyntax": true, /* Do not transform or elide any imports or exports not marked as type-only, ensuring they are written in the output file's format based on the 'module' setting. */ - // "isolatedDeclarations": true, /* Require sufficient annotation on exports so other tools can trivially generate declaration files. */ - // "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */ - "esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */ - // "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */ - "forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */ - - /* Type Checking */ - "strict": true, /* Enable all strict type-checking options. */ - // "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied 'any' type. */ - // "strictNullChecks": true, /* When type checking, take into account 'null' and 'undefined'. */ - // "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */ - // "strictBindCallApply": true, /* Check that the arguments for 'bind', 'call', and 'apply' methods match the original function. */ - // "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */ - // "noImplicitThis": true, /* Enable error reporting when 'this' is given the type 'any'. */ - // "useUnknownInCatchVariables": true, /* Default catch clause variables as 'unknown' instead of 'any'. */ - // "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */ - // "noUnusedLocals": true, /* Enable error reporting when local variables aren't read. */ - // "noUnusedParameters": true, /* Raise an error when a function parameter isn't read. */ - // "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */ - // "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */ - // "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */ - // "noUncheckedIndexedAccess": true, /* Add 'undefined' to a type when accessed using an index. */ - // "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */ - // "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type. */ - // "allowUnusedLabels": true, /* Disable error reporting for unused labels. */ - // "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */ - - /* Completeness */ - // "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */ - "skipLibCheck": true /* Skip type checking all .d.ts files. */ - } -} diff --git a/packages/llm-info/README.md b/packages/llm-info/README.md new file mode 100644 index 0000000000..068a1d24af --- /dev/null +++ b/packages/llm-info/README.md @@ -0,0 +1,3 @@ +# @continuedev/llm-info + +A lightweight package providing information about various Large Language Models (LLMs). diff --git a/packages/control-plane-types/package.json b/packages/llm-info/package.json similarity index 54% rename from packages/control-plane-types/package.json rename to packages/llm-info/package.json index 0bc5b95d53..450614c853 100644 --- a/packages/control-plane-types/package.json +++ b/packages/llm-info/package.json @@ -1,15 +1,18 @@ { - "name": "control-plane-types", - "version": "1.0.0", + "name": "@continuedev/llm-info", + "version": "1.0.1", "description": "", "main": "dist/index.js", "types": "dist/index.d.ts", + "type": "module", "scripts": { - "test": "echo \"Error: no test specified\" && exit 1" + "test": "echo \"Error: no test specified\" && exit 1", + "build": "tsc" }, "author": "Nate Sesti and Ty Dunn", "license": "Apache-2.0", "devDependencies": { "typescript": "^5.5.2" - } + }, + "dependencies": {} } diff --git a/packages/llm-info/src/index.ts b/packages/llm-info/src/index.ts new file mode 100644 index 0000000000..2c1e148083 --- /dev/null +++ b/packages/llm-info/src/index.ts @@ -0,0 +1,18 @@ +import { AnthropicLlms } from "./models/anthropic.js"; +import { GoogleLlms } from "./models/google.js"; +import { MistralLlms } from "./models/mistral.js"; +import { OpenAiLlms } from "./models/openai.js"; +import { LlmInfo } from "./types.js"; + +export const allLlms: LlmInfo[] = [ + ...OpenAiLlms, + ...GoogleLlms, + ...AnthropicLlms, + ...MistralLlms, +]; + +export function findLlmInfo(model: string): LlmInfo | undefined { + return allLlms.find((llm) => + llm.regex ? llm.regex.test(model) : llm.model === model, + ); +} diff --git a/packages/llm-info/src/models/anthropic.ts b/packages/llm-info/src/models/anthropic.ts new file mode 100644 index 0000000000..e3c85230c2 --- /dev/null +++ b/packages/llm-info/src/models/anthropic.ts @@ -0,0 +1,60 @@ +import { LlmInfo } from "../types.js"; + +export const AnthropicLlms: LlmInfo[] = [ + { + model: "claude-3-5-sonnet-20240620", + displayName: "Claude 3.5 Sonnet", + contextLength: 200_000, + description: + "Most intelligent model with the highest level of intelligence and capability.", + regex: /claude-3\.5-sonnet/i, + }, + { + model: "claude-3-opus-20240229", + displayName: "Claude 3 Opus", + contextLength: 200_000, + description: + "Powerful model for highly complex tasks with top-level performance, intelligence, fluency, and understanding.", + regex: /claude-3-opus/i, + }, + { + model: "claude-3-sonnet-20240229", + displayName: "Claude 3 Sonnet", + contextLength: 200_000, + description: + "Balance of intelligence and speed with strong utility, balanced for scaled deployments.", + regex: /claude-3-sonnet/i, + }, + { + model: "claude-3-haiku-20240307", + displayName: "Claude 3 Haiku", + contextLength: 200_000, + description: + "Fastest and most compact model for near-instant responsiveness with quick and accurate targeted performance.", + regex: /claude-3-haiku/i, + }, + { + model: "claude-2.1", + displayName: "Claude 2.1", + contextLength: 200_000, + description: + "Updated version of Claude 2 with improved accuracy and consistency.", + regex: /claude-2\.1/i, + }, + { + model: "claude-2.0", + displayName: "Claude 2", + contextLength: 100_000, + description: + "Predecessor to Claude 3, offering strong all-round performance.", + regex: /claude-2\.0/i, + }, + { + model: "claude-instant-1.2", + displayName: "Claude Instant 1.2", + contextLength: 100_000, + description: + "Our cheapest small and fast model, a predecessor of Claude Haiku.", + regex: /claude-instant-1\.2/i, + }, +]; diff --git a/packages/llm-info/src/models/google.ts b/packages/llm-info/src/models/google.ts new file mode 100644 index 0000000000..65704cdba4 --- /dev/null +++ b/packages/llm-info/src/models/google.ts @@ -0,0 +1,22 @@ +import { AllMediaTypes, LlmInfo } from "../types.js"; + +export const GoogleLlms: LlmInfo[] = [ + { + model: "gemini-1.5-flash", + displayName: "Gemini 1.5 Flash", + contextLength: 1_048_576, + mediaTypes: AllMediaTypes, + regex: /gemini-1\.5-flash/i, + }, + { + model: "gemini-1.5-pro", + displayName: "Gemini 1.5 Pro", + contextLength: 2_097_152, + regex: /gemini-1\.5-pro/i, + }, + { + model: "gemini-1.0-pro", + displayName: "Gemini 1.0 Pro", + regex: /gemini-1\.0-pro/i, + }, +]; diff --git a/packages/llm-info/src/models/mistral.ts b/packages/llm-info/src/models/mistral.ts new file mode 100644 index 0000000000..094f097db8 --- /dev/null +++ b/packages/llm-info/src/models/mistral.ts @@ -0,0 +1,68 @@ +import { LlmInfo } from "../types.js"; + +export const MistralLlms: LlmInfo[] = [ + { + model: "mistral-large-latest", + displayName: "Mistral Large", + contextLength: 32768, + description: + "Flagship model ideal for complex tasks requiring large reasoning capabilities or highly specialized tasks like synthetic text generation, code generation, RAG, or agents.", + regex: /mistral-large/i, + }, + { + model: "mistral-medium-latest", + displayName: "Mistral Medium", + contextLength: 32768, + description: + "Ideal for intermediate tasks requiring moderate reasoning such as data extraction, document summarization, email writing, job descriptions, or product descriptions. (Note: Will be deprecated in the coming months)", + regex: /mistral-medium/i, + }, + { + model: "mistral-small-latest", + displayName: "Mistral Small", + contextLength: 32768, + description: + "Suitable for simple tasks that can be done in bulk like classification, customer support, or text generation.", + regex: /mistral-small/i, + }, + { + model: "open-mistral-7b", + displayName: "Mistral 7B", + contextLength: 32768, + description: + "First dense model released by Mistral AI, perfect for experimentation, customization, and quick iteration. Matches capabilities of models up to 30B parameters at release time.", + regex: /open-mistral-7b/i, + }, + { + model: "open-mixtral-8x7b", + displayName: "Mixtral 8x7B", + contextLength: 32768, + description: + "Sparse mixture of experts model leveraging up to 45B parameters but using about 12B during inference, offering better inference throughput at the cost of more vRAM.", + regex: /open-mixtral-8x7b/i, + }, + { + model: "open-mixtral-8x22b", + displayName: "Mixtral 8x22B", + contextLength: 65536, + description: + "Larger sparse mixture of experts model leveraging up to 141B parameters but using about 39B during inference, providing better inference throughput at the cost of more vRAM.", + regex: /open-mixtral-8x22b/i, + }, + { + model: "mistral-embed", + displayName: "Mistral Embeddings", + contextLength: 8192, + description: + "Model that converts text into numerical vectors of embeddings in 1024 dimensions. Enables retrieval and retrieval-augmented generation applications with a retrieval score of 55.26 on MTEB.", + regex: /mistral-embed/i, + }, + { + model: "codestral-latest", + displayName: "Codestral", + contextLength: 32768, + description: + "Cutting-edge generative model specifically designed and optimized for code generation tasks, including fill-in-the-middle and code completion.", + regex: /codestral/i, + }, +]; diff --git a/packages/llm-info/src/models/openai.ts b/packages/llm-info/src/models/openai.ts new file mode 100644 index 0000000000..2c7b70e1e9 --- /dev/null +++ b/packages/llm-info/src/models/openai.ts @@ -0,0 +1,69 @@ +import { LlmInfo } from "../types.js"; + +export const OpenAiLlms: LlmInfo[] = [ + { + model: "gpt-3.5-turbo", + displayName: "GPT-3.5 Turbo", + contextLength: 4096, + }, + { + model: "gpt-3.5-turbo-0613", + displayName: "GPT-3.5 Turbo (0613)", + contextLength: 4096, + }, + { + model: "gpt-3.5-turbo-16k", + displayName: "GPT-3.5 Turbo 16K", + contextLength: 16384, + }, + { + model: "gpt-35-turbo-16k", + displayName: "GPT-3.5 Turbo 16K", + contextLength: 16384, + }, + { + model: "gpt-35-turbo-0613", + displayName: "GPT-3.5 Turbo (0613)", + contextLength: 4096, + }, + { + model: "gpt-35-turbo", + displayName: "GPT-3.5 Turbo", + contextLength: 4096, + }, + { + model: "gpt-4", + displayName: "GPT-4", + contextLength: 4096, + }, + { + model: "gpt-4-32k", + displayName: "GPT-4 32K", + contextLength: 4096, + }, + { + model: "gpt-4-turbo-preview", + displayName: "GPT-4 Turbo Preview", + contextLength: 4096, + }, + { + model: "gpt-4o", + displayName: "GPT-4o", + contextLength: 4096, + }, + { + model: "gpt-4-vision", + displayName: "GPT-4 Vision", + contextLength: 4096, + }, + { + model: "gpt-4-0125-preview", + displayName: "GPT-4 (0125 Preview)", + contextLength: 4096, + }, + { + model: "gpt-4-1106-preview", + displayName: "GPT-4 (1106 Preview)", + contextLength: 4096, + }, +]; diff --git a/packages/llm-info/src/types.ts b/packages/llm-info/src/types.ts new file mode 100644 index 0000000000..176989d579 --- /dev/null +++ b/packages/llm-info/src/types.ts @@ -0,0 +1,30 @@ +export interface LlmInfo { + model: string; + displayName?: string; + description?: string; + contextLength?: number; + regex?: RegExp; + + /** If not set, assumes "text" only */ + mediaTypes?: MediaType[]; +} + +export enum MediaType { + Text = "text", + Image = "image", + Audio = "audio", + Video = "video", +} + +export const AllMediaTypes = [ + MediaType.Text, + MediaType.Image, + MediaType.Audio, + MediaType.Video, +]; + +export interface ApiProviderInfo { + displayName: string; + supportsStreaming: boolean; + handlesTemplating: boolean; +} diff --git a/packages/llm-info/tsconfig.json b/packages/llm-info/tsconfig.json new file mode 100644 index 0000000000..f21949f4ba --- /dev/null +++ b/packages/llm-info/tsconfig.json @@ -0,0 +1,23 @@ +{ + "compilerOptions": { + "target": "ESNext", + "module": "NodeNext", + "useDefineForClassFields": true, + "lib": ["DOM", "ESNext"], + "allowJs": true, + "skipLibCheck": true, + "esModuleInterop": true, + "allowSyntheticDefaultImports": true, + "strict": true, + "forceConsistentCasingInFileNames": true, + "moduleResolution": "NodeNext", + "resolveJsonModule": true, + "isolatedModules": true, + "noEmitOnError": false, + "types": ["node"], + "outDir": "dist", + "declaration": true + // "sourceMap": true + }, + "include": ["src/**/*"] +}