settings .env+json config, logging fix

frdel committed Aug 26, 2025 at 14:44 UTC 8a6355aa63cc554301ffba5f0d8c5cb4c81589f2
3 files changed +53 -26
python/helpers/settings.py
+48 -25
@@ -12,6 +12,7 @@ from . import files, dotenv
12 from python.helpers.print_style import PrintStyle
13 from python.helpers.providers import get_providers
14 from python.helpers.secrets import SecretsManager
15 +from python.helpers import dirty_json
16
17
18 class Settings(TypedDict):
@@ -20,7 +21,7 @@ class Settings(TypedDict):
21 chat_model_provider: str
22 chat_model_name: str
23 chat_model_api_base: str
23 - chat_model_kwargs: dict[str, str]
24 + chat_model_kwargs: dict[str, Any]
25 chat_model_ctx_length: int
26 chat_model_ctx_history: float
27 chat_model_vision: bool
@@ -31,7 +32,7 @@ class Settings(TypedDict):
32 util_model_provider: str
33 util_model_name: str
34 util_model_api_base: str
34 - util_model_kwargs: dict[str, str]
35 + util_model_kwargs: dict[str, Any]
36 util_model_ctx_length: int
37 util_model_ctx_input: float
38 util_model_rl_requests: int
@@ -41,7 +42,7 @@ class Settings(TypedDict):
42 embed_model_provider: str
43 embed_model_name: str
44 embed_model_api_base: str
44 - embed_model_kwargs: dict[str, str]
45 + embed_model_kwargs: dict[str, Any]
46 embed_model_rl_requests: int
47 embed_model_rl_input: int
48
@@ -52,8 +53,8 @@ class Settings(TypedDict):
53 browser_model_rl_requests: int
54 browser_model_rl_input: int
55 browser_model_rl_output: int
55 - browser_model_kwargs: dict[str, str]
56 - browser_http_headers: dict[str, str]
56 + browser_model_kwargs: dict[str, Any]
57 + browser_http_headers: dict[str, Any]
58
59 agent_profile: str
60 agent_memory_subdir: str
@@ -108,7 +109,7 @@ class Settings(TypedDict):
109 secrets: str
110
111 # LiteLLM global kwargs applied to all model calls
111 - litellm_global_kwargs: dict[str, str]
112 + litellm_global_kwargs: dict[str, Any]
113
114 class PartialSettings(Settings, total=False):
115 pass
@@ -264,7 +265,7 @@ def convert_out(settings: Settings) -> SettingsOutput:
265 {
266 "id": "chat_model_kwargs",
267 "title": "Chat model additional parameters",
267 - "description": "Any other parameters supported by <a href='https://docs.litellm.ai/docs/set_keys' target='_blank'>LiteLLM</a>. Format is KEY=VALUE on individual lines, just like .env file.",
268 + "description": "Any other parameters supported by <a href='https://docs.litellm.ai/docs/set_keys' target='_blank'>LiteLLM</a>. Format is KEY=VALUE on individual lines, like .env file. Value can also contain JSON objects - when unquoted, it is treated as object, number etc., when quoted, it is treated as string.",
269 "type": "textarea",
270 "value": _dict_to_env(settings["chat_model_kwargs"]),
271 }
@@ -344,7 +345,7 @@ def convert_out(settings: Settings) -> SettingsOutput:
345 {
346 "id": "util_model_kwargs",
347 "title": "Utility model additional parameters",
347 - "description": "Any other parameters supported by <a href='https://docs.litellm.ai/docs/set_keys' target='_blank'>LiteLLM</a>. Format is KEY=VALUE on individual lines, just like .env file.",
348 + "description": "Any other parameters supported by <a href='https://docs.litellm.ai/docs/set_keys' target='_blank'>LiteLLM</a>. Format is KEY=VALUE on individual lines, like .env file. Value can also contain JSON objects - when unquoted, it is treated as object, number etc., when quoted, it is treated as string.",
349 "type": "textarea",
350 "value": _dict_to_env(settings["util_model_kwargs"]),
351 }
@@ -414,7 +415,7 @@ def convert_out(settings: Settings) -> SettingsOutput:
415 {
416 "id": "embed_model_kwargs",
417 "title": "Embedding model additional parameters",
417 - "description": "Any other parameters supported by <a href='https://docs.litellm.ai/docs/set_keys' target='_blank'>LiteLLM</a>. Format is KEY=VALUE on individual lines, just like .env file.",
418 + "description": "Any other parameters supported by <a href='https://docs.litellm.ai/docs/set_keys' target='_blank'>LiteLLM</a>. Format is KEY=VALUE on individual lines, like .env file. Value can also contain JSON objects - when unquoted, it is treated as object, number etc., when quoted, it is treated as string.",
419 "type": "textarea",
420 "value": _dict_to_env(settings["embed_model_kwargs"]),
421 }
@@ -504,7 +505,7 @@ def convert_out(settings: Settings) -> SettingsOutput:
505 {
506 "id": "browser_model_kwargs",
507 "title": "Web Browser model additional parameters",
507 - "description": "Any other parameters supported by <a href='https://docs.litellm.ai/docs/set_keys' target='_blank'>LiteLLM</a>. Format is KEY=VALUE on individual lines, just like .env file.",
508 + "description": "Any other parameters supported by <a href='https://docs.litellm.ai/docs/set_keys' target='_blank'>LiteLLM</a>. Format is KEY=VALUE on individual lines, like .env file. Value can also contain JSON objects - when unquoted, it is treated as object, number etc., when quoted, it is treated as string.",
509 "type": "textarea",
510 "value": _dict_to_env(settings["browser_model_kwargs"]),
511 }
@@ -514,7 +515,7 @@ def convert_out(settings: Settings) -> SettingsOutput:
515 {
516 "id": "browser_http_headers",
517 "title": "HTTP Headers",
517 - "description": "HTTP headers to include with all browser requests. Format is KEY=VALUE on individual lines, just like .env file. Example: Authorization=Bearer token123",
518 + "description": "HTTP headers to include with all browser requests. Format is KEY=VALUE on individual lines, like .env file. Value can also contain JSON objects - when unquoted, it is treated as object, number etc., when quoted, it is treated as string. Example: Authorization=Bearer token123",
519 "type": "textarea",
520 "value": _dict_to_env(settings.get("browser_http_headers", {})),
521 }
@@ -1603,26 +1604,48 @@ def _apply_settings(previous: Settings | None):
1604
1605
1606 def _env_to_dict(data: str):
1606 - env_dict = {}
1607 - line_pattern = re.compile(r"\s*([^#][^=]*)\s*=\s*(.*)")
1607 + result = {}
1608 for line in data.splitlines():
1609 - match = line_pattern.match(line)
1610 - if match:
1611 - key, value = match.groups()
1612 - # Remove optional surrounding quotes (single or double)
1613 - value = value.strip().strip('"').strip("'")
1614 - env_dict[key.strip()] = value
1615 - return env_dict
1609 + line = line.strip()
1610 + if not line or line.startswith('#'):
1611 + continue
1612 +
1613 + if '=' not in line:
1614 + continue
1615 +
1616 + key, value = line.split('=', 1)
1617 + key = key.strip()
1618 + value = value.strip()
1619 +
1620 + # If quoted, treat as string
1621 + if value.startswith('"') and value.endswith('"'):
1622 + result[key] = value[1:-1].replace('\\"', '"') # Unescape quotes
1623 + elif value.startswith("'") and value.endswith("'"):
1624 + result[key] = value[1:-1].replace("\\'", "'") # Unescape quotes
1625 + else:
1626 + # Not quoted, try JSON parse
1627 + try:
1628 + result[key] = json.loads(value)
1629 + except (json.JSONDecodeError, ValueError):
1630 + result[key] = value
1631 +
1632 + return result
1633
1634
1635 def _dict_to_env(data_dict):
1636 lines = []
1637 for key, value in data_dict.items():
1621 - if "\n" in value:
1622 - value = f"'{value}'"
1623 - elif " " in value or value == "" or any(c in value for c in "\"'"):
1624 - value = f'"{value}"'
1625 - lines.append(f"{key}={value}")
1638 + if isinstance(value, str):
1639 + # Quote strings and escape internal quotes
1640 + escaped_value = value.replace('"', '\\"')
1641 + lines.append(f'{key}="{escaped_value}"')
1642 + elif isinstance(value, (dict, list, bool)) or value is None:
1643 + # Serialize as unquoted JSON
1644 + lines.append(f'{key}={json.dumps(value, separators=(",", ":"))}')
1645 + else:
1646 + # Numbers and other types as unquoted strings
1647 + lines.append(f'{key}={value}')
1648 +
1649 return "\n".join(lines)
1650
1651
requirements.txt
+1 -1
@@ -33,7 +33,7 @@ unstructured-client==0.31.0
33 webcolors==24.6.0
34 nest-asyncio==1.6.0
35 crontab==1.0.1
36 -litellm==1.75.3
36 +litellm==1.75.0
37 markdownify==1.1.0
38 pymupdf==1.25.3
39 pytesseract==0.3.13
run_ui.py
+4
@@ -16,6 +16,10 @@ from python.helpers.extract_tools import load_classes_from_folder
16 from python.helpers.api import ApiHandler
17 from python.helpers.print_style import PrintStyle
18
19 +# disable logging
20 +import logging
21 +logging.getLogger().setLevel(logging.WARNING)
22 +
23
24 # Set the new timezone to 'UTC'
25 os.environ["TZ"] = "UTC"