mirror of
https://github.com/OpenHands/OpenHands.git
synced 2026-10-06 15:03:43 +08:00
feat: fix default and free models (#16922)
Co-authored-by: neubig <neubig@users.noreply.github.com> Co-authored-by: openhands <openhands@all-hands.dev> Co-authored-by: Juan Pedro Michelini Jorge <juan@juan.com.uy> Co-authored-by: allhands-bot <allhands-bot@users.noreply.github.com>
This commit is contained in:
co-authored by
neubig
openhands
Juan Pedro Michelini Jorge
allhands-bot
parent
f95b9b71f7
commit
d104ffdc33
@@ -53,7 +53,7 @@ describe("SettingsService", () => {
|
||||
|
||||
// Should have normalized settings with derived fields
|
||||
expect(settings.agent).toBe("CodeActAgent");
|
||||
expect(settings.llm_model).toBe("openhands/kimi-k3");
|
||||
expect(settings.llm_model).toBe("openai/gpt-5.6-sol");
|
||||
expect(settings.confirmation_mode).toBe(false);
|
||||
expect(settings.security_analyzer).toBe("llm");
|
||||
});
|
||||
|
||||
+7
-5
@@ -101,27 +101,29 @@ describe("ChatInputLlmProfilePicker", () => {
|
||||
profiles: [
|
||||
{
|
||||
name: "Free",
|
||||
model: "openhands/glm-5.2",
|
||||
model: "openhands/deepseek-v4-flash",
|
||||
base_url: null,
|
||||
api_key_set: true,
|
||||
},
|
||||
],
|
||||
currentProfileName: "Free",
|
||||
currentProfileModel: "openhands/glm-5.2",
|
||||
currentProfileModel: "openhands/deepseek-v4-flash",
|
||||
}),
|
||||
);
|
||||
|
||||
renderWithProviders(<ChatInputLlmProfilePicker />);
|
||||
fireEvent.click(screen.getByTestId("chat-input-llm-profile"));
|
||||
|
||||
expect(screen.getByText("OpenHands GLM-5.2 (free)")).toBeInTheDocument();
|
||||
expect(
|
||||
screen.getByText("OpenHands DeepSeek V4 Flash (free)"),
|
||||
).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("labels a free OpenHands route in the read-only profile menu", () => {
|
||||
useChatInputLlmProfileStateMock.mockReturnValue(
|
||||
state({
|
||||
canSwitchProfile: false,
|
||||
currentProfileModel: "openhands/glm-5.2",
|
||||
currentProfileModel: "openhands/deepseek-v4-flash",
|
||||
}),
|
||||
);
|
||||
|
||||
@@ -130,7 +132,7 @@ describe("ChatInputLlmProfilePicker", () => {
|
||||
|
||||
expect(
|
||||
screen.getByTestId("chat-input-llm-profile-current"),
|
||||
).toHaveTextContent("OpenHands GLM-5.2 (free)");
|
||||
).toHaveTextContent("OpenHands DeepSeek V4 Flash (free)");
|
||||
});
|
||||
|
||||
it("links to the LLM profiles settings page", () => {
|
||||
|
||||
@@ -110,7 +110,7 @@ describe("ModelMessages", () => {
|
||||
useModelStore.getState().show(CONVERSATION_ID, "event-1", [
|
||||
{
|
||||
name: "free",
|
||||
model: "openhands/glm-5.2",
|
||||
model: "openhands/deepseek-v4-flash",
|
||||
base_url: null,
|
||||
api_key_set: true,
|
||||
},
|
||||
@@ -129,7 +129,7 @@ describe("ModelMessages", () => {
|
||||
);
|
||||
|
||||
expect(
|
||||
screen.getByText(/model:\s+OpenHands GLM-5\.2 \(free\)/),
|
||||
screen.getByText(/model:\s+OpenHands DeepSeek V4 Flash \(free\)/),
|
||||
).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -198,8 +198,12 @@ describe("ConversationCard", () => {
|
||||
const model = screen.getByTestId("conversation-card-agent-chip");
|
||||
const tags = screen.getByTestId("conversation-card-tag-chips");
|
||||
|
||||
expect(repo.compareDocumentPosition(model) & Node.DOCUMENT_POSITION_FOLLOWING).toBeTruthy();
|
||||
expect(model.compareDocumentPosition(tags) & Node.DOCUMENT_POSITION_FOLLOWING).toBeTruthy();
|
||||
expect(
|
||||
repo.compareDocumentPosition(model) & Node.DOCUMENT_POSITION_FOLLOWING,
|
||||
).toBeTruthy();
|
||||
expect(
|
||||
model.compareDocumentPosition(tags) & Node.DOCUMENT_POSITION_FOLLOWING,
|
||||
).toBeTruthy();
|
||||
});
|
||||
|
||||
it("renders the workspace folder name when no repository is selected", () => {
|
||||
@@ -985,10 +989,7 @@ describe("ConversationCard", () => {
|
||||
|
||||
const chip = screen.getByTestId("conversation-card-agent-chip");
|
||||
expect(chip).toHaveTextContent("Claude Opus (1M)");
|
||||
expect(chip).toHaveAttribute(
|
||||
"title",
|
||||
"Claude Code · Claude Opus (1M)",
|
||||
);
|
||||
expect(chip).toHaveAttribute("title", "Claude Code · Claude Opus (1M)");
|
||||
});
|
||||
|
||||
it("falls back to the provider display name for an ACP conversation with no model", () => {
|
||||
@@ -1098,7 +1099,7 @@ describe("ConversationCard", () => {
|
||||
);
|
||||
|
||||
const chip = screen.getByTestId("conversation-card-agent-chip");
|
||||
expect(chip).toHaveTextContent("OpenHands GLM-5.2 (free)");
|
||||
expect(chip).toHaveTextContent("glm-5.2");
|
||||
expect(chip).toHaveAttribute("title", "openhands/glm-5.2");
|
||||
});
|
||||
|
||||
|
||||
@@ -69,31 +69,25 @@ describe("ModelSelector — OpenHands provider display", () => {
|
||||
|
||||
it("makes clear which OpenHands models are free", async () => {
|
||||
const user = userEvent.setup();
|
||||
renderWithQuery(<ModelSelector currentModel="openhands/glm-5.2" />);
|
||||
renderWithQuery(<ModelSelector currentModel="openhands/deepseek-v4-flash" />);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(
|
||||
screen.getByTestId("openhands-free-models-note"),
|
||||
).toHaveTextContent("openhands/glm-5.2");
|
||||
});
|
||||
expect(screen.getByTestId("openhands-free-models-note")).toHaveTextContent(
|
||||
"openhands/deepseek-v4-flash",
|
||||
);
|
||||
expect(screen.getByTestId("openhands-free-models-note")).toHaveTextContent(
|
||||
"openhands/minimax-m2.7",
|
||||
);
|
||||
expect(screen.getByTestId("selected-free-model-badge")).toHaveTextContent(
|
||||
"Free",
|
||||
);
|
||||
|
||||
await user.click(screen.getByLabelText("LLM$MODEL"));
|
||||
|
||||
expect(screen.getAllByText("Free")).toHaveLength(4);
|
||||
expect(screen.getByLabelText("LLM$MODEL")).toHaveValue("glm-5.2");
|
||||
expect(screen.getAllByText("Free")).toHaveLength(2);
|
||||
expect(screen.getByLabelText("LLM$MODEL")).toHaveValue("deepseek-v4-flash");
|
||||
|
||||
await user.click(screen.getByText("minimax-m2.7"));
|
||||
await user.click(screen.getByRole("option", { name: /deepseek-v4-flash/ }));
|
||||
|
||||
expect(screen.getByLabelText("LLM$MODEL")).toHaveValue("minimax-m2.7");
|
||||
expect(screen.getByLabelText("LLM$MODEL")).toHaveValue("deepseek-v4-flash");
|
||||
expect(screen.getByTestId("selected-free-model-badge")).toHaveTextContent(
|
||||
"Free",
|
||||
);
|
||||
|
||||
@@ -344,12 +344,12 @@ describe("LlmSettingsLocalView", () => {
|
||||
// Should be in create view
|
||||
expect(screen.getByTestId("profile-name-input")).toBeInTheDocument();
|
||||
|
||||
// The profile name is auto-derived from the prefilled free default model.
|
||||
// The profile name is auto-derived from the prefilled default model.
|
||||
const nameInput = screen.getByTestId("profile-name-input");
|
||||
expect(nameInput).toHaveValue("kimi-k3");
|
||||
expect(nameInput).toHaveValue("gpt-5.6-sol");
|
||||
|
||||
expect(screen.getByTestId("mock-basic-model-input")).toHaveValue(
|
||||
"openhands/kimi-k3",
|
||||
"openai/gpt-5.6-sol",
|
||||
);
|
||||
});
|
||||
|
||||
@@ -363,7 +363,7 @@ describe("LlmSettingsLocalView", () => {
|
||||
|
||||
// The profile name starts from the free-model derived default.
|
||||
const nameInput = screen.getByTestId("profile-name-input");
|
||||
expect(nameInput).toHaveValue("kimi-k3");
|
||||
expect(nameInput).toHaveValue("gpt-5.6-sol");
|
||||
|
||||
// Go back to list
|
||||
await user.click(screen.getByTestId("back-to-profiles"));
|
||||
@@ -375,7 +375,7 @@ describe("LlmSettingsLocalView", () => {
|
||||
// The profile name should return to the free-model derived default
|
||||
// again (fresh form).
|
||||
const freshNameInput = screen.getByTestId("profile-name-input");
|
||||
expect(freshNameInput).toHaveValue("kimi-k3");
|
||||
expect(freshNameInput).toHaveValue("gpt-5.6-sol");
|
||||
});
|
||||
|
||||
it("does not carry over values from edit mode to create mode", async () => {
|
||||
@@ -390,7 +390,7 @@ describe("LlmSettingsLocalView", () => {
|
||||
|
||||
// Should be in create view with the free-model derived profile name.
|
||||
const nameInput = screen.getByTestId("profile-name-input");
|
||||
expect(nameInput).toHaveValue("kimi-k3");
|
||||
expect(nameInput).toHaveValue("gpt-5.6-sol");
|
||||
|
||||
// The key "new-profile" should be used, ensuring a fresh form mount
|
||||
// that doesn't inherit any existing profile data
|
||||
|
||||
@@ -56,14 +56,13 @@ describe("ProfileRow", () => {
|
||||
render(
|
||||
<ProfileRow
|
||||
{...defaultProps}
|
||||
profile={{ ...mockProfile, model: "openhands/glm-5.2" }}
|
||||
profile={{ ...mockProfile, model: "openhands/deepseek-v4-flash" }}
|
||||
/>,
|
||||
);
|
||||
|
||||
expect(screen.getByText("OpenHands GLM-5.2 (free)")).toHaveAttribute(
|
||||
"title",
|
||||
"openhands/glm-5.2",
|
||||
);
|
||||
expect(
|
||||
screen.getByText("OpenHands DeepSeek V4 Flash (free)"),
|
||||
).toHaveAttribute("title", "openhands/deepseek-v4-flash");
|
||||
});
|
||||
|
||||
it("does not display model when null", () => {
|
||||
|
||||
@@ -156,7 +156,7 @@ describe("useSlashCommand", () => {
|
||||
},
|
||||
{
|
||||
name: "free",
|
||||
model: "openhands/glm-5.2",
|
||||
model: "openhands/deepseek-v4-flash",
|
||||
base_url: null,
|
||||
api_key_set: true,
|
||||
},
|
||||
@@ -180,7 +180,7 @@ describe("useSlashCommand", () => {
|
||||
expect(
|
||||
result.current.filteredItems.find((i) => i.command === "/model free")
|
||||
?.skill.content,
|
||||
).toBe("Switch to OpenHands GLM-5.2 (free)");
|
||||
).toBe("Switch to OpenHands DeepSeek V4 Flash (free)");
|
||||
});
|
||||
|
||||
it("filters saved LLM profile suggestions by profile name or model", () => {
|
||||
|
||||
@@ -17,10 +17,7 @@ describe("formatNativeModelName", () => {
|
||||
|
||||
it("labels only configured OpenHands free-model routes as free", () => {
|
||||
expect(Object.keys(FREE_OPENHANDS_MODELS)).toEqual([
|
||||
"openhands/kimi-k3",
|
||||
"openhands/glm-5.2",
|
||||
"openhands/deepseek-v4-flash",
|
||||
"openhands/minimax-m2.7",
|
||||
]);
|
||||
|
||||
for (const [model, label] of Object.entries(FREE_OPENHANDS_MODELS)) {
|
||||
@@ -39,9 +36,7 @@ describe("formatNativeModelName", () => {
|
||||
});
|
||||
|
||||
it("keeps free OpenHands labels on native conversation chips", () => {
|
||||
expect(formatNativeModelName("openhands/glm-5.2")).toBe(
|
||||
FREE_OPENHANDS_MODELS["openhands/glm-5.2"],
|
||||
);
|
||||
expect(formatNativeModelName("openhands/glm-5.2")).toBe("glm-5.2");
|
||||
expect(formatNativeModelName("openhands/deepseek-v4-flash")).toBe(
|
||||
FREE_OPENHANDS_MODELS["openhands/deepseek-v4-flash"],
|
||||
);
|
||||
|
||||
@@ -3,7 +3,8 @@
|
||||
---
|
||||
|
||||
### LLD-001: Frontend always sends its chosen default model
|
||||
- [x] When the agent-server returns an absent or empty `llm.model` (e.g. because the user has never saved settings), the frontend adapter shall substitute `DEFAULT_SETTINGS.llm_model` (`"openhands/kimi-k3"`) before sending the conversation-start request.
|
||||
|
||||
- [x] When the agent-server returns an absent or empty `llm.model` (e.g. because the user has never saved settings), the frontend adapter shall substitute `DEFAULT_SETTINGS.llm_model` (`"openai/gpt-5.6-sol"`) before sending the conversation-start request.
|
||||
- [x] The frontend shall never rely on the agent-server SDK's own default model (`gpt-5.5`); it shall always send an explicit model value.
|
||||
- [x] Whitespace-only model strings shall be treated as absent and fall back to the default.
|
||||
- [x] The same guard applies when LLM settings arrive via `encryptedAgentSettings` (the conversation-start encrypted payload path).
|
||||
|
||||
@@ -16,16 +16,12 @@ interface SetupLlmStepProps {
|
||||
}
|
||||
|
||||
/**
|
||||
* Pre-fills the LLM form with the OpenHands provider's free default model
|
||||
* (`openhands/kimi-k3`), matching `DEFAULT_SETTINGS.llm_model`. The OpenHands
|
||||
* provider is the agent the user just selected, so the onboarding override
|
||||
* keeps the LLM provider aligned with that choice rather than silently
|
||||
* switching to OpenAI. Canvas stores provider-qualified LiteLLM model ids,
|
||||
* so the override uses this OpenHands-prefixed model id, and keeping it as an
|
||||
* explicit override marks the model dirty so the Next button persists the
|
||||
* suggested default immediately.
|
||||
* Pre-fills the LLM form with the OpenAI GPT-5.6 Sol default
|
||||
* (`openai/gpt-5.6-sol`), matching `DEFAULT_SETTINGS.llm_model`. The explicit
|
||||
* override marks the model dirty so the Next button persists the suggested
|
||||
* default immediately.
|
||||
*/
|
||||
export const ONBOARDING_DEFAULT_LLM_MODEL = "openhands/kimi-k3";
|
||||
export const ONBOARDING_DEFAULT_LLM_MODEL = "openai/gpt-5.6-sol";
|
||||
|
||||
/**
|
||||
* Step 2: embed the LLM settings form. The screen runs in `embedded`
|
||||
|
||||
+15
-15
@@ -30583,21 +30583,21 @@
|
||||
"uk": "Налаштуйте свій LLM"
|
||||
},
|
||||
"ONBOARDING$LLM_SUBTITLE": {
|
||||
"en": "Pick a provider and model. We've pre-selected OpenHands GLM-5.2.",
|
||||
"ja": "プロバイダーとモデルを選択してください。OpenHands GLM-5.2 を事前に選択しています。",
|
||||
"zh-CN": "选择提供商和模型。我们已为你预先选择 OpenHands GLM-5.2。",
|
||||
"zh-TW": "選擇供應商與模型。我們已預先選好 OpenHands GLM-5.2。",
|
||||
"ko-KR": "공급자와 모델을 선택하세요. 기본값으로 OpenHands GLM-5.2가 선택되어 있습니다.",
|
||||
"no": "Velg en leverandør og modell. Vi har forhåndsvalgt OpenHands GLM-5.2.",
|
||||
"ar": "اختر مزوّدًا ونموذجًا. لقد اخترنا مسبقًا OpenHands GLM-5.2.",
|
||||
"de": "Wählen Sie Anbieter und Modell. Wir haben OpenHands GLM-5.2 vorausgewählt.",
|
||||
"fr": "Choisissez un fournisseur et un modèle. Nous avons présélectionné OpenHands GLM-5.2.",
|
||||
"it": "Scegli un provider e un modello. Abbiamo preselezionato OpenHands GLM-5.2.",
|
||||
"pt": "Escolha um provedor e modelo. Já selecionamos o OpenHands GLM-5.2.",
|
||||
"es": "Elige un proveedor y modelo. Hemos preseleccionado OpenHands GLM-5.2.",
|
||||
"ca": "Trieu un proveïdor i model. Hem preseleccionat OpenHands GLM-5.2.",
|
||||
"tr": "Bir sağlayıcı ve model seçin. OpenHands GLM-5.2'i önceden seçtik.",
|
||||
"uk": "Виберіть провайдера та модель. OpenHands GLM-5.2 уже обрано."
|
||||
"en": "Pick a provider and model. We've pre-selected OpenAI GPT-5.6 Sol.",
|
||||
"ja": "プロバイダーとモデルを選択してください。OpenAI GPT-5.6 Sol を事前に選択しています。",
|
||||
"zh-CN": "选择提供商和模型。我们已为你预先选择 OpenAI GPT-5.6 Sol。",
|
||||
"zh-TW": "選擇供應商與模型。我們已預先選好 OpenAI GPT-5.6 Sol。",
|
||||
"ko-KR": "공급자와 모델을 선택하세요. 기본값으로 OpenAI GPT-5.6 Sol가 선택되어 있습니다.",
|
||||
"no": "Velg en leverandør og modell. Vi har forhåndsvalgt OpenAI GPT-5.6 Sol.",
|
||||
"ar": "اختر مزوّدًا ونموذجًا. لقد اخترنا مسبقًا OpenAI GPT-5.6 Sol.",
|
||||
"de": "Wählen Sie Anbieter und Modell. Wir haben OpenAI GPT-5.6 Sol vorausgewählt.",
|
||||
"fr": "Choisissez un fournisseur et un modèle. Nous avons présélectionné OpenAI GPT-5.6 Sol.",
|
||||
"it": "Scegli un provider e un modello. Abbiamo preselezionato OpenAI GPT-5.6 Sol.",
|
||||
"pt": "Escolha um provedor e modelo. Já selecionamos o OpenAI GPT-5.6 Sol.",
|
||||
"es": "Elige un proveedor y modelo. Hemos preseleccionado OpenAI GPT-5.6 Sol.",
|
||||
"ca": "Trieu un proveïdor i model. Hem preseleccionat OpenAI GPT-5.6 Sol.",
|
||||
"tr": "Bir sağlayıcı ve model seçin. OpenAI GPT-5.6 Sol'i önceden seçtik.",
|
||||
"uk": "Виберіть провайдера та модель. OpenAI GPT-5.6 Sol уже обрано."
|
||||
},
|
||||
"ONBOARDING$HELLO_TITLE": {
|
||||
"en": "Say hello 👋",
|
||||
|
||||
@@ -610,7 +610,8 @@ const MOCK_MODELS = [
|
||||
"openhands/claude-sonnet-4-5-20250929",
|
||||
"openhands/claude-haiku-4-5-20251001",
|
||||
"openhands/claude-opus-4-5-20251101",
|
||||
"openhands/kimi-k3",
|
||||
"openai/gpt-5.6-sol",
|
||||
"openhands/deepseek-v4-flash",
|
||||
"openhands/glm-5.2",
|
||||
"sambanova/Meta-Llama-3.1-8B-Instruct",
|
||||
];
|
||||
@@ -624,7 +625,8 @@ const MOCK_VERIFIED_MODELS = new Set([
|
||||
"openai/gpt-5.5",
|
||||
"openhands/claude-opus-4-5-20251101",
|
||||
"openhands/claude-sonnet-4-5-20250929",
|
||||
"openhands/kimi-k3",
|
||||
"openai/gpt-5.6-sol",
|
||||
"openhands/deepseek-v4-flash",
|
||||
"openhands/glm-5.2",
|
||||
]);
|
||||
|
||||
@@ -755,7 +757,7 @@ export const SETTINGS_HANDLERS = [
|
||||
"claude-sonnet-4-5-20250929",
|
||||
],
|
||||
verified_providers: MOCK_VERIFIED_PROVIDERS,
|
||||
default_model: "openhands/kimi-k3",
|
||||
default_model: "openai/gpt-5.6-sol",
|
||||
}),
|
||||
),
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ import { Settings } from "#/types/settings";
|
||||
export const LATEST_SETTINGS_VERSION = 5;
|
||||
|
||||
export const DEFAULT_SETTINGS: Settings = {
|
||||
llm_model: "openhands/kimi-k3",
|
||||
llm_model: "openai/gpt-5.6-sol",
|
||||
llm_base_url: "",
|
||||
agent: "CodeActAgent",
|
||||
language: "en",
|
||||
@@ -37,7 +37,7 @@ export const DEFAULT_SETTINGS: Settings = {
|
||||
agent_kind: "openhands",
|
||||
agent: "CodeActAgent",
|
||||
llm: {
|
||||
model: "openhands/kimi-k3",
|
||||
model: "openai/gpt-5.6-sol",
|
||||
},
|
||||
condenser: {
|
||||
enabled: true,
|
||||
|
||||
@@ -1,10 +1,7 @@
|
||||
export const FREE_MODEL_BADGE_LABEL = "Free";
|
||||
|
||||
export const FREE_OPENHANDS_MODELS = {
|
||||
"openhands/kimi-k3": "OpenHands Kimi K3 (free)",
|
||||
"openhands/glm-5.2": "OpenHands GLM-5.2 (free)",
|
||||
"openhands/deepseek-v4-flash": "OpenHands DeepSeek V4 Flash (free)",
|
||||
"openhands/minimax-m2.7": "OpenHands MiniMax M2.7 (free)",
|
||||
} as const;
|
||||
|
||||
export const FREE_OPENHANDS_MODEL_IDS = Object.keys(FREE_OPENHANDS_MODELS);
|
||||
|
||||
@@ -61,10 +61,11 @@ test.describe("onboarding recent regressions", () => {
|
||||
});
|
||||
|
||||
// Regression coverage for #1077 / PR #1089: first-run LLM setup
|
||||
// should default users to the OpenHands provider, matching the agent
|
||||
// selected on the previous step.
|
||||
// should default users to the configured default provider and model.
|
||||
|
||||
test("defaults the LLM setup step to OpenHands GLM-5.2", async ({ page }) => {
|
||||
test("defaults the LLM setup step to OpenAI GPT-5.6 Sol", async ({
|
||||
page,
|
||||
}) => {
|
||||
await showOnboarding(page, {
|
||||
apiKey: SESSION_API_KEY,
|
||||
beforeGoto: async () => {
|
||||
@@ -94,18 +95,18 @@ test.describe("onboarding recent regressions", () => {
|
||||
|
||||
await expect(
|
||||
providerInput,
|
||||
"first-run onboarding should default to the OpenHands provider",
|
||||
).toHaveValue("OpenHands", { timeout: 10_000 });
|
||||
"first-run onboarding should default to the OpenAI provider",
|
||||
).toHaveValue("OpenAI", { timeout: 10_000 });
|
||||
// The model input displays the model ID without the provider prefix.
|
||||
await expect(
|
||||
modelInput,
|
||||
"first-run onboarding should default to Kimi K3",
|
||||
).toHaveValue("kimi-k3", {
|
||||
"first-run onboarding should default to GPT-5.6 Sol",
|
||||
).toHaveValue("gpt-5.6-sol", {
|
||||
timeout: 10_000,
|
||||
});
|
||||
await expect(
|
||||
page.getByTestId("openhands-account-help"),
|
||||
"OpenHands account helper should be visible for OpenHands defaults",
|
||||
).toBeVisible();
|
||||
"OpenHands account helper should be hidden for OpenAI defaults",
|
||||
).toBeHidden();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -36,7 +36,7 @@ export async function routeOnboardingLlmCatalog(page: Page) {
|
||||
"anthropic/claude-opus-4-8",
|
||||
"openai/gpt-5.5",
|
||||
"openhands/claude-opus-4-5-20251101",
|
||||
"openhands/kimi-k3",
|
||||
"openai/gpt-5.6-sol",
|
||||
"openhands/glm-5.2",
|
||||
],
|
||||
}),
|
||||
|
||||
Reference in New Issue
Block a user