mirror of
https://github.com/All-Hands-AI/OpenHands.git
synced 2026-04-29 03:00:45 -04:00
Hide All toggle on SaaS LLM settings (#14013)
Co-authored-by: openhands <openhands@all-hands.dev> Co-authored-by: allhands-bot <allhands-bot@users.noreply.github.com>
This commit is contained in:
@@ -18,6 +18,8 @@ Local run troubleshooting notes:
|
||||
- If local runtime startup fails with `duplicate session: test-session`, clear the stale tmux session on the default socket: `tmux -S /tmp/tmux-$(id -u)/default kill-session -t test-session`.
|
||||
- Local runtime browser startup expects Playwright browsers under `~/.cache/playwright`; if needed run `PLAYWRIGHT_BROWSERS_PATH=$HOME/.cache/playwright poetry run playwright install chromium`.
|
||||
- In this sandbox environment, an inherited `SESSION_API_KEY` can make `/api/v1/settings` return 401 in the browser. Unset it before `make run` when you want to use the local web UI directly.
|
||||
- In this sandbox, `frontend`'s `npm run dev:mock` / `dev:mock:saas` can start but still be awkward to browse through the work-host proxy. For PR QA screenshots, a reliable fallback is to `npm run build` with the desired `VITE_MOCK_*` env, then serve `build/` with a tiny custom HTTP server that returns the minimal mock JSON endpoints needed by the settings page.
|
||||
|
||||
|
||||
IMPORTANT: Before making any changes to the codebase, ALWAYS run `make install-pre-commit-hooks` to ensure pre-commit hooks are properly installed.
|
||||
|
||||
|
||||
@@ -435,6 +435,20 @@ describe("SdkSectionPage", () => {
|
||||
|
||||
|
||||
|
||||
it("shows the advanced toggle when it is forced for a critical-only schema", async () => {
|
||||
vi.spyOn(SettingsService, "getSettings").mockResolvedValue(buildSavableSettings());
|
||||
|
||||
renderSdkSectionPage({
|
||||
sectionKeys: ["llm"],
|
||||
forceShowAdvancedView: true,
|
||||
});
|
||||
|
||||
await screen.findByTestId("sdk-section-basic-toggle");
|
||||
expect(screen.getByTestId("sdk-section-advanced-toggle")).toBeInTheDocument();
|
||||
expect(screen.queryByTestId("sdk-section-all-toggle")).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
|
||||
it("shows the all toggle instead of an empty advanced tier for minor-only schemas", async () => {
|
||||
const schema: NonNullable<Settings["agent_settings_schema"]> = {
|
||||
model_name: "AgentSettings",
|
||||
|
||||
@@ -243,6 +243,49 @@ describe("LlmSettingsScreen", () => {
|
||||
expect(screen.getByTestId("base-url-input")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("shows Advanced and All toggles in OSS mode for the default LLM route schema", async () => {
|
||||
vi.spyOn(SettingsService, "getSettings").mockResolvedValue(buildSettings());
|
||||
|
||||
renderLlmSettingsScreen({ appMode: "oss" });
|
||||
|
||||
await screen.findByTestId("llm-settings-screen");
|
||||
expect(
|
||||
screen.getByTestId("sdk-section-advanced-toggle"),
|
||||
).toBeInTheDocument();
|
||||
expect(screen.getByTestId("sdk-section-all-toggle")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("keeps Advanced visible but hides All in SaaS mode for the default LLM route schema", async () => {
|
||||
vi.spyOn(
|
||||
organizationService,
|
||||
"getOrganizationAgentSettings",
|
||||
).mockResolvedValue(
|
||||
buildSettings({
|
||||
agent_settings: {
|
||||
llm: {
|
||||
model: "openai/gpt-4o",
|
||||
},
|
||||
},
|
||||
}),
|
||||
);
|
||||
|
||||
renderLlmSettingsScreen({ appMode: "saas", scope: "org" });
|
||||
|
||||
await screen.findByTestId("llm-settings-screen");
|
||||
expect(
|
||||
screen.getByTestId("sdk-section-advanced-toggle"),
|
||||
).toBeInTheDocument();
|
||||
expect(
|
||||
screen.queryByTestId("sdk-section-all-toggle"),
|
||||
).not.toBeInTheDocument();
|
||||
|
||||
await userEvent.click(screen.getByTestId("sdk-section-advanced-toggle"));
|
||||
|
||||
expect(screen.getByTestId("llm-settings-form-advanced")).toBeInTheDocument();
|
||||
expect(screen.getByTestId("llm-custom-model-input")).toBeInTheDocument();
|
||||
expect(screen.getByTestId("base-url-input")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("uses schema defaults for custom-rendered advanced fields", async () => {
|
||||
const schema = structuredClone(
|
||||
MOCK_DEFAULT_USER_SETTINGS.agent_settings_schema!,
|
||||
|
||||
@@ -49,6 +49,35 @@ const getLessDetailedView = (
|
||||
): SettingsView =>
|
||||
VIEW_ORDER[nextView] < VIEW_ORDER[currentView] ? nextView : currentView;
|
||||
|
||||
const normalizeView = (
|
||||
view: SettingsView,
|
||||
{
|
||||
showAdvanced,
|
||||
showAll,
|
||||
}: {
|
||||
showAdvanced: boolean;
|
||||
showAll: boolean;
|
||||
},
|
||||
): SettingsView => {
|
||||
if (view === "all") {
|
||||
if (showAll) {
|
||||
return "all";
|
||||
}
|
||||
|
||||
return showAdvanced ? "advanced" : "basic";
|
||||
}
|
||||
|
||||
if (view === "advanced") {
|
||||
if (showAdvanced) {
|
||||
return "advanced";
|
||||
}
|
||||
|
||||
return showAll ? "all" : "basic";
|
||||
}
|
||||
|
||||
return "basic";
|
||||
};
|
||||
|
||||
export interface SdkSectionHeaderProps {
|
||||
values: SettingsFormValues;
|
||||
isDisabled: boolean;
|
||||
@@ -75,6 +104,8 @@ export function SdkSectionPage({
|
||||
buildPayload,
|
||||
onSaveSuccess,
|
||||
getInitialView,
|
||||
forceShowAdvancedView = false,
|
||||
allowAllView = true,
|
||||
testId = "sdk-section-settings-screen",
|
||||
}: {
|
||||
sectionKeys: string[];
|
||||
@@ -97,6 +128,8 @@ export function SdkSectionPage({
|
||||
settings: Settings,
|
||||
filteredSchema: SettingsSchema,
|
||||
) => SettingsView;
|
||||
forceShowAdvancedView?: boolean;
|
||||
allowAllView?: boolean;
|
||||
testId?: string;
|
||||
}) {
|
||||
const { t } = useTranslation();
|
||||
@@ -148,8 +181,9 @@ export function SdkSectionPage({
|
||||
};
|
||||
}, [schema, stableSectionKeys]);
|
||||
|
||||
const showAdvanced = hasAdvancedSettings(filteredSchema);
|
||||
const showAll = hasMinorSettings(filteredSchema);
|
||||
const showAdvanced =
|
||||
forceShowAdvancedView || hasAdvancedSettings(filteredSchema);
|
||||
const showAll = allowAllView && hasMinorSettings(filteredSchema);
|
||||
|
||||
const initialValues = React.useMemo(() => {
|
||||
if (!settings || !filteredSchema) return null;
|
||||
@@ -162,10 +196,20 @@ export function SdkSectionPage({
|
||||
|
||||
const initialView = React.useMemo(() => {
|
||||
if (!settings || !filteredSchema) return null;
|
||||
return getInitialView
|
||||
|
||||
const resolvedInitialView = getInitialView
|
||||
? getInitialView(settings, filteredSchema)
|
||||
: inferInitialView(settings, filteredSchema, settingsSource);
|
||||
}, [settings, filteredSchema, getInitialView, settingsSource]);
|
||||
|
||||
return normalizeView(resolvedInitialView, { showAdvanced, showAll });
|
||||
}, [
|
||||
settings,
|
||||
filteredSchema,
|
||||
getInitialView,
|
||||
settingsSource,
|
||||
showAdvanced,
|
||||
showAll,
|
||||
]);
|
||||
|
||||
React.useEffect(() => {
|
||||
hasHydratedViewRef.current = false;
|
||||
|
||||
@@ -120,6 +120,20 @@ const MOCK_AGENT_SETTINGS_SCHEMA: NonNullable<
|
||||
secret: false,
|
||||
required: false,
|
||||
},
|
||||
{
|
||||
key: "llm.temperature",
|
||||
label: "Temperature",
|
||||
description: "Adjust randomness for non-deterministic model outputs.",
|
||||
section: "llm",
|
||||
section_label: "LLM",
|
||||
value_type: "number",
|
||||
default: null,
|
||||
choices: [],
|
||||
depends_on: [],
|
||||
prominence: "minor",
|
||||
secret: false,
|
||||
required: false,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
|
||||
@@ -391,6 +391,8 @@ export function LlmSettingsScreen({
|
||||
header={buildHeader}
|
||||
buildPayload={buildPayload}
|
||||
getInitialView={getInitialView}
|
||||
forceShowAdvancedView
|
||||
allowAllView={!isSaasMode}
|
||||
testId="llm-settings-screen"
|
||||
/>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user