Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions core/llm/llms/OpenAI-compatible.vitest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ import NCompass from "./NCompass.js";
import LlamaStack from "./LlamaStack.js";
import Nebius from "./Nebius.js";
import OVHcloud from "./OVHcloud.js";
import SaladCloud from "./SaladCloud.js";

// Base OpenAI tests
import { afterEach, describe, expect, test, vi } from "vitest";
Expand Down Expand Up @@ -457,3 +458,13 @@ createOpenAISubclassTests(OVHcloud, {
providerName: "ovhcloud",
defaultApiBase: "https://oai.endpoints.kepler.ai.cloud.ovh.net/v1/",
});

createOpenAISubclassTests(SaladCloud, {
providerName: "saladcloud",
defaultApiBase: "https://ai.salad.cloud/v1/",
customBodyOptions: {
chat_template_kwargs: {
enable_thinking: false,
},
},
});
50 changes: 50 additions & 0 deletions core/llm/llms/SaladCloud.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import { ChatCompletionCreateParams } from "openai/resources/index";
import { ChatMessage, CompletionOptions, LLMOptions } from "../..";

import OpenAI from "./OpenAI";

class SaladCloud extends OpenAI {
static providerName = "saladcloud";
static defaultOptions: Partial<LLMOptions> = {
apiBase: "https://ai.salad.cloud/v1/",
useLegacyCompletionsEndpoint: false,
};

protected _convertArgs(
options: CompletionOptions,
messages: ChatMessage[],
): ChatCompletionCreateParams {
return this.addDefaultChatTemplateKwargs(
super._convertArgs(options, messages),
);
}

protected modifyChatBody(
body: ChatCompletionCreateParams,
): ChatCompletionCreateParams {
return this.addDefaultChatTemplateKwargs(super.modifyChatBody(body));
}

private addDefaultChatTemplateKwargs(
body: ChatCompletionCreateParams,
): ChatCompletionCreateParams {
const extendedBody = body as ChatCompletionCreateParams & {
chat_template_kwargs?: Record<string, unknown>;
};
const existingKwargs =
extendedBody.chat_template_kwargs &&
typeof extendedBody.chat_template_kwargs === "object" &&
!Array.isArray(extendedBody.chat_template_kwargs)
? extendedBody.chat_template_kwargs
: {};

extendedBody.chat_template_kwargs = {
enable_thinking: false,
...existingKwargs,
};

return extendedBody;
}
}

export default SaladCloud;
45 changes: 45 additions & 0 deletions core/llm/llms/SaladCloud.vitest.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { describe, expect, it } from "vitest";

import SaladCloud from "./SaladCloud.js";

describe("SaladCloud", () => {
it("disables Qwen thinking output by default", () => {
const saladCloud = new SaladCloud({
apiKey: "test-api-key",
model: "qwen3.6-35b-a3b",
});

const body = (saladCloud as any)._convertArgs(
{
model: "qwen3.6-35b-a3b",
maxTokens: 128,
},
[{ role: "user", content: "hello" }],
);

expect(body.chat_template_kwargs).toEqual({
enable_thinking: false,
});
});

it("preserves explicitly provided chat template kwargs", () => {
const saladCloud = new SaladCloud({
apiKey: "test-api-key",
model: "qwen3.6-35b-a3b",
});

const body = (saladCloud as any).modifyChatBody({
model: "qwen3.6-35b-a3b",
messages: [],
chat_template_kwargs: {
enable_thinking: true,
custom: "value",
},
});

expect(body.chat_template_kwargs).toEqual({
enable_thinking: true,
custom: "value",
});
});
});
2 changes: 2 additions & 0 deletions core/llm/llms/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ import OVHcloud from "./OVHcloud";
import { Relace } from "./Relace";
import Replicate from "./Replicate";
import SageMaker from "./SageMaker";
import SaladCloud from "./SaladCloud";
import SambaNova from "./SambaNova";
import Scaleway from "./Scaleway";
import SiliconFlow from "./SiliconFlow";
Expand Down Expand Up @@ -115,6 +116,7 @@ export const LLMClasses = [
ClawRouter,
Nvidia,
Vllm,
SaladCloud,
SambaNova,
MockLLM,
TestLLM,
Expand Down
14 changes: 14 additions & 0 deletions core/llm/toolSupport.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,20 @@ describe("PROVIDER_TOOL_SUPPORT", () => {
});
});

describe("saladcloud", () => {
const supportsFn = PROVIDER_TOOL_SUPPORT["saladcloud"];

it("should return true for SaladCloud Qwen models", () => {
expect(supportsFn("qwen3.6-35b-a3b")).toBe(true);
expect(supportsFn("qwen3.6-27b")).toBe(true);
expect(supportsFn("qwen3.5-9b")).toBe(true);
});

it("should return false for unverified SaladCloud models", () => {
expect(supportsFn("gemma-4-26b-a4b-instruct")).toBe(false);
});
});

