feat: add models.dev-backed model capabilities
This commit is contained in:
@@ -3,6 +3,7 @@ import { afterEach, beforeEach, describe, expect, it, mock } from "bun:test"
|
||||
const mockShowConfigErrorsIfAny = mock(async () => {})
|
||||
const mockShowModelCacheWarningIfNeeded = mock(async () => {})
|
||||
const mockUpdateAndShowConnectedProvidersCacheStatus = mock(async () => {})
|
||||
const mockRefreshModelCapabilitiesOnStartup = mock(async () => {})
|
||||
const mockShowLocalDevToast = mock(async () => {})
|
||||
const mockShowVersionToast = mock(async () => {})
|
||||
const mockRunBackgroundUpdateCheck = mock(async () => {})
|
||||
@@ -22,6 +23,10 @@ mock.module("./hook/connected-providers-status", () => ({
|
||||
mockUpdateAndShowConnectedProvidersCacheStatus,
|
||||
}))
|
||||
|
||||
mock.module("./hook/model-capabilities-status", () => ({
|
||||
refreshModelCapabilitiesOnStartup: mockRefreshModelCapabilitiesOnStartup,
|
||||
}))
|
||||
|
||||
mock.module("./hook/startup-toasts", () => ({
|
||||
showLocalDevToast: mockShowLocalDevToast,
|
||||
showVersionToast: mockShowVersionToast,
|
||||
@@ -78,6 +83,7 @@ beforeEach(() => {
|
||||
mockShowConfigErrorsIfAny.mockClear()
|
||||
mockShowModelCacheWarningIfNeeded.mockClear()
|
||||
mockUpdateAndShowConnectedProvidersCacheStatus.mockClear()
|
||||
mockRefreshModelCapabilitiesOnStartup.mockClear()
|
||||
mockShowLocalDevToast.mockClear()
|
||||
mockShowVersionToast.mockClear()
|
||||
mockRunBackgroundUpdateCheck.mockClear()
|
||||
@@ -112,6 +118,7 @@ describe("createAutoUpdateCheckerHook", () => {
|
||||
expect(mockShowConfigErrorsIfAny).not.toHaveBeenCalled()
|
||||
expect(mockShowModelCacheWarningIfNeeded).not.toHaveBeenCalled()
|
||||
expect(mockUpdateAndShowConnectedProvidersCacheStatus).not.toHaveBeenCalled()
|
||||
expect(mockRefreshModelCapabilitiesOnStartup).not.toHaveBeenCalled()
|
||||
expect(mockShowLocalDevToast).not.toHaveBeenCalled()
|
||||
expect(mockShowVersionToast).not.toHaveBeenCalled()
|
||||
expect(mockRunBackgroundUpdateCheck).not.toHaveBeenCalled()
|
||||
@@ -129,6 +136,7 @@ describe("createAutoUpdateCheckerHook", () => {
|
||||
//#then - startup checks, toast, and background check run
|
||||
expect(mockShowConfigErrorsIfAny).toHaveBeenCalledTimes(1)
|
||||
expect(mockUpdateAndShowConnectedProvidersCacheStatus).toHaveBeenCalledTimes(1)
|
||||
expect(mockRefreshModelCapabilitiesOnStartup).toHaveBeenCalledTimes(1)
|
||||
expect(mockShowModelCacheWarningIfNeeded).toHaveBeenCalledTimes(1)
|
||||
expect(mockShowVersionToast).toHaveBeenCalledTimes(1)
|
||||
expect(mockRunBackgroundUpdateCheck).toHaveBeenCalledTimes(1)
|
||||
@@ -146,6 +154,7 @@ describe("createAutoUpdateCheckerHook", () => {
|
||||
//#then - no startup actions run
|
||||
expect(mockShowConfigErrorsIfAny).not.toHaveBeenCalled()
|
||||
expect(mockUpdateAndShowConnectedProvidersCacheStatus).not.toHaveBeenCalled()
|
||||
expect(mockRefreshModelCapabilitiesOnStartup).not.toHaveBeenCalled()
|
||||
expect(mockShowModelCacheWarningIfNeeded).not.toHaveBeenCalled()
|
||||
expect(mockShowLocalDevToast).not.toHaveBeenCalled()
|
||||
expect(mockShowVersionToast).not.toHaveBeenCalled()
|
||||
@@ -165,6 +174,7 @@ describe("createAutoUpdateCheckerHook", () => {
|
||||
//#then - side effects execute only once
|
||||
expect(mockShowConfigErrorsIfAny).toHaveBeenCalledTimes(1)
|
||||
expect(mockUpdateAndShowConnectedProvidersCacheStatus).toHaveBeenCalledTimes(1)
|
||||
expect(mockRefreshModelCapabilitiesOnStartup).toHaveBeenCalledTimes(1)
|
||||
expect(mockShowModelCacheWarningIfNeeded).toHaveBeenCalledTimes(1)
|
||||
expect(mockShowVersionToast).toHaveBeenCalledTimes(1)
|
||||
expect(mockRunBackgroundUpdateCheck).toHaveBeenCalledTimes(1)
|
||||
@@ -183,6 +193,7 @@ describe("createAutoUpdateCheckerHook", () => {
|
||||
//#then - local dev toast is shown and background check is skipped
|
||||
expect(mockShowConfigErrorsIfAny).toHaveBeenCalledTimes(1)
|
||||
expect(mockUpdateAndShowConnectedProvidersCacheStatus).toHaveBeenCalledTimes(1)
|
||||
expect(mockRefreshModelCapabilitiesOnStartup).toHaveBeenCalledTimes(1)
|
||||
expect(mockShowModelCacheWarningIfNeeded).toHaveBeenCalledTimes(1)
|
||||
expect(mockShowLocalDevToast).toHaveBeenCalledTimes(1)
|
||||
expect(mockShowVersionToast).not.toHaveBeenCalled()
|
||||
@@ -205,6 +216,7 @@ describe("createAutoUpdateCheckerHook", () => {
|
||||
//#then - no startup actions run
|
||||
expect(mockShowConfigErrorsIfAny).not.toHaveBeenCalled()
|
||||
expect(mockUpdateAndShowConnectedProvidersCacheStatus).not.toHaveBeenCalled()
|
||||
expect(mockRefreshModelCapabilitiesOnStartup).not.toHaveBeenCalled()
|
||||
expect(mockShowModelCacheWarningIfNeeded).not.toHaveBeenCalled()
|
||||
expect(mockShowLocalDevToast).not.toHaveBeenCalled()
|
||||
expect(mockShowVersionToast).not.toHaveBeenCalled()
|
||||
|
||||
@@ -5,11 +5,17 @@ import type { AutoUpdateCheckerOptions } from "./types"
|
||||
import { runBackgroundUpdateCheck } from "./hook/background-update-check"
|
||||
import { showConfigErrorsIfAny } from "./hook/config-errors-toast"
|
||||
import { updateAndShowConnectedProvidersCacheStatus } from "./hook/connected-providers-status"
|
||||
import { refreshModelCapabilitiesOnStartup } from "./hook/model-capabilities-status"
|
||||
import { showModelCacheWarningIfNeeded } from "./hook/model-cache-warning"
|
||||
import { showLocalDevToast, showVersionToast } from "./hook/startup-toasts"
|
||||
|
||||
export function createAutoUpdateCheckerHook(ctx: PluginInput, options: AutoUpdateCheckerOptions = {}) {
|
||||
const { showStartupToast = true, isSisyphusEnabled = false, autoUpdate = true } = options
|
||||
const {
|
||||
showStartupToast = true,
|
||||
isSisyphusEnabled = false,
|
||||
autoUpdate = true,
|
||||
modelCapabilities,
|
||||
} = options
|
||||
const isCliRunMode = process.env.OPENCODE_CLI_RUN_MODE === "true"
|
||||
|
||||
const getToastMessage = (isUpdate: boolean, latestVersion?: string): string => {
|
||||
@@ -43,6 +49,7 @@ export function createAutoUpdateCheckerHook(ctx: PluginInput, options: AutoUpdat
|
||||
|
||||
await showConfigErrorsIfAny(ctx)
|
||||
await updateAndShowConnectedProvidersCacheStatus(ctx)
|
||||
await refreshModelCapabilitiesOnStartup(modelCapabilities)
|
||||
await showModelCacheWarningIfNeeded(ctx)
|
||||
|
||||
if (localDevVersion) {
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
import type { ModelCapabilitiesConfig } from "../../../config/schema/model-capabilities"
|
||||
import { refreshModelCapabilitiesCache } from "../../../shared/model-capabilities-cache"
|
||||
import { log } from "../../../shared/logger"
|
||||
|
||||
const DEFAULT_REFRESH_TIMEOUT_MS = 5000
|
||||
|
||||
export async function refreshModelCapabilitiesOnStartup(
|
||||
config: ModelCapabilitiesConfig | undefined,
|
||||
): Promise<void> {
|
||||
if (config?.enabled === false) {
|
||||
return
|
||||
}
|
||||
|
||||
if (config?.auto_refresh_on_start === false) {
|
||||
return
|
||||
}
|
||||
|
||||
const timeoutMs = config?.refresh_timeout_ms ?? DEFAULT_REFRESH_TIMEOUT_MS
|
||||
|
||||
let timeoutId: ReturnType<typeof setTimeout> | undefined
|
||||
try {
|
||||
await Promise.race([
|
||||
refreshModelCapabilitiesCache({
|
||||
sourceUrl: config?.source_url,
|
||||
}),
|
||||
new Promise<never>((_, reject) => {
|
||||
timeoutId = setTimeout(() => reject(new Error("Model capabilities refresh timed out")), timeoutMs)
|
||||
}),
|
||||
])
|
||||
} catch (error) {
|
||||
log("[auto-update-checker] Model capabilities refresh failed", { error: String(error) })
|
||||
} finally {
|
||||
if (timeoutId) {
|
||||
clearTimeout(timeoutId)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,5 @@
|
||||
import type { ModelCapabilitiesConfig } from "../../config/schema/model-capabilities"
|
||||
|
||||
export interface NpmDistTags {
|
||||
latest: string
|
||||
[key: string]: string
|
||||
@@ -26,4 +28,5 @@ export interface AutoUpdateCheckerOptions {
|
||||
showStartupToast?: boolean
|
||||
isSisyphusEnabled?: boolean
|
||||
autoUpdate?: boolean
|
||||
modelCapabilities?: ModelCapabilitiesConfig
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user