open-swarm 0.1.1743070217__py3-none-any.whl → 0.1.1743364176__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- open_swarm-0.1.1743364176.dist-info/METADATA +286 -0
- open_swarm-0.1.1743364176.dist-info/RECORD +260 -0
- {open_swarm-0.1.1743070217.dist-info → open_swarm-0.1.1743364176.dist-info}/WHEEL +1 -2
- open_swarm-0.1.1743364176.dist-info/entry_points.txt +2 -0
- swarm/__init__.py +0 -2
- swarm/auth.py +53 -49
- swarm/blueprints/README.md +67 -0
- swarm/blueprints/burnt_noodles/blueprint_burnt_noodles.py +412 -0
- swarm/blueprints/chatbot/blueprint_chatbot.py +98 -0
- swarm/blueprints/chatbot/templates/chatbot/chatbot.html +33 -0
- swarm/blueprints/digitalbutlers/blueprint_digitalbutlers.py +183 -0
- swarm/blueprints/dilbot_universe/blueprint_dilbot_universe.py +285 -0
- swarm/blueprints/divine_code/__init__.py +0 -0
- swarm/blueprints/divine_code/apps.py +11 -0
- swarm/blueprints/divine_code/blueprint_divine_code.py +219 -0
- swarm/blueprints/django_chat/apps.py +6 -0
- swarm/blueprints/django_chat/blueprint_django_chat.py +84 -0
- swarm/blueprints/django_chat/templates/django_chat/django_chat_webpage.html +37 -0
- swarm/blueprints/django_chat/urls.py +8 -0
- swarm/blueprints/django_chat/views.py +32 -0
- swarm/blueprints/echocraft/blueprint_echocraft.py +44 -0
- swarm/blueprints/family_ties/apps.py +11 -0
- swarm/blueprints/family_ties/blueprint_family_ties.py +152 -0
- swarm/blueprints/family_ties/models.py +19 -0
- swarm/blueprints/family_ties/serializers.py +7 -0
- swarm/blueprints/family_ties/settings.py +16 -0
- swarm/blueprints/family_ties/urls.py +10 -0
- swarm/blueprints/family_ties/views.py +26 -0
- swarm/blueprints/flock/__init__.py +0 -0
- swarm/blueprints/gaggle/blueprint_gaggle.py +184 -0
- swarm/blueprints/gotchaman/blueprint_gotchaman.py +232 -0
- swarm/blueprints/mcp_demo/blueprint_mcp_demo.py +133 -0
- swarm/blueprints/messenger/templates/messenger/messenger.html +46 -0
- swarm/blueprints/mission_improbable/blueprint_mission_improbable.py +234 -0
- swarm/blueprints/monkai_magic/blueprint_monkai_magic.py +248 -0
- swarm/blueprints/nebula_shellz/blueprint_nebula_shellz.py +156 -0
- swarm/blueprints/omniplex/blueprint_omniplex.py +221 -0
- swarm/blueprints/rue_code/__init__.py +0 -0
- swarm/blueprints/rue_code/blueprint_rue_code.py +291 -0
- swarm/blueprints/suggestion/blueprint_suggestion.py +110 -0
- swarm/blueprints/unapologetic_press/blueprint_unapologetic_press.py +298 -0
- swarm/blueprints/whiskeytango_foxtrot/__init__.py +0 -0
- swarm/blueprints/whiskeytango_foxtrot/apps.py +11 -0
- swarm/blueprints/whiskeytango_foxtrot/blueprint_whiskeytango_foxtrot.py +256 -0
- swarm/extensions/blueprint/__init__.py +30 -15
- swarm/extensions/blueprint/agent_utils.py +16 -40
- swarm/extensions/blueprint/blueprint_base.py +141 -543
- swarm/extensions/blueprint/blueprint_discovery.py +112 -98
- swarm/extensions/blueprint/cli_handler.py +185 -0
- swarm/extensions/blueprint/config_loader.py +122 -0
- swarm/extensions/blueprint/django_utils.py +181 -79
- swarm/extensions/blueprint/interactive_mode.py +1 -1
- swarm/extensions/config/config_loader.py +83 -200
- swarm/extensions/launchers/build_swarm_wrapper.py +0 -0
- swarm/extensions/launchers/swarm_cli.py +199 -287
- swarm/llm/chat_completion.py +26 -55
- swarm/management/__init__.py +0 -0
- swarm/management/commands/__init__.py +0 -0
- swarm/management/commands/runserver.py +58 -0
- swarm/permissions.py +38 -0
- swarm/serializers.py +96 -5
- swarm/settings.py +95 -110
- swarm/static/contrib/fonts/fontawesome-webfont.ttf +7 -0
- swarm/static/contrib/fonts/fontawesome-webfont.woff +7 -0
- swarm/static/contrib/fonts/fontawesome-webfont.woff2 +7 -0
- swarm/static/contrib/markedjs/marked.min.js +6 -0
- swarm/static/contrib/tabler-icons/adjustments-horizontal.svg +27 -0
- swarm/static/contrib/tabler-icons/alert-triangle.svg +21 -0
- swarm/static/contrib/tabler-icons/archive.svg +21 -0
- swarm/static/contrib/tabler-icons/artboard.svg +27 -0
- swarm/static/contrib/tabler-icons/automatic-gearbox.svg +23 -0
- swarm/static/contrib/tabler-icons/box-multiple.svg +19 -0
- swarm/static/contrib/tabler-icons/carambola.svg +19 -0
- swarm/static/contrib/tabler-icons/copy.svg +20 -0
- swarm/static/contrib/tabler-icons/download.svg +21 -0
- swarm/static/contrib/tabler-icons/edit.svg +21 -0
- swarm/static/contrib/tabler-icons/filled/carambola.svg +13 -0
- swarm/static/contrib/tabler-icons/filled/paint.svg +13 -0
- swarm/static/contrib/tabler-icons/headset.svg +22 -0
- swarm/static/contrib/tabler-icons/layout-sidebar-left-collapse.svg +21 -0
- swarm/static/contrib/tabler-icons/layout-sidebar-left-expand.svg +21 -0
- swarm/static/contrib/tabler-icons/layout-sidebar-right-collapse.svg +21 -0
- swarm/static/contrib/tabler-icons/layout-sidebar-right-expand.svg +21 -0
- swarm/static/contrib/tabler-icons/message-chatbot.svg +22 -0
- swarm/static/contrib/tabler-icons/message-star.svg +22 -0
- swarm/static/contrib/tabler-icons/message-x.svg +23 -0
- swarm/static/contrib/tabler-icons/message.svg +21 -0
- swarm/static/contrib/tabler-icons/paperclip.svg +18 -0
- swarm/static/contrib/tabler-icons/playlist-add.svg +22 -0
- swarm/static/contrib/tabler-icons/robot.svg +26 -0
- swarm/static/contrib/tabler-icons/search.svg +19 -0
- swarm/static/contrib/tabler-icons/settings.svg +20 -0
- swarm/static/contrib/tabler-icons/thumb-down.svg +19 -0
- swarm/static/contrib/tabler-icons/thumb-up.svg +19 -0
- swarm/static/css/dropdown.css +22 -0
- swarm/static/htmx/htmx.min.js +0 -0
- swarm/static/js/dropdown.js +23 -0
- swarm/static/rest_mode/css/base.css +470 -0
- swarm/static/rest_mode/css/chat-history.css +286 -0
- swarm/static/rest_mode/css/chat.css +251 -0
- swarm/static/rest_mode/css/chatbot.css +74 -0
- swarm/static/rest_mode/css/chatgpt.css +62 -0
- swarm/static/rest_mode/css/colors/corporate.css +74 -0
- swarm/static/rest_mode/css/colors/pastel.css +81 -0
- swarm/static/rest_mode/css/colors/tropical.css +82 -0
- swarm/static/rest_mode/css/general.css +142 -0
- swarm/static/rest_mode/css/layout.css +167 -0
- swarm/static/rest_mode/css/layouts/messenger-layout.css +17 -0
- swarm/static/rest_mode/css/layouts/minimalist-layout.css +57 -0
- swarm/static/rest_mode/css/layouts/mobile-layout.css +8 -0
- swarm/static/rest_mode/css/messages.css +84 -0
- swarm/static/rest_mode/css/messenger.css +135 -0
- swarm/static/rest_mode/css/settings.css +91 -0
- swarm/static/rest_mode/css/simple.css +44 -0
- swarm/static/rest_mode/css/slack.css +58 -0
- swarm/static/rest_mode/css/style.css +156 -0
- swarm/static/rest_mode/css/theme.css +30 -0
- swarm/static/rest_mode/css/toast.css +40 -0
- swarm/static/rest_mode/js/auth.js +9 -0
- swarm/static/rest_mode/js/blueprint.js +41 -0
- swarm/static/rest_mode/js/blueprintUtils.js +12 -0
- swarm/static/rest_mode/js/chatLogic.js +79 -0
- swarm/static/rest_mode/js/debug.js +63 -0
- swarm/static/rest_mode/js/events.js +98 -0
- swarm/static/rest_mode/js/main.js +19 -0
- swarm/static/rest_mode/js/messages.js +264 -0
- swarm/static/rest_mode/js/messengerLogic.js +355 -0
- swarm/static/rest_mode/js/modules/apiService.js +84 -0
- swarm/static/rest_mode/js/modules/blueprintManager.js +162 -0
- swarm/static/rest_mode/js/modules/chatHistory.js +110 -0
- swarm/static/rest_mode/js/modules/debugLogger.js +14 -0
- swarm/static/rest_mode/js/modules/eventHandlers.js +107 -0
- swarm/static/rest_mode/js/modules/messageProcessor.js +120 -0
- swarm/static/rest_mode/js/modules/state.js +7 -0
- swarm/static/rest_mode/js/modules/userInteractions.js +29 -0
- swarm/static/rest_mode/js/modules/validation.js +23 -0
- swarm/static/rest_mode/js/rendering.js +119 -0
- swarm/static/rest_mode/js/settings.js +130 -0
- swarm/static/rest_mode/js/sidebar.js +94 -0
- swarm/static/rest_mode/js/simpleLogic.js +37 -0
- swarm/static/rest_mode/js/slackLogic.js +66 -0
- swarm/static/rest_mode/js/splash.js +76 -0
- swarm/static/rest_mode/js/theme.js +111 -0
- swarm/static/rest_mode/js/toast.js +36 -0
- swarm/static/rest_mode/js/ui.js +265 -0
- swarm/static/rest_mode/js/validation.js +57 -0
- swarm/static/rest_mode/svg/animated_spinner.svg +12 -0
- swarm/static/rest_mode/svg/arrow_down.svg +5 -0
- swarm/static/rest_mode/svg/arrow_left.svg +5 -0
- swarm/static/rest_mode/svg/arrow_right.svg +5 -0
- swarm/static/rest_mode/svg/arrow_up.svg +5 -0
- swarm/static/rest_mode/svg/attach.svg +8 -0
- swarm/static/rest_mode/svg/avatar.svg +7 -0
- swarm/static/rest_mode/svg/canvas.svg +6 -0
- swarm/static/rest_mode/svg/chat_history.svg +4 -0
- swarm/static/rest_mode/svg/close.svg +5 -0
- swarm/static/rest_mode/svg/copy.svg +4 -0
- swarm/static/rest_mode/svg/dark_mode.svg +3 -0
- swarm/static/rest_mode/svg/edit.svg +5 -0
- swarm/static/rest_mode/svg/layout.svg +9 -0
- swarm/static/rest_mode/svg/logo.svg +29 -0
- swarm/static/rest_mode/svg/logout.svg +5 -0
- swarm/static/rest_mode/svg/mobile.svg +5 -0
- swarm/static/rest_mode/svg/new_chat.svg +4 -0
- swarm/static/rest_mode/svg/not_visible.svg +5 -0
- swarm/static/rest_mode/svg/plus.svg +7 -0
- swarm/static/rest_mode/svg/run_code.svg +6 -0
- swarm/static/rest_mode/svg/save.svg +4 -0
- swarm/static/rest_mode/svg/search.svg +6 -0
- swarm/static/rest_mode/svg/settings.svg +4 -0
- swarm/static/rest_mode/svg/speaker.svg +5 -0
- swarm/static/rest_mode/svg/stop.svg +6 -0
- swarm/static/rest_mode/svg/thumbs_down.svg +3 -0
- swarm/static/rest_mode/svg/thumbs_up.svg +3 -0
- swarm/static/rest_mode/svg/toggle_off.svg +6 -0
- swarm/static/rest_mode/svg/toggle_on.svg +6 -0
- swarm/static/rest_mode/svg/trash.svg +10 -0
- swarm/static/rest_mode/svg/undo.svg +3 -0
- swarm/static/rest_mode/svg/visible.svg +8 -0
- swarm/static/rest_mode/svg/voice.svg +10 -0
- swarm/templates/account/login.html +22 -0
- swarm/templates/account/signup.html +32 -0
- swarm/templates/base.html +30 -0
- swarm/templates/chat.html +43 -0
- swarm/templates/index.html +35 -0
- swarm/templates/rest_mode/components/chat_sidebar.html +55 -0
- swarm/templates/rest_mode/components/header.html +45 -0
- swarm/templates/rest_mode/components/main_chat_pane.html +41 -0
- swarm/templates/rest_mode/components/settings_dialog.html +97 -0
- swarm/templates/rest_mode/components/splash_screen.html +7 -0
- swarm/templates/rest_mode/components/top_bar.html +28 -0
- swarm/templates/rest_mode/message_ui.html +50 -0
- swarm/templates/rest_mode/slackbot.html +30 -0
- swarm/templates/simple_blueprint_page.html +24 -0
- swarm/templates/websocket_partials/final_system_message.html +3 -0
- swarm/templates/websocket_partials/system_message.html +4 -0
- swarm/templates/websocket_partials/user_message.html +5 -0
- swarm/urls.py +57 -74
- swarm/utils/log_utils.py +63 -0
- swarm/views/api_views.py +48 -39
- swarm/views/chat_views.py +156 -70
- swarm/views/core_views.py +85 -90
- swarm/views/model_views.py +64 -121
- swarm/views/utils.py +65 -441
- open_swarm-0.1.1743070217.dist-info/METADATA +0 -258
- open_swarm-0.1.1743070217.dist-info/RECORD +0 -89
- open_swarm-0.1.1743070217.dist-info/entry_points.txt +0 -3
- open_swarm-0.1.1743070217.dist-info/top_level.txt +0 -1
- swarm/agent/agent.py +0 -49
- swarm/core.py +0 -326
- swarm/extensions/mcp/__init__.py +0 -1
- swarm/extensions/mcp/cache_utils.py +0 -36
- swarm/extensions/mcp/mcp_client.py +0 -341
- swarm/extensions/mcp/mcp_constants.py +0 -7
- swarm/extensions/mcp/mcp_tool_provider.py +0 -110
- swarm/types.py +0 -126
- {open_swarm-0.1.1743070217.dist-info → open_swarm-0.1.1743364176.dist-info}/licenses/LICENSE +0 -0
@@ -1,208 +1,91 @@
|
|
1
|
-
"""
|
2
|
-
Configuration Loader for Open Swarm MCP Framework.
|
3
|
-
"""
|
4
|
-
|
5
|
-
import os
|
6
1
|
import json
|
7
|
-
import
|
8
|
-
import logging
|
9
|
-
from typing import Any, Dict, List, Tuple, Optional
|
2
|
+
import os
|
10
3
|
from pathlib import Path
|
11
|
-
|
12
|
-
|
13
|
-
except ImportError: save_server_config = None
|
14
|
-
|
15
|
-
SWARM_DEBUG = os.getenv("SWARM_DEBUG", "False").lower() in ("true", "1", "yes")
|
16
|
-
try: from swarm.settings import BASE_DIR
|
17
|
-
except ImportError: BASE_DIR = Path(__file__).resolve().parent.parent.parent
|
18
|
-
|
19
|
-
from swarm.utils.redact import redact_sensitive_data
|
4
|
+
import logging
|
5
|
+
from typing import Dict, Any, Optional
|
20
6
|
|
21
7
|
logger = logging.getLogger(__name__)
|
22
|
-
logger.setLevel(logging.DEBUG if SWARM_DEBUG else logging.INFO)
|
23
8
|
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
logger.
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
logger.debug("
|
37
|
-
|
38
|
-
|
39
|
-
|
9
|
+
DEFAULT_CONFIG_FILENAME = "swarm_config.json"
|
10
|
+
|
11
|
+
# --- find_config_file, load_config, save_config, validate_config, get_profile_from_config, _substitute_env_vars_recursive ---
|
12
|
+
# (Keep these functions as they were)
|
13
|
+
def find_config_file( specific_path: Optional[str]=None, start_dir: Optional[Path]=None, default_dir: Optional[Path]=None,) -> Optional[Path]:
|
14
|
+
if specific_path: p=Path(specific_path); return p.resolve() if p.is_file() else logger.warning(f"Specified config path DNE: {specific_path}") or None # Fall through
|
15
|
+
if start_dir:
|
16
|
+
current=start_dir.resolve()
|
17
|
+
while current != current.parent:
|
18
|
+
if (cp := current / DEFAULT_CONFIG_FILENAME).is_file(): logger.debug(f"Found config upwards: {cp}"); return cp.resolve()
|
19
|
+
current = current.parent
|
20
|
+
if (cp := current / DEFAULT_CONFIG_FILENAME).is_file(): logger.debug(f"Found config at root: {cp}"); return cp.resolve()
|
21
|
+
if default_dir and (cp := default_dir.resolve() / DEFAULT_CONFIG_FILENAME).is_file(): logger.debug(f"Found config default: {cp}"); return cp.resolve()
|
22
|
+
cwd=Path.cwd();
|
23
|
+
if start_dir is None or cwd != start_dir.resolve():
|
24
|
+
if (cp := cwd / DEFAULT_CONFIG_FILENAME).is_file(): logger.debug(f"Found config cwd: {cp}"); return cp.resolve()
|
25
|
+
logger.debug(f"Config '{DEFAULT_CONFIG_FILENAME}' not found."); return None
|
26
|
+
|
27
|
+
def load_config(config_path: Path) -> Dict[str, Any]:
|
28
|
+
logger.debug(f"Loading config from {config_path}")
|
40
29
|
try:
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
return
|
64
|
-
|
65
|
-
def
|
66
|
-
|
67
|
-
if isinstance(
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
"
|
96
|
-
config_path: Optional[Path] = None
|
97
|
-
if file_path:
|
98
|
-
path_obj = Path(file_path)
|
99
|
-
if path_obj.is_file(): config_path = path_obj; logger.info(f"Using provided config file path: {config_path}")
|
100
|
-
else: logger.warning(f"Provided path '{file_path}' not found. Searching standard locations.")
|
101
|
-
if not config_path:
|
102
|
-
standard_paths = [ Path.cwd() / "swarm_config.json", Path(BASE_DIR) / "swarm_config.json", Path.home() / ".swarm" / "swarm_config.json" ]
|
103
|
-
config_path = next((p for p in standard_paths if p.is_file()), None)
|
104
|
-
if not config_path: raise FileNotFoundError(f"Config file 'swarm_config.json' not found. Checked: {[str(p) for p in standard_paths]}")
|
105
|
-
logger.info(f"Using config file found at: {config_path}")
|
30
|
+
with open(config_path, 'r') as f: config = json.load(f)
|
31
|
+
logger.info(f"Loaded config from {config_path}"); validate_config(config); return config
|
32
|
+
except FileNotFoundError: logger.error(f"Config DNE: {config_path}"); raise
|
33
|
+
except json.JSONDecodeError as e: logger.error(f"JSON error {config_path}: {e}"); raise ValueError(f"Invalid JSON: {config_path}") from e
|
34
|
+
except Exception as e: logger.error(f"Load error {config_path}: {e}"); raise
|
35
|
+
|
36
|
+
def save_config(config: Dict[str, Any], config_path: Path):
|
37
|
+
logger.info(f"Saving config to {config_path}")
|
38
|
+
try: config_path.parent.mkdir(parents=True,exist_ok=True); f = config_path.open('w'); json.dump(config, f, indent=4); f.close(); logger.debug("Save OK.")
|
39
|
+
except Exception as e: logger.error(f"Save failed {config_path}: {e}", exc_info=True); raise
|
40
|
+
|
41
|
+
def validate_config(config: Dict[str, Any]):
|
42
|
+
logger.debug("Validating config structure...")
|
43
|
+
if "llm" not in config or not isinstance(config["llm"],dict): raise ValueError("Config 'llm' section missing/malformed.")
|
44
|
+
for name, prof in config.get("llm",{}).items():
|
45
|
+
if not isinstance(prof,dict): raise ValueError(f"LLM profile '{name}' not dict.")
|
46
|
+
logger.debug("Config basic structure OK.")
|
47
|
+
|
48
|
+
def get_profile_from_config(config: Dict[str, Any], profile_name: str) -> Dict[str, Any]:
|
49
|
+
profile_data = config.get("llm", {}).get(profile_name)
|
50
|
+
if profile_data is None: raise ValueError(f"LLM profile '{profile_name}' not found.")
|
51
|
+
if not isinstance(profile_data, dict): raise ValueError(f"LLM profile '{profile_name}' not dict.")
|
52
|
+
return _substitute_env_vars_recursive(profile_data)
|
53
|
+
|
54
|
+
def _substitute_env_vars_recursive(data: Any) -> Any:
|
55
|
+
if isinstance(data,dict): return {k:_substitute_env_vars_recursive(v) for k,v in data.items()}
|
56
|
+
if isinstance(data,list): return [_substitute_env_vars_recursive(i) for i in data]
|
57
|
+
if isinstance(data,str): return os.path.expandvars(data)
|
58
|
+
return data
|
59
|
+
|
60
|
+
def create_default_config(config_path: Path):
|
61
|
+
"""Creates a default configuration file with valid JSON."""
|
62
|
+
default_config = {
|
63
|
+
"llm": {
|
64
|
+
"default": {
|
65
|
+
"provider": "openai",
|
66
|
+
"model": "gpt-4o",
|
67
|
+
"api_key": "${OPENAI_API_KEY}",
|
68
|
+
"base_url": None,
|
69
|
+
"description": "Default OpenAI profile. Requires OPENAI_API_KEY env var."
|
70
|
+
},
|
71
|
+
"ollama_example": {
|
72
|
+
"provider": "ollama",
|
73
|
+
"model": "llama3",
|
74
|
+
"api_key": "ollama", # Usually not needed
|
75
|
+
"base_url": "http://localhost:11434",
|
76
|
+
"description": "Example for local Ollama Llama 3 model."
|
77
|
+
}
|
78
|
+
},
|
79
|
+
"agents": {},
|
80
|
+
"settings": {
|
81
|
+
"default_markdown_output": True
|
82
|
+
}
|
83
|
+
}
|
84
|
+
logger.info(f"Creating default configuration file at {config_path}")
|
106
85
|
try:
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
return processed_config
|
113
|
-
except json.JSONDecodeError as e: logger.critical(f"Invalid JSON in {config_path}: {e}"); raise ValueError(f"Invalid JSON") from e
|
114
|
-
except Exception as e: logger.critical(f"Failed to read/process config {config_path}: {e}"); raise ValueError("Failed to load/process config") from e
|
115
|
-
|
116
|
-
def load_llm_config(config_dict: Optional[Dict[str, Any]] = None, llm_name: Optional[str] = None) -> Dict[str, Any]:
|
117
|
-
"""Loads, validates, and resolves API keys for a specific LLM profile."""
|
118
|
-
if config_dict is None:
|
119
|
-
global_config = globals().get("config")
|
120
|
-
if not global_config:
|
121
|
-
try: config_dict = load_server_config(); globals()["config"] = config_dict
|
122
|
-
except Exception as e: raise ValueError("Global config not loaded and no config_dict provided.") from e
|
123
|
-
else: config_dict = global_config
|
124
|
-
|
125
|
-
target_llm_name = llm_name or os.getenv("DEFAULT_LLM", "default")
|
126
|
-
logger.debug(f"LOAD_LLM: Loading profile: '{target_llm_name}'.")
|
127
|
-
|
128
|
-
resolved_config = resolve_placeholders(config_dict)
|
129
|
-
llm_profiles = resolved_config.get("llm", {})
|
130
|
-
if not isinstance(llm_profiles, dict): raise ValueError("'llm' section must be a dictionary.")
|
131
|
-
|
132
|
-
llm_config = llm_profiles.get(target_llm_name)
|
133
|
-
config_source = f"config file ('{target_llm_name}')"
|
134
|
-
logger.debug(f"LOAD_LLM: Initial lookup for '{target_llm_name}': {'Found' if llm_config else 'Missing'}")
|
135
|
-
|
136
|
-
if not llm_config:
|
137
|
-
logger.warning(f"LOAD_LLM: Config for '{target_llm_name}' not found. Generating fallback.")
|
138
|
-
config_source = "fallback generation"
|
139
|
-
fb_provider = os.getenv("DEFAULT_LLM_PROVIDER", "openai"); fb_model = os.getenv("DEFAULT_LLM_MODEL", "gpt-4o")
|
140
|
-
llm_config = { "provider": fb_provider, "model": fb_model, "api_key": None, "base_url": None }
|
141
|
-
logger.debug(f"LOAD_LLM: Generated fallback core config: {llm_config}")
|
142
|
-
|
143
|
-
if not isinstance(llm_config, dict): raise ValueError(f"LLM profile '{target_llm_name}' must be a dictionary.")
|
144
|
-
|
145
|
-
final_api_key = llm_config.get("api_key"); key_log_source = f"{config_source} (resolved)" if final_api_key else config_source
|
146
|
-
provider = llm_config.get("provider"); api_key_required = llm_config.get("api_key_required", True)
|
147
|
-
logger.debug(f"LOAD_LLM: Initial key from {key_log_source}: {'****' if final_api_key else 'None'}. Required={api_key_required}")
|
148
|
-
|
149
|
-
logger.debug(f"LOAD_LLM: Checking ENV vars for potential override.")
|
150
|
-
specific_env_var_name = f"{provider.upper()}_API_KEY" if provider else "PROVIDER_API_KEY"
|
151
|
-
common_fallback_var = "OPENAI_API_KEY"
|
152
|
-
specific_key_from_env = os.getenv(specific_env_var_name); fallback_key_from_env = os.getenv(common_fallback_var)
|
153
|
-
logger.debug(f"LOAD_LLM: Env Check: Specific ('{specific_env_var_name}')={'****' if specific_key_from_env else 'None'}, Fallback ('{common_fallback_var}')={'****' if fallback_key_from_env else 'None'}")
|
154
|
-
|
155
|
-
if specific_key_from_env:
|
156
|
-
if final_api_key != specific_key_from_env: logger.info(f"LOAD_LLM: Overriding key with env var '{specific_env_var_name}'.")
|
157
|
-
final_api_key = specific_key_from_env; key_log_source = f"env var '{specific_env_var_name}'"
|
158
|
-
elif fallback_key_from_env:
|
159
|
-
if not specific_key_from_env or specific_env_var_name == common_fallback_var:
|
160
|
-
if final_api_key != fallback_key_from_env: logger.info(f"LOAD_LLM: Overriding key with fallback env var '{common_fallback_var}'.")
|
161
|
-
final_api_key = fallback_key_from_env; key_log_source = f"env var '{common_fallback_var}'"
|
162
|
-
else: logger.debug(f"LOAD_LLM: Specific env key '{specific_env_var_name}' unset, NOT using fallback.")
|
163
|
-
else: logger.debug(f"LOAD_LLM: No relevant API key found in environment variables.")
|
164
|
-
|
165
|
-
key_is_still_missing_or_empty = final_api_key is None or (isinstance(final_api_key, str) and not final_api_key.strip())
|
166
|
-
logger.debug(f"LOAD_LLM: Key after env check: {'****' if final_api_key else 'None'}. Source: {key_log_source}. Still MissingOrEmpty={key_is_still_missing_or_empty}")
|
167
|
-
|
168
|
-
if key_is_still_missing_or_empty:
|
169
|
-
if api_key_required and not os.getenv("SUPPRESS_DUMMY_KEY"):
|
170
|
-
final_api_key = "sk-DUMMYKEY"; key_log_source = "dummy key"; logger.warning(f"LOAD_LLM: Applying dummy key for '{target_llm_name}'.")
|
171
|
-
elif api_key_required:
|
172
|
-
key_log_source = "MISSING - ERROR"; raise ValueError(f"Required API key for LLM profile '{target_llm_name}' is missing.")
|
173
|
-
else: key_log_source = "Not Required/Not Found"
|
174
|
-
|
175
|
-
final_llm_config = llm_config.copy(); final_llm_config["api_key"] = final_api_key; final_llm_config["_log_key_source"] = key_log_source
|
176
|
-
logger.debug(f"LOAD_LLM: Returning final config for '{target_llm_name}': {redact_sensitive_data(final_llm_config)}")
|
177
|
-
return final_llm_config
|
178
|
-
|
179
|
-
def get_llm_model(config_dict: Dict[str, Any], llm_name: Optional[str] = None) -> str:
|
180
|
-
target_llm_name = llm_name or os.getenv("DEFAULT_LLM", "default")
|
181
|
-
try: llm_config = load_llm_config(config_dict, target_llm_name)
|
182
|
-
except ValueError as e: raise ValueError(f"Could not load config for LLM '{target_llm_name}': {e}") from e
|
183
|
-
model_name = llm_config.get("model")
|
184
|
-
if not model_name or not isinstance(model_name, str): raise ValueError(f"'model' name missing/invalid for LLM '{target_llm_name}'.")
|
185
|
-
logger.debug(f"Retrieved model name '{model_name}' for LLM '{target_llm_name}'")
|
186
|
-
return model_name
|
187
|
-
|
188
|
-
def load_and_validate_llm(config_dict: Dict[str, Any], llm_name: Optional[str] = None) -> Dict[str, Any]:
|
189
|
-
target_llm_name = llm_name or os.getenv("DEFAULT_LLM", "default")
|
190
|
-
logger.debug(f"Loading and validating LLM (via load_llm_config) for profile: {target_llm_name}")
|
191
|
-
return load_llm_config(config_dict, target_llm_name)
|
192
|
-
|
193
|
-
def get_server_params(server_config: Dict[str, Any], server_name: str) -> Optional[Dict[str, Any]]:
|
194
|
-
"""Extracts and validates parameters needed to start an MCP server."""
|
195
|
-
command = server_config.get("command"); args = server_config.get("args", []); config_env = server_config.get("env", {})
|
196
|
-
if not command: logger.error(f"MCP server '{server_name}' missing 'command'."); return None
|
197
|
-
if not isinstance(args, list): logger.error(f"MCP server '{server_name}' 'args' must be list."); return None
|
198
|
-
if not isinstance(config_env, dict): logger.error(f"MCP server '{server_name}' 'env' must be dict."); return None
|
199
|
-
env = {**os.environ.copy(), **config_env}; valid_env = {}
|
200
|
-
for k, v in env.items():
|
201
|
-
if v is None: logger.warning(f"Env var '{k}' for MCP server '{server_name}' resolved to None. Omitting.")
|
202
|
-
else: valid_env[k] = str(v)
|
203
|
-
return {"command": command, "args": args, "env": valid_env}
|
204
|
-
|
205
|
-
def list_mcp_servers(config_dict: Dict[str, Any]) -> List[str]:
|
206
|
-
"""Returns a list of configured MCP server names."""
|
207
|
-
return list(config_dict.get("mcpServers", {}).keys())
|
86
|
+
save_config(default_config, config_path) # Use save_config to write valid JSON
|
87
|
+
logger.debug("Default configuration file created successfully.")
|
88
|
+
except Exception as e:
|
89
|
+
logger.error(f"Failed to create default config file at {config_path}: {e}", exc_info=True)
|
90
|
+
raise
|
208
91
|
|
File without changes
|