refactor(athena): consolidate parseModelString to single source of truth
This commit is contained in:
parent
9748688983
commit
01331af10c
@ -1,5 +1,4 @@
|
|||||||
export * from "./types"
|
export * from "./types"
|
||||||
export * from "./agent"
|
export * from "./agent"
|
||||||
export * from "./council-member-agent"
|
export * from "./council-member-agent"
|
||||||
export * from "./model-parser"
|
|
||||||
|
|
||||||
|
|||||||
@ -1,23 +0,0 @@
|
|||||||
export interface ParsedModel {
|
|
||||||
providerID: string
|
|
||||||
modelID: string
|
|
||||||
}
|
|
||||||
|
|
||||||
export function parseModelString(model: string): ParsedModel | null {
|
|
||||||
if (!model) {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
const slashIndex = model.indexOf("/")
|
|
||||||
if (slashIndex <= 0) {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
const providerID = model.substring(0, slashIndex)
|
|
||||||
const modelID = model.substring(slashIndex + 1)
|
|
||||||
if (!modelID) {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
return { providerID, modelID }
|
|
||||||
}
|
|
||||||
@ -1,7 +1,7 @@
|
|||||||
import type { AgentConfig } from "@opencode-ai/sdk"
|
import type { AgentConfig } from "@opencode-ai/sdk"
|
||||||
import type { CouncilConfig, CouncilMemberConfig } from "../athena/types"
|
import type { CouncilConfig, CouncilMemberConfig } from "../athena/types"
|
||||||
import { createCouncilMemberAgent } from "../athena/council-member-agent"
|
import { createCouncilMemberAgent } from "../athena/council-member-agent"
|
||||||
import { parseModelString } from "../athena/model-parser"
|
import { parseModelString } from "../../tools/delegate-task/model-string-parser"
|
||||||
import { log } from "../../shared/logger"
|
import { log } from "../../shared/logger"
|
||||||
|
|
||||||
/** Prefix used for all dynamically-registered council member agent keys. */
|
/** Prefix used for all dynamically-registered council member agent keys. */
|
||||||
|
|||||||
@ -1,12 +1,12 @@
|
|||||||
import { z } from "zod"
|
import { z } from "zod"
|
||||||
import { parseModelString } from "../../agents/athena/model-parser"
|
import { parseModelString } from "../../tools/delegate-task/model-string-parser"
|
||||||
|
|
||||||
/** Validates model string format: "provider/model-id" (e.g., "openai/gpt-5.3-codex"). */
|
/** Validates model string format: "provider/model-id" (e.g., "openai/gpt-5.3-codex"). */
|
||||||
const ModelStringSchema = z
|
const ModelStringSchema = z
|
||||||
.string()
|
.string()
|
||||||
.min(1)
|
.min(1)
|
||||||
.refine(
|
.refine(
|
||||||
(model) => parseModelString(model) !== null,
|
(model) => parseModelString(model) !== undefined,
|
||||||
{ message: 'Model must be in "provider/model-id" format (e.g., "openai/gpt-5.3-codex")' }
|
{ message: 'Model must be in "provider/model-id" format (e.g., "openai/gpt-5.3-codex")' }
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
import { describe, expect, test } from "bun:test"
|
import { describe, expect, test } from "bun:test"
|
||||||
import { parseModelString } from "./model-parser"
|
import { parseModelString } from "./model-string-parser"
|
||||||
|
|
||||||
describe("parseModelString", () => {
|
describe("parseModelString", () => {
|
||||||
describe("valid model strings", () => {
|
describe("valid model strings", () => {
|
||||||
@ -52,22 +52,47 @@ describe("parseModelString", () => {
|
|||||||
describe("invalid model strings", () => {
|
describe("invalid model strings", () => {
|
||||||
//#given malformed or empty model strings
|
//#given malformed or empty model strings
|
||||||
//#when parsing model strings
|
//#when parsing model strings
|
||||||
//#then it returns null
|
//#then it returns undefined
|
||||||
|
|
||||||
test("returns null for empty string", () => {
|
test("returns undefined for empty string", () => {
|
||||||
expect(parseModelString("")).toBeNull()
|
expect(parseModelString("")).toBeUndefined()
|
||||||
})
|
})
|
||||||
|
|
||||||
test("returns null for model without slash", () => {
|
test("returns undefined for model without slash", () => {
|
||||||
expect(parseModelString("no-slash-model")).toBeNull()
|
expect(parseModelString("no-slash-model")).toBeUndefined()
|
||||||
})
|
})
|
||||||
|
|
||||||
test("returns null for empty provider", () => {
|
test("returns undefined for empty provider", () => {
|
||||||
expect(parseModelString("/missing-provider")).toBeNull()
|
expect(parseModelString("/missing-provider")).toBeUndefined()
|
||||||
})
|
})
|
||||||
|
|
||||||
test("returns null for empty model", () => {
|
test("returns undefined for empty model", () => {
|
||||||
expect(parseModelString("missing-model/")).toBeNull()
|
expect(parseModelString("missing-model/")).toBeUndefined()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe("whitespace handling", () => {
|
||||||
|
//#given model strings with whitespace
|
||||||
|
//#when parsing
|
||||||
|
//#then it rejects whitespace-only parts and trims valid parts
|
||||||
|
|
||||||
|
test("returns undefined for whitespace-only string", () => {
|
||||||
|
expect(parseModelString(" ")).toBeUndefined()
|
||||||
|
})
|
||||||
|
|
||||||
|
test("returns undefined for whitespace-only provider", () => {
|
||||||
|
expect(parseModelString(" /model")).toBeUndefined()
|
||||||
|
})
|
||||||
|
|
||||||
|
test("returns undefined for whitespace-only model", () => {
|
||||||
|
expect(parseModelString("provider/ ")).toBeUndefined()
|
||||||
|
})
|
||||||
|
|
||||||
|
test("trims whitespace from provider and model", () => {
|
||||||
|
expect(parseModelString(" openai / gpt-5.3-codex ")).toEqual({
|
||||||
|
providerID: "openai",
|
||||||
|
modelID: "gpt-5.3-codex",
|
||||||
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
@ -2,9 +2,21 @@
|
|||||||
* Parse a model string in "provider/model" format.
|
* Parse a model string in "provider/model" format.
|
||||||
*/
|
*/
|
||||||
export function parseModelString(model: string): { providerID: string; modelID: string } | undefined {
|
export function parseModelString(model: string): { providerID: string; modelID: string } | undefined {
|
||||||
const parts = model.split("/")
|
if (!model || !model.trim()) {
|
||||||
if (parts.length >= 2) {
|
|
||||||
return { providerID: parts[0], modelID: parts.slice(1).join("/") }
|
|
||||||
}
|
|
||||||
return undefined
|
return undefined
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const slashIndex = model.indexOf("/")
|
||||||
|
if (slashIndex <= 0) {
|
||||||
|
return undefined
|
||||||
|
}
|
||||||
|
|
||||||
|
const providerID = model.substring(0, slashIndex).trim()
|
||||||
|
const modelID = model.substring(slashIndex + 1).trim()
|
||||||
|
|
||||||
|
if (!providerID || !modelID) {
|
||||||
|
return undefined
|
||||||
|
}
|
||||||
|
|
||||||
|
return { providerID, modelID }
|
||||||
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user