Skip to content

Commit 618f8b4

Browse files
authored
Merge pull request #26 from BukeLy/feat/architecture-upgrade-from-omnicloud
feat: sync architecture upgrades from omnicloud-support-agent
2 parents 2f3c231 + fd04837 commit 618f8b4

9 files changed

Lines changed: 605 additions & 213 deletions

File tree

.github/workflows/release-changelog.yml

Lines changed: 0 additions & 55 deletions
This file was deleted.

agent-sdk-client/config.py

Lines changed: 68 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,14 @@
66
from pathlib import Path
77
from typing import Optional
88

9+
10+
@dataclass
11+
class LocalCommand:
12+
"""Local command configuration."""
13+
type: str # "static" or "handler"
14+
response: str = "" # for static type
15+
handler: str = "" # for handler type
16+
917
logger = logging.getLogger(__name__)
1018
DEFAULT_CONFIG_PATH = Path(__file__).with_name("config.toml")
1119

@@ -28,7 +36,46 @@ def extract_command(text: Optional[str]) -> Optional[str]:
2836
return command
2937

3038

31-
def _load_config(config_path: Path = DEFAULT_CONFIG_PATH) -> tuple[list[str], dict[str, str], list[int | str]]:
39+
def _parse_local_command(name: str, value) -> tuple[str, LocalCommand | None]:
40+
"""Parse a single local command entry.
41+
42+
Args:
43+
name: Command name (with or without leading slash)
44+
value: Command value (string for legacy, dict for new format)
45+
46+
Returns:
47+
Tuple of (normalized_cmd, LocalCommand) or (normalized_cmd, None) if invalid
48+
"""
49+
cmd = f"/{name.lstrip('/')}" if not name.startswith('/') else name
50+
51+
# Legacy format: string value = static response
52+
if isinstance(value, str):
53+
return cmd, LocalCommand(type="static", response=value)
54+
55+
# New format: dict with type field
56+
if isinstance(value, dict):
57+
cmd_type = value.get('type', '')
58+
if cmd_type == 'static':
59+
response = value.get('response', '')
60+
if not response:
61+
logger.warning(f"Local command {cmd} has no response; skipping")
62+
return cmd, None
63+
return cmd, LocalCommand(type="static", response=response)
64+
elif cmd_type == 'handler':
65+
handler = value.get('handler', '')
66+
if not handler:
67+
logger.warning(f"Local command {cmd} has no handler; skipping")
68+
return cmd, None
69+
return cmd, LocalCommand(type="handler", handler=handler)
70+
else:
71+
logger.warning(f"Local command {cmd} has unknown type: {cmd_type}; skipping")
72+
return cmd, None
73+
74+
logger.warning(f"Local command {cmd} has invalid value type; skipping")
75+
return cmd, None
76+
77+
78+
def _load_config(config_path: Path = DEFAULT_CONFIG_PATH) -> tuple[list[str], dict[str, LocalCommand], list[int | str]]:
3279
"""Load commands and security config from TOML config file.
3380
3481
Returns:
@@ -48,16 +95,18 @@ def _load_config(config_path: Path = DEFAULT_CONFIG_PATH) -> tuple[list[str], di
4895
agent_commands = []
4996
agent_commands = [cmd for cmd in agent_commands if isinstance(cmd, str)]
5097

51-
# Load local commands
98+
# Load local commands (supports both legacy string and new dict format)
5299
local_commands_raw = data.get('local_commands', {})
53100
if not isinstance(local_commands_raw, dict):
54101
logger.warning("Local commands config is not a table; ignoring configuration")
55102
local_commands_raw = {}
56-
local_commands = {
57-
f"/{name.lstrip('/')}" if not name.startswith('/') else name: str(value)
58-
for name, value in local_commands_raw.items()
59-
if isinstance(name, str) and isinstance(value, str)
60-
}
103+
local_commands: dict[str, LocalCommand] = {}
104+
for name, value in local_commands_raw.items():
105+
if not isinstance(name, str):
106+
continue
107+
cmd, parsed = _parse_local_command(name, value)
108+
if parsed:
109+
local_commands[cmd] = parsed
61110

62111
# Load security whitelist
63112
security = data.get('security', {})
@@ -92,8 +141,9 @@ class Config:
92141
auth_token: str
93142
queue_url: str
94143
agent_commands: list[str]
95-
local_commands: dict[str, str]
144+
local_commands: dict[str, LocalCommand]
96145
user_whitelist: list[int | str]
146+
telegram_webhook_secret: str = ""
97147

98148
@classmethod
99149
def from_env(cls, config_path: Optional[Path] = None) -> 'Config':
@@ -107,6 +157,7 @@ def from_env(cls, config_path: Optional[Path] = None) -> 'Config':
107157
agent_commands=agent_cmds,
108158
local_commands=local_cmds,
109159
user_whitelist=whitelist,
160+
telegram_webhook_secret=os.getenv('TELEGRAM_WEBHOOK_SECRET', ''),
110161
)
111162

112163
def get_command(self, text: Optional[str]) -> Optional[str]:
@@ -118,8 +169,16 @@ def is_agent_command(self, cmd: Optional[str]) -> bool:
118169
def is_local_command(self, cmd: Optional[str]) -> bool:
119170
return bool(cmd) and cmd in self.local_commands
120171

172+
def get_local_command(self, cmd: str) -> LocalCommand | None:
173+
"""Get local command config by command name."""
174+
return self.local_commands.get(cmd)
175+
121176
def local_response(self, cmd: str) -> str:
122-
return self.local_commands.get(cmd, "Unsupported command.")
177+
"""Get static response for a local command (legacy compatibility)."""
178+
local_cmd = self.local_commands.get(cmd)
179+
if local_cmd and local_cmd.type == "static":
180+
return local_cmd.response
181+
return "Unsupported command."
123182

124183
def unknown_command_message(self) -> str:
125184
parts = []

agent-sdk-client/config.toml

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,15 @@ commands = [
77

88
[local_commands]
99
# Local-only commands handled by the client
10-
help = "Hello World"
11-
newchat = "创建新对话"
10+
# Supports two formats:
11+
# - Legacy: help = "static response text"
12+
# - New: help = { type = "static", response = "text" }
13+
# - Handler: newchat = { type = "handler", handler = "newchat" }
14+
15+
help = { type = "static", response = "Hello World" }
16+
newchat = { type = "handler", handler = "newchat" }
17+
start = { type = "handler", handler = "start" }
18+
debug = { type = "handler", handler = "debug" }
1219

1320
[security]
1421
# User IDs allowed to add bot to groups and send private messages.

0 commit comments

Comments
 (0)