refactor(webui): improve visual consistency (#5249)
This commit is contained in:
@@ -2545,6 +2545,7 @@ async def test_settings_api_returns_safe_subset_and_updates_whitelist(
|
||||
)
|
||||
config.tools.web.search.provider = "brave"
|
||||
config.tools.web.search.api_key = "brave-secret"
|
||||
expected_timezone = config.agents.defaults.timezone
|
||||
save_config(config, config_path)
|
||||
monkeypatch.setattr("nanobot.config.loader._current_config_path", config_path)
|
||||
monkeypatch.setattr(
|
||||
@@ -2585,7 +2586,7 @@ async def test_settings_api_returns_safe_subset_and_updates_whitelist(
|
||||
assert body["agent"]["provider"] == "openai"
|
||||
assert body["agent"]["model_preset"] == "default"
|
||||
assert body["agent"]["max_tokens"] == 8192
|
||||
assert body["agent"]["timezone"] == "UTC"
|
||||
assert body["agent"]["timezone"] == expected_timezone
|
||||
assert "bot_name" not in body["agent"]
|
||||
assert "bot_icon" not in body["agent"]
|
||||
assert body["agent"]["tool_hint_max_length"] == 40
|
||||
|
||||
@@ -2,11 +2,12 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from pathlib import Path
|
||||
from typing import TYPE_CHECKING, Any, ClassVar, Literal
|
||||
from typing import TYPE_CHECKING, Any, ClassVar, Literal, cast
|
||||
|
||||
from pydantic import AliasChoices, ConfigDict, Field, field_validator, model_validator
|
||||
from pydantic_settings import BaseSettings, SettingsConfigDict
|
||||
|
||||
from nanobot.config.timezone import detect_system_timezone
|
||||
from nanobot.config_base import Base
|
||||
from nanobot.cron.types import CronSchedule
|
||||
|
||||
@@ -140,7 +141,8 @@ class AgentDefaults(Base):
|
||||
serialization_alias="toolHintMaxLength",
|
||||
) # Max characters for tool hint display (e.g. "$ cd …/project && npm test")
|
||||
reasoning_effort: str | None = None # low / medium / high / xhigh / max / adaptive / none — LLM thinking effort; None preserves the provider default
|
||||
timezone: str = "UTC" # IANA timezone, e.g. "Asia/Shanghai", "America/New_York"
|
||||
timezone: str = "UTC" # Effective IANA timezone, e.g. "Asia/Shanghai"
|
||||
timezone_mode: Literal["auto", "manual"] = "auto"
|
||||
bot_name: str = "nanobot" # Display name shown in CLI prompts (e.g. "{name} is thinking...")
|
||||
bot_icon: str = "🐈" # Short icon (emoji or text) shown next to the bot name in CLI; "" to omit
|
||||
unified_session: bool = False # Share one session across all channels (single-user multi-device)
|
||||
@@ -164,6 +166,22 @@ class AgentDefaults(Base):
|
||||
) # Consolidation target ratio (0.5 = 50% of budget retained after compression)
|
||||
dream: DreamConfig = Field(default_factory=DreamConfig)
|
||||
|
||||
@model_validator(mode="before")
|
||||
@classmethod
|
||||
def resolve_timezone(cls, value: object) -> object:
|
||||
"""Detect new defaults server-side while preserving configured timezones."""
|
||||
if not isinstance(value, dict):
|
||||
return value
|
||||
|
||||
data = dict(cast(dict[str, object], value))
|
||||
timezone_mode = data.get("timezoneMode", data.get("timezone_mode"))
|
||||
if timezone_mode is None:
|
||||
timezone_mode = "manual" if "timezone" in data else "auto"
|
||||
data["timezoneMode"] = timezone_mode
|
||||
if timezone_mode == "auto":
|
||||
data["timezone"] = detect_system_timezone()
|
||||
return data
|
||||
|
||||
@field_validator("timezone")
|
||||
@classmethod
|
||||
def validate_timezone(cls, value: str) -> str:
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
"""Backend timezone detection for automatic agent defaults."""
|
||||
|
||||
from zoneinfo import ZoneInfo
|
||||
|
||||
from tzlocal import get_localzone_name
|
||||
|
||||
_UTC_ALIASES = frozenset(
|
||||
{"Etc/GMT", "Etc/UTC", "GMT", "GMT0", "Greenwich", "UCT", "Universal", "Zulu"}
|
||||
)
|
||||
|
||||
|
||||
def detect_system_timezone() -> str:
|
||||
"""Return the host's IANA timezone, falling back safely to UTC."""
|
||||
try:
|
||||
timezone = get_localzone_name()
|
||||
ZoneInfo(timezone)
|
||||
except Exception:
|
||||
return "UTC"
|
||||
return "UTC" if timezone in _UTC_ALIASES else timezone
|
||||
@@ -1399,10 +1399,12 @@ def update_agent_settings(query: QueryParams) -> dict[str, Any]:
|
||||
ZoneInfo(timezone)
|
||||
except Exception:
|
||||
raise WebUISettingsError("invalid timezone") from None
|
||||
if defaults.timezone != timezone:
|
||||
timezone_changed = defaults.timezone != timezone
|
||||
if timezone_changed or defaults.timezone_mode != "manual":
|
||||
defaults.timezone = timezone
|
||||
defaults.timezone_mode = "manual"
|
||||
changed = True
|
||||
restart_required = True
|
||||
restart_required = timezone_changed
|
||||
|
||||
tool_hint_max_length = _query_first_alias(
|
||||
query,
|
||||
|
||||
Reference in New Issue
Block a user