From 87d6b2b519cf2c5f0837ff4a64ca39060f334c59 Mon Sep 17 00:00:00 2001 From: YeonGyu-Kim Date: Thu, 26 Feb 2026 12:13:31 +0900 Subject: [PATCH] feat(agents): simplify GPT detection to name-based check, add hephaestus providers (venice uses gpt-5.3-codex) Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-opencode) Co-authored-by: Sisyphus --- src/agents/types.test.ts | 20 ++++++++++++++------ src/agents/types.ts | 7 +------ src/agents/utils.test.ts | 8 ++++---- src/shared/model-requirements.test.ts | 6 +++--- src/shared/model-requirements.ts | 5 +++-- 5 files changed, 25 insertions(+), 21 deletions(-) diff --git a/src/agents/types.test.ts b/src/agents/types.test.ts index 614991867..ad39fb538 100644 --- a/src/agents/types.test.ts +++ b/src/agents/types.test.ts @@ -2,11 +2,17 @@ import { describe, test, expect } from "bun:test"; import { isGptModel, isGeminiModel } from "./types"; describe("isGptModel", () => { - test("standard openai provider models", () => { + test("standard openai provider gpt models", () => { expect(isGptModel("openai/gpt-5.2")).toBe(true); expect(isGptModel("openai/gpt-4o")).toBe(true); - expect(isGptModel("openai/o1")).toBe(true); - expect(isGptModel("openai/o3-mini")).toBe(true); + }); + + test("o-series models are not gpt by name", () => { + expect(isGptModel("openai/o1")).toBe(false); + expect(isGptModel("openai/o3-mini")).toBe(false); + expect(isGptModel("litellm/o1")).toBe(false); + expect(isGptModel("litellm/o3-mini")).toBe(false); + expect(isGptModel("litellm/o4-mini")).toBe(false); }); test("github copilot gpt models", () => { @@ -17,9 +23,6 @@ describe("isGptModel", () => { test("litellm proxied gpt models", () => { expect(isGptModel("litellm/gpt-5.2")).toBe(true); expect(isGptModel("litellm/gpt-4o")).toBe(true); - expect(isGptModel("litellm/o1")).toBe(true); - expect(isGptModel("litellm/o3-mini")).toBe(true); - expect(isGptModel("litellm/o4-mini")).toBe(true); }); test("other proxied gpt models", () => { @@ -27,6 +30,11 @@ describe("isGptModel", () => { expect(isGptModel("custom-provider/gpt-5.2")).toBe(true); }); + test("venice provider gpt models", () => { + expect(isGptModel("venice/gpt-5.2")).toBe(true); + expect(isGptModel("venice/gpt-4o")).toBe(true); + }); + test("gpt4 prefix without hyphen (legacy naming)", () => { expect(isGptModel("litellm/gpt4o")).toBe(true); expect(isGptModel("ollama/gpt4")).toBe(true); diff --git a/src/agents/types.ts b/src/agents/types.ts index 2d4f6c0cb..bdb60007a 100644 --- a/src/agents/types.ts +++ b/src/agents/types.ts @@ -70,14 +70,9 @@ function extractModelName(model: string): string { return model.includes("/") ? model.split("/").pop() ?? model : model } -const GPT_MODEL_PREFIXES = ["gpt-", "gpt4", "o1", "o3", "o4"] - export function isGptModel(model: string): boolean { - if (model.startsWith("openai/") || model.startsWith("github-copilot/gpt-")) - return true - const modelName = extractModelName(model).toLowerCase() - return GPT_MODEL_PREFIXES.some((prefix) => modelName.startsWith(prefix)) + return modelName.includes("gpt") } const GEMINI_PROVIDERS = ["google/", "google-vertex/"] diff --git a/src/agents/utils.test.ts b/src/agents/utils.test.ts index fdee0a606..fc15d9d48 100644 --- a/src/agents/utils.test.ts +++ b/src/agents/utils.test.ts @@ -589,8 +589,8 @@ describe("createBuiltinAgents with requiresProvider gating (hephaestus)", () => } }) - test("hephaestus is NOT created when only github-copilot is connected (gpt-5.3-codex unavailable via github-copilot)", async () => { - // #given - github-copilot provider has models available, but no cache + test("hephaestus IS created when github-copilot is connected with a GPT model", async () => { + // #given - github-copilot provider has gpt-5.3-codex available const fetchSpy = spyOn(shared, "fetchAvailableModels").mockResolvedValue( new Set(["github-copilot/gpt-5.3-codex"]) ) @@ -600,8 +600,8 @@ describe("createBuiltinAgents with requiresProvider gating (hephaestus)", () => // #when const agents = await createBuiltinAgents([], {}, undefined, TEST_DEFAULT_MODEL, undefined, undefined, [], {}) - // #then - hephaestus requires openai/opencode, github-copilot alone is insufficient - expect(agents.hephaestus).toBeUndefined() + // #then - github-copilot is now a valid provider for hephaestus + expect(agents.hephaestus).toBeDefined() } finally { fetchSpy.mockRestore() cacheSpy.mockRestore() diff --git a/src/shared/model-requirements.test.ts b/src/shared/model-requirements.test.ts index 2991775eb..112b2b774 100644 --- a/src/shared/model-requirements.test.ts +++ b/src/shared/model-requirements.test.ts @@ -168,14 +168,14 @@ describe("AGENT_MODEL_REQUIREMENTS", () => { expect(primary.providers[0]).toBe("opencode") }) - test("hephaestus requires openai/opencode provider (not github-copilot since gpt-5.3-codex unavailable there)", () => { + test("hephaestus supports openai, github-copilot, venice, and opencode providers", () => { // #given - hephaestus agent requirement const hephaestus = AGENT_MODEL_REQUIREMENTS["hephaestus"] // #when - accessing hephaestus requirement - // #then - requiresProvider is set to openai and opencode only (github-copilot removed) + // #then - requiresProvider includes openai, github-copilot, venice, and opencode expect(hephaestus).toBeDefined() - expect(hephaestus.requiresProvider).toEqual(["openai", "opencode"]) + expect(hephaestus.requiresProvider).toEqual(["openai", "github-copilot", "venice", "opencode"]) expect(hephaestus.requiresModel).toBeUndefined() }) diff --git a/src/shared/model-requirements.ts b/src/shared/model-requirements.ts index 9a795ba76..42a860b96 100644 --- a/src/shared/model-requirements.ts +++ b/src/shared/model-requirements.ts @@ -24,9 +24,10 @@ export const AGENT_MODEL_REQUIREMENTS: Record = { }, hephaestus: { fallbackChain: [ - { providers: ["openai", "opencode"], model: "gpt-5.3-codex", variant: "medium" }, + { providers: ["openai", "venice", "opencode"], model: "gpt-5.3-codex", variant: "medium" }, + { providers: ["github-copilot"], model: "gpt-5.2", variant: "medium" }, ], - requiresProvider: ["openai", "opencode"], + requiresProvider: ["openai", "github-copilot", "venice", "opencode"], }, oracle: { fallbackChain: [