describe("cohere", () => {
const supportsFn = PROVIDER_TOOL_SUPPORT["cohere"];

Expand Down
4 changes: 4 additions & 0 deletions core/llm/toolSupport.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,10 @@ export const PROVIDER_TOOL_SUPPORT: Record<string, (model: string) => boolean> =

return false;
},
saladcloud: (model) => {
const lower = model.toLowerCase();
return lower.startsWith("qwen3.5-") || lower.startsWith("qwen3.6-");
},
cohere: (model) => {
const lower = model.toLowerCase();
if (lower.startsWith("command-a-vision")) {
Expand Down
53 changes: 53 additions & 0 deletions docs/customize/model-providers/more/saladcloud.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
---
title: "SaladCloud"
description: "Configure SaladCloud AI Gateway with Continue"
---

You can get an API key from the [SaladCloud portal](https://portal.salad.com/api-key)

## Available Models

Available models can be found on the [SaladCloud AI Gateway pricing page](https://docs.salad.com/ai-gateway/reference/pricing). Continue includes presets for the Qwen models currently returned by SaladCloud's `/v1/models` endpoint: `qwen3.6-35b-a3b`, `qwen3.6-27b`, and `qwen3.5-9b`.

## Chat Model

<Tabs>
<Tab title="YAML">
```yaml title="config.yaml"
name: My Config
version: 0.0.1
schema: v1

models:
- name: Qwen 3.6 35B A3B
provider: saladcloud
model: qwen3.6-35b-a3b
apiKey: <YOUR_SALAD_CLOUD_API_KEY>
```
</Tab>
<Tab title="JSON">
```json title="config.json"
{
"models": [
{
"title": "Qwen 3.6 35B A3B",
"provider": "saladcloud",
"model": "qwen3.6-35b-a3b",
"apiKey": "<YOUR_SALAD_CLOUD_API_KEY>"
}
]
}
```
</Tab>
</Tabs>

<Note>
Continue disables Qwen thinking output by default with
`chat_template_kwargs.enable_thinking: false`. To enable thinking, add
`requestOptions.extraBodyProperties.chat_template_kwargs.enable_thinking` and
set it to `true` in your model configuration.
</Note>

## Embeddings Model

SaladCloud AI Gateway currently focuses on chat/completion models.
46 changes: 46 additions & 0 deletions gui/src/pages/AddNewModel/configs/models.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3310,6 +3310,52 @@ export const models: { [key: string]: ModelPackage } = {
isOpenSource: false,
},

// SaladCloud models
saladQwen36_35bA3B: {
title: "Qwen 3.6 35B A3B",
description:
"Qwen 3.6 35B A3B is a fast mixture-of-experts model with 35B total parameters and 3B active, available via SaladCloud AI Gateway.",
refUrl: "https://docs.salad.com/ai-gateway/explanation/overview",
params: {
title: "Qwen 3.6 35B A3B",
model: "qwen3.6-35b-a3b",
contextLength: 262_144,
capabilities: { tools: true },
},
icon: "qwen.png",
providerOptions: ["saladcloud"],
isOpenSource: true,
},
saladQwen36_27b: {
title: "Qwen 3.6 27B",
description:
"Qwen 3.6 27B is a capable instruction-tuned model available via SaladCloud AI Gateway with a 256K context window.",
refUrl: "https://docs.salad.com/ai-gateway/explanation/overview",
params: {
title: "Qwen 3.6 27B",
model: "qwen3.6-27b",
contextLength: 262_144,
capabilities: { tools: true },
},
icon: "qwen.png",
providerOptions: ["saladcloud"],
isOpenSource: true,
},
saladQwen35_9b: {
title: "Qwen 3.5 9B",
description:
"Qwen 3.5 9B is a lightweight and efficient model available via SaladCloud AI Gateway.",
refUrl: "https://docs.salad.com/ai-gateway/explanation/overview",
params: {
title: "Qwen 3.5 9B",
model: "qwen3.5-9b",
contextLength: 262_144,
capabilities: { tools: true },
},
icon: "qwen.png",
providerOptions: ["saladcloud"],
isOpenSource: true,
},
// Xiaomi Mimo models
mimoV2Flash: {
title: "mimo-v2-flash",
Expand Down
27 changes: 27 additions & 0 deletions gui/src/pages/AddNewModel/configs/providers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -996,6 +996,33 @@ To get started, [register](https://dataplatform.cloud.ibm.com/registration/stepo
models.MetaLlama3,
],
},
saladcloud: {
title: "SaladCloud",
provider: "saladcloud",
refPage: "saladcloud",
description: "Use SaladCloud AI Gateway to access open-source models",
longDescription: `SaladCloud AI Gateway provides OpenAI-compatible access to open-source models at flat-rate pricing. To get started:\n1. Sign up at [salad.com](https://salad.com)\n2. Obtain an API key from the [SaladCloud portal](https://portal.salad.com)\n3. Paste below\n4. Select a model preset`,
tags: [ModelProviderTags.RequiresApiKey, ModelProviderTags.OpenSource],
params: {
apiKey: "",
},
collectInputFor: [
{
inputType: "text",
key: "apiKey",
label: "API Key",
placeholder: "Enter your SaladCloud API key",
required: true,
},
...completionParamsInputsConfigs,
],
packages: [
models.saladQwen36_35bA3B,
models.saladQwen36_27b,
models.saladQwen35_9b,
],
apiKeyUrl: "https://portal.salad.com/api-key",
},
sambanova: {
title: "SambaNova",
provider: "sambanova",
Expand Down
Loading