main
py 83 lines 3.3 KB
Raw
1 from copy import deepcopy
2
3 from helpers.api import ApiHandler, Request, Response
4 from helpers import defer, dotenv
5 from helpers.extension import call_extensions_async
6 from plugins._model_config.helpers import model_config
7
8 API_KEY_PLACEHOLDER = "************"
9
10
11 class ModelConfigSet(ApiHandler):
12 async def process(self, input: dict, request: Request) -> dict | Response:
13 project_name = input.get("project_name", "")
14 agent_profile = input.get("agent_profile", "")
15 config = input.get("config")
16
17 if not config or not isinstance(config, dict):
18 return Response(status=400, response="Missing or invalid config")
19
20 config_to_save = deepcopy(config)
21 for section_name in ("chat_model", "utility_model", "embedding_model"):
22 section = config_to_save.get(section_name, {})
23 if not isinstance(section, dict):
24 continue
25 provider = str(section.get("provider", "")).strip()
26 api_key = section.get("api_key", "")
27 if (
28 provider
29 and isinstance(api_key, str)
30 and api_key.strip()
31 and api_key != API_KEY_PLACEHOLDER
32 ):
33 dotenv.save_dotenv_value(f"API_KEY_{provider.upper()}", api_key)
34 section.pop("api_key", None)
35
36 preset_name = str(
37 input.get("preset_name")
38 or config_to_save.get(model_config.MODEL_PRESET_CONFIG_KEY)
39 or model_config.get_configured_preset_name(
40 project_name=project_name or None,
41 agent_profile=agent_profile or None,
42 )
43 ).strip()
44 preset = model_config.resolve_preset(preset_name)
45 if not preset:
46 return Response(status=404, response=f"Preset '{preset_name}' not found")
47 preset_name = str(preset.get("name") or model_config.DEFAULT_PRESET_NAME)
48
49 # Read the preset before saving so embedding changes can still trigger
50 # the established re-index notification.
51 prev_config = model_config.resolve_config_settings(
52 {model_config.MODEL_PRESET_CONFIG_KEY: preset_name}
53 )
54
55 try:
56 model_config.update_preset_from_config(preset_name, config_to_save)
57 except ValueError as exc:
58 return Response(status=400, response=str(exc))
59
60 # Keep the requested scope pointed at the preset being edited. This is
61 # selection-only persistence; model dictionaries live in presets.yaml.
62 from helpers import plugins
63
64 plugins.save_plugin_config(
65 "_model_config",
66 project_name=project_name or None,
67 agent_profile=agent_profile or None,
68 settings={model_config.MODEL_PRESET_CONFIG_KEY: preset_name},
69 )
70
71 # Check if embedding model changed and notify
72 prev_embed = prev_config.get("embedding_model", {})
73 new_embed = config_to_save.get("embedding_model", {})
74 if (
75 prev_embed.get("provider") != new_embed.get("provider")
76 or prev_embed.get("name") != new_embed.get("name")
77 or prev_embed.get("kwargs") != new_embed.get("kwargs")
78 ):
79 defer.DeferredTask().start_task(
80 call_extensions_async, "embedding_model_changed"
81 )
82
83 return {"ok": True, "preset_name": preset_name}