This commit is contained in:
roomote-v0[bot] 2026-04-06 14:46:39 -04:00 committed by GitHub
commit 725ac5eb5a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
18 changed files with 139 additions and 39 deletions

View file

@ -841,6 +841,18 @@ describe("VertexHandler", () => {
expect(modelInfo.info.contextWindow).toBe(200_000)
})
it("should preserve custom model ID and use default model info for unknown models", () => {
const customHandler = new AnthropicVertexHandler({
apiModelId: "claude-custom-vertex-model",
vertexProjectId: "test-project",
vertexRegion: "us-central1",
})
const modelInfo = customHandler.getModel()
expect(modelInfo.id).toBe("claude-custom-vertex-model")
expect(modelInfo.info).toBeDefined()
expect(modelInfo.info.contextWindow).toBeDefined()
})
it("honors custom maxTokens for thinking models", () => {
const handler = new AnthropicVertexHandler({
apiKey: "test-api-key",

View file

@ -268,6 +268,17 @@ describe("AnthropicHandler", () => {
expect(model.info.supportsPromptCache).toBe(true)
})
it("should preserve custom model ID and use default model info for unknown models", () => {
const customHandler = new AnthropicHandler({
apiKey: "test-api-key",
apiModelId: "claude-custom-model-v1",
})
const model = customHandler.getModel()
expect(model.id).toBe("claude-custom-model-v1")
expect(model.info).toBeDefined()
expect(model.info.contextWindow).toBeDefined()
})
it("honors custom maxTokens for thinking models", () => {
const handler = new AnthropicHandler({
apiKey: "test-api-key",

View file

@ -545,4 +545,43 @@ describe("BaseOpenAiCompatibleProvider", () => {
expect(endChunks).toHaveLength(0)
})
})
describe("getModel", () => {
it("should return the default model when no apiModelId is set", () => {
const model = handler.getModel()
expect(model.id).toBe("test-model")
expect(model.info).toBeDefined()
})
it("should preserve custom model ID and use default model info for unknown models", () => {
const customHandler = new (class extends BaseOpenAiCompatibleProvider<"test-model"> {
constructor() {
const testModels: Record<"test-model", ModelInfo> = {
"test-model": {
maxTokens: 4096,
contextWindow: 128000,
supportsImages: false,
supportsPromptCache: false,
inputPrice: 0.5,
outputPrice: 1.5,
},
}
super({
providerName: "TestProvider",
baseURL: "https://test.example.com/v1",
defaultProviderModelId: "test-model",
providerModels: testModels,
apiKey: "test-key",
apiModelId: "custom-unknown-model",
})
}
})()
const model = customHandler.getModel()
expect(model.id).toBe("custom-unknown-model")
expect(model.info).toBeDefined()
expect(model.info.contextWindow).toBe(128000)
})
})
})

View file

@ -165,13 +165,17 @@ describe("GeminiHandler", () => {
expect(modelInfo.info).toBeDefined()
})
it("should return default model if invalid model specified", () => {
it("should preserve custom model ID and use default model info for unknown models", () => {
const invalidHandler = new GeminiHandler({
apiModelId: "invalid-model",
apiModelId: "gemini-custom-model",
geminiApiKey: "test-key",
})
const modelInfo = invalidHandler.getModel()
expect(modelInfo.id).toBe(geminiDefaultModelId) // Default model
expect(modelInfo.id).toBe("gemini-custom-model")
expect(modelInfo.info).toBeDefined()
// Should fall back to default model's info
const defaultHandler = new GeminiHandler({ geminiApiKey: "test-key" })
expect(modelInfo.info.contextWindow).toBe(defaultHandler.getModel().info.contextWindow)
})
it("should exclude apply_diff and include edit in tool preferences", () => {

View file

@ -134,6 +134,17 @@ describe("MiniMaxHandler", () => {
expect(model.info.cacheWritesPrice).toBe(0.375)
expect(model.info.cacheReadsPrice).toBe(0.03)
})
it("should preserve custom model ID and use default model info for unknown models", () => {
const handlerWithCustom = new MiniMaxHandler({
apiModelId: "MiniMax-M2.7-custom",
minimaxApiKey: "test-minimax-api-key",
})
const model = handlerWithCustom.getModel()
expect(model.id).toBe("MiniMax-M2.7-custom")
expect(model.info).toBeDefined()
expect(model.info).toEqual(minimaxModels[minimaxDefaultModelId])
})
})
describe("China MiniMax", () => {

View file

@ -16,12 +16,15 @@ describe("OpenAiCodexHandler.getModel", () => {
},
)
it("should fall back to default model when an invalid model id is provided", () => {
it("should preserve custom model ID and use default model info for unknown models", () => {
const handler = new OpenAiCodexHandler({ apiModelId: "not-a-real-model" })
const model = handler.getModel()
expect(model.id).toBe("gpt-5.3-codex")
expect(model.id).toBe("not-a-real-model")
expect(model.info).toBeDefined()
// Should fall back to default model's info
const defaultHandler = new OpenAiCodexHandler({})
expect(model.info.contextWindow).toBe(defaultHandler.getModel().info.contextWindow)
})
it("should use Spark-specific limits and capabilities", () => {

View file

@ -324,6 +324,17 @@ describe("OpenAiNativeHandler", () => {
expect(modelInfo.id).toBe("gpt-5.1-codex-max") // Default model
expect(modelInfo.info).toBeDefined()
})
it("should preserve custom model ID and use default model info for unknown models", () => {
const customHandler = new OpenAiNativeHandler({
...mockOptions,
apiModelId: "gpt-custom-model",
})
const modelInfo = customHandler.getModel()
expect(modelInfo.id).toBe("gpt-custom-model")
expect(modelInfo.info).toBeDefined()
expect(modelInfo.info.contextWindow).toBeDefined()
})
})
describe("GPT-5 models", () => {

View file

@ -138,6 +138,18 @@ describe("VertexHandler", () => {
expect(modelInfo.info.contextWindow).toBe(1048576)
})
it("should preserve custom model ID and use default model info for unknown models", () => {
const customHandler = new VertexHandler({
apiModelId: "gemini-custom-vertex-model",
vertexProjectId: "test-project",
vertexRegion: "us-central1",
})
const modelInfo = customHandler.getModel()
expect(modelInfo.id).toBe("gemini-custom-vertex-model")
expect(modelInfo.info).toBeDefined()
expect(modelInfo.info.contextWindow).toBeDefined()
})
it("should exclude apply_diff and include edit in tool preferences", () => {
const testHandler = new VertexHandler({
apiModelId: "gemini-2.0-flash-001",

View file

@ -80,6 +80,14 @@ describe("XAIHandler", () => {
expect(model.info).toEqual(xaiModels[testModelId])
})
it("should preserve custom model ID and use default model info for unknown models", () => {
const customHandler = new XAIHandler({ apiModelId: "grok-custom-model" })
const model = customHandler.getModel()
expect(model.id).toBe("grok-custom-model")
expect(model.info).toBeDefined()
expect(model.info).toEqual(xaiModels[xaiDefaultModelId])
})
it("should include reasoning_effort parameter for mini models", async () => {
const miniModelHandler = new XAIHandler({
apiModelId: "grok-3-mini",

View file

@ -206,9 +206,8 @@ export class AnthropicVertexHandler extends BaseProvider implements SingleComple
}
getModel() {
const modelId = this.options.apiModelId
let id = modelId && modelId in vertexModels ? (modelId as VertexModelId) : vertexDefaultModelId
let info: ModelInfo = vertexModels[id]
const id = this.options.apiModelId ?? vertexDefaultModelId
let info: ModelInfo = vertexModels[id as VertexModelId] || vertexModels[vertexDefaultModelId]
// Check if 1M context beta should be enabled for supported models
const supports1MContext = VERTEX_1M_CONTEXT_MODEL_IDS.includes(

View file

@ -333,9 +333,8 @@ export class AnthropicHandler extends BaseProvider implements SingleCompletionHa
}
getModel() {
const modelId = this.options.apiModelId
let id = modelId && modelId in anthropicModels ? (modelId as AnthropicModelId) : anthropicDefaultModelId
let info: ModelInfo = anthropicModels[id]
const id = this.options.apiModelId ?? anthropicDefaultModelId
let info: ModelInfo = anthropicModels[id as AnthropicModelId] || anthropicModels[anthropicDefaultModelId]
// If 1M context beta is enabled for supported models, update the model info
if (

View file

@ -250,11 +250,11 @@ export abstract class BaseOpenAiCompatibleProvider<ModelName extends string>
}
override getModel() {
const id =
this.options.apiModelId && this.options.apiModelId in this.providerModels
? (this.options.apiModelId as ModelName)
: this.defaultProviderModelId
const id = this.options.apiModelId ?? this.defaultProviderModelId
return { id, info: this.providerModels[id] }
return {
id,
info: this.providerModels[id as ModelName] || this.providerModels[this.defaultProviderModelId as ModelName],
}
}
}

View file

@ -347,9 +347,8 @@ export class GeminiHandler extends BaseProvider implements SingleCompletionHandl
}
override getModel() {
const modelId = this.options.apiModelId
let id = modelId && modelId in geminiModels ? (modelId as GeminiModelId) : geminiDefaultModelId
let info: ModelInfo = geminiModels[id]
const id = this.options.apiModelId ?? geminiDefaultModelId
let info: ModelInfo = geminiModels[id as GeminiModelId] || geminiModels[geminiDefaultModelId]
const params = getModelParams({
format: "gemini",

View file

@ -270,9 +270,8 @@ export class MiniMaxHandler extends BaseProvider implements SingleCompletionHand
}
getModel() {
const modelId = this.options.apiModelId
const id = modelId && modelId in minimaxModels ? (modelId as MinimaxModelId) : minimaxDefaultModelId
const info = minimaxModels[id]
const id = this.options.apiModelId ?? minimaxDefaultModelId
const info = minimaxModels[id as MinimaxModelId] || minimaxModels[minimaxDefaultModelId]
const params = getModelParams({
format: "anthropic",

View file

@ -1115,11 +1115,10 @@ export class OpenAiCodexHandler extends BaseProvider implements SingleCompletion
}
override getModel() {
const modelId = this.options.apiModelId
const id = this.options.apiModelId ?? openAiCodexDefaultModelId
let id = modelId && modelId in openAiCodexModels ? (modelId as OpenAiCodexModelId) : openAiCodexDefaultModelId
const info: ModelInfo = openAiCodexModels[id]
const info: ModelInfo =
openAiCodexModels[id as OpenAiCodexModelId] || openAiCodexModels[openAiCodexDefaultModelId]
const params = getModelParams({
format: "openai",

View file

@ -1433,12 +1433,10 @@ export class OpenAiNativeHandler extends BaseProvider implements SingleCompletio
// Removed isResponsesApiModel method as ALL models now use the Responses API
override getModel() {
const modelId = this.options.apiModelId
const id = this.options.apiModelId ?? openAiNativeDefaultModelId
let id =
modelId && modelId in openAiNativeModels ? (modelId as OpenAiNativeModelId) : openAiNativeDefaultModelId
const info: ModelInfo = openAiNativeModels[id]
const info: ModelInfo =
openAiNativeModels[id as OpenAiNativeModelId] || openAiNativeModels[openAiNativeDefaultModelId]
const params = getModelParams({
format: "openai",

View file

@ -13,9 +13,8 @@ export class VertexHandler extends GeminiHandler implements SingleCompletionHand
}
override getModel() {
const modelId = this.options.apiModelId
let id = modelId && modelId in vertexModels ? (modelId as VertexModelId) : vertexDefaultModelId
let info: ModelInfo = vertexModels[id]
const id = this.options.apiModelId ?? vertexDefaultModelId
let info: ModelInfo = vertexModels[id as VertexModelId] || vertexModels[vertexDefaultModelId]
const params = getModelParams({
format: "gemini",
modelId: id,

View file

@ -37,12 +37,9 @@ export class XAIHandler extends BaseProvider implements SingleCompletionHandler
}
override getModel() {
const id =
this.options.apiModelId && this.options.apiModelId in xaiModels
? (this.options.apiModelId as XAIModelId)
: xaiDefaultModelId
const id = this.options.apiModelId ?? xaiDefaultModelId
const info = xaiModels[id]
const info = xaiModels[id as XAIModelId] || xaiModels[xaiDefaultModelId]
const params = getModelParams({
format: "openai",
modelId: id,