fix(core): log background models.dev refresh failures at debug
This commit is contained in:
parent
5455fed78e
commit
9dfc3732ff
2 changed files with 73 additions and 6 deletions
|
|
@ -1,6 +1,6 @@
|
|||
import path from "path"
|
||||
import { Context, Duration, Effect, Layer, Option, Schedule, Schema } from "effect"
|
||||
import { FetchHttpClient, HttpClient, HttpClientRequest } from "effect/unstable/http"
|
||||
import { HttpClient, HttpClientRequest } from "effect/unstable/http"
|
||||
import { ModelsDev } from "@opencode-ai/schema/models-dev"
|
||||
import { Global } from "./global"
|
||||
import { Flag } from "./flag/flag"
|
||||
|
|
@ -212,7 +212,7 @@ const layer = Layer.effect(
|
|||
|
||||
const get = (): Effect.Effect<Record<string, Provider>> => cachedGet
|
||||
|
||||
const refresh = Effect.fn("ModelsDev.refresh")(function* (force = false) {
|
||||
const attempt = Effect.fn("ModelsDev.refresh.attempt")(function* (force = false) {
|
||||
if (!force && (yield* fresh())) return
|
||||
yield* Effect.scoped(
|
||||
Effect.gen(function* () {
|
||||
|
|
@ -224,7 +224,11 @@ const layer = Layer.effect(
|
|||
yield* invalidate
|
||||
yield* events.publish(Event.Refreshed, {})
|
||||
}),
|
||||
).pipe(
|
||||
)
|
||||
})
|
||||
|
||||
const refresh = Effect.fn("ModelsDev.refresh")(function* (force = false) {
|
||||
yield* attempt(force).pipe(
|
||||
Effect.tapCause((cause) => Effect.logError("Failed to fetch models.dev", { cause: cause })),
|
||||
Effect.ignore,
|
||||
)
|
||||
|
|
@ -232,7 +236,16 @@ const layer = Layer.effect(
|
|||
|
||||
if (!Flag.OPENCODE_DISABLE_MODELS_FETCH && !process.argv.includes("--get-yargs-completions")) {
|
||||
// Schedule.spaced runs the effect once, then waits between completions.
|
||||
yield* Effect.forkScoped(refresh().pipe(Effect.repeat(Schedule.spaced("60 minutes")), Effect.ignore))
|
||||
// Keep best-effort background failures at Debug: before the file logger is
|
||||
// installed, Effect's default logger writes to stdout, which corrupts the TUI.
|
||||
yield* Effect.forkScoped(
|
||||
attempt().pipe(
|
||||
Effect.tapCause((cause) => Effect.logDebug("Failed to fetch models.dev", { cause: cause })),
|
||||
Effect.ignore,
|
||||
Effect.repeat(Schedule.spaced("60 minutes")),
|
||||
Effect.ignore,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
return Service.of({ get, refresh })
|
||||
|
|
|
|||
|
|
@ -1,9 +1,8 @@
|
|||
import { describe, expect, beforeAll, beforeEach, afterAll } from "bun:test"
|
||||
import { Effect, Layer, Ref } from "effect"
|
||||
import { Effect, Layer, Logger, Ref, References } from "effect"
|
||||
import { HttpClient, HttpClientResponse } from "effect/unstable/http"
|
||||
import { AppNodeBuilder } from "@opencode-ai/core/effect/app-node-builder"
|
||||
import { LayerNodePlatform } from "@opencode-ai/core/effect/app-node-platform"
|
||||
import { LayerNode } from "@opencode-ai/core/effect/layer-node"
|
||||
import { Flag } from "@opencode-ai/core/flag/flag"
|
||||
import { Global } from "@opencode-ai/core/global"
|
||||
import { ModelsDev } from "@opencode-ai/core/models-dev"
|
||||
|
|
@ -97,6 +96,22 @@ const buildLayer = (state: Ref.Ref<MockState>) =>
|
|||
]),
|
||||
)
|
||||
|
||||
const makeCaptureLoggerLayer = (entries: Array<{ message: string; logLevel: string }>) =>
|
||||
Layer.mergeAll(
|
||||
Logger.layer(
|
||||
[
|
||||
Logger.make<unknown, void>((options) => {
|
||||
entries.push({ message: logMessage(options.message), logLevel: options.logLevel })
|
||||
}),
|
||||
],
|
||||
{ mergeWithExisting: false },
|
||||
),
|
||||
Layer.succeed(References.MinimumLogLevel, "Debug"),
|
||||
)
|
||||
|
||||
const logMessage = (message: unknown) =>
|
||||
Array.isArray(message) ? message.map((item) => String(item)).join(" ") : String(message)
|
||||
|
||||
const writeCacheText = (text: string, mtimeMs?: number) =>
|
||||
Effect.promise(async () => {
|
||||
await mkdir(Global.Path.cache, { recursive: true })
|
||||
|
|
@ -287,4 +302,43 @@ describe("ModelsDev Service", () => {
|
|||
expect(final.calls.length).toBeGreaterThanOrEqual(1)
|
||||
}),
|
||||
)
|
||||
|
||||
it.live("background refresh logs fetch failures at Debug", () =>
|
||||
Effect.gen(function* () {
|
||||
const entries: Array<{ message: string; logLevel: string }> = []
|
||||
const state = yield* Ref.make({ ...initialState, status: 404, body: "boom" })
|
||||
yield* Effect.acquireUseRelease(
|
||||
Effect.sync(() => {
|
||||
Flag.OPENCODE_DISABLE_MODELS_FETCH = false
|
||||
}),
|
||||
() =>
|
||||
Effect.gen(function* () {
|
||||
yield* Layer.build(buildLayer(state))
|
||||
yield* Effect.sleep("1 second")
|
||||
}).pipe(Effect.scoped, Effect.provide(makeCaptureLoggerLayer(entries))),
|
||||
() =>
|
||||
Effect.sync(() => {
|
||||
Flag.OPENCODE_DISABLE_MODELS_FETCH = true
|
||||
}),
|
||||
)
|
||||
const failures = entries.filter((entry) => entry.message.includes("Failed to fetch models.dev"))
|
||||
expect(failures.some((entry) => entry.logLevel === "Debug")).toBe(true)
|
||||
expect(failures.some((entry) => entry.logLevel === "Error")).toBe(false)
|
||||
}),
|
||||
)
|
||||
|
||||
it.live("manual refresh logs fetch failures at Error", () =>
|
||||
Effect.gen(function* () {
|
||||
yield* writeCache(fixture)
|
||||
const entries: Array<{ message: string; logLevel: string }> = []
|
||||
const state = yield* Ref.make({ ...initialState, status: 404, body: "boom" })
|
||||
yield* ModelsDev.Service.use((s) => s.refresh(true)).pipe(
|
||||
Effect.provide(buildLayer(state)),
|
||||
Effect.provide(makeCaptureLoggerLayer(entries)),
|
||||
)
|
||||
const failures = entries.filter((entry) => entry.message.includes("Failed to fetch models.dev"))
|
||||
expect(failures.some((entry) => entry.logLevel === "Error")).toBe(true)
|
||||
expect(failures.some((entry) => entry.logLevel === "Debug")).toBe(false)
|
||||
}),
|
||||
)
|
||||
})
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue