Skip to content
Merged
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
10 changes: 10 additions & 0 deletions packages/opencode/src/acp/agent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1577,6 +1577,16 @@ export namespace ACP {

if (specified && !providers.length) return specified

// altimate_change start — default to altimate-backend when configured and no model chosen yet
const altimateProvider = providers.find((p) => p.id === "altimate-backend")
if (altimateProvider && altimateProvider.models["altimate-default"]) {
return {
providerID: ProviderID.make("altimate-backend"),
modelID: ModelID.make("altimate-default"),
}
}
// altimate_change end

const opencodeProvider = providers.find((p) => p.id === "opencode")
if (opencodeProvider) {
if (opencodeProvider.models["big-pickle"]) {
Expand Down
15 changes: 15 additions & 0 deletions packages/opencode/src/provider/provider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1637,6 +1637,21 @@ export namespace Provider {
return { providerID: entry.providerID, modelID: entry.modelID }
}

// altimate_change start — default to altimate-backend when configured and no model chosen yet
const altimateProviderID = ProviderID.make("altimate-backend")
const altimateProvider = providers[altimateProviderID]
if (
altimateProvider &&
altimateProvider.models[ModelID.make("altimate-default")] &&
(!cfg.provider || Object.keys(cfg.provider).includes(String(altimateProviderID)))
) {
return {
providerID: altimateProviderID,
modelID: ModelID.make("altimate-default"),
}
}
// altimate_change end

const provider = Object.values(providers).find((p) => !cfg.provider || Object.keys(cfg.provider).includes(p.id))
if (!provider) throw new Error("no providers found")
const [model] = sort(Object.values(provider.models))
Expand Down
155 changes: 155 additions & 0 deletions packages/opencode/test/provider/provider.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { test, expect } from "bun:test"
import path from "path"
import fs from "fs/promises"

import { tmpdir } from "../fixture/fixture"
import { Instance } from "../../src/project/instance"
Expand Down Expand Up @@ -2330,4 +2331,158 @@ test("github-copilot is excluded when CODESPACES=true and only GITHUB_TOKEN is s
},
})
})

// altimate_change start — tests for altimate-backend default model preference
test("defaultModel returns altimate-backend when altimate credentials exist and no model configured", async () => {
await using tmp = await tmpdir({
init: async (dir) => {
await Bun.write(
path.join(dir, "opencode.json"),
JSON.stringify({
$schema: "https://altimate.ai/config.json",
}),
)
},
})
const originalHome = process.env.OPENCODE_TEST_HOME
process.env.OPENCODE_TEST_HOME = tmp.path
const altimateDir = path.join(tmp.path, ".altimate")
await fs.mkdir(altimateDir, { recursive: true })
await Bun.write(
path.join(altimateDir, "altimate.json"),
JSON.stringify({
altimateUrl: "https://test.altimate.ai",
altimateInstanceName: "test-instance",
altimateApiKey: "test-api-key",
}),
)
try {
await Instance.provide({
directory: tmp.path,
fn: async () => {
const model = await Provider.defaultModel()
expect(String(model.providerID)).toBe("altimate-backend")
expect(String(model.modelID)).toBe("altimate-default")
},
})
} finally {
if (originalHome === undefined) delete process.env.OPENCODE_TEST_HOME
else process.env.OPENCODE_TEST_HOME = originalHome
}
})

test("defaultModel prefers altimate-backend over other providers when altimate is configured", async () => {
await using tmp = await tmpdir({
init: async (dir) => {
await Bun.write(
path.join(dir, "opencode.json"),
JSON.stringify({
$schema: "https://altimate.ai/config.json",
}),
)
},
})
const originalHome = process.env.OPENCODE_TEST_HOME
process.env.OPENCODE_TEST_HOME = tmp.path
const altimateDir = path.join(tmp.path, ".altimate")
await fs.mkdir(altimateDir, { recursive: true })
await Bun.write(
path.join(altimateDir, "altimate.json"),
JSON.stringify({
altimateUrl: "https://test.altimate.ai",
altimateInstanceName: "test-instance",
altimateApiKey: "test-api-key",
}),
)
try {
await Instance.provide({
directory: tmp.path,
init: async () => {
Env.set("ANTHROPIC_API_KEY", "test-api-key")
},
fn: async () => {
const providers = await Provider.list()
// Both providers should be available
expect(providers["anthropic"]).toBeDefined()
expect(providers["altimate-backend"]).toBeDefined()
// But defaultModel should prefer altimate-backend
const model = await Provider.defaultModel()
expect(String(model.providerID)).toBe("altimate-backend")
expect(String(model.modelID)).toBe("altimate-default")
},
})
} finally {
if (originalHome === undefined) delete process.env.OPENCODE_TEST_HOME
else process.env.OPENCODE_TEST_HOME = originalHome
}
})

test("defaultModel respects explicit config model over altimate-backend", async () => {
await using tmp = await tmpdir({
init: async (dir) => {
await Bun.write(
path.join(dir, "opencode.json"),
JSON.stringify({
$schema: "https://altimate.ai/config.json",
model: "anthropic/claude-sonnet-4-20250514",
}),
)
},
})
const originalHome = process.env.OPENCODE_TEST_HOME
process.env.OPENCODE_TEST_HOME = tmp.path
const altimateDir = path.join(tmp.path, ".altimate")
await fs.mkdir(altimateDir, { recursive: true })
await Bun.write(
path.join(altimateDir, "altimate.json"),
JSON.stringify({
altimateUrl: "https://test.altimate.ai",
altimateInstanceName: "test-instance",
altimateApiKey: "test-api-key",
}),
)
try {
await Instance.provide({
directory: tmp.path,
init: async () => {
Env.set("ANTHROPIC_API_KEY", "test-api-key")
},
fn: async () => {
const model = await Provider.defaultModel()
expect(String(model.providerID)).toBe("anthropic")
expect(String(model.modelID)).toBe("claude-sonnet-4-20250514")
},
})
} finally {
if (originalHome === undefined) delete process.env.OPENCODE_TEST_HOME
else process.env.OPENCODE_TEST_HOME = originalHome
}
})

test("defaultModel falls through to other providers when altimate is not configured", async () => {
await using tmp = await tmpdir({
init: async (dir) => {
await Bun.write(
path.join(dir, "opencode.json"),
JSON.stringify({
$schema: "https://altimate.ai/config.json",
}),
)
},
})
await Instance.provide({
directory: tmp.path,
init: async () => {
Env.set("ANTHROPIC_API_KEY", "test-api-key")
},
fn: async () => {
const providers = await Provider.list()
// altimate-backend should NOT be available (no credentials file)
expect(providers["altimate-backend"]).toBeUndefined()
const model = await Provider.defaultModel()
// Should fall through to anthropic
expect(String(model.providerID)).toBe("anthropic")
},
})
})
// altimate_change end
Loading