mirror of
https://github.com/agent0ai/agent-zero.git
synced 2026-08-10 17:05:57 +00:00
Make model presets a single global collection and treat presets as reusable setups (main, utility, embedding). Persisted scope configs now store only a preset name (Default is always present and immutable); project-scoped preset definitions were removed and attempts to edit/reset project presets are rejected. Add embedding-model support throughout: include embedding slot in presets, surface embedding in the switcher and override APIs, and notify extensions when the effective embedding model changes. Implement rename/retire propagation to update plugin configs, saved chats, and live AgentContext objects when preset names change or presets are removed. Update CLI/integration commands to use an "inherit" action for returning to the scoped preset and to report the effective scoped-or-chat preset. Add startup migration/bootstrap to initialize presets from remote or bundled fallback and numerous docs/UI text updates to reflect the new preset model. Extra: refactor config resolution helpers and add safety checks (validation, atomic writes) when saving presets.
83 lines
3.3 KiB
Python
83 lines
3.3 KiB
Python
from copy import deepcopy
|
|
|
|
from helpers.api import ApiHandler, Request, Response
|
|
from helpers import defer, dotenv
|
|
from helpers.extension import call_extensions_async
|
|
from plugins._model_config.helpers import model_config
|
|
|
|
API_KEY_PLACEHOLDER = "************"
|
|
|
|
|
|
class ModelConfigSet(ApiHandler):
|
|
async def process(self, input: dict, request: Request) -> dict | Response:
|
|
project_name = input.get("project_name", "")
|
|
agent_profile = input.get("agent_profile", "")
|
|
config = input.get("config")
|
|
|
|
if not config or not isinstance(config, dict):
|
|
return Response(status=400, response="Missing or invalid config")
|
|
|
|
config_to_save = deepcopy(config)
|
|
for section_name in ("chat_model", "utility_model", "embedding_model"):
|
|
section = config_to_save.get(section_name, {})
|
|
if not isinstance(section, dict):
|
|
continue
|
|
provider = str(section.get("provider", "")).strip()
|
|
api_key = section.get("api_key", "")
|
|
if (
|
|
provider
|
|
and isinstance(api_key, str)
|
|
and api_key.strip()
|
|
and api_key != API_KEY_PLACEHOLDER
|
|
):
|
|
dotenv.save_dotenv_value(f"API_KEY_{provider.upper()}", api_key)
|
|
section.pop("api_key", None)
|
|
|
|
preset_name = str(
|
|
input.get("preset_name")
|
|
or config_to_save.get(model_config.MODEL_PRESET_CONFIG_KEY)
|
|
or model_config.get_configured_preset_name(
|
|
project_name=project_name or None,
|
|
agent_profile=agent_profile or None,
|
|
)
|
|
).strip()
|
|
preset = model_config.resolve_preset(preset_name)
|
|
if not preset:
|
|
return Response(status=404, response=f"Preset '{preset_name}' not found")
|
|
preset_name = str(preset.get("name") or model_config.DEFAULT_PRESET_NAME)
|
|
|
|
# Read the preset before saving so embedding changes can still trigger
|
|
# the established re-index notification.
|
|
prev_config = model_config.resolve_config_settings(
|
|
{model_config.MODEL_PRESET_CONFIG_KEY: preset_name}
|
|
)
|
|
|
|
try:
|
|
model_config.update_preset_from_config(preset_name, config_to_save)
|
|
except ValueError as exc:
|
|
return Response(status=400, response=str(exc))
|
|
|
|
# Keep the requested scope pointed at the preset being edited. This is
|
|
# selection-only persistence; model dictionaries live in presets.yaml.
|
|
from helpers import plugins
|
|
|
|
plugins.save_plugin_config(
|
|
"_model_config",
|
|
project_name=project_name or None,
|
|
agent_profile=agent_profile or None,
|
|
settings={model_config.MODEL_PRESET_CONFIG_KEY: preset_name},
|
|
)
|
|
|
|
# Check if embedding model changed and notify
|
|
prev_embed = prev_config.get("embedding_model", {})
|
|
new_embed = config_to_save.get("embedding_model", {})
|
|
if (
|
|
prev_embed.get("provider") != new_embed.get("provider")
|
|
or prev_embed.get("name") != new_embed.get("name")
|
|
or prev_embed.get("kwargs") != new_embed.get("kwargs")
|
|
):
|
|
defer.DeferredTask().start_task(
|
|
call_extensions_async, "embedding_model_changed"
|
|
)
|
|
|
|
return {"ok": True, "preset_name": preset_name}
|