diff --git a/core/autocomplete/context/root-path-context/RootPathContextService.ts b/core/autocomplete/context/root-path-context/RootPathContextService.ts index 5584ff807d..b381f21665 100644 --- a/core/autocomplete/context/root-path-context/RootPathContextService.ts +++ b/core/autocomplete/context/root-path-context/RootPathContextService.ts @@ -4,7 +4,7 @@ import { LRUCache } from "lru-cache"; import Parser from "web-tree-sitter"; import { IDE } from "../../.."; -import { getQueryForFile, TSQueryType } from "../../../util/treeSitter"; +import { getQueryForFile } from "../../../util/treeSitter"; import { AstPath } from "../../util/ast"; import { ImportDefinitionsService } from "../ImportDefinitionsService"; import { AutocompleteSnippet } from "../ranking"; @@ -27,6 +27,7 @@ export class RootPathContextService { "program", "function_declaration", "method_definition", + "class_declaration", ]); /** @@ -54,23 +55,12 @@ export class RootPathContextService { case "program": this.importDefinitionsService.get(filepath); break; - case "function_declaration": + default: query = await getQueryForFile( filepath, - TSQueryType.FunctionDeclaration, + `root-path-context-queries/${node.type}`, ); break; - case "method_definition": - query = await getQueryForFile(filepath, TSQueryType.MethodDefinition); - break; - case "function_definition": - query = await getQueryForFile(filepath, TSQueryType.FunctionDefinition); - break; - case "method_declaration": - query = await getQueryForFile(filepath, TSQueryType.MethodDeclaration); - break; - default: - break; } if (!query) { @@ -79,28 +69,37 @@ export class RootPathContextService { await Promise.all( query.matches(node).map(async (match) => { - const startPosition = match.captures[0].node.startPosition; - const endPosition = match.captures[0].node.endPosition; - const definitions = await this.ide.gotoDefinition({ - filepath, - position: { - line: endPosition.row, - character: endPosition.column, - }, - }); - const newSnippets = await Promise.all( - definitions.map(async (def) => ({ - ...def, - contents: await this.ide.readRangeInFile(def.filepath, def.range), - })), - ); - snippets.push(...newSnippets); + for (const item of match.captures) { + const endPosition = item.node.endPosition; + const newSnippets = await this.getSnippets(filepath, endPosition); + snippets.push(...newSnippets); + } }), ); return snippets; } + private async getSnippets( + filepath: string, + endPosition: Parser.Point, + ): Promise { + const definitions = await this.ide.gotoDefinition({ + filepath, + position: { + line: endPosition.row, + character: endPosition.column, + }, + }); + const newSnippets = await Promise.all( + definitions.map(async (def) => ({ + ...def, + contents: await this.ide.readRangeInFile(def.filepath, def.range), + })), + ); + return newSnippets; + } + async getContextForPath( filepath: string, astPath: AstPath, diff --git a/core/autocomplete/context/root-path-context/test/RootPathContextService.test.ts b/core/autocomplete/context/root-path-context/test/RootPathContextService.test.ts new file mode 100644 index 0000000000..597d37d12f --- /dev/null +++ b/core/autocomplete/context/root-path-context/test/RootPathContextService.test.ts @@ -0,0 +1,42 @@ +import { testRootPathContext } from "./testUtils"; + +const TEST_CASES = [ + { + description: "function", + fileName: "file1.ts", + range: { + start: { line: 10, character: 2 }, + end: { line: 10, character: 24 }, + }, + positions: [ + { row: 9, column: 34 }, // Person + { row: 9, column: 44 }, // Address + ], + }, + { + description: "class method", + fileName: "file1.ts", + range: { + start: { line: 22, character: 4 }, + end: { line: 22, character: 30 }, + }, + positions: [ + { row: 13, column: 29 }, // BaseClass + { row: 13, column: 55 }, // FirstInterface + { row: 13, column: 72 }, // SecondInterface + { row: 21, column: 33 }, // Person + { row: 21, column: 43 }, // Address + ], + }, +]; + +describe("RootPathContextService", () => { + describe("TypeScript should return expected snippets when editing inside a:", () => { + test.each(TEST_CASES)( + "should look for correct type definitions when editing inside a $description", + async ({ fileName, range, positions }) => { + await testRootPathContext("typescript", fileName, range, positions); + }, + ); + }); +}); diff --git a/core/autocomplete/context/root-path-context/RootPathContextService.test.ts b/core/autocomplete/context/root-path-context/test/testUtils.ts similarity index 68% rename from core/autocomplete/context/root-path-context/RootPathContextService.test.ts rename to core/autocomplete/context/root-path-context/test/testUtils.ts index e72a65cab0..503383fd2c 100644 --- a/core/autocomplete/context/root-path-context/RootPathContextService.test.ts +++ b/core/autocomplete/context/root-path-context/test/testUtils.ts @@ -1,12 +1,13 @@ +import { jest } from "@jest/globals"; import fs from "fs"; import path from "path"; -import { Range } from "../../.."; -import { testIde } from "../../../test/util/fixtures"; -import { getAst, getTreePathAtCursor } from "../../util/ast"; -import { ImportDefinitionsService } from "../ImportDefinitionsService"; - -import { RootPathContextService } from "./RootPathContextService"; +import Parser from "web-tree-sitter"; +import { Range } from "../../../.."; +import { testIde } from "../../../../test/util/fixtures"; +import { getAst, getTreePathAtCursor } from "../../../util/ast"; +import { ImportDefinitionsService } from "../../ImportDefinitionsService"; +import { RootPathContextService } from "../RootPathContextService"; function splitTextAtRange(fileContent: string, range: Range): [string, string] { const lines = fileContent.split("\n"); @@ -42,12 +43,21 @@ export async function testRootPathContext( folderName: string, relativeFilepath: string, rangeToFill: Range, - expectedSnippets: string[], + expectedDefinitionPositions: Parser.Point[], ) { + // Create a mocked instance of RootPathContextService const ide = testIde; const importDefinitionsService = new ImportDefinitionsService(ide); const service = new RootPathContextService(importDefinitionsService, ide); + const getSnippetsMock = jest + // @ts-ignore + .spyOn(service, "getSnippets") + // @ts-ignore + .mockImplementation(async (_filepath, _endPosition) => { + return []; + }); + // Copy the folder to the test directory const folderPath = path.join( __dirname, @@ -77,23 +87,17 @@ export async function testRootPathContext( } const treePath = await getTreePathAtCursor(ast, prefix.length); - const snippets = await service.getContextForPath(startPath, treePath); + await service.getContextForPath(startPath, treePath); - expectedSnippets.forEach((expectedSnippet) => { - const found = snippets.find((snippet) => - snippet.contents.includes(expectedSnippet), - ); - expect(found).toBeDefined(); - }); -} + expect(getSnippetsMock).toHaveBeenCalledTimes( + expectedDefinitionPositions.length, + ); -describe("RootPathContextService", () => { - it.skip("should be true", async () => { - await testRootPathContext( - "typescript", - "file1.ts", - { start: { line: 3, character: 2 }, end: { line: 3, character: 24 } }, - ["export interface Person", "export interface Address"], + expectedDefinitionPositions.forEach((position, index) => { + expect(getSnippetsMock).toHaveBeenNthCalledWith( + index + 1, + expect.any(String), // filepath argument + position, ); }); -}); +} diff --git a/core/autocomplete/context/root-path-context/test/typescript/file1.ts b/core/autocomplete/context/root-path-context/test/typescript/file1.ts index c67ae91e23..ba09c57099 100644 --- a/core/autocomplete/context/root-path-context/test/typescript/file1.ts +++ b/core/autocomplete/context/root-path-context/test/typescript/file1.ts @@ -1,13 +1,25 @@ -import { Address, Person } from "./types"; +import { + Address, + Person, + BaseClass, + FirstInterface, + SecondInterface, + // @ts-ignore +} from "./types"; function getAddress(person: Person): Address { return person.address; } -class Group { +class Group extends BaseClass implements FirstInterface, SecondInterface { people: Person[]; constructor(people: Person[]) { + super(); this.people = people; } + + getPersonAddress(person: Person): Address { + return getAddress(person); + } } diff --git a/core/autocomplete/context/root-path-context/test/typescript/types.ts b/core/autocomplete/context/root-path-context/test/typescript/types.ts deleted file mode 100644 index 272ec27d78..0000000000 --- a/core/autocomplete/context/root-path-context/test/typescript/types.ts +++ /dev/null @@ -1,11 +0,0 @@ -export interface Person { - firstName: string; - lastName: string; - age: number; - address: Address; -} - -export interface Address { - street: string; - city: string; -} diff --git a/core/config/load.ts b/core/config/load.ts index 2afcf5481d..a109482103 100644 --- a/core/config/load.ts +++ b/core/config/load.ts @@ -1,11 +1,10 @@ import { execSync } from "child_process"; +import * as JSONC from "comment-json"; import * as fs from "fs"; import os from "os"; import path from "path"; -import * as JSONC from "comment-json"; import * as tar from "tar"; - import { BrowserSerializedContinueConfig, Config, @@ -72,7 +71,6 @@ import { } from "./promptFile.js"; import { ConfigValidationError, validateConfig } from "./validation.js"; - export interface ConfigResult { config: T | undefined; errors: ConfigValidationError[] | undefined; @@ -138,6 +136,13 @@ function loadSerializedConfig( config.allowAnonymousTelemetry = true; } + if (config.ui?.getChatTitles === undefined) { + config.ui = { + ...config.ui, + getChatTitles: true, + }; + } + if (ideSettings.remoteConfigServerUrl) { try { const remoteConfigJson = resolveSerializedConfig( diff --git a/core/config/types.ts b/core/config/types.ts index 3fb9fe494b..ad80cdeaba 100644 --- a/core/config/types.ts +++ b/core/config/types.ts @@ -628,6 +628,11 @@ declare global { | "mistral-large-latest" | "mistral-7b" | "mistral-8x7b" + | "mistral-8x22b" + | "mistral-tiny" + | "mistral-small" + | "mistral-medium" + | "mistral-nemo" // Llama 2 | "llama2-7b" | "llama2-13b" @@ -641,6 +646,8 @@ declare global { | "llama3-70b" // Other Open-source | "phi2" + | "phi-3-mini" + | "phi-3-medium" | "phind-codellama-34b" | "wizardcoder-7b" | "wizardcoder-13b" @@ -649,9 +656,12 @@ declare global { | "codeup-13b" | "deepseek-7b" | "deepseek-33b" + | "deepseek-2-lite" | "neural-chat-7b" | "gemma-7b-it" + | "gemma2-2b-it" | "gemma2-9b-it" + | "olmo-7b" // Anthropic | "claude-3-5-sonnet-latest" | "claude-3-5-sonnet-20240620" @@ -669,10 +679,6 @@ declare global { | "gemini-1.5-pro" | "gemini-1.5-flash-latest" | "gemini-1.5-flash" - // Mistral - | "mistral-tiny" - | "mistral-small" - | "mistral-medium" // Tab autocomplete | "deepseek-1b" | "starcoder-1b" diff --git a/core/index.d.ts b/core/index.d.ts index ac8ea5376a..b8e941f28a 100644 --- a/core/index.d.ts +++ b/core/index.d.ts @@ -494,11 +494,14 @@ export interface IDE { stepIndex: number, ): Promise; getOpenFiles(): Promise; - getCurrentFile(): Promise; + getCurrentFile(): Promise< + | undefined + | { + isUntitled: boolean; + path: string; + contents: string; + } + >; getPinnedFiles(): Promise; getSearchResults(query: string): Promise; subprocess(command: string, cwd?: string): Promise<[string, string]>; @@ -677,10 +680,12 @@ export type ModelName = | "mistral-large-latest" | "mistral-7b" | "mistral-8x7b" + | "mistral-8x22b" | "mistral-tiny" | "mistral-small" | "mistral-medium" | "mistral-embed" + | "mistral-nemo" // Llama 2 | "llama2-7b" | "llama2-13b" @@ -705,6 +710,8 @@ export type ModelName = | "grok-beta" // Other Open-source | "phi2" + | "phi-3-mini" + | "phi-3-medium" | "phind-codellama-34b" | "wizardcoder-7b" | "wizardcoder-13b" @@ -713,9 +720,13 @@ export type ModelName = | "codeup-13b" | "deepseek-7b" | "deepseek-33b" + | "deepseek-2-lite" | "neural-chat-7b" | "gemma-7b-it" + | "gemma2-2b-it" | "gemma2-9b-it" + | "olmo-7b" + | "qwen-coder2.5-7b" // Anthropic | "claude-3-5-sonnet-latest" | "claude-3-5-sonnet-20240620" @@ -785,11 +796,13 @@ export interface CustomCommand { } interface Prediction { - type: "content" - content: string | { - type: "text" - text: string - }[] + type: "content"; + content: + | string + | { + type: "text"; + text: string; + }[]; } interface BaseCompletionOptions { @@ -931,6 +944,7 @@ export interface ContinueUIConfig { fontSize?: number; displayRawMarkdown?: boolean; showChatScrollbar?: boolean; + getChatTitles?: boolean; } interface ContextMenuConfig { @@ -993,11 +1007,6 @@ interface ExperimentalConfig { */ readResponseTTS?: boolean; - /** - * Prompt the user's LLM for a title given the current chat content - */ - getChatTitles?: boolean; - /** * If set to true, we will attempt to pull down and install an instance of Chromium * that is compatible with the current version of Puppeteer. diff --git a/core/llm/llms/Nebius.ts b/core/llm/llms/Nebius.ts index 043965b53e..e1935815dc 100644 --- a/core/llm/llms/Nebius.ts +++ b/core/llm/llms/Nebius.ts @@ -3,11 +3,32 @@ import { LLMOptions, ModelProvider } from "../.."; import OpenAI from "./OpenAI"; class Nebius extends OpenAI { - static providerName: ModelProvider = "nvidia"; + static providerName: ModelProvider = "nebius"; static defaultOptions: Partial = { apiBase: "https://api.studio.nebius.ai/v1/", useLegacyCompletionsEndpoint: false, }; + + private static MODEL_IDS: { [name: string]: string } = { + "llama3.1-70b-nemotron": "nvidia/Llama-3.1-Nemotron-70B-Instruct-HF-fast", + "llama3.1-8b": "meta-llama/Meta-Llama-3.1-70B-Instruct-fast", + "llama3.1-70b": "meta-llama/Meta-Llama-3.1-70B-Instruct-fast", + "llama3.1-405b": "meta-llama/Meta-Llama-3.1-405B-Instruct", + "mistral-nemo": "mistralai/Mistral-Nemo-Instruct-2407-fast", + "mistral-8x7b": "mistralai/Mixtral-8x7B-Instruct-v0.1-fast", + "mistral-8x22b": "mistralai/Mixtral-8x22B-Instruct-v0.1-fast", + "qwen-coder2.5-7b": "Qwen/Qwen2.5-Coder-7B-Instruct-fast", + "deepseek-2-lite": "deepseek-ai/DeepSeek-Coder-V2-Lite-Instruct-fast", + "phi-3-mini": "microsoft/Phi-3-mini-4k-instruct-fast", + "phi-3-medium": "microsoft/Phi-3-medium-128k-instruct-fast", + "gemma2-2b-it": "google/gemma-2-2b-it-fast", + "gemma2-9b-it": "google/gemma-2-9b-it-fast", + "olmo-7b": "allenai/OLMo-7B-Instruct-hf", + }; + + protected _convertModelName(model: string) { + return Nebius.MODEL_IDS[model] || this.model; + } } export default Nebius; diff --git a/core/test/llm.test.ts b/core/test/llm.test.ts index 3828eb97df..2cb81da72d 100644 --- a/core/test/llm.test.ts +++ b/core/test/llm.test.ts @@ -107,4 +107,7 @@ describe.skip("LLM", () => { // testLLM( // new Flowise({ apiKey: process.env.FLOWISE_API_KEY, model: "gpt-3.5-turbo" }) // ); + // testLLM( + // new Nebius({ apiKey: process.env.NEBIUS_API_KEY, model: "llama3.1-8b" }) + // ); }); diff --git a/core/util/chatDescriber.ts b/core/util/chatDescriber.ts index a38976c9fc..5f3d1bdc1b 100644 --- a/core/util/chatDescriber.ts +++ b/core/util/chatDescriber.ts @@ -3,8 +3,8 @@ import { stripImages } from "../llm/images"; import { removeQuotesAndEscapes } from "."; -import type { IMessenger } from "./messenger"; import type { FromCoreProtocol, ToCoreProtocol } from "../protocol"; +import type { IMessenger } from "./messenger"; /** * Removes code blocks from a message. @@ -42,7 +42,7 @@ export class ChatDescriber { return; } - completionOptions.maxTokens = 24; + completionOptions.maxTokens = 6; // Prompt the user's current LLM for the title const titleResponse = await model.chat( diff --git a/core/util/treeSitter.ts b/core/util/treeSitter.ts index 4e0d430c34..b877a251b6 100644 --- a/core/util/treeSitter.ts +++ b/core/util/treeSitter.ts @@ -140,7 +140,7 @@ export enum TSQueryType { export async function getQueryForFile( filepath: string, - queryType: TSQueryType, + queryPath: string, ): Promise { const language = await getLanguageForFile(filepath); if (!language) { @@ -154,7 +154,7 @@ export async function getQueryForFile( ...(process.env.NODE_ENV === "test" ? ["extensions", "vscode", "tree-sitter"] : ["tree-sitter"]), - queryType, + queryPath, `${fullLangName}.scm`, ); if (!fs.existsSync(sourcePath)) { diff --git a/docs/docs/chat/model-setup.mdx b/docs/docs/chat/model-setup.mdx index bf968ad128..a5668786bb 100644 --- a/docs/docs/chat/model-setup.mdx +++ b/docs/docs/chat/model-setup.mdx @@ -82,6 +82,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": "nebius", + "model": "llama3.1-405b", + "apiKey": "[NEBIUS_API_KEY]" + } + ] + ``` + ### GPT-4o from OpenAI diff --git a/docs/docs/customize/model-providers/more/nebius.md b/docs/docs/customize/model-providers/more/nebius.md new file mode 100644 index 0000000000..d4cfa344bf --- /dev/null +++ b/docs/docs/customize/model-providers/more/nebius.md @@ -0,0 +1,36 @@ +# Nebius AI Studio + +You can get an API key from the [Nebius AI Studio API keys page](https://studio.nebius.ai/settings/api-keys) + +## Availible models + +Available models can be found on the [Nebius AI Studio models page](https://studio.nebius.ai/models/text2text) + +## Chat model + +```json title="config.json" +{ + "models": [ + { + "title": "Llama 3.1 405b", + "provider": "nebius", + "model": "llama3.1-405b", + "apiKey": "[API_KEY]" + } + ] +} +``` + +## Embeddings model + +Available models can be found on the [Nebius AI Studio embeddings page](https://studio.nebius.ai/models/embedding) + +```json title="config.json" +{ + "embeddingsProvider": { + "provider": "nebius", + "model": "BAAI/bge-en-icl", + "apiKey": "[API_KEY]" + } +} +``` diff --git a/docs/docusaurus.config.js b/docs/docusaurus.config.js index 52732288b9..2562c918ac 100644 --- a/docs/docusaurus.config.js +++ b/docs/docusaurus.config.js @@ -384,6 +384,10 @@ const config = { to: "/customize/model-providers/more/watsonx", from: "/reference/Model Providers/watsonx", }, + { + to: "/customize/model-providers/more/nebius", + from: "/reference/Model Providers/nebius", + }, // Sidebar items that should route directly to a subpage { to: "/chat/how-to-use-it", diff --git a/extensions/vscode/config_schema.json b/extensions/vscode/config_schema.json index 953db6d20a..a0535391ff 100644 --- a/extensions/vscode/config_schema.json +++ b/extensions/vscode/config_schema.json @@ -204,7 +204,8 @@ "askSage", "nebius", "vertexai", - "xAI" + "xAI", + "kindo" ], "markdownEnumDescriptions": [ "### OpenAI\nUse gpt-4, gpt-3.5-turbo, or any other OpenAI model. See [here](https://openai.com/product#made-for-developers) to obtain an API key.\n\n> [Reference](https://docs.continue.dev/reference/Model%20Providers/openai)", @@ -240,7 +241,8 @@ "### vLLM\nvLLM is a highly performant way of hosting LLMs for a team. To get started, follow their [quickstart](https://docs.vllm.ai/en/latest/getting_started/quickstart.html) to set up your server.", "### Cerebras\nCerebras Inference uses specialized silicon to provides superfast inference. To get started, get your API keys from [their console](https://cloud.cerebras.ai/).", "### Ask Sage\nAsk Sage is an agnostic hosted service that provides language models. To get started with Ask Sage:\n1. Obtain an API key from your account. For more information, visit [Ask Sage](https://docs.asksage.ai/).\n2. Paste the API key below.\n3. Select a model preset.\n> [Reference](https://docs.asksage.ai/)", - "### xAI offers a world class developer tool set to build scalable applications powered by Grok. To get started, obtain an API key from [the x Console](https://console.x.ai/), and see the [docs](https://docs.x.ai/docs/)" + "### xAI offers a world class developer tool set to build scalable applications powered by Grok. To get started, obtain an API key from [the x Console](https://console.x.ai/), and see the [docs](https://docs.x.ai/docs/)", + "### Secure AI management software that helps enterprises adopt and manage AI across their workforce. To get started, obtain an API key from [the Kindo console](https://app.kindo.ai/settings/api), and see the [website](https://app.kindo.ai//)" ], "type": "string" }, @@ -467,7 +469,8 @@ "cloudflare", "sambanova", "nebius", - "xAI" + "xAI", + "kindo" ] } }, diff --git a/extensions/vscode/tree-sitter/root-path-context-queries/class_declaration/typescript.scm b/extensions/vscode/tree-sitter/root-path-context-queries/class_declaration/typescript.scm new file mode 100644 index 0000000000..bf38152b71 --- /dev/null +++ b/extensions/vscode/tree-sitter/root-path-context-queries/class_declaration/typescript.scm @@ -0,0 +1,19 @@ +( + (class_declaration + (class_heritage + (extends_clause + (identifier) @base-class + ) + ) + ) +) + +( + (class_declaration + (class_heritage + (implements_clause + (type_identifier) @interface + ) + ) + ) +) \ No newline at end of file diff --git a/extensions/vscode/tree-sitter/root-path-context-queries/function_declaration/typescript.scm b/extensions/vscode/tree-sitter/root-path-context-queries/function_declaration/typescript.scm index c0d936f5e4..ab10968a37 100644 --- a/extensions/vscode/tree-sitter/root-path-context-queries/function_declaration/typescript.scm +++ b/extensions/vscode/tree-sitter/root-path-context-queries/function_declaration/typescript.scm @@ -2,8 +2,9 @@ (function_declaration (formal_parameters (_ - (type_annotation) @type + (type_annotation) @param_type ) ) + (type_annotation) @return_type ) ) \ No newline at end of file diff --git a/extensions/vscode/tree-sitter/root-path-context-queries/method_definition/typescript.scm b/extensions/vscode/tree-sitter/root-path-context-queries/method_definition/typescript.scm index 10ee28346f..727db5090f 100644 --- a/extensions/vscode/tree-sitter/root-path-context-queries/method_definition/typescript.scm +++ b/extensions/vscode/tree-sitter/root-path-context-queries/method_definition/typescript.scm @@ -2,8 +2,9 @@ (method_definition (formal_parameters (_ - (type_annotation) @type + (type_annotation) @param_type ) ) + (type_annotation) @return_type ) -) \ No newline at end of file +) diff --git a/gui/public/logos/qwen.png b/gui/public/logos/qwen.png new file mode 100644 index 0000000000..4f5a292bcb Binary files /dev/null and b/gui/public/logos/qwen.png differ diff --git a/gui/src/hooks/useHistory.tsx b/gui/src/hooks/useHistory.tsx index 8ebcd45818..1d8963b5e6 100644 --- a/gui/src/hooks/useHistory.tsx +++ b/gui/src/hooks/useHistory.tsx @@ -2,14 +2,14 @@ import { Dispatch } from "@reduxjs/toolkit"; import { PersistedSessionInfo, SessionInfo } from "core"; import { stripImages } from "core/llm/images"; -import { useCallback, useContext, useEffect } from "react"; +import { useCallback, useContext } from "react"; import { useSelector } from "react-redux"; import { IdeMessengerContext } from "../context/IdeMessenger"; +import { useLastSessionContext } from "../context/LastSessionContext"; import { defaultModelSelector } from "../redux/selectors/modelSelectors"; import { newSession } from "../redux/slices/stateSlice"; import { RootState } from "../redux/store"; import { getLocalStorage, setLocalStorage } from "../util/localStorage"; -import { useLastSessionContext } from "../context/LastSessionContext"; const MAX_TITLE_LENGTH = 100; @@ -50,20 +50,17 @@ function useHistory(dispatch: Dispatch) { return result.status === "success" ? result.content : undefined; } - async function saveSession(open_new_session = true) { + async function saveSession(openNewSession: boolean = true) { if (state.history.length === 0) return; const stateCopy = { ...state }; - if (open_new_session) { + if (openNewSession) { dispatch(newSession()); updateLastSessionId(stateCopy.sessionId); } await new Promise((resolve) => setTimeout(resolve, 10)); - if ( - state.config?.experimental?.getChatTitles && - stateCopy.title === "New Session" - ) { + if (state.config?.ui?.getChatTitles && stateCopy.title === "New Session") { try { // Check if we have first assistant response let assistantResponse = stateCopy.history diff --git a/gui/src/pages/AddNewModel/configs/models.ts b/gui/src/pages/AddNewModel/configs/models.ts index 87bdfc5469..67c3547a7b 100644 --- a/gui/src/pages/AddNewModel/configs/models.ts +++ b/gui/src/pages/AddNewModel/configs/models.ts @@ -69,6 +69,7 @@ export const models: { [key: string]: ModelPackage } = { "replicate", "sambanova", "cerebras", + "nebius", ], isOpenSource: true, }, @@ -190,6 +191,10 @@ export const models: { [key: string]: ModelPackage } = { model: "mistral-8x7b", title: "Mixtral", }, + "8x22b (MoE)": { + model: "mistral-8x22b", + title: "Mixtral", + }, }, }, ], @@ -1005,52 +1010,23 @@ export const models: { [key: string]: ModelPackage } = { icon: "openai.png", isOpenSource: false, }, - MetaLlama3Large: { - title: "Llama 3.1 405b", - description: - "Llama 3 is an auto-regressive language model that uses an optimized transformer architecture.", - params: { - title: "Llama 3.1 405b", - model: "meta-llama/Meta-Llama-3.1-405B-Instruct", - contextLength: 128_000, - }, - icon: "meta.png", - dimensions: [ - { - name: "Parameter Count", - description: "The number of parameters in the model", - options: { - "70b": { - model: "meta-llama/Meta-Llama-3.1-70B-Instruct", - title: "Llama 3.1 70b", - }, - "405bb": { - model: "meta-llama/Meta-Llama-3.1-405B-Instruct", - title: "Llama 3.1 405b", - }, - }, - }, - ], - providerOptions: ["nebius"], - isOpenSource: true, - }, Qwen2Coder: { title: "Qwen 2.5 Coder 7b", description: "Qwen 2.5 is an auto-regressive language model that uses an optimized transformer architecture.", params: { title: "Qwen 2.5 Coder 7b", - model: "Qwen/Qwen2.5-Coder-7B-Instruct", + model: "qwen-coder2.5-7b", contextLength: 32_000, }, - icon: "meta.png", + icon: "qwen.png", dimensions: [ { name: "Parameter Count", description: "The number of parameters in the model", options: { "7b": { - model: "Qwen/Qwen2.5-Coder-7B-Instruct", + model: "qwen-coder2.5-7b", title: "Qwen 2.5 Coder 7b", }, }, diff --git a/gui/src/pages/AddNewModel/configs/providers.ts b/gui/src/pages/AddNewModel/configs/providers.ts index 9765bff1da..39613731b6 100644 --- a/gui/src/pages/AddNewModel/configs/providers.ts +++ b/gui/src/pages/AddNewModel/configs/providers.ts @@ -726,7 +726,7 @@ To get started, [register](https://dataplatform.cloud.ibm.com/registration/stepo nebius: { title: "Nebius AI Studio", provider: "nebius", - refPage: "nebiusllm", + refPage: "nebius", description: "Use the Nebius API to run open-source models", longDescription: `Nebius AI Studio is a cheap hosted service with $100 trial. To get started with Nebius AI Studio:\n1. Obtain an API key from [here](https://studio.nebius.ai)\n2. Paste below\n3. Select a model preset`, params: { @@ -744,7 +744,7 @@ To get started, [register](https://dataplatform.cloud.ibm.com/registration/stepo ], icon: "nebius.png", tags: [ModelProviderTags.RequiresApiKey, ModelProviderTags.OpenSource], - packages: [models.MetaLlama3Large, models.Qwen2Coder], + packages: [models.llama31Chat, models.Qwen2Coder, models.mistralOs], apiKeyUrl: "https://studio.nebius.ai/settings/api-keys", }, }; diff --git a/packages/openai-adapters/src/index.ts b/packages/openai-adapters/src/index.ts index 261eaf0904..f782f1c785 100644 --- a/packages/openai-adapters/src/index.ts +++ b/packages/openai-adapters/src/index.ts @@ -73,6 +73,11 @@ export function constructLlmApi(config: LlmApiConfig): BaseLlmApi { ...config, apiBase: "https://api.sambanova.ai/v1/", }); + case "nebius": + return new OpenAIApi({ + ...config, + apiBase: "https://api.studio.nebius.ai/v1/", + }); default: throw new Error(`Unsupported LLM API format: ${config.provider}`); } diff --git a/packages/openai-adapters/test/main.test.ts b/packages/openai-adapters/test/main.test.ts index a0d55097f7..efe8cc7c5b 100644 --- a/packages/openai-adapters/test/main.test.ts +++ b/packages/openai-adapters/test/main.test.ts @@ -273,6 +273,12 @@ const COMPLETION_TESTS: ({ chatOnly?: boolean } & LlmApiConfig)[] = [ apiKey: process.env.SAMBANOVA_API_KEY!, chatOnly: true, }, + { + provider: "nebius", + model: "llama3.1-8b", + apiKey: process.env.NEBIUS_API_KEY!, + chatOnly: true, + }, ]; const FIM_TESTS: LlmApiConfig[] = [ @@ -311,6 +317,11 @@ const EMBEDDINGS_TESTS: LlmApiConfig[] = [ model: "models/text-embedding-004", apiKey: process.env.GEMINI_API_KEY!, }, + { + provider: "nebius", + model: "BAAI/bge-en-icl", + apiKey: process.env.NEBIUS_API_KEY!, + }, ]; const RERANK_TESTS: LlmApiConfig[] = [