mirror of
https://github.com/OpenHands/OpenHands.git
synced 2026-03-22 13:47:19 +08:00
feat: add marketplace_path setting to GUI (local and SaaS)
Adds a configurable marketplace_path setting that controls which marketplace is used when loading public skills. Users can: - Use the default marketplace (marketplaces/default.json) - default behavior - Specify a custom marketplace path - Leave empty to load all skills without marketplace filtering Changes: - Added marketplace_path field to Settings data model (backend) - Added marketplace_path to frontend Settings type - Added DEFAULT_MARKETPLACE_PATH constant to frontend settings service - Added Skills Settings section in App Settings page with marketplace_path input This applies to both Local GUI and SaaS GUI as they share the same frontend code. Co-authored-by: openhands <openhands@all-hands.dev>
This commit is contained in:
@@ -4,7 +4,7 @@ import { usePostHog } from "posthog-js/react";
|
||||
import { useSaveSettings } from "#/hooks/mutation/use-save-settings";
|
||||
import { useSettings } from "#/hooks/query/use-settings";
|
||||
import { AvailableLanguages } from "#/i18n";
|
||||
import { DEFAULT_SETTINGS } from "#/services/settings";
|
||||
import { DEFAULT_SETTINGS, DEFAULT_MARKETPLACE_PATH } from "#/services/settings";
|
||||
import { BrandButton } from "#/components/features/settings/brand-button";
|
||||
import { SettingsSwitch } from "#/components/features/settings/settings-switch";
|
||||
import { SettingsInput } from "#/components/features/settings/settings-input";
|
||||
@@ -50,6 +50,8 @@ function AppSettingsScreen() {
|
||||
React.useState(false);
|
||||
const [gitUserEmailHasChanged, setGitUserEmailHasChanged] =
|
||||
React.useState(false);
|
||||
const [marketplacePathHasChanged, setMarketplacePathHasChanged] =
|
||||
React.useState(false);
|
||||
|
||||
const formAction = (formData: FormData) => {
|
||||
const languageLabel = formData.get("language-input")?.toString();
|
||||
@@ -82,6 +84,14 @@ function AppSettingsScreen() {
|
||||
formData.get("git-user-email-input")?.toString() ||
|
||||
DEFAULT_SETTINGS.git_user_email;
|
||||
|
||||
const marketplacePathValue = formData
|
||||
.get("marketplace-path-input")
|
||||
?.toString();
|
||||
// Empty string means no marketplace filtering, null means use default
|
||||
const marketplacePath = marketplacePathValue === ""
|
||||
? null
|
||||
: (marketplacePathValue || DEFAULT_MARKETPLACE_PATH);
|
||||
|
||||
saveSettings(
|
||||
{
|
||||
language,
|
||||
@@ -92,6 +102,7 @@ function AppSettingsScreen() {
|
||||
max_budget_per_task: maxBudgetPerTask,
|
||||
git_user_name: gitUserName,
|
||||
git_user_email: gitUserEmail,
|
||||
marketplace_path: marketplacePath,
|
||||
},
|
||||
{
|
||||
onSuccess: () => {
|
||||
@@ -110,6 +121,7 @@ function AppSettingsScreen() {
|
||||
setMaxBudgetPerTaskHasChanged(false);
|
||||
setGitUserNameHasChanged(false);
|
||||
setGitUserEmailHasChanged(false);
|
||||
setMarketplacePathHasChanged(false);
|
||||
},
|
||||
},
|
||||
);
|
||||
@@ -170,6 +182,13 @@ function AppSettingsScreen() {
|
||||
setGitUserEmailHasChanged(value !== currentValue);
|
||||
};
|
||||
|
||||
const checkIfMarketplacePathHasChanged = (value: string) => {
|
||||
const currentValue = settings?.marketplace_path || DEFAULT_MARKETPLACE_PATH;
|
||||
// Empty string means no marketplace filtering
|
||||
const newValue = value === "" ? null : value;
|
||||
setMarketplacePathHasChanged(newValue !== currentValue);
|
||||
};
|
||||
|
||||
const formIsClean =
|
||||
!languageInputHasChanged &&
|
||||
!analyticsSwitchHasChanged &&
|
||||
@@ -178,7 +197,8 @@ function AppSettingsScreen() {
|
||||
!solvabilityAnalysisSwitchHasChanged &&
|
||||
!maxBudgetPerTaskHasChanged &&
|
||||
!gitUserNameHasChanged &&
|
||||
!gitUserEmailHasChanged;
|
||||
!gitUserEmailHasChanged &&
|
||||
!marketplacePathHasChanged;
|
||||
|
||||
const shouldBeLoading = !settings || isLoading || isPending;
|
||||
|
||||
@@ -284,6 +304,31 @@ function AppSettingsScreen() {
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="border-t border-t-tertiary pt-6 mt-2">
|
||||
<h3 className="text-lg font-medium mb-2">
|
||||
Skills Settings
|
||||
</h3>
|
||||
<p className="text-xs mb-4">
|
||||
Configure which skills are loaded from the public skills marketplace.
|
||||
</p>
|
||||
<div className="flex flex-col gap-6">
|
||||
<SettingsInput
|
||||
testId="marketplace-path-input"
|
||||
name="marketplace-path-input"
|
||||
type="text"
|
||||
label="Marketplace Path"
|
||||
defaultValue={settings.marketplace_path || DEFAULT_MARKETPLACE_PATH}
|
||||
onChange={checkIfMarketplacePathHasChanged}
|
||||
placeholder={DEFAULT_MARKETPLACE_PATH}
|
||||
className="w-full max-w-[680px]"
|
||||
/>
|
||||
<p className="text-xs text-gray-500">
|
||||
Path to the marketplace JSON file in the public skills repository.
|
||||
Leave empty to load all skills without marketplace filtering.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
|
||||
@@ -2,6 +2,8 @@ import { Settings } from "#/types/settings";
|
||||
|
||||
export const LATEST_SETTINGS_VERSION = 5;
|
||||
|
||||
export const DEFAULT_MARKETPLACE_PATH = "marketplaces/default.json";
|
||||
|
||||
export const DEFAULT_SETTINGS: Settings = {
|
||||
llm_model: "openhands/claude-opus-4-5-20251101",
|
||||
llm_base_url: "",
|
||||
@@ -33,6 +35,7 @@ export const DEFAULT_SETTINGS: Settings = {
|
||||
git_user_name: "openhands",
|
||||
git_user_email: "openhands@all-hands.dev",
|
||||
v1_enabled: false,
|
||||
marketplace_path: DEFAULT_MARKETPLACE_PATH,
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -66,4 +66,6 @@ export type Settings = {
|
||||
git_user_name?: string;
|
||||
git_user_email?: string;
|
||||
v1_enabled?: boolean;
|
||||
// Path to the marketplace JSON file for public skills loading
|
||||
marketplace_path?: string | null;
|
||||
};
|
||||
|
||||
@@ -54,6 +54,9 @@ class Settings(BaseModel):
|
||||
git_user_name: str | None = None
|
||||
git_user_email: str | None = None
|
||||
v1_enabled: bool = True
|
||||
# Path to the marketplace JSON file for public skills loading
|
||||
# Defaults to 'marketplaces/default.json' in the public skills repository
|
||||
marketplace_path: str | None = 'marketplaces/default.json'
|
||||
|
||||
model_config = ConfigDict(
|
||||
validate_assignment=True,
|
||||
|
||||
Reference in New Issue
Block a user