refactor(shared): improve model availability and resolution module structure
- Use namespace import for connected-providers-cache for better clarity
- Add explicit type annotation for modelsByProvider to improve type safety
- Update tests to reflect refactored module organization
- Improve code organization while maintaining functionality
🤖 Generated with assistance of OhMyOpenCode
This commit is contained in:
parent
1324fee30f
commit
7788ba3d8a
@ -1,38 +1,43 @@
|
|||||||
import { describe, it, expect, beforeEach, afterEach, beforeAll, afterAll, mock } from "bun:test"
|
declare const require: (name: string) => any
|
||||||
|
const { describe, it, expect, beforeEach, afterEach, beforeAll } = require("bun:test")
|
||||||
import { mkdtempSync, writeFileSync, rmSync } from "fs"
|
import { mkdtempSync, writeFileSync, rmSync } from "fs"
|
||||||
import { tmpdir } from "os"
|
import { tmpdir } from "os"
|
||||||
import { join } from "path"
|
import { join } from "path"
|
||||||
import { fuzzyMatchModel, isModelAvailable } from "./model-name-matcher"
|
|
||||||
|
|
||||||
let activeCacheHomeDir: string | null = null
|
let __resetModelCache: () => void
|
||||||
const DEFAULT_CACHE_HOME_DIR = join(tmpdir(), "opencode-test-default-cache")
|
let fetchAvailableModels: (client?: unknown, options?: { connectedProviders?: string[] | null }) => Promise<Set<string>>
|
||||||
|
let fuzzyMatchModel: (target: string, available: Set<string>, providers?: string[]) => string | null
|
||||||
|
let isModelAvailable: (targetModel: string, availableModels: Set<string>) => boolean
|
||||||
|
let getConnectedProviders: (client: unknown) => Promise<string[]>
|
||||||
|
|
||||||
mock.module("./data-path", () => ({
|
beforeAll(async () => {
|
||||||
getDataDir: () => activeCacheHomeDir ?? DEFAULT_CACHE_HOME_DIR,
|
;({
|
||||||
getOpenCodeStorageDir: () => join(activeCacheHomeDir ?? DEFAULT_CACHE_HOME_DIR, "opencode", "storage"),
|
__resetModelCache,
|
||||||
getCacheDir: () => activeCacheHomeDir ?? DEFAULT_CACHE_HOME_DIR,
|
fetchAvailableModels,
|
||||||
getOmoOpenCodeCacheDir: () => join(activeCacheHomeDir ?? DEFAULT_CACHE_HOME_DIR, "oh-my-opencode"),
|
fuzzyMatchModel,
|
||||||
getOpenCodeCacheDir: () => join(activeCacheHomeDir ?? DEFAULT_CACHE_HOME_DIR, "opencode"),
|
isModelAvailable,
|
||||||
}))
|
getConnectedProviders,
|
||||||
|
} = await import("./model-availability"))
|
||||||
|
})
|
||||||
|
|
||||||
describe("fetchAvailableModels", () => {
|
describe("fetchAvailableModels", () => {
|
||||||
let tempDir: string
|
let tempDir: string
|
||||||
let fetchAvailableModels: (client?: unknown, options?: { connectedProviders?: string[] | null }) => Promise<Set<string>>
|
let originalXdgCache: string | undefined
|
||||||
let __resetModelCache: () => void
|
|
||||||
|
|
||||||
beforeAll(async () => {
|
|
||||||
;({ fetchAvailableModels } = await import("./available-models-fetcher"))
|
|
||||||
;({ __resetModelCache } = await import("./model-cache-availability"))
|
|
||||||
})
|
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
__resetModelCache()
|
__resetModelCache()
|
||||||
tempDir = mkdtempSync(join(tmpdir(), "opencode-test-"))
|
tempDir = mkdtempSync(join(tmpdir(), "opencode-test-"))
|
||||||
activeCacheHomeDir = tempDir
|
originalXdgCache = process.env.XDG_CACHE_HOME
|
||||||
|
process.env.XDG_CACHE_HOME = tempDir
|
||||||
})
|
})
|
||||||
|
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
activeCacheHomeDir = null
|
if (originalXdgCache !== undefined) {
|
||||||
|
process.env.XDG_CACHE_HOME = originalXdgCache
|
||||||
|
} else {
|
||||||
|
delete process.env.XDG_CACHE_HOME
|
||||||
|
}
|
||||||
rmSync(tempDir, { recursive: true, force: true })
|
rmSync(tempDir, { recursive: true, force: true })
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@ -2,7 +2,7 @@ import { existsSync, readFileSync } from "fs"
|
|||||||
import { join } from "path"
|
import { join } from "path"
|
||||||
import { log } from "./logger"
|
import { log } from "./logger"
|
||||||
import { getOpenCodeCacheDir } from "./data-path"
|
import { getOpenCodeCacheDir } from "./data-path"
|
||||||
import { readProviderModelsCache, hasProviderModelsCache, readConnectedProvidersCache } from "./connected-providers-cache"
|
import * as connectedProvidersCache from "./connected-providers-cache"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Fuzzy match a target model name against available models
|
* Fuzzy match a target model name against available models
|
||||||
@ -181,7 +181,7 @@ export async function fetchAvailableModels(
|
|||||||
const connectedSet = new Set(connectedProvidersList)
|
const connectedSet = new Set(connectedProvidersList)
|
||||||
const modelSet = new Set<string>()
|
const modelSet = new Set<string>()
|
||||||
|
|
||||||
const providerModelsCache = readProviderModelsCache()
|
const providerModelsCache = connectedProvidersCache.readProviderModelsCache()
|
||||||
if (providerModelsCache) {
|
if (providerModelsCache) {
|
||||||
const providerCount = Object.keys(providerModelsCache.models).length
|
const providerCount = Object.keys(providerModelsCache.models).length
|
||||||
if (providerCount === 0) {
|
if (providerCount === 0) {
|
||||||
@ -189,7 +189,8 @@ export async function fetchAvailableModels(
|
|||||||
} else {
|
} else {
|
||||||
log("[fetchAvailableModels] using provider-models cache (whitelist-filtered)")
|
log("[fetchAvailableModels] using provider-models cache (whitelist-filtered)")
|
||||||
|
|
||||||
for (const [providerId, modelIds] of Object.entries(providerModelsCache.models)) {
|
const modelsByProvider = providerModelsCache.models as Record<string, Array<string | { id?: string }>>
|
||||||
|
for (const [providerId, modelIds] of Object.entries(modelsByProvider)) {
|
||||||
if (!connectedSet.has(providerId)) {
|
if (!connectedSet.has(providerId)) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
@ -300,7 +301,7 @@ export function isAnyFallbackModelAvailable(
|
|||||||
// Fallback: check if any provider in the chain is connected
|
// Fallback: check if any provider in the chain is connected
|
||||||
// This handles race conditions where availableModels is empty or incomplete
|
// This handles race conditions where availableModels is empty or incomplete
|
||||||
// but we know the provider is connected.
|
// but we know the provider is connected.
|
||||||
const connectedProviders = readConnectedProvidersCache()
|
const connectedProviders = connectedProvidersCache.readConnectedProvidersCache()
|
||||||
if (connectedProviders) {
|
if (connectedProviders) {
|
||||||
const connectedSet = new Set(connectedProviders)
|
const connectedSet = new Set(connectedProviders)
|
||||||
for (const entry of fallbackChain) {
|
for (const entry of fallbackChain) {
|
||||||
@ -332,7 +333,7 @@ export function isAnyProviderConnected(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const connectedProviders = readConnectedProvidersCache()
|
const connectedProviders = connectedProvidersCache.readConnectedProvidersCache()
|
||||||
if (connectedProviders) {
|
if (connectedProviders) {
|
||||||
const connectedSet = new Set(connectedProviders)
|
const connectedSet = new Set(connectedProviders)
|
||||||
for (const provider of providers) {
|
for (const provider of providers) {
|
||||||
@ -349,7 +350,7 @@ export function isAnyProviderConnected(
|
|||||||
export function __resetModelCache(): void {}
|
export function __resetModelCache(): void {}
|
||||||
|
|
||||||
export function isModelCacheAvailable(): boolean {
|
export function isModelCacheAvailable(): boolean {
|
||||||
if (hasProviderModelsCache()) {
|
if (connectedProvidersCache.hasProviderModelsCache()) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
const cacheFile = join(getOpenCodeCacheDir(), "models.json")
|
const cacheFile = join(getOpenCodeCacheDir(), "models.json")
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
import { log } from "./logger"
|
import { log } from "./logger"
|
||||||
import { readConnectedProvidersCache } from "./connected-providers-cache"
|
import * as connectedProvidersCache from "./connected-providers-cache"
|
||||||
import { fuzzyMatchModel } from "./model-availability"
|
import { fuzzyMatchModel } from "./model-availability"
|
||||||
import type { FallbackEntry } from "./model-requirements"
|
import type { FallbackEntry } from "./model-requirements"
|
||||||
|
|
||||||
@ -11,6 +11,7 @@ export type ModelResolutionRequest = {
|
|||||||
}
|
}
|
||||||
constraints: {
|
constraints: {
|
||||||
availableModels: Set<string>
|
availableModels: Set<string>
|
||||||
|
connectedProviders?: string[] | null
|
||||||
}
|
}
|
||||||
policy?: {
|
policy?: {
|
||||||
fallbackChain?: FallbackEntry[]
|
fallbackChain?: FallbackEntry[]
|
||||||
@ -73,7 +74,7 @@ export function resolveModelPipeline(
|
|||||||
return { model: match, provenance: "category-default", attempted }
|
return { model: match, provenance: "category-default", attempted }
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
const connectedProviders = readConnectedProvidersCache()
|
const connectedProviders = constraints.connectedProviders ?? connectedProvidersCache.readConnectedProvidersCache()
|
||||||
if (connectedProviders === null) {
|
if (connectedProviders === null) {
|
||||||
log("Model resolved via category default (no cache, first run)", {
|
log("Model resolved via category default (no cache, first run)", {
|
||||||
model: normalizedCategoryDefault,
|
model: normalizedCategoryDefault,
|
||||||
@ -98,7 +99,7 @@ export function resolveModelPipeline(
|
|||||||
|
|
||||||
if (fallbackChain && fallbackChain.length > 0) {
|
if (fallbackChain && fallbackChain.length > 0) {
|
||||||
if (availableModels.size === 0) {
|
if (availableModels.size === 0) {
|
||||||
const connectedProviders = readConnectedProvidersCache()
|
const connectedProviders = constraints.connectedProviders ?? connectedProvidersCache.readConnectedProvidersCache()
|
||||||
const connectedSet = connectedProviders ? new Set(connectedProviders) : null
|
const connectedSet = connectedProviders ? new Set(connectedProviders) : null
|
||||||
|
|
||||||
if (connectedSet === null) {
|
if (connectedSet === null) {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user