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
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,11 @@ export interface OpenaiCompatibleProviderSettings {
*/
headers?: Record<string, string>

/**
* Custom query parameter to append to the requests
*/
queryParams?: Record<string, string>

/**
* Custom fetch implementation.
*/
Expand Down Expand Up @@ -65,11 +70,21 @@ export function createOpenaiCompatible(options: OpenaiCompatibleProviderSettings

const getHeaders = () => withUserAgentSuffix(headers, `ai-sdk/openai-compatible/${VERSION}`)

const getUrl = ({ path }: { path: string }) => {
const url = new URL(`${baseURL}${path}`)
if (options.queryParams) {
for (const [key, value] of Object.entries(options.queryParams)) {
url.searchParams.set(key, value)
}
}
return url.toString()
}

const createChatModel = (modelId: OpenaiCompatibleModelId) => {
return new OpenAICompatibleChatLanguageModel(modelId, {
provider: `${options.name ?? "openai-compatible"}.chat`,
headers: getHeaders,
url: ({ path }) => `${baseURL}${path}`,
url: getUrl,
fetch: options.fetch,
})
}
Expand All @@ -78,7 +93,7 @@ export function createOpenaiCompatible(options: OpenaiCompatibleProviderSettings
return new OpenAIResponsesLanguageModel(modelId, {
provider: `${options.name ?? "openai-compatible"}.responses`,
headers: getHeaders,
url: ({ path }) => `${baseURL}${path}`,
url: getUrl,
fetch: options.fetch,
})
}
Expand Down
132 changes: 132 additions & 0 deletions packages/opencode/test/provider/openai-compatible.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
import { test, expect } from "bun:test"
import path from "path"
import { tmpdir } from "../fixture/fixture"
import { Instance } from "../../src/project/instance"
import { Provider } from "../../src/provider/provider"

test("provider with queryParams in config", async () => {
await using tmp = await tmpdir({
init: async (dir) => {
await Bun.write(
path.join(dir, "opencode.json"),
JSON.stringify({
$schema: "https://opencode.ai/config.json",
provider: {
"custom-openai": {
name: "Custom OpenAI with Query Params",
npm: "@ai-sdk/openai-compatible",
env: [],
models: {
"gpt-4": {
name: "GPT-4",
tool_call: true,
limit: { context: 128000, output: 4096 },
},
},
options: {
apiKey: "test-key",
baseURL: "https://api.example.com/v1",
queryParams: {
"api-version": "2024-01-01",
tenant: "my-org",
},
},
},
},
}),
)
},
})
await Instance.provide({
directory: tmp.path,
fn: async () => {
const providers = await Provider.list()
expect(providers["custom-openai"]).toBeDefined()
expect(providers["custom-openai"].name).toBe("Custom OpenAI with Query Params")
expect(providers["custom-openai"].options.queryParams).toEqual({
"api-version": "2024-01-01",
tenant: "my-org",
})
},
})
})

test("provider without queryParams still works", async () => {
await using tmp = await tmpdir({
init: async (dir) => {
await Bun.write(
path.join(dir, "opencode.json"),
JSON.stringify({
$schema: "https://opencode.ai/config.json",
provider: {
"custom-openai": {
name: "Custom OpenAI",
npm: "@ai-sdk/openai-compatible",
env: [],
models: {
"gpt-4": {
name: "GPT-4",
tool_call: true,
limit: { context: 128000, output: 4096 },
},
},
options: {
apiKey: "test-key",
baseURL: "https://api.example.com/v1",
},
},
},
}),
)
},
})
await Instance.provide({
directory: tmp.path,
fn: async () => {
const providers = await Provider.list()
expect(providers["custom-openai"]).toBeDefined()
expect(providers["custom-openai"].name).toBe("Custom OpenAI")
expect(providers["custom-openai"].options.queryParams).toBeUndefined()
},
})
})

test("provider with empty queryParams", async () => {
await using tmp = await tmpdir({
init: async (dir) => {
await Bun.write(
path.join(dir, "opencode.json"),
JSON.stringify({
$schema: "https://opencode.ai/config.json",
provider: {
"custom-openai": {
name: "Custom OpenAI Empty Params",
npm: "@ai-sdk/openai-compatible",
env: [],
models: {
"gpt-4": {
name: "GPT-4",
tool_call: true,
limit: { context: 128000, output: 4096 },
},
},
options: {
apiKey: "test-key",
baseURL: "https://api.example.com/v1",
queryParams: {},
},
},
},
}),
)
},
})
await Instance.provide({
directory: tmp.path,
fn: async () => {
const providers = await Provider.list()
expect(providers["custom-openai"]).toBeDefined()
expect(providers["custom-openai"].options.queryParams).toEqual({})
},
})
})