Merge pull request #4031 from PeterPonyu/feat/config-disabled-providers
feat(config): add disabled_providers schema + helper
This commit is contained in:
@@ -44,6 +44,14 @@ export const OhMyOpenCodeConfigSchema = z.object({
|
||||
disabled_commands: z.array(BuiltinCommandNameSchema).optional(),
|
||||
/** Disable specific tools by name (e.g., ["todowrite", "todoread"]) */
|
||||
disabled_tools: z.array(z.string()).optional(),
|
||||
/**
|
||||
* Provider prefixes to exclude from every agent/category fallback chain at
|
||||
* load time. Each entry matches the first slash-separated segment of a model
|
||||
* id (e.g., "github-copilot" matches "github-copilot/gpt-5.5"). If a primary
|
||||
* `model` references a disabled provider, it is replaced with the first
|
||||
* allowed entry from the same chain.
|
||||
*/
|
||||
disabled_providers: z.array(z.string()).optional(),
|
||||
mcp_env_allowlist: z.array(z.string()).optional(),
|
||||
/** Enable hashline_edit tool/hook integrations (default: false) */
|
||||
hashline_edit: z.boolean().optional(),
|
||||
|
||||
@@ -205,6 +205,41 @@ describe("mergeConfigs", () => {
|
||||
expect(result.disabled_tools).toContain("look_at");
|
||||
expect(result.disabled_tools?.length).toBe(3);
|
||||
});
|
||||
|
||||
it("should union disabled_providers from base and override without duplicates", () => {
|
||||
const base = createConfig({
|
||||
disabled_providers: ["github-copilot", "vercel"],
|
||||
});
|
||||
|
||||
const override = createConfig({
|
||||
disabled_providers: ["vercel", "anthropic"],
|
||||
});
|
||||
|
||||
const result = mergeConfigs(base, override);
|
||||
|
||||
expect(result.disabled_providers).toContain("github-copilot");
|
||||
expect(result.disabled_providers).toContain("vercel");
|
||||
expect(result.disabled_providers).toContain("anthropic");
|
||||
expect(result.disabled_providers?.length).toBe(3);
|
||||
});
|
||||
|
||||
it("should dedupe disabled_providers case-insensitively, preserving first-seen casing", () => {
|
||||
const base = createConfig({
|
||||
disabled_providers: ["GitHub-Copilot", "vercel"],
|
||||
});
|
||||
|
||||
const override = createConfig({
|
||||
disabled_providers: ["github-copilot", "VERCEL", "anthropic"],
|
||||
});
|
||||
|
||||
const result = mergeConfigs(base, override);
|
||||
|
||||
expect(result.disabled_providers).toEqual([
|
||||
"GitHub-Copilot",
|
||||
"vercel",
|
||||
"anthropic",
|
||||
]);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1082,4 +1117,102 @@ describe("loadPluginConfig", () => {
|
||||
expect(existsSync(ancestorCanonicalPath)).toBe(true)
|
||||
expect(config.agents?.oracle?.model).toBe("ancestor-legacy/model")
|
||||
})
|
||||
|
||||
it("applies disabled_providers to agent and category chains at load time", async () => {
|
||||
// given - a project config that disables github-copilot + vercel and has
|
||||
// agents/categories whose primary or fallback chains reference them.
|
||||
const { userConfigDir, projectDir, projectConfigDir } =
|
||||
createLoadPluginConfigTestContext("omo-plugin-config-disabled-providers-")
|
||||
|
||||
writeFileSync(
|
||||
join(projectConfigDir, "oh-my-openagent.jsonc"),
|
||||
JSON.stringify({
|
||||
disabled_providers: ["github-copilot", "vercel"],
|
||||
agents: {
|
||||
hephaestus: {
|
||||
model: "github-copilot/gpt-5.5",
|
||||
fallback_models: [
|
||||
"github-copilot/gpt-5.4-mini",
|
||||
"openai/gpt-5.5",
|
||||
"vercel/openai/gpt-5.5",
|
||||
"opencode/gpt-5.5",
|
||||
],
|
||||
},
|
||||
oracle: {
|
||||
model: "anthropic/claude-opus-4-7",
|
||||
fallback_models: [
|
||||
"github-copilot/claude-sonnet-4.6",
|
||||
"opencode-go/glm-5.1",
|
||||
],
|
||||
},
|
||||
},
|
||||
categories: {
|
||||
deep: {
|
||||
model: "github-copilot/gpt-5.5",
|
||||
fallback_models: ["openai/gpt-5.5", "github-copilot/claude-sonnet-4.6"],
|
||||
},
|
||||
},
|
||||
}),
|
||||
)
|
||||
|
||||
process.env.OPENCODE_CONFIG_DIR = userConfigDir
|
||||
|
||||
// when
|
||||
const { loadPluginConfig } = await importFreshPluginConfigModule()
|
||||
const config = loadPluginConfig(projectDir, {})
|
||||
|
||||
// then - primary models that referenced a disabled provider are
|
||||
// substituted from the first allowed chain entry, and every disabled
|
||||
// provider has been filtered out of every chain.
|
||||
const hephaestus = config.agents?.hephaestus as
|
||||
| { model?: string; fallback_models?: Array<string | { model: string }> }
|
||||
| undefined
|
||||
expect(hephaestus?.model).toBe("openai/gpt-5.5")
|
||||
expect(hephaestus?.fallback_models).toEqual([
|
||||
"openai/gpt-5.5",
|
||||
"opencode/gpt-5.5",
|
||||
])
|
||||
|
||||
const oracle = config.agents?.oracle as
|
||||
| { model?: string; fallback_models?: Array<string | { model: string }> }
|
||||
| undefined
|
||||
// Primary is allowed -> untouched. Chain has the disabled entry removed.
|
||||
expect(oracle?.model).toBe("anthropic/claude-opus-4-7")
|
||||
expect(oracle?.fallback_models).toEqual(["opencode-go/glm-5.1"])
|
||||
|
||||
const deep = config.categories?.deep as
|
||||
| { model?: string; fallback_models?: Array<string | { model: string }> }
|
||||
| undefined
|
||||
expect(deep?.model).toBe("openai/gpt-5.5")
|
||||
expect(deep?.fallback_models).toEqual(["openai/gpt-5.5"])
|
||||
|
||||
// And the disabled_providers list itself survives merging unchanged.
|
||||
expect(config.disabled_providers).toEqual(["github-copilot", "vercel"])
|
||||
})
|
||||
|
||||
it("is a no-op for chains when disabled_providers is absent", async () => {
|
||||
const { userConfigDir, projectDir, projectConfigDir } =
|
||||
createLoadPluginConfigTestContext("omo-plugin-config-disabled-providers-noop-")
|
||||
|
||||
writeFileSync(
|
||||
join(projectConfigDir, "oh-my-openagent.jsonc"),
|
||||
JSON.stringify({
|
||||
agents: {
|
||||
hephaestus: {
|
||||
model: "github-copilot/gpt-5.5",
|
||||
fallback_models: ["openai/gpt-5.5"],
|
||||
},
|
||||
},
|
||||
}),
|
||||
)
|
||||
|
||||
process.env.OPENCODE_CONFIG_DIR = userConfigDir
|
||||
|
||||
const { loadPluginConfig } = await importFreshPluginConfigModule()
|
||||
const config = loadPluginConfig(projectDir, {})
|
||||
|
||||
const hephaestus = config.agents?.hephaestus as { model?: string; fallback_models?: unknown }
|
||||
expect(hephaestus?.model).toBe("github-copilot/gpt-5.5")
|
||||
expect(hephaestus?.fallback_models).toEqual(["openai/gpt-5.5"])
|
||||
})
|
||||
})
|
||||
|
||||
@@ -17,6 +17,7 @@ import {
|
||||
import { migrateLegacyConfigFile } from "./shared/migrate-legacy-config-file";
|
||||
import { CONFIG_BASENAME, LEGACY_CONFIG_BASENAME } from "./shared/plugin-identity";
|
||||
import { validateAgentOrder } from "./shared/agent-ordering";
|
||||
import { applyDisabledProviders } from "./shared/disabled-providers";
|
||||
|
||||
const CONTROL_CHARACTERS_REGEX = /[\u0000-\u001F\u007F-\u009F\u202A-\u202E\u2066-\u2069]/g;
|
||||
const MAX_AGENT_ORDER_WARNING_VALUES = 10;
|
||||
@@ -116,6 +117,7 @@ const PARTIAL_STRING_ARRAY_KEYS = new Set([
|
||||
"disabled_hooks",
|
||||
"disabled_commands",
|
||||
"disabled_tools",
|
||||
"disabled_providers",
|
||||
"mcp_env_allowlist",
|
||||
"agent_definitions",
|
||||
]);
|
||||
@@ -215,6 +217,18 @@ export function loadConfigFromPath(
|
||||
return null;
|
||||
}
|
||||
|
||||
function dedupeCaseInsensitive(values: readonly string[]): string[] {
|
||||
const seen = new Set<string>()
|
||||
const result: string[] = []
|
||||
for (const value of values) {
|
||||
const key = value.toLowerCase()
|
||||
if (seen.has(key)) continue
|
||||
seen.add(key)
|
||||
result.push(value)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
export function mergeConfigs(
|
||||
base: OhMyOpenCodeConfig,
|
||||
override: OhMyOpenCodeConfig
|
||||
@@ -267,6 +281,10 @@ export function mergeConfigs(
|
||||
...(override.disabled_tools ?? []),
|
||||
]),
|
||||
],
|
||||
disabled_providers: dedupeCaseInsensitive([
|
||||
...(base.disabled_providers ?? []),
|
||||
...(override.disabled_providers ?? []),
|
||||
]),
|
||||
mcp_env_allowlist: [
|
||||
...new Set([
|
||||
...(base.mcp_env_allowlist ?? []),
|
||||
@@ -418,12 +436,15 @@ export function loadPluginConfig(
|
||||
mcp_env_allowlist: userMcpEnvAllowlist,
|
||||
};
|
||||
|
||||
applyDisabledProviders(config);
|
||||
|
||||
log("Final merged config", {
|
||||
agents: config.agents,
|
||||
team_mode: config.team_mode,
|
||||
disabled_agents: config.disabled_agents,
|
||||
disabled_mcps: config.disabled_mcps,
|
||||
disabled_hooks: config.disabled_hooks,
|
||||
disabled_providers: config.disabled_providers,
|
||||
claude_code: config.claude_code,
|
||||
});
|
||||
return config;
|
||||
|
||||
@@ -0,0 +1,192 @@
|
||||
import { beforeEach, describe, expect, mock, test } from "bun:test"
|
||||
|
||||
mock.module("./logger", () => ({ log: (..._args: unknown[]) => {} }))
|
||||
|
||||
import {
|
||||
applyDisabledProviders,
|
||||
filterDisabledProviderModels,
|
||||
getModelProvider,
|
||||
isProviderDisabled,
|
||||
} from "./disabled-providers"
|
||||
import { clearConfigLoadErrors, getConfigLoadErrors } from "./config-errors"
|
||||
import type { OhMyOpenCodeConfig } from "../config"
|
||||
|
||||
beforeEach(() => {
|
||||
clearConfigLoadErrors()
|
||||
})
|
||||
|
||||
describe("getModelProvider", () => {
|
||||
test("returns the first segment before the slash", () => {
|
||||
expect(getModelProvider("github-copilot/gpt-5.5")).toBe("github-copilot")
|
||||
expect(getModelProvider("anthropic/claude-opus-4-7")).toBe("anthropic")
|
||||
expect(getModelProvider("vercel/openai/gpt-5.5")).toBe("vercel")
|
||||
})
|
||||
|
||||
test("returns undefined when the string has no provider prefix", () => {
|
||||
expect(getModelProvider("gpt-5.5")).toBeUndefined()
|
||||
expect(getModelProvider("")).toBeUndefined()
|
||||
expect(getModelProvider("/foo")).toBeUndefined()
|
||||
})
|
||||
})
|
||||
|
||||
describe("isProviderDisabled", () => {
|
||||
test("returns true only when the model's provider exactly matches a disabled entry", () => {
|
||||
expect(isProviderDisabled("github-copilot/foo", ["github-copilot"])).toBe(true)
|
||||
expect(isProviderDisabled("github-copilot-extra/foo", ["github-copilot"])).toBe(false)
|
||||
expect(isProviderDisabled("opencode-go/glm-5.1", ["github-copilot", "vercel"])).toBe(false)
|
||||
})
|
||||
|
||||
test("short-circuits when the disabled list is empty", () => {
|
||||
expect(isProviderDisabled("github-copilot/foo", [])).toBe(false)
|
||||
})
|
||||
|
||||
test("handles undefined model", () => {
|
||||
expect(isProviderDisabled(undefined, ["github-copilot"])).toBe(false)
|
||||
})
|
||||
|
||||
test("is case-insensitive: matches regardless of casing on either side", () => {
|
||||
expect(isProviderDisabled("GitHub-Copilot/gpt-5.5", ["github-copilot"])).toBe(true)
|
||||
expect(isProviderDisabled("github-copilot/gpt-5.5", ["GitHub-Copilot"])).toBe(true)
|
||||
expect(isProviderDisabled("OPENAI/gpt-5.5", ["openai"])).toBe(true)
|
||||
expect(isProviderDisabled("openai/gpt-5.5", ["VERCEL"])).toBe(false)
|
||||
})
|
||||
})
|
||||
|
||||
describe("filterDisabledProviderModels", () => {
|
||||
test("preserves entries whose provider is not disabled, dropping the rest", () => {
|
||||
const input = [
|
||||
"github-copilot/gpt-5.5",
|
||||
"openai/gpt-5.5",
|
||||
{ model: "vercel/openai/gpt-5.5", variant: "medium" },
|
||||
{ model: "opencode/gpt-5.5", variant: "medium" },
|
||||
]
|
||||
const result = filterDisabledProviderModels(input, ["github-copilot", "vercel"])
|
||||
expect(result).toEqual([
|
||||
"openai/gpt-5.5",
|
||||
{ model: "opencode/gpt-5.5", variant: "medium" },
|
||||
])
|
||||
})
|
||||
|
||||
test("returns a fresh copy when the disabled list is empty", () => {
|
||||
const input = ["openai/gpt-5.5"]
|
||||
const result = filterDisabledProviderModels(input, [])
|
||||
expect(result).toEqual(input)
|
||||
expect(result).not.toBe(input)
|
||||
})
|
||||
})
|
||||
|
||||
describe("applyDisabledProviders", () => {
|
||||
test("no-op when disabled_providers is unset or empty", () => {
|
||||
const config = {
|
||||
agents: {
|
||||
hephaestus: {
|
||||
model: "github-copilot/gpt-5.5",
|
||||
fallback_models: ["github-copilot/gpt-5.4-mini", "openai/gpt-5.5"],
|
||||
},
|
||||
},
|
||||
} as unknown as OhMyOpenCodeConfig
|
||||
|
||||
applyDisabledProviders(config)
|
||||
const agents = config.agents as Record<string, { model?: string; fallback_models?: unknown }>
|
||||
expect(agents.hephaestus.model).toBe("github-copilot/gpt-5.5")
|
||||
expect(agents.hephaestus.fallback_models).toEqual(["github-copilot/gpt-5.4-mini", "openai/gpt-5.5"])
|
||||
})
|
||||
|
||||
test("filters fallback chain and substitutes primary from the first allowed entry", () => {
|
||||
const config = {
|
||||
disabled_providers: ["github-copilot", "vercel"],
|
||||
agents: {
|
||||
hephaestus: {
|
||||
model: "github-copilot/gpt-5.5",
|
||||
fallback_models: [
|
||||
"github-copilot/gpt-5.4-mini",
|
||||
{ model: "openai/gpt-5.5", variant: "medium" },
|
||||
{ model: "vercel/openai/gpt-5.5", variant: "medium" },
|
||||
"opencode/gpt-5.5",
|
||||
],
|
||||
},
|
||||
sisyphus: {
|
||||
model: "anthropic/claude-opus-4-7",
|
||||
fallback_models: [{ model: "github-copilot/claude-sonnet-4.6" }, "opencode-go/glm-5.1"],
|
||||
},
|
||||
},
|
||||
} as unknown as OhMyOpenCodeConfig
|
||||
|
||||
applyDisabledProviders(config)
|
||||
|
||||
const agents = config.agents as Record<string, { model?: string; fallback_models?: unknown }>
|
||||
expect(agents.hephaestus.model).toBe("openai/gpt-5.5")
|
||||
expect(agents.hephaestus.fallback_models).toEqual([
|
||||
{ model: "openai/gpt-5.5", variant: "medium" },
|
||||
"opencode/gpt-5.5",
|
||||
])
|
||||
|
||||
expect(agents.sisyphus.model).toBe("anthropic/claude-opus-4-7")
|
||||
expect(agents.sisyphus.fallback_models).toEqual(["opencode-go/glm-5.1"])
|
||||
})
|
||||
|
||||
test("leaves primary unchanged but records a config-load error when every chain entry is also disabled", () => {
|
||||
const config = {
|
||||
disabled_providers: ["github-copilot"],
|
||||
agents: {
|
||||
oracle: {
|
||||
model: "github-copilot/gpt-5.5",
|
||||
fallback_models: ["github-copilot/gpt-5.4-mini", { model: "github-copilot/gemini-3" }],
|
||||
},
|
||||
},
|
||||
} as unknown as OhMyOpenCodeConfig
|
||||
|
||||
applyDisabledProviders(config)
|
||||
|
||||
const agents = config.agents as Record<string, { model?: string; fallback_models?: unknown }>
|
||||
expect(agents.oracle.model).toBe("github-copilot/gpt-5.5")
|
||||
// Empty chain is normalized to undefined so "no chain declared" and
|
||||
// "empty chain declared" stay semantically distinct downstream.
|
||||
expect(agents.oracle.fallback_models).toBeUndefined()
|
||||
|
||||
const errors = getConfigLoadErrors()
|
||||
expect(errors.length).toBe(1)
|
||||
expect(errors[0]!.path).toBe("disabled_providers:agents.oracle")
|
||||
expect(errors[0]!.error).toContain("github-copilot/gpt-5.5")
|
||||
expect(errors[0]!.error).toContain("disabled provider")
|
||||
})
|
||||
|
||||
test("treats provider names case-insensitively across primary and chain entries", () => {
|
||||
const config = {
|
||||
disabled_providers: ["GitHub-Copilot"],
|
||||
agents: {
|
||||
hephaestus: {
|
||||
model: "github-copilot/gpt-5.5",
|
||||
fallback_models: [
|
||||
"GITHUB-COPILOT/gpt-5.4-mini",
|
||||
"openai/gpt-5.5",
|
||||
],
|
||||
},
|
||||
},
|
||||
} as unknown as OhMyOpenCodeConfig
|
||||
|
||||
applyDisabledProviders(config)
|
||||
|
||||
const agents = config.agents as Record<string, { model?: string; fallback_models?: unknown }>
|
||||
expect(agents.hephaestus.model).toBe("openai/gpt-5.5")
|
||||
expect(agents.hephaestus.fallback_models).toEqual(["openai/gpt-5.5"])
|
||||
})
|
||||
|
||||
test("applies the same rules to categories", () => {
|
||||
const config = {
|
||||
disabled_providers: ["github-copilot"],
|
||||
categories: {
|
||||
deep: {
|
||||
model: "github-copilot/gpt-5.5",
|
||||
fallback_models: ["openai/gpt-5.5", "github-copilot/claude-sonnet-4.6"],
|
||||
},
|
||||
},
|
||||
} as unknown as OhMyOpenCodeConfig
|
||||
|
||||
applyDisabledProviders(config)
|
||||
|
||||
const cats = config.categories as Record<string, { model?: string; fallback_models?: unknown }>
|
||||
expect(cats.deep.model).toBe("openai/gpt-5.5")
|
||||
expect(cats.deep.fallback_models).toEqual(["openai/gpt-5.5"])
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,123 @@
|
||||
import type { OhMyOpenCodeConfig } from "../config"
|
||||
import type { FallbackModelObject } from "../config/schema/fallback-models"
|
||||
import { addConfigLoadError } from "./config-errors"
|
||||
import { log } from "./logger"
|
||||
import { normalizeFallbackModels } from "./model-resolver"
|
||||
|
||||
const HOOK_NAME = "disabled-providers"
|
||||
|
||||
export function getModelProvider(model: string): string | undefined {
|
||||
const slash = model.indexOf("/")
|
||||
if (slash <= 0) return undefined
|
||||
return model.slice(0, slash)
|
||||
}
|
||||
|
||||
export function isProviderDisabled(
|
||||
model: string | undefined,
|
||||
disabled: readonly string[],
|
||||
): boolean {
|
||||
if (!model || disabled.length === 0) return false
|
||||
const provider = getModelProvider(model)
|
||||
if (provider === undefined) return false
|
||||
const providerLower = provider.toLowerCase()
|
||||
return disabled.some((entry) => entry.toLowerCase() === providerLower)
|
||||
}
|
||||
|
||||
export function filterDisabledProviderModels<T extends string | FallbackModelObject>(
|
||||
models: readonly T[],
|
||||
disabled: readonly string[],
|
||||
): T[] {
|
||||
if (disabled.length === 0) return [...models]
|
||||
return models.filter((entry) => {
|
||||
const model = typeof entry === "string" ? entry : entry.model
|
||||
return !isProviderDisabled(model, disabled)
|
||||
})
|
||||
}
|
||||
|
||||
type ModelHolder = {
|
||||
model?: string | unknown
|
||||
fallback_models?: string | (string | FallbackModelObject)[]
|
||||
}
|
||||
|
||||
function findFirstAllowedReplacement(
|
||||
chain: (string | FallbackModelObject)[] | undefined,
|
||||
disabled: readonly string[],
|
||||
): string | undefined {
|
||||
if (!chain) return undefined
|
||||
for (const entry of chain) {
|
||||
const model = typeof entry === "string" ? entry : entry.model
|
||||
if (!isProviderDisabled(model, disabled)) return model
|
||||
}
|
||||
return undefined
|
||||
}
|
||||
|
||||
function applyToHolder(label: string, holder: ModelHolder, disabled: readonly string[]): void {
|
||||
const normalizedChain = normalizeFallbackModels(holder.fallback_models)
|
||||
if (normalizedChain) {
|
||||
const filteredChain = filterDisabledProviderModels(normalizedChain, disabled)
|
||||
if (filteredChain.length !== normalizedChain.length) {
|
||||
log(`[${HOOK_NAME}] Filtered disabled-provider entries from fallback chain`, {
|
||||
label,
|
||||
removed: normalizedChain.length - filteredChain.length,
|
||||
remaining: filteredChain.length,
|
||||
})
|
||||
}
|
||||
// Normalize empty chain to undefined so downstream "no chain declared"
|
||||
// and "empty chain declared" stay semantically distinct.
|
||||
holder.fallback_models = filteredChain.length === 0 ? undefined : filteredChain
|
||||
}
|
||||
|
||||
if (typeof holder.model === "string" && isProviderDisabled(holder.model, disabled)) {
|
||||
const replacement = findFirstAllowedReplacement(
|
||||
normalizeFallbackModels(holder.fallback_models),
|
||||
disabled,
|
||||
)
|
||||
if (replacement) {
|
||||
log(`[${HOOK_NAME}] Substituted primary model from fallback chain`, {
|
||||
label,
|
||||
from: holder.model,
|
||||
to: replacement,
|
||||
})
|
||||
holder.model = replacement
|
||||
} else {
|
||||
// Surface to the user-facing config-error channel so this does not
|
||||
// hide as a runtime ProviderModelNotFoundError on first delegation.
|
||||
const message =
|
||||
`${label} primary model "${holder.model}" uses a disabled provider and no allowed entry is available in fallback_models. ` +
|
||||
`Either remove the provider from disabled_providers or add an allowed entry to fallback_models.`
|
||||
addConfigLoadError({ path: `disabled_providers:${label}`, error: message })
|
||||
log(`[${HOOK_NAME}] ${message}`, { label, primary: holder.model })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Filters `disabled_providers`-listed entries out of every agent/category
|
||||
* fallback chain and substitutes any primary `model` referencing a disabled
|
||||
* provider with the first allowed entry from the same chain.
|
||||
*
|
||||
* Returns the same config reference (mutated in place). Safe to call when
|
||||
* `disabled_providers` is unset or empty - it becomes a no-op.
|
||||
*/
|
||||
export function applyDisabledProviders(config: OhMyOpenCodeConfig): OhMyOpenCodeConfig {
|
||||
const disabled = config.disabled_providers ?? []
|
||||
if (disabled.length === 0) return config
|
||||
|
||||
if (config.agents) {
|
||||
for (const [name, agentConfig] of Object.entries(config.agents)) {
|
||||
if (agentConfig && typeof agentConfig === "object") {
|
||||
applyToHolder(`agents.${name}`, agentConfig as ModelHolder, disabled)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (config.categories) {
|
||||
for (const [name, categoryConfig] of Object.entries(config.categories)) {
|
||||
if (categoryConfig && typeof categoryConfig === "object") {
|
||||
applyToHolder(`categories.${name}`, categoryConfig as ModelHolder, disabled)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return config
|
||||
}
|
||||
Reference in New Issue
Block a user