-
Notifications
You must be signed in to change notification settings - Fork 0
Mirror: Dynamic OpenAI compatible model fetching on front page (#5562) #12
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
a413a1f
2f97799
a64ebb8
23f86a7
23f774a
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,6 @@ | ||
| --- | ||
| "kilo-code": patch | ||
| "@kilocode/types": patch | ||
| --- | ||
|
|
||
| Implement dynamic model fetching for OpenAI-compatible providers |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,80 @@ | ||
| import { ModelInfo } from "@roo-code/types" | ||
| import { RouterModels } from "@roo/api" | ||
| import { getModelsByProvider } from "../useProviderModels" | ||
|
|
||
| describe("PR #5562: Dynamic OpenAI model fetching on front page", () => { | ||
| const testModel: ModelInfo = { | ||
| maxTokens: 4096, | ||
| contextWindow: 8192, | ||
| supportsImages: false, | ||
| supportsPromptCache: false, | ||
| inputPrice: 0.1, | ||
| outputPrice: 0.2, | ||
| description: "Test model", | ||
| } | ||
|
|
||
| const routerModels: RouterModels = { | ||
| openrouter: { "test-model": testModel }, | ||
| requesty: { "test-model": testModel }, | ||
| glama: { "test-model": testModel }, | ||
| unbound: { "test-model": testModel }, | ||
| litellm: { "test-model": testModel }, | ||
| kilocode: { "test-model": testModel }, | ||
| "nano-gpt": { "test-model": testModel }, | ||
| ollama: { "test-model": testModel }, | ||
| lmstudio: { "test-model": testModel }, | ||
| "io-intelligence": { "test-model": testModel }, | ||
| deepinfra: { "test-model": testModel }, | ||
| "vercel-ai-gateway": { "test-model": testModel }, | ||
| huggingface: { "test-model": testModel }, | ||
| gemini: { "test-model": testModel }, | ||
| ovhcloud: { "test-model": testModel }, | ||
| chutes: { "test-model": testModel }, | ||
| "sap-ai-core": { "test-model": testModel }, | ||
| synthetic: { "test-model": testModel }, | ||
| inception: { "test-model": testModel }, | ||
| roo: { "test-model": testModel }, | ||
| } | ||
|
|
||
| const baseArgs = { | ||
| routerModels, | ||
| kilocodeDefaultModel: "test-model", | ||
| options: { isChina: false }, | ||
| } | ||
|
|
||
| it("returns dynamically fetched models when openAiModels is provided", () => { | ||
| const result = getModelsByProvider({ | ||
| ...baseArgs, | ||
| provider: "openai", | ||
| openAiModels: ["gpt-4o", "gpt-4o-mini", "o1-preview"], | ||
| }) | ||
|
|
||
| expect(Object.keys(result.models)).toEqual(["gpt-4o", "gpt-4o-mini", "o1-preview"]) | ||
| expect(result.defaultModel).toBe("gpt-4o") | ||
| // Each model should have sane defaults (128K context, supports images) | ||
| expect(result.models["gpt-4o"].contextWindow).toBe(128_000) | ||
| expect(result.models["gpt-4o"].supportsImages).toBe(true) | ||
| }) | ||
|
|
||
| it("returns empty models when openAiModels is not provided", () => { | ||
| const result = getModelsByProvider({ | ||
| ...baseArgs, | ||
| provider: "openai", | ||
| }) | ||
|
|
||
| expect(Object.keys(result.models)).toHaveLength(0) | ||
| expect(result.defaultModel).toBe("") | ||
| }) | ||
|
|
||
| it("handles empty openAiModels array gracefully", () => { | ||
| const result = getModelsByProvider({ | ||
| ...baseArgs, | ||
| provider: "openai", | ||
| openAiModels: [], | ||
| }) | ||
|
|
||
| // Empty array is truthy but has no models | ||
| expect(Object.keys(result.models)).toHaveLength(0) | ||
| expect(result.defaultModel).toBe("") | ||
| }) | ||
| }) | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -59,6 +59,7 @@ import { | |
| internationalZAiDefaultModelId, | ||
| mainlandZAiModels, | ||
| mainlandZAiDefaultModelId, | ||
| openAiModelInfoSaneDefaults, | ||
| } from "@roo-code/types" | ||
| import type { ModelRecord, RouterModels } from "@roo/api" | ||
| import { useRouterModels } from "../../ui/hooks/useRouterModels" | ||
|
|
@@ -73,11 +74,13 @@ export const getModelsByProvider = ({ | |
| provider, | ||
| routerModels, | ||
| kilocodeDefaultModel, | ||
| openAiModels, | ||
| options = { isChina: false }, | ||
| }: { | ||
| provider: ProviderName | ||
| routerModels: RouterModels | ||
| kilocodeDefaultModel: string | ||
| openAiModels?: string[] | ||
| options: { isChina?: boolean } | ||
| }): { models: ModelRecord; defaultModel: string } => { | ||
| switch (provider) { | ||
|
|
@@ -181,7 +184,12 @@ export const getModelsByProvider = ({ | |
| } | ||
| } | ||
| case "openai": { | ||
| // TODO(catrielmuller): Support the fetch here | ||
| if (openAiModels) { | ||
| return { | ||
| models: Object.fromEntries(openAiModels.map((model) => [model, openAiModelInfoSaneDefaults])), | ||
| defaultModel: openAiModels[0] || "", | ||
| } | ||
| } | ||
|
Comment on lines
+187
to
+192
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The if (openAiModels && openAiModels.length > 0) {
return {
models: Object.fromEntries(openAiModels.map((model) => [model, openAiModelInfoSaneDefaults])),
defaultModel: openAiModels[0],
}
} |
||
| return { | ||
| models: {}, | ||
| defaultModel: "", | ||
|
|
@@ -351,7 +359,7 @@ export const getOptionsForProvider = (provider: ProviderName, apiConfiguration?: | |
| export const useProviderModels = (apiConfiguration?: ProviderSettings) => { | ||
| const provider = apiConfiguration?.apiProvider || "anthropic" | ||
|
|
||
| const { kilocodeDefaultModel } = useExtensionState() | ||
| const { kilocodeDefaultModel, openAiModels } = useExtensionState() | ||
|
|
||
| const routerModels = useRouterModels({ | ||
| openRouterBaseUrl: apiConfiguration?.openRouterBaseUrl, | ||
|
|
@@ -375,6 +383,7 @@ export const useProviderModels = (apiConfiguration?: ProviderSettings) => { | |
| provider, | ||
| routerModels: routerModels.data, | ||
| kilocodeDefaultModel, | ||
| openAiModels, | ||
| options, | ||
| }) | ||
| : FALLBACK_MODELS | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,4 +1,5 @@ | ||
| import React, { createContext, useCallback, useContext, useEffect, useState } from "react" | ||
| import { useDebounce } from "react-use" | ||
|
|
||
| import { | ||
| type ProviderSettings, | ||
|
|
@@ -220,6 +221,7 @@ export interface ExtensionStateContextType extends ExtensionState { | |
| setIncludeCurrentTime: (value: boolean) => void | ||
| includeCurrentCost?: boolean | ||
| setIncludeCurrentCost: (value: boolean) => void | ||
| openAiModels?: string[] | ||
| } | ||
|
|
||
| export const ExtensionStateContext = createContext<ExtensionStateContextType | undefined>(undefined) | ||
|
|
@@ -360,6 +362,7 @@ export const ExtensionStateContextProvider: React.FC<{ children: React.ReactNode | |
| openRouterImageGenerationSelectedModel: "", | ||
| includeCurrentTime: true, | ||
| includeCurrentCost: true, | ||
| openAiModels: [], | ||
| }) | ||
|
|
||
| const [didHydrateState, setDidHydrateState] = useState(false) | ||
|
|
@@ -518,6 +521,10 @@ export const ExtensionStateContextProvider: React.FC<{ children: React.ReactNode | |
| setExtensionRouterModels(message.routerModels) | ||
| break | ||
| } | ||
| case "openAiModels": { | ||
| setState((prevState) => ({ ...prevState, openAiModels: message.openAiModels ?? [] })) | ||
| break | ||
| } | ||
| case "marketplaceData": { | ||
| if (message.marketplaceItems !== undefined) { | ||
| setMarketplaceItems(message.marketplaceItems) | ||
|
|
@@ -554,6 +561,38 @@ export const ExtensionStateContextProvider: React.FC<{ children: React.ReactNode | |
| setPrevCloudIsAuthenticated(currentAuth) | ||
| }, [state.cloudIsAuthenticated, prevCloudIsAuthenticated, state.apiConfiguration?.apiProvider]) | ||
|
|
||
| // Fetch OpenAI models on startup or when configuration changes | ||
| useDebounce( | ||
| () => { | ||
| if (!didHydrateState) { | ||
| return | ||
| } | ||
|
|
||
| const { apiProvider, openAiBaseUrl, openAiApiKey, openAiHeaders } = state.apiConfiguration || {} | ||
|
|
||
| if (apiProvider === "openai" || apiProvider === "openai-responses") { | ||
| if (openAiBaseUrl) { | ||
| vscode.postMessage({ | ||
| type: "requestOpenAiModels", | ||
| values: { | ||
| baseUrl: openAiBaseUrl, | ||
| apiKey: openAiApiKey, | ||
| openAiHeaders, | ||
| }, | ||
| }) | ||
| } | ||
| } | ||
| }, | ||
| 500, | ||
| [ | ||
| didHydrateState, | ||
| state.apiConfiguration?.apiProvider, | ||
| state.apiConfiguration?.openAiBaseUrl, | ||
| state.apiConfiguration?.openAiApiKey, | ||
| state.apiConfiguration?.openAiHeaders, | ||
| ], | ||
| ) | ||
|
Comment on lines
+565
to
+594
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The current implementation of the |
||
|
|
||
| const contextValue: ExtensionStateContextType = { | ||
| ...state, | ||
| reasoningBlockCollapsed: state.reasoningBlockCollapsed ?? true, | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🏁 Script executed:
Repository: jeremylongshore/kilocode
Length of output: 50
🏁 Script executed:
Repository: jeremylongshore/kilocode
Length of output: 678
🏁 Script executed:
Repository: jeremylongshore/kilocode
Length of output: 11631
🏁 Script executed:
Repository: jeremylongshore/kilocode
Length of output: 1031
🏁 Script executed:
Repository: jeremylongshore/kilocode
Length of output: 699
🏁 Script executed:
Repository: jeremylongshore/kilocode
Length of output: 29014
🏁 Script executed:
Repository: jeremylongshore/kilocode
Length of output: 1261
🏁 Script executed:
Repository: jeremylongshore/kilocode
Length of output: 95
🏁 Script executed:
Repository: jeremylongshore/kilocode
Length of output: 95
🏁 Script executed:
Repository: jeremylongshore/kilocode
Length of output: 3569
🏁 Script executed:
Repository: jeremylongshore/kilocode
Length of output: 238
🏁 Script executed:
Repository: jeremylongshore/kilocode
Length of output: 14398
🏁 Script executed:
Repository: jeremylongshore/kilocode
Length of output: 817
🏁 Script executed:
Repository: jeremylongshore/kilocode
Length of output: 6925
Add missing case for
openai-responsesingetModelsByProvider.The
ExtensionStateContext(line 573) triggers model fetching for both"openai"and"openai-responses"providers, butgetModelsByProvideronly has acase "openai"branch (lines 186-197). WhenapiProvideris"openai-responses", the function falls through to the default case and returns empty models and defaultModel, silently discarding the dynamically fetched models. Either add a matching case for"openai-responses"that handles theopenAiModelsparameter, or remove"openai-responses"from the fetch trigger inExtensionStateContext.🤖 Prompt for AI Agents