mirror of
https://github.com/OpenHands/OpenHands.git
synced 2025-12-26 05:48:36 +08:00
refactor(frontend): Consolidate duplicate Settings type definitions (#12006)
This commit is contained in:
parent
976d9d1ab9
commit
5a21c59a3c
@ -16,7 +16,7 @@ describe("SettingsForm", () => {
|
||||
Component: () => (
|
||||
<SettingsForm
|
||||
settings={DEFAULT_SETTINGS}
|
||||
models={[DEFAULT_SETTINGS.LLM_MODEL]}
|
||||
models={[DEFAULT_SETTINGS.llm_model]}
|
||||
onClose={onCloseMock}
|
||||
/>
|
||||
),
|
||||
@ -33,7 +33,7 @@ describe("SettingsForm", () => {
|
||||
|
||||
expect(saveSettingsSpy).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
llm_model: DEFAULT_SETTINGS.LLM_MODEL,
|
||||
llm_model: DEFAULT_SETTINGS.llm_model,
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
@ -12,20 +12,20 @@ describe("hasAdvancedSettingsSet", () => {
|
||||
});
|
||||
|
||||
describe("should be true if", () => {
|
||||
test("LLM_BASE_URL is set", () => {
|
||||
test("llm_base_url is set", () => {
|
||||
expect(
|
||||
hasAdvancedSettingsSet({
|
||||
...DEFAULT_SETTINGS,
|
||||
LLM_BASE_URL: "test",
|
||||
llm_base_url: "test",
|
||||
}),
|
||||
).toBe(true);
|
||||
});
|
||||
|
||||
test("AGENT is not default value", () => {
|
||||
test("agent is not default value", () => {
|
||||
expect(
|
||||
hasAdvancedSettingsSet({
|
||||
...DEFAULT_SETTINGS,
|
||||
AGENT: "test",
|
||||
agent: "test",
|
||||
}),
|
||||
).toBe(true);
|
||||
});
|
||||
|
||||
@ -13,7 +13,7 @@ describe("Model name case preservation", () => {
|
||||
const settings = extractSettings(formData);
|
||||
|
||||
// Test that model names maintain their original casing
|
||||
expect(settings.LLM_MODEL).toBe("SambaNova/Meta-Llama-3.1-8B-Instruct");
|
||||
expect(settings.llm_model).toBe("SambaNova/Meta-Llama-3.1-8B-Instruct");
|
||||
});
|
||||
|
||||
it("should preserve openai model case", () => {
|
||||
@ -24,7 +24,7 @@ describe("Model name case preservation", () => {
|
||||
formData.set("language", "en");
|
||||
|
||||
const settings = extractSettings(formData);
|
||||
expect(settings.LLM_MODEL).toBe("openai/gpt-4o");
|
||||
expect(settings.llm_model).toBe("openai/gpt-4o");
|
||||
});
|
||||
|
||||
it("should preserve anthropic model case", () => {
|
||||
@ -35,7 +35,7 @@ describe("Model name case preservation", () => {
|
||||
formData.set("language", "en");
|
||||
|
||||
const settings = extractSettings(formData);
|
||||
expect(settings.LLM_MODEL).toBe("anthropic/claude-sonnet-4-20250514");
|
||||
expect(settings.llm_model).toBe("anthropic/claude-sonnet-4-20250514");
|
||||
});
|
||||
|
||||
it("should not automatically lowercase model names", () => {
|
||||
@ -48,7 +48,7 @@ describe("Model name case preservation", () => {
|
||||
const settings = extractSettings(formData);
|
||||
|
||||
// Test that camelCase and PascalCase are preserved
|
||||
expect(settings.LLM_MODEL).not.toBe("sambanova/meta-llama-3.1-8b-instruct");
|
||||
expect(settings.LLM_MODEL).toBe("SambaNova/Meta-Llama-3.1-8B-Instruct");
|
||||
expect(settings.llm_model).not.toBe("sambanova/meta-llama-3.1-8b-instruct");
|
||||
expect(settings.llm_model).toBe("SambaNova/Meta-Llama-3.1-8B-Instruct");
|
||||
});
|
||||
});
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import { openHands } from "../open-hands-axios";
|
||||
import { ApiSettings, PostApiSettings } from "./settings.types";
|
||||
import { Settings } from "#/types/settings";
|
||||
|
||||
/**
|
||||
* Settings service for managing application settings
|
||||
@ -8,8 +8,8 @@ class SettingsService {
|
||||
/**
|
||||
* Get the settings from the server or use the default settings if not found
|
||||
*/
|
||||
static async getSettings(): Promise<ApiSettings> {
|
||||
const { data } = await openHands.get<ApiSettings>("/api/settings");
|
||||
static async getSettings(): Promise<Settings> {
|
||||
const { data } = await openHands.get<Settings>("/api/settings");
|
||||
return data;
|
||||
}
|
||||
|
||||
@ -17,9 +17,7 @@ class SettingsService {
|
||||
* Save the settings to the server. Only valid settings are saved.
|
||||
* @param settings - the settings to save
|
||||
*/
|
||||
static async saveSettings(
|
||||
settings: Partial<PostApiSettings>,
|
||||
): Promise<boolean> {
|
||||
static async saveSettings(settings: Partial<Settings>): Promise<boolean> {
|
||||
const data = await openHands.post("/api/settings", settings);
|
||||
return data.status === 200;
|
||||
}
|
||||
|
||||
@ -1,54 +0,0 @@
|
||||
import { Provider } from "#/types/settings";
|
||||
|
||||
export type ApiSettings = {
|
||||
llm_model: string;
|
||||
llm_base_url: string;
|
||||
agent: string;
|
||||
language: string;
|
||||
llm_api_key: string | null;
|
||||
llm_api_key_set: boolean;
|
||||
search_api_key_set: boolean;
|
||||
confirmation_mode: boolean;
|
||||
security_analyzer: string | null;
|
||||
remote_runtime_resource_factor: number | null;
|
||||
enable_default_condenser: boolean;
|
||||
// Max size for condenser in backend settings
|
||||
condenser_max_size: number | null;
|
||||
enable_sound_notifications: boolean;
|
||||
enable_proactive_conversation_starters: boolean;
|
||||
enable_solvability_analysis: boolean;
|
||||
user_consents_to_analytics: boolean | null;
|
||||
search_api_key?: string;
|
||||
provider_tokens_set: Partial<Record<Provider, string | null>>;
|
||||
max_budget_per_task: number | null;
|
||||
mcp_config?: {
|
||||
sse_servers: (string | { url: string; api_key?: string })[];
|
||||
stdio_servers: {
|
||||
name: string;
|
||||
command: string;
|
||||
args?: string[];
|
||||
env?: Record<string, string>;
|
||||
}[];
|
||||
shttp_servers: (string | { url: string; api_key?: string })[];
|
||||
};
|
||||
email?: string;
|
||||
email_verified?: boolean;
|
||||
git_user_name?: string;
|
||||
git_user_email?: string;
|
||||
v1_enabled?: boolean;
|
||||
};
|
||||
|
||||
export type PostApiSettings = ApiSettings & {
|
||||
user_consents_to_analytics: boolean | null;
|
||||
search_api_key?: string;
|
||||
mcp_config?: {
|
||||
sse_servers: (string | { url: string; api_key?: string })[];
|
||||
stdio_servers: {
|
||||
name: string;
|
||||
command: string;
|
||||
args?: string[];
|
||||
env?: Record<string, string>;
|
||||
}[];
|
||||
shttp_servers: (string | { url: string; api_key?: string })[];
|
||||
};
|
||||
};
|
||||
@ -9,7 +9,7 @@ function ConfirmationModeEnabled() {
|
||||
|
||||
const { data: settings } = useSettings();
|
||||
|
||||
if (!settings?.CONFIRMATION_MODE) {
|
||||
if (!settings?.confirmation_mode) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
@ -20,13 +20,13 @@ export function EmailVerificationGuard({
|
||||
if (isLoading) return;
|
||||
|
||||
// If EMAIL_VERIFIED is explicitly false (not undefined or null)
|
||||
if (settings?.EMAIL_VERIFIED === false) {
|
||||
if (settings?.email_verified === false) {
|
||||
// Allow access to /settings/user but redirect from any other page
|
||||
if (pathname !== "/settings/user") {
|
||||
navigate("/settings/user", { replace: true });
|
||||
}
|
||||
}
|
||||
}, [settings?.EMAIL_VERIFIED, pathname, navigate, isLoading]);
|
||||
}, [settings?.email_verified, pathname, navigate, isLoading]);
|
||||
|
||||
return children;
|
||||
}
|
||||
|
||||
@ -71,19 +71,19 @@ export function Sidebar() {
|
||||
<OpenHandsLogoButton />
|
||||
</div>
|
||||
<div>
|
||||
<NewProjectButton disabled={settings?.EMAIL_VERIFIED === false} />
|
||||
<NewProjectButton disabled={settings?.email_verified === false} />
|
||||
</div>
|
||||
<ConversationPanelButton
|
||||
isOpen={conversationPanelIsOpen}
|
||||
onClick={() =>
|
||||
settings?.EMAIL_VERIFIED === false
|
||||
settings?.email_verified === false
|
||||
? null
|
||||
: setConversationPanelIsOpen((prev) => !prev)
|
||||
}
|
||||
disabled={settings?.EMAIL_VERIFIED === false}
|
||||
disabled={settings?.email_verified === false}
|
||||
/>
|
||||
<MicroagentManagementButton
|
||||
disabled={settings?.EMAIL_VERIFIED === false}
|
||||
disabled={settings?.email_verified === false}
|
||||
/>
|
||||
</div>
|
||||
|
||||
|
||||
@ -41,11 +41,11 @@ export function SettingsForm({ settings, models, onClose }: SettingsFormProps) {
|
||||
onClose();
|
||||
|
||||
posthog.capture("settings_saved", {
|
||||
LLM_MODEL: newSettings.LLM_MODEL,
|
||||
LLM_API_KEY_SET: newSettings.LLM_API_KEY_SET ? "SET" : "UNSET",
|
||||
SEARCH_API_KEY_SET: newSettings.SEARCH_API_KEY ? "SET" : "UNSET",
|
||||
LLM_MODEL: newSettings.llm_model,
|
||||
LLM_API_KEY_SET: newSettings.llm_api_key_set ? "SET" : "UNSET",
|
||||
SEARCH_API_KEY_SET: newSettings.search_api_key ? "SET" : "UNSET",
|
||||
REMOTE_RUNTIME_RESOURCE_FACTOR:
|
||||
newSettings.REMOTE_RUNTIME_RESOURCE_FACTOR,
|
||||
newSettings.remote_runtime_resource_factor,
|
||||
});
|
||||
},
|
||||
});
|
||||
@ -67,7 +67,7 @@ export function SettingsForm({ settings, models, onClose }: SettingsFormProps) {
|
||||
}
|
||||
};
|
||||
|
||||
const isLLMKeySet = settings.LLM_API_KEY_SET;
|
||||
const isLLMKeySet = settings.llm_api_key_set;
|
||||
|
||||
return (
|
||||
<div>
|
||||
@ -80,7 +80,7 @@ export function SettingsForm({ settings, models, onClose }: SettingsFormProps) {
|
||||
<div className="flex flex-col gap-[17px]">
|
||||
<ModelSelector
|
||||
models={organizeModelsAndProviders(models)}
|
||||
currentModel={settings.LLM_MODEL}
|
||||
currentModel={settings.llm_model}
|
||||
wrapperClassName="!flex-col !gap-[17px]"
|
||||
labelClassName={SETTINGS_FORM.LABEL_CLASSNAME}
|
||||
/>
|
||||
|
||||
@ -24,7 +24,7 @@ export function useAddMcpServer() {
|
||||
mutationFn: async (server: MCPServerConfig): Promise<void> => {
|
||||
if (!settings) return;
|
||||
|
||||
const currentConfig = settings.MCP_CONFIG || {
|
||||
const currentConfig = settings.mcp_config || {
|
||||
sse_servers: [],
|
||||
stdio_servers: [],
|
||||
shttp_servers: [],
|
||||
@ -57,7 +57,7 @@ export function useAddMcpServer() {
|
||||
|
||||
const apiSettings = {
|
||||
mcp_config: newConfig,
|
||||
v1_enabled: settings.V1_ENABLED,
|
||||
v1_enabled: settings.v1_enabled,
|
||||
};
|
||||
|
||||
await SettingsService.saveSettings(apiSettings);
|
||||
|
||||
@ -51,7 +51,7 @@ export const useCreateConversation = () => {
|
||||
agentType,
|
||||
} = variables;
|
||||
|
||||
const useV1 = !!settings?.V1_ENABLED && !createMicroagent;
|
||||
const useV1 = !!settings?.v1_enabled && !createMicroagent;
|
||||
|
||||
if (useV1) {
|
||||
// Use V1 API - creates a conversation start task
|
||||
|
||||
@ -9,9 +9,9 @@ export function useDeleteMcpServer() {
|
||||
|
||||
return useMutation({
|
||||
mutationFn: async (serverId: string): Promise<void> => {
|
||||
if (!settings?.MCP_CONFIG) return;
|
||||
if (!settings?.mcp_config) return;
|
||||
|
||||
const newConfig: MCPConfig = { ...settings.MCP_CONFIG };
|
||||
const newConfig: MCPConfig = { ...settings.mcp_config };
|
||||
const [serverType, indexStr] = serverId.split("-");
|
||||
const index = parseInt(indexStr, 10);
|
||||
|
||||
@ -25,7 +25,7 @@ export function useDeleteMcpServer() {
|
||||
|
||||
const apiSettings = {
|
||||
mcp_config: newConfig,
|
||||
v1_enabled: settings.V1_ENABLED,
|
||||
v1_enabled: settings.v1_enabled,
|
||||
};
|
||||
|
||||
await SettingsService.saveSettings(apiSettings);
|
||||
|
||||
@ -2,43 +2,28 @@ import { useMutation, useQueryClient } from "@tanstack/react-query";
|
||||
import { usePostHog } from "posthog-js/react";
|
||||
import { DEFAULT_SETTINGS } from "#/services/settings";
|
||||
import SettingsService from "#/api/settings-service/settings-service.api";
|
||||
import { PostSettings } from "#/types/settings";
|
||||
import { PostApiSettings } from "#/api/settings-service/settings.types";
|
||||
import { Settings } from "#/types/settings";
|
||||
import { useSettings } from "../query/use-settings";
|
||||
|
||||
const saveSettingsMutationFn = async (settings: Partial<PostSettings>) => {
|
||||
const apiSettings: Partial<PostApiSettings> = {
|
||||
llm_model: settings.LLM_MODEL,
|
||||
llm_base_url: settings.LLM_BASE_URL,
|
||||
agent: settings.AGENT || DEFAULT_SETTINGS.AGENT,
|
||||
language: settings.LANGUAGE || DEFAULT_SETTINGS.LANGUAGE,
|
||||
confirmation_mode: settings.CONFIRMATION_MODE,
|
||||
security_analyzer: settings.SECURITY_ANALYZER,
|
||||
const saveSettingsMutationFn = async (settings: Partial<Settings>) => {
|
||||
const settingsToSave: Partial<Settings> = {
|
||||
...settings,
|
||||
agent: settings.agent || DEFAULT_SETTINGS.agent,
|
||||
language: settings.language || DEFAULT_SETTINGS.language,
|
||||
llm_api_key:
|
||||
settings.llm_api_key === ""
|
||||
? ""
|
||||
: settings.llm_api_key?.trim() || undefined,
|
||||
remote_runtime_resource_factor: settings.REMOTE_RUNTIME_RESOURCE_FACTOR,
|
||||
enable_default_condenser: settings.ENABLE_DEFAULT_CONDENSER,
|
||||
condenser_max_size:
|
||||
settings.CONDENSER_MAX_SIZE ?? DEFAULT_SETTINGS.CONDENSER_MAX_SIZE,
|
||||
enable_sound_notifications: settings.ENABLE_SOUND_NOTIFICATIONS,
|
||||
user_consents_to_analytics: settings.user_consents_to_analytics,
|
||||
provider_tokens_set: settings.PROVIDER_TOKENS_SET,
|
||||
mcp_config: settings.MCP_CONFIG,
|
||||
enable_proactive_conversation_starters:
|
||||
settings.ENABLE_PROACTIVE_CONVERSATION_STARTERS,
|
||||
enable_solvability_analysis: settings.ENABLE_SOLVABILITY_ANALYSIS,
|
||||
search_api_key: settings.SEARCH_API_KEY?.trim() || "",
|
||||
max_budget_per_task: settings.MAX_BUDGET_PER_TASK,
|
||||
settings.condenser_max_size ?? DEFAULT_SETTINGS.condenser_max_size,
|
||||
search_api_key: settings.search_api_key?.trim() || "",
|
||||
git_user_name:
|
||||
settings.GIT_USER_NAME?.trim() || DEFAULT_SETTINGS.GIT_USER_NAME,
|
||||
settings.git_user_name?.trim() || DEFAULT_SETTINGS.git_user_name,
|
||||
git_user_email:
|
||||
settings.GIT_USER_EMAIL?.trim() || DEFAULT_SETTINGS.GIT_USER_EMAIL,
|
||||
v1_enabled: settings.V1_ENABLED,
|
||||
settings.git_user_email?.trim() || DEFAULT_SETTINGS.git_user_email,
|
||||
};
|
||||
|
||||
await SettingsService.saveSettings(apiSettings);
|
||||
await SettingsService.saveSettings(settingsToSave);
|
||||
};
|
||||
|
||||
export const useSaveSettings = () => {
|
||||
@ -47,18 +32,18 @@ export const useSaveSettings = () => {
|
||||
const { data: currentSettings } = useSettings();
|
||||
|
||||
return useMutation({
|
||||
mutationFn: async (settings: Partial<PostSettings>) => {
|
||||
mutationFn: async (settings: Partial<Settings>) => {
|
||||
const newSettings = { ...currentSettings, ...settings };
|
||||
|
||||
// Track MCP configuration changes
|
||||
if (
|
||||
settings.MCP_CONFIG &&
|
||||
currentSettings?.MCP_CONFIG !== settings.MCP_CONFIG
|
||||
settings.mcp_config &&
|
||||
currentSettings?.mcp_config !== settings.mcp_config
|
||||
) {
|
||||
const hasMcpConfig = !!settings.MCP_CONFIG;
|
||||
const sseServersCount = settings.MCP_CONFIG?.sse_servers?.length || 0;
|
||||
const hasMcpConfig = !!settings.mcp_config;
|
||||
const sseServersCount = settings.mcp_config?.sse_servers?.length || 0;
|
||||
const stdioServersCount =
|
||||
settings.MCP_CONFIG?.stdio_servers?.length || 0;
|
||||
settings.mcp_config?.stdio_servers?.length || 0;
|
||||
|
||||
// Track MCP configuration usage
|
||||
posthog.capture("mcp_config_updated", {
|
||||
|
||||
@ -28,9 +28,9 @@ export function useUpdateMcpServer() {
|
||||
serverId: string;
|
||||
server: MCPServerConfig;
|
||||
}): Promise<void> => {
|
||||
if (!settings?.MCP_CONFIG) return;
|
||||
if (!settings?.mcp_config) return;
|
||||
|
||||
const newConfig = { ...settings.MCP_CONFIG };
|
||||
const newConfig = { ...settings.mcp_config };
|
||||
const [serverType, indexStr] = serverId.split("-");
|
||||
const index = parseInt(indexStr, 10);
|
||||
|
||||
@ -59,7 +59,7 @@ export function useUpdateMcpServer() {
|
||||
|
||||
const apiSettings = {
|
||||
mcp_config: newConfig,
|
||||
v1_enabled: settings.V1_ENABLED,
|
||||
v1_enabled: settings.v1_enabled,
|
||||
};
|
||||
|
||||
await SettingsService.saveSettings(apiSettings);
|
||||
|
||||
@ -6,37 +6,18 @@ import { Settings } from "#/types/settings";
|
||||
import { useIsAuthed } from "./use-is-authed";
|
||||
|
||||
const getSettingsQueryFn = async (): Promise<Settings> => {
|
||||
const apiSettings = await SettingsService.getSettings();
|
||||
const settings = await SettingsService.getSettings();
|
||||
|
||||
return {
|
||||
LLM_MODEL: apiSettings.llm_model,
|
||||
LLM_BASE_URL: apiSettings.llm_base_url,
|
||||
AGENT: apiSettings.agent,
|
||||
LANGUAGE: apiSettings.language,
|
||||
CONFIRMATION_MODE: apiSettings.confirmation_mode,
|
||||
SECURITY_ANALYZER: apiSettings.security_analyzer,
|
||||
LLM_API_KEY_SET: apiSettings.llm_api_key_set,
|
||||
SEARCH_API_KEY_SET: apiSettings.search_api_key_set,
|
||||
REMOTE_RUNTIME_RESOURCE_FACTOR: apiSettings.remote_runtime_resource_factor,
|
||||
PROVIDER_TOKENS_SET: apiSettings.provider_tokens_set,
|
||||
ENABLE_DEFAULT_CONDENSER: apiSettings.enable_default_condenser,
|
||||
CONDENSER_MAX_SIZE:
|
||||
apiSettings.condenser_max_size ?? DEFAULT_SETTINGS.CONDENSER_MAX_SIZE,
|
||||
ENABLE_SOUND_NOTIFICATIONS: apiSettings.enable_sound_notifications,
|
||||
ENABLE_PROACTIVE_CONVERSATION_STARTERS:
|
||||
apiSettings.enable_proactive_conversation_starters,
|
||||
ENABLE_SOLVABILITY_ANALYSIS: apiSettings.enable_solvability_analysis,
|
||||
USER_CONSENTS_TO_ANALYTICS: apiSettings.user_consents_to_analytics,
|
||||
SEARCH_API_KEY: apiSettings.search_api_key || "",
|
||||
MAX_BUDGET_PER_TASK: apiSettings.max_budget_per_task,
|
||||
EMAIL: apiSettings.email || "",
|
||||
EMAIL_VERIFIED: apiSettings.email_verified,
|
||||
MCP_CONFIG: apiSettings.mcp_config,
|
||||
GIT_USER_NAME: apiSettings.git_user_name || DEFAULT_SETTINGS.GIT_USER_NAME,
|
||||
GIT_USER_EMAIL:
|
||||
apiSettings.git_user_email || DEFAULT_SETTINGS.GIT_USER_EMAIL,
|
||||
IS_NEW_USER: false,
|
||||
V1_ENABLED: apiSettings.v1_enabled ?? DEFAULT_SETTINGS.V1_ENABLED,
|
||||
...settings,
|
||||
condenser_max_size:
|
||||
settings.condenser_max_size ?? DEFAULT_SETTINGS.condenser_max_size,
|
||||
search_api_key: settings.search_api_key || "",
|
||||
email: settings.email || "",
|
||||
git_user_name: settings.git_user_name || DEFAULT_SETTINGS.git_user_name,
|
||||
git_user_email: settings.git_user_email || DEFAULT_SETTINGS.git_user_email,
|
||||
is_new_user: false,
|
||||
v1_enabled: settings.v1_enabled ?? DEFAULT_SETTINGS.v1_enabled,
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@ -15,7 +15,7 @@ import { useSettings } from "#/hooks/query/use-settings";
|
||||
*/
|
||||
export const useStartTasks = (limit = 10) => {
|
||||
const { data: settings } = useSettings();
|
||||
const isV1Enabled = settings?.V1_ENABLED;
|
||||
const isV1Enabled = settings?.v1_enabled;
|
||||
|
||||
return useQuery({
|
||||
queryKey: ["start-tasks", "search", limit],
|
||||
|
||||
@ -19,7 +19,7 @@ export const useSyncPostHogConsent = () => {
|
||||
return;
|
||||
}
|
||||
|
||||
const backendConsent = settings.USER_CONSENTS_TO_ANALYTICS;
|
||||
const backendConsent = settings.user_consents_to_analytics;
|
||||
|
||||
// Only sync if there's a backend preference set
|
||||
if (backendConsent !== null) {
|
||||
|
||||
@ -17,7 +17,7 @@ export const useTracking = () => {
|
||||
app_surface: config?.APP_MODE || "unknown",
|
||||
plan_tier: null,
|
||||
current_url: window.location.href,
|
||||
user_email: settings?.EMAIL || settings?.GIT_USER_EMAIL || null,
|
||||
user_email: settings?.email || settings?.git_user_email || null,
|
||||
};
|
||||
|
||||
const trackLoginButtonClick = ({ provider }: { provider: Provider }) => {
|
||||
|
||||
@ -6,8 +6,8 @@ export const useUserProviders = () => {
|
||||
const { data: settings, isLoading: isLoadingSettings } = useSettings();
|
||||
|
||||
const providers = React.useMemo(
|
||||
() => convertRawProvidersToList(settings?.PROVIDER_TOKENS_SET),
|
||||
[settings?.PROVIDER_TOKENS_SET],
|
||||
() => convertRawProvidersToList(settings?.provider_tokens_set),
|
||||
[settings?.provider_tokens_set],
|
||||
);
|
||||
|
||||
return {
|
||||
|
||||
@ -3,7 +3,6 @@ import { FILE_SERVICE_HANDLERS } from "./file-service-handlers";
|
||||
import { TASK_SUGGESTIONS_HANDLERS } from "./task-suggestions-handlers";
|
||||
import { SECRETS_HANDLERS } from "./secrets-handlers";
|
||||
import { GIT_REPOSITORY_HANDLERS } from "./git-repository-handlers";
|
||||
|
||||
import {
|
||||
SETTINGS_HANDLERS,
|
||||
MOCK_DEFAULT_USER_SETTINGS,
|
||||
|
||||
@ -1,37 +1,33 @@
|
||||
import { http, delay, HttpResponse } from "msw";
|
||||
import {
|
||||
ApiSettings,
|
||||
PostApiSettings,
|
||||
} from "#/api/settings-service/settings.types";
|
||||
import { GetConfigResponse } from "#/api/option-service/option.types";
|
||||
import { DEFAULT_SETTINGS } from "#/services/settings";
|
||||
import { Provider } from "#/types/settings";
|
||||
import { Provider, Settings } from "#/types/settings";
|
||||
|
||||
export const MOCK_DEFAULT_USER_SETTINGS: ApiSettings | PostApiSettings = {
|
||||
llm_model: DEFAULT_SETTINGS.LLM_MODEL,
|
||||
llm_base_url: DEFAULT_SETTINGS.LLM_BASE_URL,
|
||||
export const MOCK_DEFAULT_USER_SETTINGS: Settings = {
|
||||
llm_model: DEFAULT_SETTINGS.llm_model,
|
||||
llm_base_url: DEFAULT_SETTINGS.llm_base_url,
|
||||
llm_api_key: null,
|
||||
llm_api_key_set: DEFAULT_SETTINGS.LLM_API_KEY_SET,
|
||||
search_api_key_set: DEFAULT_SETTINGS.SEARCH_API_KEY_SET,
|
||||
agent: DEFAULT_SETTINGS.AGENT,
|
||||
language: DEFAULT_SETTINGS.LANGUAGE,
|
||||
confirmation_mode: DEFAULT_SETTINGS.CONFIRMATION_MODE,
|
||||
security_analyzer: DEFAULT_SETTINGS.SECURITY_ANALYZER,
|
||||
llm_api_key_set: DEFAULT_SETTINGS.llm_api_key_set,
|
||||
search_api_key_set: DEFAULT_SETTINGS.search_api_key_set,
|
||||
agent: DEFAULT_SETTINGS.agent,
|
||||
language: DEFAULT_SETTINGS.language,
|
||||
confirmation_mode: DEFAULT_SETTINGS.confirmation_mode,
|
||||
security_analyzer: DEFAULT_SETTINGS.security_analyzer,
|
||||
remote_runtime_resource_factor:
|
||||
DEFAULT_SETTINGS.REMOTE_RUNTIME_RESOURCE_FACTOR,
|
||||
DEFAULT_SETTINGS.remote_runtime_resource_factor,
|
||||
provider_tokens_set: {},
|
||||
enable_default_condenser: DEFAULT_SETTINGS.ENABLE_DEFAULT_CONDENSER,
|
||||
condenser_max_size: DEFAULT_SETTINGS.CONDENSER_MAX_SIZE,
|
||||
enable_sound_notifications: DEFAULT_SETTINGS.ENABLE_SOUND_NOTIFICATIONS,
|
||||
enable_default_condenser: DEFAULT_SETTINGS.enable_default_condenser,
|
||||
condenser_max_size: DEFAULT_SETTINGS.condenser_max_size,
|
||||
enable_sound_notifications: DEFAULT_SETTINGS.enable_sound_notifications,
|
||||
enable_proactive_conversation_starters:
|
||||
DEFAULT_SETTINGS.ENABLE_PROACTIVE_CONVERSATION_STARTERS,
|
||||
enable_solvability_analysis: DEFAULT_SETTINGS.ENABLE_SOLVABILITY_ANALYSIS,
|
||||
user_consents_to_analytics: DEFAULT_SETTINGS.USER_CONSENTS_TO_ANALYTICS,
|
||||
max_budget_per_task: DEFAULT_SETTINGS.MAX_BUDGET_PER_TASK,
|
||||
DEFAULT_SETTINGS.enable_proactive_conversation_starters,
|
||||
enable_solvability_analysis: DEFAULT_SETTINGS.enable_solvability_analysis,
|
||||
user_consents_to_analytics: DEFAULT_SETTINGS.user_consents_to_analytics,
|
||||
max_budget_per_task: DEFAULT_SETTINGS.max_budget_per_task,
|
||||
};
|
||||
|
||||
const MOCK_USER_PREFERENCES: {
|
||||
settings: ApiSettings | PostApiSettings | null;
|
||||
settings: Settings | null;
|
||||
} = {
|
||||
settings: null,
|
||||
};
|
||||
@ -111,7 +107,7 @@ export const SETTINGS_HANDLERS = [
|
||||
|
||||
MOCK_USER_PREFERENCES.settings = {
|
||||
...current,
|
||||
...(body as Partial<ApiSettings>),
|
||||
...(body as Partial<Settings>),
|
||||
};
|
||||
|
||||
return HttpResponse.json(null, { status: 200 });
|
||||
|
||||
@ -56,7 +56,7 @@ function AppSettingsScreen() {
|
||||
const languageValue = AvailableLanguages.find(
|
||||
({ label }) => label === languageLabel,
|
||||
)?.value;
|
||||
const language = languageValue || DEFAULT_SETTINGS.LANGUAGE;
|
||||
const language = languageValue || DEFAULT_SETTINGS.language;
|
||||
|
||||
const enableAnalytics =
|
||||
formData.get("enable-analytics-switch")?.toString() === "on";
|
||||
@ -77,21 +77,21 @@ function AppSettingsScreen() {
|
||||
|
||||
const gitUserName =
|
||||
formData.get("git-user-name-input")?.toString() ||
|
||||
DEFAULT_SETTINGS.GIT_USER_NAME;
|
||||
DEFAULT_SETTINGS.git_user_name;
|
||||
const gitUserEmail =
|
||||
formData.get("git-user-email-input")?.toString() ||
|
||||
DEFAULT_SETTINGS.GIT_USER_EMAIL;
|
||||
DEFAULT_SETTINGS.git_user_email;
|
||||
|
||||
saveSettings(
|
||||
{
|
||||
LANGUAGE: language,
|
||||
language,
|
||||
user_consents_to_analytics: enableAnalytics,
|
||||
ENABLE_SOUND_NOTIFICATIONS: enableSoundNotifications,
|
||||
ENABLE_PROACTIVE_CONVERSATION_STARTERS: enableProactiveConversations,
|
||||
ENABLE_SOLVABILITY_ANALYSIS: enableSolvabilityAnalysis,
|
||||
MAX_BUDGET_PER_TASK: maxBudgetPerTask,
|
||||
GIT_USER_NAME: gitUserName,
|
||||
GIT_USER_EMAIL: gitUserEmail,
|
||||
enable_sound_notifications: enableSoundNotifications,
|
||||
enable_proactive_conversation_starters: enableProactiveConversations,
|
||||
enable_solvability_analysis: enableSolvabilityAnalysis,
|
||||
max_budget_per_task: maxBudgetPerTask,
|
||||
git_user_name: gitUserName,
|
||||
git_user_email: gitUserEmail,
|
||||
},
|
||||
{
|
||||
onSuccess: () => {
|
||||
@ -120,7 +120,7 @@ function AppSettingsScreen() {
|
||||
({ label: langValue }) => langValue === value,
|
||||
)?.label;
|
||||
const currentLanguage = AvailableLanguages.find(
|
||||
({ value: langValue }) => langValue === settings?.LANGUAGE,
|
||||
({ value: langValue }) => langValue === settings?.language,
|
||||
)?.label;
|
||||
|
||||
setLanguageInputHasChanged(selectedLanguage !== currentLanguage);
|
||||
@ -128,12 +128,12 @@ function AppSettingsScreen() {
|
||||
|
||||
const checkIfAnalyticsSwitchHasChanged = (checked: boolean) => {
|
||||
// Treat null as true since analytics is opt-in by default
|
||||
const currentAnalytics = settings?.USER_CONSENTS_TO_ANALYTICS ?? true;
|
||||
const currentAnalytics = settings?.user_consents_to_analytics ?? true;
|
||||
setAnalyticsSwitchHasChanged(checked !== currentAnalytics);
|
||||
};
|
||||
|
||||
const checkIfSoundNotificationsSwitchHasChanged = (checked: boolean) => {
|
||||
const currentSoundNotifications = !!settings?.ENABLE_SOUND_NOTIFICATIONS;
|
||||
const currentSoundNotifications = !!settings?.enable_sound_notifications;
|
||||
setSoundNotificationsSwitchHasChanged(
|
||||
checked !== currentSoundNotifications,
|
||||
);
|
||||
@ -141,14 +141,14 @@ function AppSettingsScreen() {
|
||||
|
||||
const checkIfProactiveConversationsSwitchHasChanged = (checked: boolean) => {
|
||||
const currentProactiveConversations =
|
||||
!!settings?.ENABLE_PROACTIVE_CONVERSATION_STARTERS;
|
||||
!!settings?.enable_proactive_conversation_starters;
|
||||
setProactiveConversationsSwitchHasChanged(
|
||||
checked !== currentProactiveConversations,
|
||||
);
|
||||
};
|
||||
|
||||
const checkIfSolvabilityAnalysisSwitchHasChanged = (checked: boolean) => {
|
||||
const currentSolvabilityAnalysis = !!settings?.ENABLE_SOLVABILITY_ANALYSIS;
|
||||
const currentSolvabilityAnalysis = !!settings?.enable_solvability_analysis;
|
||||
setSolvabilityAnalysisSwitchHasChanged(
|
||||
checked !== currentSolvabilityAnalysis,
|
||||
);
|
||||
@ -156,17 +156,17 @@ function AppSettingsScreen() {
|
||||
|
||||
const checkIfMaxBudgetPerTaskHasChanged = (value: string) => {
|
||||
const newValue = parseMaxBudgetPerTask(value);
|
||||
const currentValue = settings?.MAX_BUDGET_PER_TASK;
|
||||
const currentValue = settings?.max_budget_per_task;
|
||||
setMaxBudgetPerTaskHasChanged(newValue !== currentValue);
|
||||
};
|
||||
|
||||
const checkIfGitUserNameHasChanged = (value: string) => {
|
||||
const currentValue = settings?.GIT_USER_NAME;
|
||||
const currentValue = settings?.git_user_name;
|
||||
setGitUserNameHasChanged(value !== currentValue);
|
||||
};
|
||||
|
||||
const checkIfGitUserEmailHasChanged = (value: string) => {
|
||||
const currentValue = settings?.GIT_USER_EMAIL;
|
||||
const currentValue = settings?.git_user_email;
|
||||
setGitUserEmailHasChanged(value !== currentValue);
|
||||
};
|
||||
|
||||
@ -193,14 +193,14 @@ function AppSettingsScreen() {
|
||||
<div className="flex flex-col gap-6">
|
||||
<LanguageInput
|
||||
name="language-input"
|
||||
defaultKey={settings.LANGUAGE}
|
||||
defaultKey={settings.language}
|
||||
onChange={checkIfLanguageInputHasChanged}
|
||||
/>
|
||||
|
||||
<SettingsSwitch
|
||||
testId="enable-analytics-switch"
|
||||
name="enable-analytics-switch"
|
||||
defaultIsToggled={settings.USER_CONSENTS_TO_ANALYTICS ?? true}
|
||||
defaultIsToggled={settings.user_consents_to_analytics ?? true}
|
||||
onToggle={checkIfAnalyticsSwitchHasChanged}
|
||||
>
|
||||
{t(I18nKey.ANALYTICS$SEND_ANONYMOUS_DATA)}
|
||||
@ -209,7 +209,7 @@ function AppSettingsScreen() {
|
||||
<SettingsSwitch
|
||||
testId="enable-sound-notifications-switch"
|
||||
name="enable-sound-notifications-switch"
|
||||
defaultIsToggled={!!settings.ENABLE_SOUND_NOTIFICATIONS}
|
||||
defaultIsToggled={!!settings.enable_sound_notifications}
|
||||
onToggle={checkIfSoundNotificationsSwitchHasChanged}
|
||||
>
|
||||
{t(I18nKey.SETTINGS$SOUND_NOTIFICATIONS)}
|
||||
@ -220,7 +220,7 @@ function AppSettingsScreen() {
|
||||
testId="enable-proactive-conversations-switch"
|
||||
name="enable-proactive-conversations-switch"
|
||||
defaultIsToggled={
|
||||
!!settings.ENABLE_PROACTIVE_CONVERSATION_STARTERS
|
||||
!!settings.enable_proactive_conversation_starters
|
||||
}
|
||||
onToggle={checkIfProactiveConversationsSwitchHasChanged}
|
||||
>
|
||||
@ -232,20 +232,20 @@ function AppSettingsScreen() {
|
||||
<SettingsSwitch
|
||||
testId="enable-solvability-analysis-switch"
|
||||
name="enable-solvability-analysis-switch"
|
||||
defaultIsToggled={!!settings.ENABLE_SOLVABILITY_ANALYSIS}
|
||||
defaultIsToggled={!!settings.enable_solvability_analysis}
|
||||
onToggle={checkIfSolvabilityAnalysisSwitchHasChanged}
|
||||
>
|
||||
{t(I18nKey.SETTINGS$SOLVABILITY_ANALYSIS)}
|
||||
</SettingsSwitch>
|
||||
)}
|
||||
|
||||
{!settings?.V1_ENABLED && (
|
||||
{!settings?.v1_enabled && (
|
||||
<SettingsInput
|
||||
testId="max-budget-per-task-input"
|
||||
name="max-budget-per-task-input"
|
||||
type="number"
|
||||
label={t(I18nKey.SETTINGS$MAX_BUDGET_PER_CONVERSATION)}
|
||||
defaultValue={settings.MAX_BUDGET_PER_TASK?.toString() || ""}
|
||||
defaultValue={settings.max_budget_per_task?.toString() || ""}
|
||||
onChange={checkIfMaxBudgetPerTaskHasChanged}
|
||||
placeholder={t(I18nKey.SETTINGS$MAXIMUM_BUDGET_USD)}
|
||||
min={1}
|
||||
@ -267,7 +267,7 @@ function AppSettingsScreen() {
|
||||
name="git-user-name-input"
|
||||
type="text"
|
||||
label={t(I18nKey.SETTINGS$GIT_USERNAME)}
|
||||
defaultValue={settings.GIT_USER_NAME || ""}
|
||||
defaultValue={settings.git_user_name || ""}
|
||||
onChange={checkIfGitUserNameHasChanged}
|
||||
placeholder="Username for git commits"
|
||||
className="w-full max-w-[680px]"
|
||||
@ -277,7 +277,7 @@ function AppSettingsScreen() {
|
||||
name="git-user-email-input"
|
||||
type="email"
|
||||
label={t(I18nKey.SETTINGS$GIT_EMAIL)}
|
||||
defaultValue={settings.GIT_USER_EMAIL || ""}
|
||||
defaultValue={settings.git_user_email || ""}
|
||||
onChange={checkIfGitUserEmailHasChanged}
|
||||
placeholder="Email for git commits"
|
||||
className="w-full max-w-[680px]"
|
||||
|
||||
@ -50,10 +50,10 @@ function GitSettingsScreen() {
|
||||
const [azureDevOpsHostInputHasValue, setAzureDevOpsHostInputHasValue] =
|
||||
React.useState(false);
|
||||
|
||||
const existingGithubHost = settings?.PROVIDER_TOKENS_SET.github;
|
||||
const existingGitlabHost = settings?.PROVIDER_TOKENS_SET.gitlab;
|
||||
const existingBitbucketHost = settings?.PROVIDER_TOKENS_SET.bitbucket;
|
||||
const existingAzureDevOpsHost = settings?.PROVIDER_TOKENS_SET.azure_devops;
|
||||
const existingGithubHost = settings?.provider_tokens_set.github;
|
||||
const existingGitlabHost = settings?.provider_tokens_set.gitlab;
|
||||
const existingBitbucketHost = settings?.provider_tokens_set.bitbucket;
|
||||
const existingAzureDevOpsHost = settings?.provider_tokens_set.azure_devops;
|
||||
|
||||
const isSaas = config?.APP_MODE === "saas";
|
||||
const isGitHubTokenSet = providers.includes("github");
|
||||
|
||||
@ -91,15 +91,15 @@ function LlmSettingsScreen() {
|
||||
|
||||
// Track confirmation mode state to control security analyzer visibility
|
||||
const [confirmationModeEnabled, setConfirmationModeEnabled] = React.useState(
|
||||
settings?.CONFIRMATION_MODE ?? DEFAULT_SETTINGS.CONFIRMATION_MODE,
|
||||
settings?.confirmation_mode ?? DEFAULT_SETTINGS.confirmation_mode,
|
||||
);
|
||||
|
||||
// Track selected security analyzer for form submission
|
||||
const [selectedSecurityAnalyzer, setSelectedSecurityAnalyzer] =
|
||||
React.useState(
|
||||
settings?.SECURITY_ANALYZER === null
|
||||
settings?.security_analyzer === null
|
||||
? "none"
|
||||
: (settings?.SECURITY_ANALYZER ?? DEFAULT_SETTINGS.SECURITY_ANALYZER),
|
||||
: (settings?.security_analyzer ?? DEFAULT_SETTINGS.security_analyzer),
|
||||
);
|
||||
|
||||
const [selectedProvider, setSelectedProvider] = React.useState<string | null>(
|
||||
@ -111,7 +111,7 @@ function LlmSettingsScreen() {
|
||||
);
|
||||
|
||||
// Determine if we should hide the API key input and use OpenHands-managed key (when using OpenHands provider in SaaS mode)
|
||||
const currentModel = currentSelectedModel || settings?.LLM_MODEL;
|
||||
const currentModel = currentSelectedModel || settings?.llm_model;
|
||||
|
||||
const isSaasMode = config?.APP_MODE === "saas";
|
||||
|
||||
@ -124,7 +124,7 @@ function LlmSettingsScreen() {
|
||||
if (dirtyInputs.model) {
|
||||
return currentModel?.startsWith("openhands/");
|
||||
}
|
||||
return settings?.LLM_MODEL?.startsWith("openhands/");
|
||||
return settings?.llm_model?.startsWith("openhands/");
|
||||
}
|
||||
|
||||
return false;
|
||||
@ -133,13 +133,13 @@ function LlmSettingsScreen() {
|
||||
const shouldUseOpenHandsKey = isOpenHandsProvider() && isSaasMode;
|
||||
|
||||
// Determine if we should hide the agent dropdown when V1 conversation API is enabled
|
||||
const isV1Enabled = settings?.V1_ENABLED;
|
||||
const isV1Enabled = settings?.v1_enabled;
|
||||
|
||||
React.useEffect(() => {
|
||||
const determineWhetherToToggleAdvancedSettings = () => {
|
||||
if (resources && settings) {
|
||||
return (
|
||||
isCustomModel(resources.models, settings.LLM_MODEL) ||
|
||||
isCustomModel(resources.models, settings.llm_model) ||
|
||||
hasAdvancedSettingsSet({
|
||||
...settings,
|
||||
})
|
||||
@ -157,24 +157,24 @@ function LlmSettingsScreen() {
|
||||
|
||||
// Initialize currentSelectedModel with the current settings
|
||||
React.useEffect(() => {
|
||||
if (settings?.LLM_MODEL) {
|
||||
setCurrentSelectedModel(settings.LLM_MODEL);
|
||||
if (settings?.llm_model) {
|
||||
setCurrentSelectedModel(settings.llm_model);
|
||||
}
|
||||
}, [settings?.LLM_MODEL]);
|
||||
}, [settings?.llm_model]);
|
||||
|
||||
// Update confirmation mode state when settings change
|
||||
React.useEffect(() => {
|
||||
if (settings?.CONFIRMATION_MODE !== undefined) {
|
||||
setConfirmationModeEnabled(settings.CONFIRMATION_MODE);
|
||||
if (settings?.confirmation_mode !== undefined) {
|
||||
setConfirmationModeEnabled(settings.confirmation_mode);
|
||||
}
|
||||
}, [settings?.CONFIRMATION_MODE]);
|
||||
}, [settings?.confirmation_mode]);
|
||||
|
||||
// Update selected security analyzer state when settings change
|
||||
React.useEffect(() => {
|
||||
if (settings?.SECURITY_ANALYZER !== undefined) {
|
||||
setSelectedSecurityAnalyzer(settings.SECURITY_ANALYZER || "none");
|
||||
if (settings?.security_analyzer !== undefined) {
|
||||
setSelectedSecurityAnalyzer(settings.security_analyzer || "none");
|
||||
}
|
||||
}, [settings?.SECURITY_ANALYZER]);
|
||||
}, [settings?.security_analyzer]);
|
||||
|
||||
// Handle URL parameters for SaaS subscription redirects
|
||||
React.useEffect(() => {
|
||||
@ -230,19 +230,19 @@ function LlmSettingsScreen() {
|
||||
|
||||
saveSettings(
|
||||
{
|
||||
LLM_MODEL: fullLlmModel,
|
||||
llm_model: fullLlmModel,
|
||||
llm_api_key: finalApiKey || null,
|
||||
SEARCH_API_KEY: searchApiKey || "",
|
||||
CONFIRMATION_MODE: confirmationMode,
|
||||
SECURITY_ANALYZER:
|
||||
search_api_key: searchApiKey || "",
|
||||
confirmation_mode: confirmationMode,
|
||||
security_analyzer:
|
||||
securityAnalyzer === "none"
|
||||
? null
|
||||
: securityAnalyzer || DEFAULT_SETTINGS.SECURITY_ANALYZER,
|
||||
: securityAnalyzer || DEFAULT_SETTINGS.security_analyzer,
|
||||
|
||||
// reset advanced settings
|
||||
LLM_BASE_URL: DEFAULT_SETTINGS.LLM_BASE_URL,
|
||||
AGENT: DEFAULT_SETTINGS.AGENT,
|
||||
ENABLE_DEFAULT_CONDENSER: DEFAULT_SETTINGS.ENABLE_DEFAULT_CONDENSER,
|
||||
llm_base_url: DEFAULT_SETTINGS.llm_base_url,
|
||||
agent: DEFAULT_SETTINGS.agent,
|
||||
enable_default_condenser: DEFAULT_SETTINGS.enable_default_condenser,
|
||||
},
|
||||
{
|
||||
onSuccess: handleSuccessfulMutation,
|
||||
@ -281,19 +281,19 @@ function LlmSettingsScreen() {
|
||||
|
||||
saveSettings(
|
||||
{
|
||||
LLM_MODEL: model,
|
||||
LLM_BASE_URL: baseUrl,
|
||||
llm_model: model,
|
||||
llm_base_url: baseUrl,
|
||||
llm_api_key: finalApiKey || null,
|
||||
SEARCH_API_KEY: searchApiKey || "",
|
||||
AGENT: agent,
|
||||
CONFIRMATION_MODE: confirmationMode,
|
||||
ENABLE_DEFAULT_CONDENSER: enableDefaultCondenser,
|
||||
CONDENSER_MAX_SIZE:
|
||||
condenserMaxSize ?? DEFAULT_SETTINGS.CONDENSER_MAX_SIZE,
|
||||
SECURITY_ANALYZER:
|
||||
search_api_key: searchApiKey || "",
|
||||
agent,
|
||||
confirmation_mode: confirmationMode,
|
||||
enable_default_condenser: enableDefaultCondenser,
|
||||
condenser_max_size:
|
||||
condenserMaxSize ?? DEFAULT_SETTINGS.condenser_max_size,
|
||||
security_analyzer:
|
||||
securityAnalyzer === "none"
|
||||
? null
|
||||
: securityAnalyzer || DEFAULT_SETTINGS.SECURITY_ANALYZER,
|
||||
: securityAnalyzer || DEFAULT_SETTINGS.security_analyzer,
|
||||
},
|
||||
{
|
||||
onSuccess: handleSuccessfulMutation,
|
||||
@ -323,7 +323,7 @@ function LlmSettingsScreen() {
|
||||
) => {
|
||||
// openai providers are special case; see ModelSelector
|
||||
// component for details
|
||||
const modelIsDirty = model !== settings?.LLM_MODEL.replace("openai/", "");
|
||||
const modelIsDirty = model !== settings?.llm_model.replace("openai/", "");
|
||||
setDirtyInputs((prev) => ({
|
||||
...prev,
|
||||
model: modelIsDirty,
|
||||
@ -351,7 +351,7 @@ function LlmSettingsScreen() {
|
||||
};
|
||||
|
||||
const handleSearchApiKeyIsDirty = (searchApiKey: string) => {
|
||||
const searchApiKeyIsDirty = searchApiKey !== settings?.SEARCH_API_KEY;
|
||||
const searchApiKeyIsDirty = searchApiKey !== settings?.search_api_key;
|
||||
setDirtyInputs((prev) => ({
|
||||
...prev,
|
||||
searchApiKey: searchApiKeyIsDirty,
|
||||
@ -359,7 +359,7 @@ function LlmSettingsScreen() {
|
||||
};
|
||||
|
||||
const handleCustomModelIsDirty = (model: string) => {
|
||||
const modelIsDirty = model !== settings?.LLM_MODEL && model !== "";
|
||||
const modelIsDirty = model !== settings?.llm_model && model !== "";
|
||||
setDirtyInputs((prev) => ({
|
||||
...prev,
|
||||
model: modelIsDirty,
|
||||
@ -370,7 +370,7 @@ function LlmSettingsScreen() {
|
||||
};
|
||||
|
||||
const handleBaseUrlIsDirty = (baseUrl: string) => {
|
||||
const baseUrlIsDirty = baseUrl !== settings?.LLM_BASE_URL;
|
||||
const baseUrlIsDirty = baseUrl !== settings?.llm_base_url;
|
||||
setDirtyInputs((prev) => ({
|
||||
...prev,
|
||||
baseUrl: baseUrlIsDirty,
|
||||
@ -378,7 +378,7 @@ function LlmSettingsScreen() {
|
||||
};
|
||||
|
||||
const handleAgentIsDirty = (agent: string) => {
|
||||
const agentIsDirty = agent !== settings?.AGENT && agent !== "";
|
||||
const agentIsDirty = agent !== settings?.agent && agent !== "";
|
||||
setDirtyInputs((prev) => ({
|
||||
...prev,
|
||||
agent: agentIsDirty,
|
||||
@ -386,7 +386,7 @@ function LlmSettingsScreen() {
|
||||
};
|
||||
|
||||
const handleConfirmationModeIsDirty = (isToggled: boolean) => {
|
||||
const confirmationModeIsDirty = isToggled !== settings?.CONFIRMATION_MODE;
|
||||
const confirmationModeIsDirty = isToggled !== settings?.confirmation_mode;
|
||||
setDirtyInputs((prev) => ({
|
||||
...prev,
|
||||
confirmationMode: confirmationModeIsDirty,
|
||||
@ -395,7 +395,7 @@ function LlmSettingsScreen() {
|
||||
|
||||
// When confirmation mode is enabled, set default security analyzer to "llm" if not already set
|
||||
if (isToggled && !selectedSecurityAnalyzer) {
|
||||
setSelectedSecurityAnalyzer(DEFAULT_SETTINGS.SECURITY_ANALYZER);
|
||||
setSelectedSecurityAnalyzer(DEFAULT_SETTINGS.security_analyzer);
|
||||
setDirtyInputs((prev) => ({
|
||||
...prev,
|
||||
securityAnalyzer: true,
|
||||
@ -405,7 +405,7 @@ function LlmSettingsScreen() {
|
||||
|
||||
const handleEnableDefaultCondenserIsDirty = (isToggled: boolean) => {
|
||||
const enableDefaultCondenserIsDirty =
|
||||
isToggled !== settings?.ENABLE_DEFAULT_CONDENSER;
|
||||
isToggled !== settings?.enable_default_condenser;
|
||||
setDirtyInputs((prev) => ({
|
||||
...prev,
|
||||
enableDefaultCondenser: enableDefaultCondenserIsDirty,
|
||||
@ -416,8 +416,8 @@ function LlmSettingsScreen() {
|
||||
const parsed = value ? Number.parseInt(value, 10) : undefined;
|
||||
const bounded = parsed !== undefined ? Math.max(20, parsed) : undefined;
|
||||
const condenserMaxSizeIsDirty =
|
||||
(bounded ?? DEFAULT_SETTINGS.CONDENSER_MAX_SIZE) !==
|
||||
(settings?.CONDENSER_MAX_SIZE ?? DEFAULT_SETTINGS.CONDENSER_MAX_SIZE);
|
||||
(bounded ?? DEFAULT_SETTINGS.condenser_max_size) !==
|
||||
(settings?.condenser_max_size ?? DEFAULT_SETTINGS.condenser_max_size);
|
||||
setDirtyInputs((prev) => ({
|
||||
...prev,
|
||||
condenserMaxSize: condenserMaxSizeIsDirty,
|
||||
@ -426,7 +426,7 @@ function LlmSettingsScreen() {
|
||||
|
||||
const handleSecurityAnalyzerIsDirty = (securityAnalyzer: string) => {
|
||||
const securityAnalyzerIsDirty =
|
||||
securityAnalyzer !== settings?.SECURITY_ANALYZER;
|
||||
securityAnalyzer !== settings?.security_analyzer;
|
||||
setDirtyInputs((prev) => ({
|
||||
...prev,
|
||||
securityAnalyzer: securityAnalyzerIsDirty,
|
||||
@ -512,12 +512,12 @@ function LlmSettingsScreen() {
|
||||
<>
|
||||
<ModelSelector
|
||||
models={modelsAndProviders}
|
||||
currentModel={settings.LLM_MODEL || DEFAULT_OPENHANDS_MODEL}
|
||||
currentModel={settings.llm_model || DEFAULT_OPENHANDS_MODEL}
|
||||
onChange={handleModelIsDirty}
|
||||
onDefaultValuesChanged={onDefaultValuesChanged}
|
||||
wrapperClassName="!flex-col !gap-6"
|
||||
/>
|
||||
{(settings.LLM_MODEL?.startsWith("openhands/") ||
|
||||
{(settings.llm_model?.startsWith("openhands/") ||
|
||||
currentSelectedModel?.startsWith("openhands/")) && (
|
||||
<OpenHandsApiKeyHelp testId="openhands-api-key-help" />
|
||||
)}
|
||||
@ -532,11 +532,11 @@ function LlmSettingsScreen() {
|
||||
label={t(I18nKey.SETTINGS_FORM$API_KEY)}
|
||||
type="password"
|
||||
className="w-full max-w-[680px]"
|
||||
placeholder={settings.LLM_API_KEY_SET ? "<hidden>" : ""}
|
||||
placeholder={settings.llm_api_key_set ? "<hidden>" : ""}
|
||||
onChange={handleApiKeyIsDirty}
|
||||
startContent={
|
||||
settings.LLM_API_KEY_SET && (
|
||||
<KeyStatusIcon isSet={settings.LLM_API_KEY_SET} />
|
||||
settings.llm_api_key_set && (
|
||||
<KeyStatusIcon isSet={settings.llm_api_key_set} />
|
||||
)
|
||||
}
|
||||
/>
|
||||
@ -561,13 +561,13 @@ function LlmSettingsScreen() {
|
||||
testId="llm-custom-model-input"
|
||||
name="llm-custom-model-input"
|
||||
label={t(I18nKey.SETTINGS$CUSTOM_MODEL)}
|
||||
defaultValue={settings.LLM_MODEL || DEFAULT_OPENHANDS_MODEL}
|
||||
defaultValue={settings.llm_model || DEFAULT_OPENHANDS_MODEL}
|
||||
placeholder={DEFAULT_OPENHANDS_MODEL}
|
||||
type="text"
|
||||
className="w-full max-w-[680px]"
|
||||
onChange={handleCustomModelIsDirty}
|
||||
/>
|
||||
{(settings.LLM_MODEL?.startsWith("openhands/") ||
|
||||
{(settings.llm_model?.startsWith("openhands/") ||
|
||||
currentSelectedModel?.startsWith("openhands/")) && (
|
||||
<OpenHandsApiKeyHelp testId="openhands-api-key-help-2" />
|
||||
)}
|
||||
@ -576,7 +576,7 @@ function LlmSettingsScreen() {
|
||||
testId="base-url-input"
|
||||
name="base-url-input"
|
||||
label={t(I18nKey.SETTINGS$BASE_URL)}
|
||||
defaultValue={settings.LLM_BASE_URL}
|
||||
defaultValue={settings.llm_base_url}
|
||||
placeholder="https://api.openai.com"
|
||||
type="text"
|
||||
className="w-full max-w-[680px]"
|
||||
@ -591,11 +591,11 @@ function LlmSettingsScreen() {
|
||||
label={t(I18nKey.SETTINGS_FORM$API_KEY)}
|
||||
type="password"
|
||||
className="w-full max-w-[680px]"
|
||||
placeholder={settings.LLM_API_KEY_SET ? "<hidden>" : ""}
|
||||
placeholder={settings.llm_api_key_set ? "<hidden>" : ""}
|
||||
onChange={handleApiKeyIsDirty}
|
||||
startContent={
|
||||
settings.LLM_API_KEY_SET && (
|
||||
<KeyStatusIcon isSet={settings.LLM_API_KEY_SET} />
|
||||
settings.llm_api_key_set && (
|
||||
<KeyStatusIcon isSet={settings.llm_api_key_set} />
|
||||
)
|
||||
}
|
||||
/>
|
||||
@ -616,12 +616,12 @@ function LlmSettingsScreen() {
|
||||
label={t(I18nKey.SETTINGS$SEARCH_API_KEY)}
|
||||
type="password"
|
||||
className="w-full max-w-[680px]"
|
||||
defaultValue={settings.SEARCH_API_KEY || ""}
|
||||
defaultValue={settings.search_api_key || ""}
|
||||
onChange={handleSearchApiKeyIsDirty}
|
||||
placeholder={t(I18nKey.API$TVLY_KEY_EXAMPLE)}
|
||||
startContent={
|
||||
settings.SEARCH_API_KEY_SET && (
|
||||
<KeyStatusIcon isSet={settings.SEARCH_API_KEY_SET} />
|
||||
settings.search_api_key_set && (
|
||||
<KeyStatusIcon isSet={settings.search_api_key_set} />
|
||||
)
|
||||
}
|
||||
/>
|
||||
@ -644,7 +644,7 @@ function LlmSettingsScreen() {
|
||||
label: agent, // TODO: Add i18n support for agent names
|
||||
})) || []
|
||||
}
|
||||
defaultSelectedKey={settings.AGENT}
|
||||
defaultSelectedKey={settings.agent}
|
||||
isClearable={false}
|
||||
onInputChange={handleAgentIsDirty}
|
||||
wrapperClassName="w-full max-w-[680px]"
|
||||
@ -662,11 +662,11 @@ function LlmSettingsScreen() {
|
||||
step={1}
|
||||
label={t(I18nKey.SETTINGS$CONDENSER_MAX_SIZE)}
|
||||
defaultValue={(
|
||||
settings.CONDENSER_MAX_SIZE ??
|
||||
DEFAULT_SETTINGS.CONDENSER_MAX_SIZE
|
||||
settings.condenser_max_size ??
|
||||
DEFAULT_SETTINGS.condenser_max_size
|
||||
)?.toString()}
|
||||
onChange={(value) => handleCondenserMaxSizeIsDirty(value)}
|
||||
isDisabled={!settings.ENABLE_DEFAULT_CONDENSER}
|
||||
isDisabled={!settings.enable_default_condenser}
|
||||
/>
|
||||
<p className="text-xs text-tertiary-alt mt-1">
|
||||
{t(I18nKey.SETTINGS$CONDENSER_MAX_SIZE_TOOLTIP)}
|
||||
@ -676,7 +676,7 @@ function LlmSettingsScreen() {
|
||||
<SettingsSwitch
|
||||
testId="enable-memory-condenser-switch"
|
||||
name="enable-memory-condenser-switch"
|
||||
defaultIsToggled={settings.ENABLE_DEFAULT_CONDENSER}
|
||||
defaultIsToggled={settings.enable_default_condenser}
|
||||
onToggle={handleEnableDefaultCondenserIsDirty}
|
||||
>
|
||||
{t(I18nKey.SETTINGS$ENABLE_MEMORY_CONDENSATION)}
|
||||
@ -688,7 +688,7 @@ function LlmSettingsScreen() {
|
||||
testId="enable-confirmation-mode-switch"
|
||||
name="enable-confirmation-mode-switch"
|
||||
onToggle={handleConfirmationModeIsDirty}
|
||||
defaultIsToggled={settings.CONFIRMATION_MODE}
|
||||
defaultIsToggled={settings.confirmation_mode}
|
||||
isBeta
|
||||
>
|
||||
{t(I18nKey.SETTINGS$CONFIRMATION_MODE)}
|
||||
|
||||
@ -41,7 +41,7 @@ function MCPSettingsScreen() {
|
||||
useState(false);
|
||||
const [serverToDelete, setServerToDelete] = useState<string | null>(null);
|
||||
|
||||
const mcpConfig: MCPConfig = settings?.MCP_CONFIG || {
|
||||
const mcpConfig: MCPConfig = settings?.mcp_config || {
|
||||
sse_servers: [],
|
||||
stdio_servers: [],
|
||||
shttp_servers: [],
|
||||
|
||||
@ -106,16 +106,16 @@ export default function MainApp() {
|
||||
|
||||
React.useEffect(() => {
|
||||
// Don't change language when on TOS page
|
||||
if (!isOnTosPage && settings?.LANGUAGE) {
|
||||
i18n.changeLanguage(settings.LANGUAGE);
|
||||
if (!isOnTosPage && settings?.language) {
|
||||
i18n.changeLanguage(settings.language);
|
||||
}
|
||||
}, [settings?.LANGUAGE, isOnTosPage]);
|
||||
}, [settings?.language, isOnTosPage]);
|
||||
|
||||
React.useEffect(() => {
|
||||
// Don't show consent form when on TOS page
|
||||
if (!isOnTosPage) {
|
||||
const consentFormModalIsOpen =
|
||||
settings?.USER_CONSENTS_TO_ANALYTICS === null;
|
||||
settings?.user_consents_to_analytics === null;
|
||||
|
||||
setConsentFormIsOpen(consentFormModalIsOpen);
|
||||
}
|
||||
@ -134,10 +134,10 @@ export default function MainApp() {
|
||||
}, [isOnTosPage]);
|
||||
|
||||
React.useEffect(() => {
|
||||
if (settings?.IS_NEW_USER && config.data?.APP_MODE === "saas") {
|
||||
if (settings?.is_new_user && config.data?.APP_MODE === "saas") {
|
||||
displaySuccessToast(t(I18nKey.BILLING$YOURE_IN));
|
||||
}
|
||||
}, [settings?.IS_NEW_USER, config.data?.APP_MODE]);
|
||||
}, [settings?.is_new_user, config.data?.APP_MODE]);
|
||||
|
||||
React.useEffect(() => {
|
||||
// Don't do any redirects when on TOS page
|
||||
@ -249,7 +249,7 @@ export default function MainApp() {
|
||||
|
||||
{config.data?.FEATURE_FLAGS.ENABLE_BILLING &&
|
||||
config.data?.APP_MODE === "saas" &&
|
||||
settings?.IS_NEW_USER && <SetupPaymentModal />}
|
||||
settings?.is_new_user && <SetupPaymentModal />}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@ -122,12 +122,12 @@ function UserSettingsScreen() {
|
||||
const prevVerificationStatusRef = useRef<boolean | undefined>(undefined);
|
||||
|
||||
useEffect(() => {
|
||||
if (settings?.EMAIL) {
|
||||
setEmail(settings.EMAIL);
|
||||
setOriginalEmail(settings.EMAIL);
|
||||
setIsEmailValid(EMAIL_REGEX.test(settings.EMAIL));
|
||||
if (settings?.email) {
|
||||
setEmail(settings.email);
|
||||
setOriginalEmail(settings.email);
|
||||
setIsEmailValid(EMAIL_REGEX.test(settings.email));
|
||||
}
|
||||
}, [settings?.EMAIL]);
|
||||
}, [settings?.email]);
|
||||
|
||||
useEffect(() => {
|
||||
if (pollingIntervalRef.current) {
|
||||
@ -137,7 +137,7 @@ function UserSettingsScreen() {
|
||||
|
||||
if (
|
||||
prevVerificationStatusRef.current === false &&
|
||||
settings?.EMAIL_VERIFIED === true
|
||||
settings?.email_verified === true
|
||||
) {
|
||||
// Display toast notification instead of setting state
|
||||
displaySuccessToast(t("SETTINGS$EMAIL_VERIFIED_SUCCESSFULLY"));
|
||||
@ -146,9 +146,9 @@ function UserSettingsScreen() {
|
||||
}, 2000);
|
||||
}
|
||||
|
||||
prevVerificationStatusRef.current = settings?.EMAIL_VERIFIED;
|
||||
prevVerificationStatusRef.current = settings?.email_verified;
|
||||
|
||||
if (settings?.EMAIL_VERIFIED === false) {
|
||||
if (settings?.email_verified === false) {
|
||||
pollingIntervalRef.current = window.setInterval(() => {
|
||||
refetch();
|
||||
}, 5000);
|
||||
@ -160,7 +160,7 @@ function UserSettingsScreen() {
|
||||
pollingIntervalRef.current = null;
|
||||
}
|
||||
};
|
||||
}, [settings?.EMAIL_VERIFIED, refetch, queryClient, t]);
|
||||
}, [settings?.email_verified, refetch, queryClient, t]);
|
||||
|
||||
const handleEmailChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
const newEmail = e.target.value;
|
||||
@ -215,10 +215,10 @@ function UserSettingsScreen() {
|
||||
isSaving={isSaving}
|
||||
isResendingVerification={isResendingVerification}
|
||||
isEmailChanged={isEmailChanged}
|
||||
emailVerified={settings?.EMAIL_VERIFIED}
|
||||
emailVerified={settings?.email_verified}
|
||||
isEmailValid={isEmailValid}
|
||||
>
|
||||
{settings?.EMAIL_VERIFIED === false && <VerificationAlert />}
|
||||
{settings?.email_verified === false && <VerificationAlert />}
|
||||
</EmailInputSection>
|
||||
)}
|
||||
</div>
|
||||
|
||||
@ -3,35 +3,36 @@ import { Settings } from "#/types/settings";
|
||||
export const LATEST_SETTINGS_VERSION = 5;
|
||||
|
||||
export const DEFAULT_SETTINGS: Settings = {
|
||||
LLM_MODEL: "openhands/claude-sonnet-4-20250514",
|
||||
LLM_BASE_URL: "",
|
||||
AGENT: "CodeActAgent",
|
||||
LANGUAGE: "en",
|
||||
LLM_API_KEY_SET: false,
|
||||
SEARCH_API_KEY_SET: false,
|
||||
CONFIRMATION_MODE: false,
|
||||
SECURITY_ANALYZER: "llm",
|
||||
REMOTE_RUNTIME_RESOURCE_FACTOR: 1,
|
||||
PROVIDER_TOKENS_SET: {},
|
||||
ENABLE_DEFAULT_CONDENSER: true,
|
||||
CONDENSER_MAX_SIZE: 120,
|
||||
ENABLE_SOUND_NOTIFICATIONS: false,
|
||||
USER_CONSENTS_TO_ANALYTICS: false,
|
||||
ENABLE_PROACTIVE_CONVERSATION_STARTERS: false,
|
||||
ENABLE_SOLVABILITY_ANALYSIS: false,
|
||||
SEARCH_API_KEY: "",
|
||||
IS_NEW_USER: true,
|
||||
MAX_BUDGET_PER_TASK: null,
|
||||
EMAIL: "",
|
||||
EMAIL_VERIFIED: true, // Default to true to avoid restricting access unnecessarily
|
||||
MCP_CONFIG: {
|
||||
llm_model: "openhands/claude-sonnet-4-20250514",
|
||||
llm_base_url: "",
|
||||
agent: "CodeActAgent",
|
||||
language: "en",
|
||||
llm_api_key: null,
|
||||
llm_api_key_set: false,
|
||||
search_api_key_set: false,
|
||||
confirmation_mode: false,
|
||||
security_analyzer: "llm",
|
||||
remote_runtime_resource_factor: 1,
|
||||
provider_tokens_set: {},
|
||||
enable_default_condenser: true,
|
||||
condenser_max_size: 120,
|
||||
enable_sound_notifications: false,
|
||||
user_consents_to_analytics: false,
|
||||
enable_proactive_conversation_starters: false,
|
||||
enable_solvability_analysis: false,
|
||||
search_api_key: "",
|
||||
is_new_user: true,
|
||||
max_budget_per_task: null,
|
||||
email: "",
|
||||
email_verified: true, // Default to true to avoid restricting access unnecessarily
|
||||
mcp_config: {
|
||||
sse_servers: [],
|
||||
stdio_servers: [],
|
||||
shttp_servers: [],
|
||||
},
|
||||
GIT_USER_NAME: "openhands",
|
||||
GIT_USER_EMAIL: "openhands@all-hands.dev",
|
||||
V1_ENABLED: false,
|
||||
git_user_name: "openhands",
|
||||
git_user_email: "openhands@all-hands.dev",
|
||||
v1_enabled: false,
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@ -38,37 +38,31 @@ export type MCPConfig = {
|
||||
};
|
||||
|
||||
export type Settings = {
|
||||
LLM_MODEL: string;
|
||||
LLM_BASE_URL: string;
|
||||
AGENT: string;
|
||||
LANGUAGE: string;
|
||||
LLM_API_KEY_SET: boolean;
|
||||
SEARCH_API_KEY_SET: boolean;
|
||||
CONFIRMATION_MODE: boolean;
|
||||
SECURITY_ANALYZER: string | null;
|
||||
REMOTE_RUNTIME_RESOURCE_FACTOR: number | null;
|
||||
PROVIDER_TOKENS_SET: Partial<Record<Provider, string | null>>;
|
||||
ENABLE_DEFAULT_CONDENSER: boolean;
|
||||
llm_model: string;
|
||||
llm_base_url: string;
|
||||
agent: string;
|
||||
language: string;
|
||||
llm_api_key: string | null;
|
||||
llm_api_key_set: boolean;
|
||||
search_api_key_set: boolean;
|
||||
confirmation_mode: boolean;
|
||||
security_analyzer: string | null;
|
||||
remote_runtime_resource_factor: number | null;
|
||||
provider_tokens_set: Partial<Record<Provider, string | null>>;
|
||||
enable_default_condenser: boolean;
|
||||
// Maximum number of events before the condenser runs
|
||||
CONDENSER_MAX_SIZE: number | null;
|
||||
ENABLE_SOUND_NOTIFICATIONS: boolean;
|
||||
ENABLE_PROACTIVE_CONVERSATION_STARTERS: boolean;
|
||||
ENABLE_SOLVABILITY_ANALYSIS: boolean;
|
||||
USER_CONSENTS_TO_ANALYTICS: boolean | null;
|
||||
SEARCH_API_KEY?: string;
|
||||
IS_NEW_USER?: boolean;
|
||||
MCP_CONFIG?: MCPConfig;
|
||||
MAX_BUDGET_PER_TASK: number | null;
|
||||
EMAIL?: string;
|
||||
EMAIL_VERIFIED?: boolean;
|
||||
GIT_USER_NAME?: string;
|
||||
GIT_USER_EMAIL?: string;
|
||||
V1_ENABLED?: boolean;
|
||||
};
|
||||
|
||||
export type PostSettings = Settings & {
|
||||
condenser_max_size: number | null;
|
||||
enable_sound_notifications: boolean;
|
||||
enable_proactive_conversation_starters: boolean;
|
||||
enable_solvability_analysis: boolean;
|
||||
user_consents_to_analytics: boolean | null;
|
||||
llm_api_key?: string | null;
|
||||
search_api_key?: string;
|
||||
is_new_user?: boolean;
|
||||
mcp_config?: MCPConfig;
|
||||
max_budget_per_task: number | null;
|
||||
email?: string;
|
||||
email_verified?: boolean;
|
||||
git_user_name?: string;
|
||||
git_user_email?: string;
|
||||
v1_enabled?: boolean;
|
||||
};
|
||||
|
||||
@ -67,10 +67,10 @@ describe("extractSettings", () => {
|
||||
|
||||
// Verify that the model name case is preserved
|
||||
const expectedModel = `${provider}/${model}`;
|
||||
expect(settings.LLM_MODEL).toBe(expectedModel);
|
||||
expect(settings.llm_model).toBe(expectedModel);
|
||||
// Only test that it's not lowercased if the original has uppercase letters
|
||||
if (expectedModel !== expectedModel.toLowerCase()) {
|
||||
expect(settings.LLM_MODEL).not.toBe(expectedModel.toLowerCase());
|
||||
expect(settings.llm_model).not.toBe(expectedModel.toLowerCase());
|
||||
}
|
||||
});
|
||||
});
|
||||
@ -85,7 +85,7 @@ describe("extractSettings", () => {
|
||||
const settings = extractSettings(formData);
|
||||
|
||||
// Custom model should take precedence and preserve case
|
||||
expect(settings.LLM_MODEL).toBe("Custom-Model-Name");
|
||||
expect(settings.LLM_MODEL).not.toBe("custom-model-name");
|
||||
expect(settings.llm_model).toBe("Custom-Model-Name");
|
||||
expect(settings.llm_model).not.toBe("custom-model-name");
|
||||
});
|
||||
});
|
||||
|
||||
@ -3,4 +3,4 @@ import { Settings } from "#/types/settings";
|
||||
|
||||
export const hasAdvancedSettingsSet = (settings: Partial<Settings>): boolean =>
|
||||
Object.keys(settings).length > 0 &&
|
||||
(!!settings.LLM_BASE_URL || settings.AGENT !== DEFAULT_SETTINGS.AGENT);
|
||||
(!!settings.llm_base_url || settings.agent !== DEFAULT_SETTINGS.agent);
|
||||
|
||||
@ -67,9 +67,7 @@ export const parseMaxBudgetPerTask = (value: string): number | null => {
|
||||
: null;
|
||||
};
|
||||
|
||||
export const extractSettings = (
|
||||
formData: FormData,
|
||||
): Partial<Settings> & { llm_api_key?: string | null } => {
|
||||
export const extractSettings = (formData: FormData): Partial<Settings> => {
|
||||
const { LLM_MODEL, LLM_API_KEY, AGENT, LANGUAGE } =
|
||||
extractBasicFormData(formData);
|
||||
|
||||
@ -82,14 +80,14 @@ export const extractSettings = (
|
||||
} = extractAdvancedFormData(formData);
|
||||
|
||||
return {
|
||||
LLM_MODEL: CUSTOM_LLM_MODEL || LLM_MODEL,
|
||||
LLM_API_KEY_SET: !!LLM_API_KEY,
|
||||
AGENT,
|
||||
LANGUAGE,
|
||||
LLM_BASE_URL,
|
||||
CONFIRMATION_MODE,
|
||||
SECURITY_ANALYZER,
|
||||
ENABLE_DEFAULT_CONDENSER,
|
||||
llm_model: CUSTOM_LLM_MODEL || LLM_MODEL,
|
||||
llm_api_key_set: !!LLM_API_KEY,
|
||||
agent: AGENT,
|
||||
language: LANGUAGE,
|
||||
llm_base_url: LLM_BASE_URL,
|
||||
confirmation_mode: CONFIRMATION_MODE,
|
||||
security_analyzer: SECURITY_ANALYZER,
|
||||
enable_default_condenser: ENABLE_DEFAULT_CONDENSER,
|
||||
llm_api_key: LLM_API_KEY,
|
||||
};
|
||||
};
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user