From e101d2db43eedbf91d6a87245c0a216c6c3fe5ca Mon Sep 17 00:00:00 2001 From: modelsell Date: Tue, 28 Jul 2026 10:52:27 +0800 Subject: [PATCH] feat: add Modelsell provider --- core/llm/llms/Modelsell.ts | 13 +++++ core/llm/llms/Modelsell.vitest.ts | 46 ++++++++++++++++ core/llm/llms/index.ts | 2 + .../model-providers/more/modelsell.mdx | 55 +++++++++++++++++++ docs/docs.json | 1 + 5 files changed, 117 insertions(+) create mode 100644 core/llm/llms/Modelsell.ts create mode 100644 core/llm/llms/Modelsell.vitest.ts create mode 100644 docs/customize/model-providers/more/modelsell.mdx diff --git a/core/llm/llms/Modelsell.ts b/core/llm/llms/Modelsell.ts new file mode 100644 index 00000000000..a2078746167 --- /dev/null +++ b/core/llm/llms/Modelsell.ts @@ -0,0 +1,13 @@ +import { LLMOptions } from "../../index.js"; + +import OpenAI from "./OpenAI.js"; + +class Modelsell extends OpenAI { + static providerName = "modelsell"; + static defaultOptions: Partial = { + apiBase: "https://modelsell.com/v1/", + useLegacyCompletionsEndpoint: false, + }; +} + +export default Modelsell; diff --git a/core/llm/llms/Modelsell.vitest.ts b/core/llm/llms/Modelsell.vitest.ts new file mode 100644 index 00000000000..761d5248bf4 --- /dev/null +++ b/core/llm/llms/Modelsell.vitest.ts @@ -0,0 +1,46 @@ +import { describe, expect, test, vi } from "vitest"; + +import Modelsell from "./Modelsell.js"; +import { LLMClasses } from "./index.js"; + +describe("Modelsell", () => { + test("registers the provider with the Modelsell API base", () => { + expect(Modelsell.providerName).toBe("modelsell"); + expect(Modelsell.defaultOptions).toMatchObject({ + apiBase: "https://modelsell.com/v1/", + useLegacyCompletionsEndpoint: false, + }); + expect(LLMClasses).toContain(Modelsell); + }); + + test("discovers model IDs from the Modelsell models endpoint", async () => { + const modelsell = new Modelsell({ + apiKey: "test-api-key", + model: "", + }); + const mockFetch = vi.fn().mockResolvedValue( + new Response( + JSON.stringify({ + data: [{ id: "provider/model-a" }, { id: "model-b" }], + }), + { headers: { "Content-Type": "application/json" } }, + ), + ); + (modelsell as any).fetch = mockFetch; + + await expect(modelsell.listModels()).resolves.toEqual([ + "provider/model-a", + "model-b", + ]); + expect(mockFetch).toHaveBeenCalledTimes(1); + + const [url, options] = mockFetch.mock.calls[0]; + expect(url.toString()).toBe("https://modelsell.com/v1/models"); + expect(options).toMatchObject({ + method: "GET", + headers: { + Authorization: "Bearer test-api-key", + }, + }); + }); +}); diff --git a/core/llm/llms/index.ts b/core/llm/llms/index.ts index 4978f0617f2..a7888abea96 100644 --- a/core/llm/llms/index.ts +++ b/core/llm/llms/index.ts @@ -39,6 +39,7 @@ import LMStudio from "./LMStudio"; import Mistral from "./Mistral"; import Mimo from "./Mimo"; import MiniMax from "./MiniMax"; +import Modelsell from "./Modelsell"; import MockLLM from "./Mock"; import Moonshot from "./Moonshot"; import Msty from "./Msty"; @@ -95,6 +96,7 @@ export const LLMClasses = [ Mistral, Mimo, MiniMax, + Modelsell, Bedrock, BedrockImport, SageMaker, diff --git a/docs/customize/model-providers/more/modelsell.mdx b/docs/customize/model-providers/more/modelsell.mdx new file mode 100644 index 00000000000..290a37ff4e7 --- /dev/null +++ b/docs/customize/model-providers/more/modelsell.mdx @@ -0,0 +1,55 @@ +--- +title: "Modelsell" +description: "Configure Modelsell with Continue through its OpenAI-compatible Chat Completions API and dynamic model discovery." +--- + +Modelsell provides a unified OpenAI-compatible endpoint for using multiple AI model providers in Continue. + +- **API base:** `https://modelsell.com/v1` +- **API keys:** [modelsell.com/console/token](https://modelsell.com/console/token) +- **API documentation:** [modelsell.com/docs/api-reference](https://modelsell.com/docs/api-reference) + +## Discover available models + +Modelsell's model catalog is dynamic. Fetch the model IDs available to your API key from `GET /v1/models` rather than relying on a static list: + +```bash +curl https://modelsell.com/v1/models \ + -H "Authorization: Bearer $MODELSELL_API_KEY" +``` + +Use an `id` from the response as the `model` value in your Continue configuration. + +## Configuration + + + + ```yaml title="config.yaml" + name: My Config + version: 0.0.1 + schema: v1 + + models: + - name: Modelsell + provider: modelsell + model: + apiKey: ${{ secrets.MODELSELL_API_KEY }} + ``` + + + ```json title="config.json" + { + "models": [ + { + "title": "Modelsell", + "provider": "modelsell", + "model": "", + "apiKey": "" + } + ] + } + ``` + + + +The initial integration uses Modelsell's OpenAI-compatible Chat Completions endpoint. It does not maintain a hardcoded model catalog in Continue. diff --git a/docs/docs.json b/docs/docs.json index b7a1d83f13a..a9475505f53 100644 --- a/docs/docs.json +++ b/docs/docs.json @@ -114,6 +114,7 @@ "customize/model-providers/more/llamastack", "customize/model-providers/more/mimo", "customize/model-providers/more/mistral", + "customize/model-providers/more/modelsell", "customize/model-providers/more/moonshot", "customize/model-providers/more/nous", "customize/model-providers/more/nvidia",