Skip to content

Commit 3a276e5

Browse files
author
alcholiclg
committed
support for slash command; support for session management and project management; support for personalization module
1 parent 7f92365 commit 3a276e5

38 files changed

Lines changed: 2779 additions & 5 deletions

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,7 @@ venv.bak/
121121
.vscode
122122
.idea
123123
.cursor
124+
.firecrawl
124125

125126
# custom
126127
*.pkl

ms_agent/agent/llm_agent.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,9 @@
2424
from ms_agent.utils import async_retry, read_history, save_history
2525
from ms_agent.utils.constants import DEFAULT_TAG, DEFAULT_USER
2626
from ms_agent.utils.logger import get_logger
27+
from ms_agent.personalization.injector import PersonalizationInjector
28+
from ms_agent.personalization.profile import ProfileManager
29+
from ms_agent.personalization.types import PersonalizationConfig
2730
from ms_agent.skill.catalog import SkillCatalog
2831
from ms_agent.skill.prompt_injector import SkillPromptInjector
2932
from ms_agent.skill.search import SkillSearchEngine
@@ -111,6 +114,9 @@ def __init__(
111114
self._skill_catalog = None
112115
self._skill_injector = None
113116

117+
# Personalization (lazy-loaded in _build_personalization_section)
118+
self._profile_manager = ProfileManager()
119+
114120
async def prepare_skills(self):
115121
"""Initialize the skill system from config.skills.
116122
@@ -474,6 +480,11 @@ async def create_messages(
474480
Message(role='user', content=messages or self.query),
475481
]
476482

483+
# Inject personalization section (before skills)
484+
personalization_section = self._build_personalization_section()
485+
if personalization_section:
486+
messages[0].content += "\n\n" + personalization_section
487+
477488
# Inject skill prompt section into system message
478489
if self._skill_injector:
479490
skill_section = self._skill_injector.build_skill_prompt_section()
@@ -482,6 +493,19 @@ async def create_messages(
482493

483494
return messages
484495

496+
def _build_personalization_section(self) -> str:
497+
p_config = getattr(self.config, 'personalization', None)
498+
config = PersonalizationConfig(
499+
global_instruction=(
500+
getattr(p_config, 'global_instruction', '') or ''
501+
) if p_config else '',
502+
project_instruction=(
503+
getattr(p_config, 'project_instruction', '') or ''
504+
) if p_config else '',
505+
user_profile=self._profile_manager.read(),
506+
)
507+
return PersonalizationInjector.build(config)
508+
485509
async def do_rag(self, messages: List[Message]):
486510
"""Process RAG or knowledge search to enrich the user query with context.
487511
Lines changed: 64 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,38 @@
11
# Copyright (c) ModelScope Contributors. All rights reserved.
2-
from typing import List
2+
from typing import TYPE_CHECKING, List, Optional
33

44
from ms_agent.agent.runtime import Runtime
55
from ms_agent.callbacks import Callback
66
from ms_agent.llm.utils import Message
77
from ms_agent.utils import get_logger
88
from omegaconf import DictConfig
99

10+
if TYPE_CHECKING:
11+
from ms_agent.command.router import CommandRouter
12+
1013
logger = get_logger()
1114

1215

1316
class InputCallback(Callback):
14-
"""Waiting for human inputs."""
17+
"""Waiting for human inputs. Supports slash command interception."""
1518

16-
def __init__(self, config: DictConfig):
19+
def __init__(
20+
self,
21+
config: DictConfig,
22+
command_router: Optional['CommandRouter'] = None,
23+
):
1724
super().__init__(config)
25+
if command_router is None:
26+
command_router = self._build_default_router()
27+
self._command_router = command_router
28+
29+
@staticmethod
30+
def _build_default_router() -> 'CommandRouter':
31+
from ms_agent.command import CommandRouter, register_builtin_commands
32+
33+
router = CommandRouter()
34+
register_builtin_commands(router)
35+
return router
1836

1937
async def after_tool_call(self, runtime: Runtime, messages: List[Message]):
2038
if messages[-1].tool_calls or messages[-1].role in ('tool', 'user'):
@@ -27,6 +45,47 @@ async def after_tool_call(self, runtime: Runtime, messages: List[Message]):
2745

2846
if not query:
2947
runtime.should_stop = True
30-
else:
48+
return
49+
50+
if self._command_router:
51+
handled = await self._try_command(query, runtime, messages)
52+
if handled:
53+
return
54+
55+
runtime.should_stop = False
56+
messages.append(Message(role='user', content=query))
57+
58+
async def _try_command(
59+
self,
60+
query: str,
61+
runtime: Runtime,
62+
messages: List[Message],
63+
) -> bool:
64+
"""Try to dispatch as slash command. Returns True if handled."""
65+
from ms_agent.command.router import CommandRouter
66+
from ms_agent.command.types import CommandContext, CommandResultType
67+
68+
if not CommandRouter.is_command(query):
69+
return False
70+
71+
cmd_name, args = CommandRouter.parse_input(query)
72+
ctx = CommandContext(
73+
raw_input=query,
74+
command_name=cmd_name,
75+
args=args,
76+
source='cli',
77+
runtime=runtime,
78+
extra={'router': self._command_router},
79+
)
80+
result = await self._command_router.dispatch(ctx)
81+
if result is None:
82+
return False
83+
84+
if result.type == CommandResultType.QUIT:
85+
runtime.should_stop = True
86+
elif result.type == CommandResultType.MESSAGE:
87+
print(result.content)
88+
elif result.type == CommandResultType.SUBMIT_PROMPT:
89+
messages.append(Message(role='user', content=result.content))
3190
runtime.should_stop = False
32-
messages.append(Message(role='user', content=query))
91+
return True

ms_agent/command/__init__.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
from ms_agent.command.router import CommandRouter
2+
from ms_agent.command.types import (
3+
CommandContext,
4+
CommandDef,
5+
CommandHandler,
6+
CommandResult,
7+
CommandResultType,
8+
)
9+
from ms_agent.command.builtin import register_builtin_commands
10+
11+
__all__ = [
12+
'CommandContext',
13+
'CommandDef',
14+
'CommandHandler',
15+
'CommandResult',
16+
'CommandResultType',
17+
'CommandRouter',
18+
'register_builtin_commands',
19+
]
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
from ms_agent.command.router import CommandRouter
2+
from ms_agent.command.builtin.session_cmds import register_session_commands
3+
from ms_agent.command.builtin.info_cmds import register_info_commands
4+
5+
6+
def register_builtin_commands(router: CommandRouter) -> None:
7+
register_session_commands(router)
8+
register_info_commands(router)
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
from ms_agent.command.router import CommandRouter
2+
from ms_agent.command.types import (
3+
CommandContext,
4+
CommandDef,
5+
CommandResult,
6+
CommandResultType,
7+
)
8+
9+
CMD_HELP = CommandDef(
10+
name='help',
11+
description='Show available commands',
12+
category='info',
13+
aliases=('?',),
14+
)
15+
16+
CMD_VERSION = CommandDef(
17+
name='version',
18+
description='Show MS-Agent version',
19+
category='info',
20+
)
21+
22+
23+
async def cmd_help(ctx: CommandContext) -> CommandResult:
24+
router = ctx.extra.get('router')
25+
if not router:
26+
return CommandResult(
27+
type=CommandResultType.MESSAGE, content='No commands available.'
28+
)
29+
30+
lines = ['Available commands:\n']
31+
for category, cmds in router.list_commands(ctx.source).items():
32+
lines.append(f'\n [{category}]')
33+
for cmd in cmds:
34+
aliases = f' ({", ".join(cmd.aliases)})' if cmd.aliases else ''
35+
lines.append(f' /{cmd.name}{aliases}{cmd.description}')
36+
37+
return CommandResult(
38+
type=CommandResultType.MESSAGE, content='\n'.join(lines)
39+
)
40+
41+
42+
async def cmd_version(ctx: CommandContext) -> CommandResult:
43+
try:
44+
from ms_agent import __version__
45+
46+
ver = __version__
47+
except (ImportError, AttributeError):
48+
ver = 'unknown'
49+
return CommandResult(
50+
type=CommandResultType.MESSAGE, content=f'MS-Agent v{ver}'
51+
)
52+
53+
54+
def register_info_commands(router: CommandRouter) -> None:
55+
router.register(CMD_HELP, cmd_help)
56+
router.register(CMD_VERSION, cmd_version)
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
from ms_agent.command.router import CommandRouter
2+
from ms_agent.command.types import (
3+
CommandContext,
4+
CommandDef,
5+
CommandResult,
6+
CommandResultType,
7+
)
8+
9+
CMD_STOP = CommandDef(
10+
name='stop',
11+
description='Stop the current agent execution',
12+
category='session',
13+
priority=0,
14+
aliases=('abort', 'cancel'),
15+
)
16+
17+
CMD_NEW = CommandDef(
18+
name='new',
19+
description='End current session',
20+
category='session',
21+
aliases=('reset',),
22+
)
23+
24+
CMD_STATUS = CommandDef(
25+
name='status',
26+
description='Show current agent status',
27+
category='session',
28+
)
29+
30+
31+
async def cmd_stop(ctx: CommandContext) -> CommandResult:
32+
if ctx.runtime:
33+
ctx.runtime.should_stop = True
34+
return CommandResult(type=CommandResultType.MESSAGE, content='Agent stopped.')
35+
36+
37+
async def cmd_new(ctx: CommandContext) -> CommandResult:
38+
if ctx.runtime:
39+
ctx.runtime.should_stop = True
40+
return CommandResult(
41+
type=CommandResultType.QUIT, content='Session ended. Start a new one.'
42+
)
43+
44+
45+
async def cmd_status(ctx: CommandContext) -> CommandResult:
46+
if ctx.runtime:
47+
content = (
48+
f'Round: {ctx.runtime.round}\n'
49+
f'Tag: {ctx.runtime.tag}\n'
50+
f'Should stop: {ctx.runtime.should_stop}'
51+
)
52+
else:
53+
content = 'No active agent.'
54+
return CommandResult(type=CommandResultType.MESSAGE, content=content)
55+
56+
57+
def register_session_commands(router: CommandRouter) -> None:
58+
router.register(CMD_STOP, cmd_stop)
59+
router.register(CMD_NEW, cmd_new)
60+
router.register(CMD_STATUS, cmd_status)

0 commit comments

Comments
 (0)