Skip to content

Commit e33a469

Browse files
kitlangtonmrsimpson
authored andcommitted
refactor(provider): remove async facade exports (anomalyco#22320)
1 parent 8c9e80e commit e33a469

10 files changed

Lines changed: 261 additions & 199 deletions

File tree

packages/opencode/src/cli/cmd/debug/agent.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,14 @@ function parseToolParams(input?: string) {
125125
async function createToolContext(agent: Agent.Info) {
126126
const session = await Session.create({ title: `Debug tool run (${agent.name})` })
127127
const messageID = MessageID.ascending()
128-
const model = agent.model ?? (await Provider.defaultModel())
128+
const model =
129+
agent.model ??
130+
(await AppRuntime.runPromise(
131+
Effect.gen(function* () {
132+
const provider = yield* Provider.Service
133+
return yield* provider.defaultModel()
134+
}),
135+
))
129136
const now = Date.now()
130137
const message: MessageV2.Assistant = {
131138
id: messageID,

packages/opencode/src/cli/cmd/models.ts

Lines changed: 41 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ import { ModelsDev } from "../../provider/models"
66
import { cmd } from "./cmd"
77
import { UI } from "../ui"
88
import { EOL } from "os"
9+
import { AppRuntime } from "@/effect/app-runtime"
10+
import { Effect } from "effect"
911

1012
export const ModelsCommand = cmd({
1113
command: "models [provider]",
@@ -35,43 +37,51 @@ export const ModelsCommand = cmd({
3537
await Instance.provide({
3638
directory: process.cwd(),
3739
async fn() {
38-
const providers = await Provider.list()
40+
await AppRuntime.runPromise(
41+
Effect.gen(function* () {
42+
const svc = yield* Provider.Service
43+
const providers = yield* svc.list()
3944

40-
function printModels(providerID: ProviderID, verbose?: boolean) {
41-
const provider = providers[providerID]
42-
const sortedModels = Object.entries(provider.models).sort(([a], [b]) => a.localeCompare(b))
43-
for (const [modelID, model] of sortedModels) {
44-
process.stdout.write(`${providerID}/${modelID}`)
45-
process.stdout.write(EOL)
46-
if (verbose) {
47-
process.stdout.write(JSON.stringify(model, null, 2))
48-
process.stdout.write(EOL)
45+
const print = (providerID: ProviderID, verbose?: boolean) => {
46+
const provider = providers[providerID]
47+
const sorted = Object.entries(provider.models).sort(([a], [b]) => a.localeCompare(b))
48+
for (const [modelID, model] of sorted) {
49+
process.stdout.write(`${providerID}/${modelID}`)
50+
process.stdout.write(EOL)
51+
if (verbose) {
52+
process.stdout.write(JSON.stringify(model, null, 2))
53+
process.stdout.write(EOL)
54+
}
55+
}
4956
}
50-
}
51-
}
5257

53-
if (args.provider) {
54-
const provider = providers[ProviderID.make(args.provider)]
55-
if (!provider) {
56-
UI.error(`Provider not found: ${args.provider}`)
57-
return
58-
}
58+
if (args.provider) {
59+
const providerID = ProviderID.make(args.provider)
60+
const provider = providers[providerID]
61+
if (!provider) {
62+
yield* Effect.sync(() => UI.error(`Provider not found: ${args.provider}`))
63+
return
64+
}
5965

60-
printModels(ProviderID.make(args.provider), args.verbose)
61-
return
62-
}
66+
yield* Effect.sync(() => print(providerID, args.verbose))
67+
return
68+
}
6369

64-
const providerIDs = Object.keys(providers).sort((a, b) => {
65-
const aIsOpencode = a.startsWith("opencode")
66-
const bIsOpencode = b.startsWith("opencode")
67-
if (aIsOpencode && !bIsOpencode) return -1
68-
if (!aIsOpencode && bIsOpencode) return 1
69-
return a.localeCompare(b)
70-
})
70+
const ids = Object.keys(providers).sort((a, b) => {
71+
const aIsOpencode = a.startsWith("opencode")
72+
const bIsOpencode = b.startsWith("opencode")
73+
if (aIsOpencode && !bIsOpencode) return -1
74+
if (!aIsOpencode && bIsOpencode) return 1
75+
return a.localeCompare(b)
76+
})
7177

72-
for (const providerID of providerIDs) {
73-
printModels(ProviderID.make(providerID), args.verbose)
74-
}
78+
yield* Effect.sync(() => {
79+
for (const providerID of ids) {
80+
print(ProviderID.make(providerID), args.verbose)
81+
}
82+
})
83+
}),
84+
)
7585
},
7686
})
7787
},

packages/opencode/src/provider/provider.ts

Lines changed: 0 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@ import path from "path"
2121
import { Effect, Layer, Context } from "effect"
2222
import { EffectLogger } from "@/effect/logger"
2323
import { InstanceState } from "@/effect/instance-state"
24-
import { makeRuntime } from "@/effect/run-service"
2524
import { AppFileSystem } from "@/filesystem"
2625
import { isRecord } from "@/util/record"
2726

@@ -1693,36 +1692,6 @@ export namespace Provider {
16931692
),
16941693
)
16951694

1696-
const { runPromise } = makeRuntime(Service, defaultLayer)
1697-
1698-
export async function list() {
1699-
return runPromise((svc) => svc.list())
1700-
}
1701-
1702-
export async function getProvider(providerID: ProviderID) {
1703-
return runPromise((svc) => svc.getProvider(providerID))
1704-
}
1705-
1706-
export async function getModel(providerID: ProviderID, modelID: ModelID) {
1707-
return runPromise((svc) => svc.getModel(providerID, modelID))
1708-
}
1709-
1710-
export async function getLanguage(model: Model) {
1711-
return runPromise((svc) => svc.getLanguage(model))
1712-
}
1713-
1714-
export async function closest(providerID: ProviderID, query: string[]) {
1715-
return runPromise((svc) => svc.closest(providerID, query))
1716-
}
1717-
1718-
export async function getSmallModel(providerID: ProviderID) {
1719-
return runPromise((svc) => svc.getSmallModel(providerID))
1720-
}
1721-
1722-
export async function defaultModel() {
1723-
return runPromise((svc) => svc.defaultModel())
1724-
}
1725-
17261695
const priority = ["gpt-5", "claude-sonnet-4", "big-pickle", "gemini-3-pro"]
17271696
export function sort<T extends { id: string }>(models: T[]) {
17281697
return sortBy(

packages/opencode/src/server/instance/config.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ import { mapValues } from "remeda"
77
import { errors } from "../error"
88
import { Log } from "../../util/log"
99
import { lazy } from "../../util/lazy"
10+
import { AppRuntime } from "../../effect/app-runtime"
11+
import { Effect } from "effect"
1012

1113
const log = Log.create({ service: "server" })
1214

@@ -82,7 +84,12 @@ export const ConfigRoutes = lazy(() =>
8284
}),
8385
async (c) => {
8486
using _ = log.time("providers")
85-
const providers = await Provider.list().then((x) => mapValues(x, (item) => item))
87+
const providers = await AppRuntime.runPromise(
88+
Effect.gen(function* () {
89+
const svc = yield* Provider.Service
90+
return mapValues(yield* svc.list(), (item) => item)
91+
}),
92+
)
8693
return c.json({
8794
providers: Object.values(providers),
8895
default: mapValues(providers, (item) => Provider.sort(Object.values(item.models))[0].id),

packages/opencode/src/server/instance/provider.ts

Lines changed: 28 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import { mapValues } from "remeda"
1111
import { errors } from "../error"
1212
import { lazy } from "../../util/lazy"
1313
import { Log } from "../../util/log"
14+
import { Effect } from "effect"
1415

1516
const log = Log.create({ service: "server" })
1617

@@ -40,27 +41,35 @@ export const ProviderRoutes = lazy(() =>
4041
},
4142
}),
4243
async (c) => {
43-
const config = await Config.get()
44-
const disabled = new Set(config.disabled_providers ?? [])
45-
const enabled = config.enabled_providers ? new Set(config.enabled_providers) : undefined
46-
47-
const allProviders = await ModelsDev.get()
48-
const filteredProviders: Record<string, (typeof allProviders)[string]> = {}
49-
for (const [key, value] of Object.entries(allProviders)) {
50-
if ((enabled ? enabled.has(key) : true) && !disabled.has(key)) {
51-
filteredProviders[key] = value
52-
}
53-
}
54-
55-
const connected = await Provider.list()
56-
const providers = Object.assign(
57-
mapValues(filteredProviders, (x) => Provider.fromModelsDevProvider(x)),
58-
connected,
44+
const result = await AppRuntime.runPromise(
45+
Effect.gen(function* () {
46+
const svc = yield* Provider.Service
47+
const config = yield* Effect.promise(() => Config.get())
48+
const all = yield* Effect.promise(() => ModelsDev.get())
49+
const disabled = new Set(config.disabled_providers ?? [])
50+
const enabled = config.enabled_providers ? new Set(config.enabled_providers) : undefined
51+
const filtered: Record<string, (typeof all)[string]> = {}
52+
for (const [key, value] of Object.entries(all)) {
53+
if ((enabled ? enabled.has(key) : true) && !disabled.has(key)) {
54+
filtered[key] = value
55+
}
56+
}
57+
const connected = yield* svc.list()
58+
const providers = Object.assign(
59+
mapValues(filtered, (x) => Provider.fromModelsDevProvider(x)),
60+
connected,
61+
)
62+
return {
63+
all: Object.values(providers),
64+
default: mapValues(providers, (item) => Provider.sort(Object.values(item.models))[0].id),
65+
connected: Object.keys(connected),
66+
}
67+
}),
5968
)
6069
return c.json({
61-
all: Object.values(providers),
62-
default: mapValues(providers, (item) => Provider.sort(Object.values(item.models))[0].id),
63-
connected: Object.keys(connected),
70+
all: result.all,
71+
default: result.default,
72+
connected: result.connected,
6473
})
6574
},
6675
)

packages/opencode/test/provider/amazon-bedrock.test.ts

Lines changed: 21 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,17 @@ import { Provider } from "../../src/provider/provider"
99
import { Env } from "../../src/env"
1010
import { Global } from "../../src/global"
1111
import { Filesystem } from "../../src/util/filesystem"
12+
import { Effect } from "effect"
13+
import { AppRuntime } from "../../src/effect/app-runtime"
14+
15+
async function list() {
16+
return AppRuntime.runPromise(
17+
Effect.gen(function* () {
18+
const provider = yield* Provider.Service
19+
return yield* provider.list()
20+
}),
21+
)
22+
}
1223

1324
test("Bedrock: config region takes precedence over AWS_REGION env var", async () => {
1425
await using tmp = await tmpdir({
@@ -35,7 +46,7 @@ test("Bedrock: config region takes precedence over AWS_REGION env var", async ()
3546
Env.set("AWS_PROFILE", "default")
3647
},
3748
fn: async () => {
38-
const providers = await Provider.list()
49+
const providers = await list()
3950
expect(providers[ProviderID.amazonBedrock]).toBeDefined()
4051
expect(providers[ProviderID.amazonBedrock].options?.region).toBe("eu-west-1")
4152
},
@@ -60,7 +71,7 @@ test("Bedrock: falls back to AWS_REGION env var when no config region", async ()
6071
Env.set("AWS_PROFILE", "default")
6172
},
6273
fn: async () => {
63-
const providers = await Provider.list()
74+
const providers = await list()
6475
expect(providers[ProviderID.amazonBedrock]).toBeDefined()
6576
expect(providers[ProviderID.amazonBedrock].options?.region).toBe("eu-west-1")
6677
},
@@ -116,7 +127,7 @@ test("Bedrock: loads when bearer token from auth.json is present", async () => {
116127
Env.set("AWS_BEARER_TOKEN_BEDROCK", "")
117128
},
118129
fn: async () => {
119-
const providers = await Provider.list()
130+
const providers = await list()
120131
expect(providers[ProviderID.amazonBedrock]).toBeDefined()
121132
expect(providers[ProviderID.amazonBedrock].options?.region).toBe("eu-west-1")
122133
},
@@ -161,7 +172,7 @@ test("Bedrock: config profile takes precedence over AWS_PROFILE env var", async
161172
Env.set("AWS_ACCESS_KEY_ID", "test-key-id")
162173
},
163174
fn: async () => {
164-
const providers = await Provider.list()
175+
const providers = await list()
165176
expect(providers[ProviderID.amazonBedrock]).toBeDefined()
166177
expect(providers[ProviderID.amazonBedrock].options?.region).toBe("us-east-1")
167178
},
@@ -192,7 +203,7 @@ test("Bedrock: includes custom endpoint in options when specified", async () =>
192203
Env.set("AWS_PROFILE", "default")
193204
},
194205
fn: async () => {
195-
const providers = await Provider.list()
206+
const providers = await list()
196207
expect(providers[ProviderID.amazonBedrock]).toBeDefined()
197208
expect(providers[ProviderID.amazonBedrock].options?.endpoint).toBe(
198209
"https://bedrock-runtime.us-east-1.vpce-xxxxx.amazonaws.com",
@@ -228,7 +239,7 @@ test("Bedrock: autoloads when AWS_WEB_IDENTITY_TOKEN_FILE is present", async ()
228239
Env.set("AWS_ACCESS_KEY_ID", "")
229240
},
230241
fn: async () => {
231-
const providers = await Provider.list()
242+
const providers = await list()
232243
expect(providers[ProviderID.amazonBedrock]).toBeDefined()
233244
expect(providers[ProviderID.amazonBedrock].options?.region).toBe("us-east-1")
234245
},
@@ -268,7 +279,7 @@ test("Bedrock: model with us. prefix should not be double-prefixed", async () =>
268279
Env.set("AWS_PROFILE", "default")
269280
},
270281
fn: async () => {
271-
const providers = await Provider.list()
282+
const providers = await list()
272283
expect(providers[ProviderID.amazonBedrock]).toBeDefined()
273284
// The model should exist with the us. prefix
274285
expect(providers[ProviderID.amazonBedrock].models["us.anthropic.claude-opus-4-5-20251101-v1:0"]).toBeDefined()
@@ -305,7 +316,7 @@ test("Bedrock: model with global. prefix should not be prefixed", async () => {
305316
Env.set("AWS_PROFILE", "default")
306317
},
307318
fn: async () => {
308-
const providers = await Provider.list()
319+
const providers = await list()
309320
expect(providers[ProviderID.amazonBedrock]).toBeDefined()
310321
expect(providers[ProviderID.amazonBedrock].models["global.anthropic.claude-opus-4-5-20251101-v1:0"]).toBeDefined()
311322
},
@@ -341,7 +352,7 @@ test("Bedrock: model with eu. prefix should not be double-prefixed", async () =>
341352
Env.set("AWS_PROFILE", "default")
342353
},
343354
fn: async () => {
344-
const providers = await Provider.list()
355+
const providers = await list()
345356
expect(providers[ProviderID.amazonBedrock]).toBeDefined()
346357
expect(providers[ProviderID.amazonBedrock].models["eu.anthropic.claude-opus-4-5-20251101-v1:0"]).toBeDefined()
347358
},
@@ -377,7 +388,7 @@ test("Bedrock: model without prefix in US region should get us. prefix added", a
377388
Env.set("AWS_PROFILE", "default")
378389
},
379390
fn: async () => {
380-
const providers = await Provider.list()
391+
const providers = await list()
381392
expect(providers[ProviderID.amazonBedrock]).toBeDefined()
382393
// Non-prefixed model should still be registered
383394
expect(providers[ProviderID.amazonBedrock].models["anthropic.claude-opus-4-5-20251101-v1:0"]).toBeDefined()

0 commit comments

Comments
 (0)