diff --git a/core/llm/autodetect.ts b/core/llm/autodetect.ts index c8511554b8b..fc71af74a45 100644 --- a/core/llm/autodetect.ts +++ b/core/llm/autodetect.ts @@ -65,6 +65,7 @@ const PROVIDER_HANDLES_TEMPLATING: string[] = [ "relace", "openrouter", "clawrouter", + "manifest", "deepseek", "xAI", "minimax", @@ -123,6 +124,7 @@ const PROVIDER_SUPPORTS_IMAGES: string[] = [ "sagemaker", "openrouter", "clawrouter", + "manifest", "venice", "sambanova", "vertexai", diff --git a/core/llm/llms/Manifest.ts b/core/llm/llms/Manifest.ts new file mode 100644 index 00000000000..16e6274ee50 --- /dev/null +++ b/core/llm/llms/Manifest.ts @@ -0,0 +1,28 @@ +import { LLMOptions } from "../../index.js"; + +import OpenAI from "./OpenAI.js"; + +/** + * Manifest LLM Provider + * + * Manifest is an open-source, OpenAI-compatible gateway for AI agents. It + * exposes a single endpoint in front of multiple providers (API keys, + * subscriptions, local models), with per-message cost/token tracking and + * automatic fallback when a provider fails. + * + * The served model is resolved server-side from the user's dashboard + * configuration, so requests always target the single `auto` model id. + * + * @see https://github.com/mnfst/manifest + */ +class Manifest extends OpenAI { + static providerName = "manifest"; + + static defaultOptions: Partial = { + apiBase: "https://app.manifest.build/v1/", + model: "auto", + useLegacyCompletionsEndpoint: false, + }; +} + +export default Manifest; diff --git a/core/llm/llms/Manifest.vitest.ts b/core/llm/llms/Manifest.vitest.ts new file mode 100644 index 00000000000..24f15426c41 --- /dev/null +++ b/core/llm/llms/Manifest.vitest.ts @@ -0,0 +1,36 @@ +import { describe, expect, it } from "vitest"; + +import Manifest from "./Manifest"; + +describe("Manifest", () => { + it("should have correct provider name", () => { + expect(Manifest.providerName).toBe("manifest"); + }); + + it("should have correct default options", () => { + expect(Manifest.defaultOptions?.apiBase).toBe( + "https://app.manifest.build/v1/", + ); + expect(Manifest.defaultOptions?.model).toBe("auto"); + expect(Manifest.defaultOptions?.useLegacyCompletionsEndpoint).toBe(false); + }); + + it("should default to the auto model", () => { + const manifest = new Manifest({ + model: "auto", + apiKey: "mnfst_test", + }); + + expect(manifest.model).toBe("auto"); + }); + + it("should allow overriding the apiBase", () => { + const manifest = new Manifest({ + model: "auto", + apiKey: "mnfst_test", + apiBase: "http://localhost:3000/v1/", + }); + + expect(manifest.apiBase).toBe("http://localhost:3000/v1/"); + }); +}); diff --git a/core/llm/llms/index.ts b/core/llm/llms/index.ts index 4978f0617f2..6a8f3d0cbd7 100644 --- a/core/llm/llms/index.ts +++ b/core/llm/llms/index.ts @@ -36,6 +36,7 @@ import Llamafile from "./Llamafile"; import LlamaStack from "./LlamaStack"; import Lemonade from "./Lemonade"; import LMStudio from "./LMStudio"; +import Manifest from "./Manifest"; import Mistral from "./Mistral"; import Mimo from "./Mimo"; import MiniMax from "./MiniMax"; @@ -92,6 +93,7 @@ export const LLMClasses = [ OVHcloud, Lemonade, LMStudio, + Manifest, Mistral, Mimo, MiniMax, diff --git a/core/llm/toolSupport.test.ts b/core/llm/toolSupport.test.ts index be14a76d619..aea39b1123e 100644 --- a/core/llm/toolSupport.test.ts +++ b/core/llm/toolSupport.test.ts @@ -380,6 +380,19 @@ describe("PROVIDER_TOOL_SUPPORT", () => { }); }); + describe("manifest", () => { + const supportsFn = PROVIDER_TOOL_SUPPORT["manifest"]; + + it("should return true for auto model", () => { + expect(supportsFn("auto")).toBe(true); + }); + + it("should return true for any model", () => { + expect(supportsFn("gpt-4o")).toBe(true); + expect(supportsFn("claude-sonnet-4")).toBe(true); + }); + }); + describe("edge cases", () => { it("should handle empty model names", () => { expect(PROVIDER_TOOL_SUPPORT["anthropic"]("")).toBe(false); diff --git a/core/llm/toolSupport.ts b/core/llm/toolSupport.ts index 3f65473233c..7c702cd2d61 100644 --- a/core/llm/toolSupport.ts +++ b/core/llm/toolSupport.ts @@ -412,6 +412,12 @@ export const PROVIDER_TOOL_SUPPORT: Record boolean> = !!lower.match(/\bo[1-9]\b/) ); }, + manifest: (model) => { + // Manifest is a gateway that routes to various providers server-side. + // The "auto" model resolves to whatever the user has configured in + // their dashboard, which may support tools. + return true; + }, zAI: (model) => { const lower = model.toLowerCase(); return !!lower.match(/^glm-[4-9]/); diff --git a/docs/customize/model-providers/more/manifest.mdx b/docs/customize/model-providers/more/manifest.mdx new file mode 100644 index 00000000000..ab172c7c68f --- /dev/null +++ b/docs/customize/model-providers/more/manifest.mdx @@ -0,0 +1,56 @@ +--- +title: Manifest +description: OpenAI-compatible gateway with multi-provider support +--- + +# Manifest + +[Manifest](https://manifest.build) is an open-source, OpenAI-compatible gateway for AI agents. It exposes a single endpoint in front of multiple providers (API keys, subscriptions, local models), with per-message cost/token tracking and automatic fallback when a provider fails. + +The served model is resolved server-side from your dashboard configuration, so requests always target the single `auto` model id. + +## Setup + +1. Sign up at [manifest.build](https://manifest.build) +2. Add your provider API keys (OpenAI, Anthropic, etc.) in the dashboard +3. Configure Continue to use Manifest as your provider + +### config.json + +```json +{ + "models": [ + { + "title": "Manifest Auto", + "provider": "manifest", + "model": "auto", + "apiKey": "YOUR_MANIFEST_API_KEY" + } + ] +} +``` + +### config.yaml + +```yaml +models: + - title: "Manifest Auto" + provider: manifest + model: auto + apiKey: "YOUR_MANIFEST_API_KEY" +``` + +## Configuration Options + +| Field | Required | Default | Description | +| ---------- | -------- | --------------------------- | ------------------------------------ | +| `apiKey` | Yes | - | Your Manifest API key | +| `apiBase` | No | `https://app.manifest.build/v1/` | Custom API base URL (e.g. for self-hosted) | +| `model` | No | `auto` | Model identifier (always `auto`) | + +## Features + +- **Multi-provider routing** — one endpoint, multiple providers +- **Cost & token tracking** — per-message breakdown in the dashboard +- **Automatic fallback** — if one provider fails, Manifest retries with the next +- **Self-hostable** — deploy your own instance from [github.com/mnfst/manifest](https://github.com/mnfst/manifest) diff --git a/docs/customize/model-providers/overview.mdx b/docs/customize/model-providers/overview.mdx index 7ba030dcb4d..783fae94dba 100644 --- a/docs/customize/model-providers/overview.mdx +++ b/docs/customize/model-providers/overview.mdx @@ -35,6 +35,7 @@ Beyond the top-level providers, Continue supports many other options: | [DeepInfra](/customize/model-providers/more/deepinfra) | Hosting for various open source models | | [OpenRouter](/customize/model-providers/top-level/openrouter) | Gateway to multiple model providers | | [ClawRouter](/customize/model-providers/more/clawrouter) | Open-source LLM router with automatic cost-optimized model selection | +| [Manifest](/customize/model-providers/more/manifest) | OpenAI-compatible gateway with multi-provider routing, cost tracking, and automatic fallback | | [Tetrate Agent Router Service](/customize/model-providers/top-level/tetrate_agent_router_service) | Gateway with intelligent routing across multiple model providers | | [Cohere](/customize/model-providers/more/cohere) | Models specialized for semantic search and text generation | | [NVIDIA](/customize/model-providers/more/nvidia) | GPU-accelerated model hosting | diff --git a/docs/docs.json b/docs/docs.json index b7a1d83f13a..1435e58a2a0 100644 --- a/docs/docs.json +++ b/docs/docs.json @@ -107,6 +107,7 @@ "pages": [ "customize/model-providers/more/asksage", "customize/model-providers/more/clawrouter", + "customize/model-providers/more/manifest", "customize/model-providers/more/deepseek", "customize/model-providers/more/deepinfra", "customize/model-providers/more/groq", diff --git a/extensions/vscode/config_schema.json b/extensions/vscode/config_schema.json index fb3f4c61362..d037391d133 100644 --- a/extensions/vscode/config_schema.json +++ b/extensions/vscode/config_schema.json @@ -217,6 +217,7 @@ "watsonx", "openrouter", "clawrouter", + "manifest", "sambanova", "nvidia", "vllm", diff --git a/gui/public/logos/manifest.png b/gui/public/logos/manifest.png new file mode 100644 index 00000000000..14dfa8566aa Binary files /dev/null and b/gui/public/logos/manifest.png differ diff --git a/gui/src/pages/AddNewModel/configs/models.ts b/gui/src/pages/AddNewModel/configs/models.ts index 7065248d68f..4360505095e 100644 --- a/gui/src/pages/AddNewModel/configs/models.ts +++ b/gui/src/pages/AddNewModel/configs/models.ts @@ -2788,6 +2788,20 @@ export const models: { [key: string]: ModelPackage } = { isOpenSource: true, }, + manifestAuto: { + title: "Manifest Auto", + description: + "Single endpoint that routes to models configured in your Manifest dashboard", + params: { + title: "Manifest Auto", + model: "auto", + contextLength: 128_000, + }, + icon: "manifest.png", + providerOptions: ["manifest"], + isOpenSource: true, + }, + AUTODETECT: { title: "Autodetect", description: diff --git a/gui/src/pages/AddNewModel/configs/providers.ts b/gui/src/pages/AddNewModel/configs/providers.ts index 9e2aba08c5c..092281e23ce 100644 --- a/gui/src/pages/AddNewModel/configs/providers.ts +++ b/gui/src/pages/AddNewModel/configs/providers.ts @@ -224,6 +224,42 @@ export const providers: Partial> = { ], }, + manifest: { + title: "Manifest", + provider: "manifest", + description: + "OpenAI-compatible gateway with multi-provider support, cost/token tracking, and automatic fallback.", + longDescription: `[Manifest](https://manifest.build) is an open-source OpenAI-compatible gateway for AI agents. It exposes a single endpoint in front of multiple providers (API keys, subscriptions, local models), with per-message cost/token tracking and automatic fallback when a provider fails. + +The served model is resolved server-side from your dashboard configuration, so you always use the \`auto\` model id. + +To get started: +1. Sign up at [manifest.build](https://manifest.build) +2. Add your provider API keys in the dashboard +3. Enter your API key below + +**Features:** +- Multi-provider routing (one endpoint) +- Per-message cost and token tracking +- Automatic fallback on provider failures +- Compatible with any OpenAI SDK`, + icon: "manifest.png", + tags: [ModelProviderTags.RequiresApiKey], + refPage: "manifest", + apiKeyUrl: "https://app.manifest.build/settings/keys", + collectInputFor: [ + { + inputType: "text", + key: "apiKey", + label: "API Key", + placeholder: "Enter your Manifest API key", + required: true, + }, + ...completionParamsInputsConfigs, + ], + packages: [models.manifestAuto], + }, + moonshot: { title: "Moonshot", provider: "moonshot", diff --git a/packages/openai-adapters/package.json b/packages/openai-adapters/package.json index bfc5cda47be..fba0cdcfac8 100644 --- a/packages/openai-adapters/package.json +++ b/packages/openai-adapters/package.json @@ -48,7 +48,7 @@ "semantic-release": "^24.2.7", "ts-jest": "^29.2.3", "ts-node": "^10.9.2", - "vitest": "^3.2.4" + "vitest": "^3.1.4" }, "overrides": { "picomatch": "^4.0.4" diff --git a/packages/openai-adapters/src/apis/AiSdk.ts b/packages/openai-adapters/src/apis/AiSdk.ts index c0f68689988..620e7ae0b1c 100644 --- a/packages/openai-adapters/src/apis/AiSdk.ts +++ b/packages/openai-adapters/src/apis/AiSdk.ts @@ -46,6 +46,11 @@ const PROVIDER_MAP: Record = { ...options, baseURL: options.baseURL ?? "http://localhost:1337/v1/", }), + manifest: (options) => + createOpenAI({ + ...options, + baseURL: options.baseURL ?? "https://app.manifest.build/v1/", + }), }; export class AiSdkApi implements BaseLlmApi { diff --git a/packages/openai-adapters/src/apis/Manifest.test.ts b/packages/openai-adapters/src/apis/Manifest.test.ts new file mode 100644 index 00000000000..a6bc24bab5c --- /dev/null +++ b/packages/openai-adapters/src/apis/Manifest.test.ts @@ -0,0 +1,34 @@ +import { describe, expect, it } from "vitest"; + +import { ManifestApi } from "./Manifest.js"; + +describe("ManifestApi", () => { + const baseConfig = { + provider: "manifest" as const, + }; + + it("should use default apiBase when not provided", () => { + const api = new ManifestApi(baseConfig); + expect(api["config"].apiBase).toBe("https://app.manifest.build/v1/"); + }); + + it("should allow custom apiBase", () => { + const api = new ManifestApi({ + ...baseConfig, + apiBase: "http://localhost:3000/v1/", + }); + expect(api["config"].apiBase).toBe("http://localhost:3000/v1/"); + }); + + it("should include standard OpenAI headers", () => { + const api = new ManifestApi({ + ...baseConfig, + apiKey: "mnfst_test", + }); + const headers = api["getHeaders"](); + + expect(headers["Content-Type"]).toBe("application/json"); + expect(headers["Accept"]).toBe("application/json"); + expect(headers["Authorization"]).toBe("Bearer mnfst_test"); + }); +}); diff --git a/packages/openai-adapters/src/apis/Manifest.ts b/packages/openai-adapters/src/apis/Manifest.ts new file mode 100644 index 00000000000..9eb05eba7ac --- /dev/null +++ b/packages/openai-adapters/src/apis/Manifest.ts @@ -0,0 +1,28 @@ +import { OpenAIApi } from "./OpenAI.js"; +import { OpenAIConfig } from "../types.js"; + +export interface ManifestConfig extends OpenAIConfig {} + +/** + * Manifest API adapter + * + * Manifest is an open-source, OpenAI-compatible gateway for AI agents. + * It exposes a single endpoint in front of multiple providers (API keys, + * subscriptions, local models), with per-message cost/token tracking and + * automatic fallback when a provider fails. + * + * The served model is resolved server-side from the user's dashboard + * configuration, so requests always target the single `auto` model id. + * + * @see https://github.com/mnfst/manifest + */ +export class ManifestApi extends OpenAIApi { + constructor(config: ManifestConfig) { + super({ + ...config, + apiBase: config.apiBase ?? "https://app.manifest.build/v1/", + }); + } +} + +export default ManifestApi; diff --git a/packages/openai-adapters/src/index.ts b/packages/openai-adapters/src/index.ts index 52fb2d33a0c..d836176426d 100644 --- a/packages/openai-adapters/src/index.ts +++ b/packages/openai-adapters/src/index.ts @@ -19,6 +19,7 @@ import { MoonshotApi } from "./apis/Moonshot.js"; import { OpenAIApi } from "./apis/OpenAI.js"; import { OpenRouterApi } from "./apis/OpenRouter.js"; import { ClawRouterApi } from "./apis/ClawRouter.js"; +import { ManifestApi } from "./apis/Manifest.js"; import { RelaceApi } from "./apis/Relace.js"; import { VertexAIApi } from "./apis/VertexAI.js"; import { WatsonXApi } from "./apis/WatsonX.js"; @@ -180,6 +181,8 @@ export function constructLlmApi(config: LLMConfig): BaseLlmApi | undefined { return new OpenRouterApi(config); case "clawrouter": return new ClawRouterApi(config); + case "manifest": + return new ManifestApi(config); case "llama.cpp": case "llamafile": return openAICompatible("http://localhost:8000/", config); diff --git a/packages/openai-adapters/src/types.ts b/packages/openai-adapters/src/types.ts index 14b9512f75b..648064026da 100644 --- a/packages/openai-adapters/src/types.ts +++ b/packages/openai-adapters/src/types.ts @@ -53,6 +53,7 @@ export const OpenAIConfigSchema = BasePlusConfig.extend({ z.literal("msty"), z.literal("openrouter"), z.literal("clawrouter"), + z.literal("manifest"), z.literal("sambanova"), z.literal("text-gen-webui"), z.literal("vllm"),