aury-agent 0.0.4__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.
- aury/__init__.py +2 -0
- aury/agents/__init__.py +55 -0
- aury/agents/a2a/__init__.py +168 -0
- aury/agents/backends/__init__.py +196 -0
- aury/agents/backends/artifact/__init__.py +9 -0
- aury/agents/backends/artifact/memory.py +130 -0
- aury/agents/backends/artifact/types.py +133 -0
- aury/agents/backends/code/__init__.py +65 -0
- aury/agents/backends/file/__init__.py +11 -0
- aury/agents/backends/file/local.py +66 -0
- aury/agents/backends/file/types.py +40 -0
- aury/agents/backends/invocation/__init__.py +8 -0
- aury/agents/backends/invocation/memory.py +81 -0
- aury/agents/backends/invocation/types.py +110 -0
- aury/agents/backends/memory/__init__.py +8 -0
- aury/agents/backends/memory/memory.py +179 -0
- aury/agents/backends/memory/types.py +136 -0
- aury/agents/backends/message/__init__.py +9 -0
- aury/agents/backends/message/memory.py +122 -0
- aury/agents/backends/message/types.py +124 -0
- aury/agents/backends/sandbox.py +275 -0
- aury/agents/backends/session/__init__.py +8 -0
- aury/agents/backends/session/memory.py +93 -0
- aury/agents/backends/session/types.py +124 -0
- aury/agents/backends/shell/__init__.py +11 -0
- aury/agents/backends/shell/local.py +110 -0
- aury/agents/backends/shell/types.py +55 -0
- aury/agents/backends/shell.py +209 -0
- aury/agents/backends/snapshot/__init__.py +19 -0
- aury/agents/backends/snapshot/git.py +95 -0
- aury/agents/backends/snapshot/hybrid.py +125 -0
- aury/agents/backends/snapshot/memory.py +86 -0
- aury/agents/backends/snapshot/types.py +59 -0
- aury/agents/backends/state/__init__.py +29 -0
- aury/agents/backends/state/composite.py +49 -0
- aury/agents/backends/state/file.py +57 -0
- aury/agents/backends/state/memory.py +52 -0
- aury/agents/backends/state/sqlite.py +262 -0
- aury/agents/backends/state/types.py +178 -0
- aury/agents/backends/subagent/__init__.py +165 -0
- aury/agents/cli/__init__.py +41 -0
- aury/agents/cli/chat.py +239 -0
- aury/agents/cli/config.py +236 -0
- aury/agents/cli/extensions.py +460 -0
- aury/agents/cli/main.py +189 -0
- aury/agents/cli/session.py +337 -0
- aury/agents/cli/workflow.py +276 -0
- aury/agents/context_providers/__init__.py +66 -0
- aury/agents/context_providers/artifact.py +299 -0
- aury/agents/context_providers/base.py +177 -0
- aury/agents/context_providers/memory.py +70 -0
- aury/agents/context_providers/message.py +130 -0
- aury/agents/context_providers/skill.py +50 -0
- aury/agents/context_providers/subagent.py +46 -0
- aury/agents/context_providers/tool.py +68 -0
- aury/agents/core/__init__.py +83 -0
- aury/agents/core/base.py +573 -0
- aury/agents/core/context.py +797 -0
- aury/agents/core/context_builder.py +303 -0
- aury/agents/core/event_bus/__init__.py +15 -0
- aury/agents/core/event_bus/bus.py +203 -0
- aury/agents/core/factory.py +169 -0
- aury/agents/core/isolator.py +97 -0
- aury/agents/core/logging.py +95 -0
- aury/agents/core/parallel.py +194 -0
- aury/agents/core/runner.py +139 -0
- aury/agents/core/services/__init__.py +5 -0
- aury/agents/core/services/file_session.py +144 -0
- aury/agents/core/services/message.py +53 -0
- aury/agents/core/services/session.py +53 -0
- aury/agents/core/signals.py +109 -0
- aury/agents/core/state.py +363 -0
- aury/agents/core/types/__init__.py +107 -0
- aury/agents/core/types/action.py +176 -0
- aury/agents/core/types/artifact.py +135 -0
- aury/agents/core/types/block.py +736 -0
- aury/agents/core/types/message.py +350 -0
- aury/agents/core/types/recall.py +144 -0
- aury/agents/core/types/session.py +257 -0
- aury/agents/core/types/subagent.py +154 -0
- aury/agents/core/types/tool.py +205 -0
- aury/agents/eval/__init__.py +331 -0
- aury/agents/hitl/__init__.py +57 -0
- aury/agents/hitl/ask_user.py +242 -0
- aury/agents/hitl/compaction.py +230 -0
- aury/agents/hitl/exceptions.py +87 -0
- aury/agents/hitl/permission.py +617 -0
- aury/agents/hitl/revert.py +216 -0
- aury/agents/llm/__init__.py +31 -0
- aury/agents/llm/adapter.py +367 -0
- aury/agents/llm/openai.py +294 -0
- aury/agents/llm/provider.py +476 -0
- aury/agents/mcp/__init__.py +153 -0
- aury/agents/memory/__init__.py +46 -0
- aury/agents/memory/compaction.py +394 -0
- aury/agents/memory/manager.py +465 -0
- aury/agents/memory/processor.py +177 -0
- aury/agents/memory/store.py +187 -0
- aury/agents/memory/types.py +137 -0
- aury/agents/messages/__init__.py +40 -0
- aury/agents/messages/config.py +47 -0
- aury/agents/messages/raw_store.py +224 -0
- aury/agents/messages/store.py +118 -0
- aury/agents/messages/types.py +88 -0
- aury/agents/middleware/__init__.py +31 -0
- aury/agents/middleware/base.py +341 -0
- aury/agents/middleware/chain.py +342 -0
- aury/agents/middleware/message.py +129 -0
- aury/agents/middleware/message_container.py +126 -0
- aury/agents/middleware/raw_message.py +153 -0
- aury/agents/middleware/truncation.py +139 -0
- aury/agents/middleware/types.py +81 -0
- aury/agents/plugin.py +162 -0
- aury/agents/react/__init__.py +4 -0
- aury/agents/react/agent.py +1923 -0
- aury/agents/sandbox/__init__.py +23 -0
- aury/agents/sandbox/local.py +239 -0
- aury/agents/sandbox/remote.py +200 -0
- aury/agents/sandbox/types.py +115 -0
- aury/agents/skill/__init__.py +16 -0
- aury/agents/skill/loader.py +180 -0
- aury/agents/skill/types.py +83 -0
- aury/agents/tool/__init__.py +39 -0
- aury/agents/tool/builtin/__init__.py +23 -0
- aury/agents/tool/builtin/ask_user.py +155 -0
- aury/agents/tool/builtin/bash.py +107 -0
- aury/agents/tool/builtin/delegate.py +726 -0
- aury/agents/tool/builtin/edit.py +121 -0
- aury/agents/tool/builtin/plan.py +277 -0
- aury/agents/tool/builtin/read.py +91 -0
- aury/agents/tool/builtin/thinking.py +111 -0
- aury/agents/tool/builtin/yield_result.py +130 -0
- aury/agents/tool/decorator.py +252 -0
- aury/agents/tool/set.py +204 -0
- aury/agents/usage/__init__.py +12 -0
- aury/agents/usage/tracker.py +236 -0
- aury/agents/workflow/__init__.py +85 -0
- aury/agents/workflow/adapter.py +268 -0
- aury/agents/workflow/dag.py +116 -0
- aury/agents/workflow/dsl.py +575 -0
- aury/agents/workflow/executor.py +659 -0
- aury/agents/workflow/expression.py +136 -0
- aury/agents/workflow/parser.py +182 -0
- aury/agents/workflow/state.py +145 -0
- aury/agents/workflow/types.py +86 -0
- aury_agent-0.0.4.dist-info/METADATA +90 -0
- aury_agent-0.0.4.dist-info/RECORD +149 -0
- aury_agent-0.0.4.dist-info/WHEEL +4 -0
- aury_agent-0.0.4.dist-info/entry_points.txt +2 -0
aury/agents/cli/chat.py
ADDED
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
"""
|
|
2
|
+
交互式对话命令
|
|
3
|
+
==============
|
|
4
|
+
|
|
5
|
+
提供命令行交互式对话功能。
|
|
6
|
+
"""
|
|
7
|
+
import asyncio
|
|
8
|
+
from typing import Optional, Any
|
|
9
|
+
from uuid import uuid4
|
|
10
|
+
|
|
11
|
+
from rich.console import Console
|
|
12
|
+
from rich.markdown import Markdown
|
|
13
|
+
from rich.panel import Panel
|
|
14
|
+
from rich.prompt import Prompt
|
|
15
|
+
|
|
16
|
+
from aury.agents.core import Session, InvocationContext
|
|
17
|
+
from aury.agents.react import ReactAgent
|
|
18
|
+
|
|
19
|
+
console = Console()
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class DefaultChatAgent(ReactAgent):
|
|
23
|
+
"""默认对话 Agent。"""
|
|
24
|
+
|
|
25
|
+
name = "chat_agent"
|
|
26
|
+
description = "通用对话助手"
|
|
27
|
+
system_prompt = """你是一个友好的 AI 助手。请用中文回答用户的问题。"""
|
|
28
|
+
tools = []
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
async def chat_command(
|
|
32
|
+
agent_name: Optional[str] = None,
|
|
33
|
+
session_id: Optional[str] = None,
|
|
34
|
+
config: Optional[dict] = None,
|
|
35
|
+
verbose: bool = False,
|
|
36
|
+
):
|
|
37
|
+
"""
|
|
38
|
+
运行交互式对话。
|
|
39
|
+
|
|
40
|
+
Args:
|
|
41
|
+
agent_name: Agent 名称或模块路径
|
|
42
|
+
session_id: 会话 ID(可选,用于恢复会话)
|
|
43
|
+
config: 配置字典
|
|
44
|
+
verbose: 是否显示详细信息
|
|
45
|
+
"""
|
|
46
|
+
config = config or {}
|
|
47
|
+
|
|
48
|
+
# 显示欢迎信息
|
|
49
|
+
console.print(Panel(
|
|
50
|
+
"[bold blue]Aury Agent 交互式对话[/bold blue]\n\n"
|
|
51
|
+
"输入消息与 Agent 对话\n"
|
|
52
|
+
"输入 /help 查看可用命令\n"
|
|
53
|
+
"输入 /quit 或按 Ctrl+C 退出",
|
|
54
|
+
title="欢迎",
|
|
55
|
+
))
|
|
56
|
+
|
|
57
|
+
# 创建或加载会话
|
|
58
|
+
if session_id:
|
|
59
|
+
console.print(f"[dim]恢复会话: {session_id}[/dim]")
|
|
60
|
+
session = Session(session_id=session_id)
|
|
61
|
+
# TODO: 从存储加载会话
|
|
62
|
+
else:
|
|
63
|
+
session_id = f"chat-{uuid4().hex[:8]}"
|
|
64
|
+
session = Session(session_id=session_id)
|
|
65
|
+
console.print(f"[dim]新会话: {session_id}[/dim]")
|
|
66
|
+
|
|
67
|
+
# 创建 Agent
|
|
68
|
+
agent = await _create_agent(agent_name, config)
|
|
69
|
+
|
|
70
|
+
if verbose:
|
|
71
|
+
console.print(f"[dim]使用 Agent: {agent.name}[/dim]")
|
|
72
|
+
|
|
73
|
+
# 创建上下文
|
|
74
|
+
context = InvocationContext(session=session)
|
|
75
|
+
|
|
76
|
+
# 主循环
|
|
77
|
+
while True:
|
|
78
|
+
try:
|
|
79
|
+
# 获取用户输入
|
|
80
|
+
user_input = Prompt.ask("\n[bold cyan]你[/bold cyan]")
|
|
81
|
+
|
|
82
|
+
if not user_input.strip():
|
|
83
|
+
continue
|
|
84
|
+
|
|
85
|
+
# 处理命令
|
|
86
|
+
if user_input.startswith("/"):
|
|
87
|
+
should_continue = await _handle_command(
|
|
88
|
+
user_input, session, context, verbose
|
|
89
|
+
)
|
|
90
|
+
if not should_continue:
|
|
91
|
+
break
|
|
92
|
+
continue
|
|
93
|
+
|
|
94
|
+
# 运行 Agent
|
|
95
|
+
console.print("[dim]思考中...[/dim]")
|
|
96
|
+
|
|
97
|
+
try:
|
|
98
|
+
result = await agent.run(user_input, context)
|
|
99
|
+
|
|
100
|
+
# 显示响应
|
|
101
|
+
console.print()
|
|
102
|
+
console.print(Panel(
|
|
103
|
+
Markdown(result.output) if result.output else "[dim]无响应[/dim]",
|
|
104
|
+
title="[bold green]Agent[/bold green]",
|
|
105
|
+
border_style="green",
|
|
106
|
+
))
|
|
107
|
+
|
|
108
|
+
# 显示工具调用(详细模式)
|
|
109
|
+
if verbose and hasattr(result, 'tool_calls') and result.tool_calls:
|
|
110
|
+
console.print("[dim]工具调用:[/dim]")
|
|
111
|
+
for tc in result.tool_calls:
|
|
112
|
+
console.print(f" [dim]- {tc.name}[/dim]")
|
|
113
|
+
|
|
114
|
+
except Exception as e:
|
|
115
|
+
console.print(f"[red]错误: {e}[/red]")
|
|
116
|
+
if verbose:
|
|
117
|
+
import traceback
|
|
118
|
+
console.print(f"[dim]{traceback.format_exc()}[/dim]")
|
|
119
|
+
|
|
120
|
+
except KeyboardInterrupt:
|
|
121
|
+
console.print("\n[yellow]收到中断信号[/yellow]")
|
|
122
|
+
break
|
|
123
|
+
except EOFError:
|
|
124
|
+
break
|
|
125
|
+
|
|
126
|
+
# 退出
|
|
127
|
+
console.print("\n[dim]再见![/dim]")
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
async def _create_agent(
|
|
131
|
+
agent_name: Optional[str],
|
|
132
|
+
config: dict,
|
|
133
|
+
) -> ReactAgent:
|
|
134
|
+
"""创建 Agent 实例。"""
|
|
135
|
+
|
|
136
|
+
if agent_name is None:
|
|
137
|
+
# 使用默认 Agent
|
|
138
|
+
return DefaultChatAgent()
|
|
139
|
+
|
|
140
|
+
# 尝试从模块加载
|
|
141
|
+
if "." in agent_name or agent_name.endswith(".py"):
|
|
142
|
+
try:
|
|
143
|
+
import importlib.util
|
|
144
|
+
from pathlib import Path
|
|
145
|
+
|
|
146
|
+
if agent_name.endswith(".py"):
|
|
147
|
+
# 从文件加载
|
|
148
|
+
path = Path(agent_name)
|
|
149
|
+
spec = importlib.util.spec_from_file_location("agent_module", path)
|
|
150
|
+
module = importlib.util.module_from_spec(spec)
|
|
151
|
+
spec.loader.exec_module(module)
|
|
152
|
+
else:
|
|
153
|
+
# 从模块路径加载
|
|
154
|
+
module = importlib.import_module(agent_name)
|
|
155
|
+
|
|
156
|
+
# 查找 Agent 类
|
|
157
|
+
for name in dir(module):
|
|
158
|
+
obj = getattr(module, name)
|
|
159
|
+
if (
|
|
160
|
+
isinstance(obj, type)
|
|
161
|
+
and issubclass(obj, ReactAgent)
|
|
162
|
+
and obj is not ReactAgent
|
|
163
|
+
):
|
|
164
|
+
return obj()
|
|
165
|
+
|
|
166
|
+
raise ValueError(f"模块中未找到 Agent 类: {agent_name}")
|
|
167
|
+
|
|
168
|
+
except Exception as e:
|
|
169
|
+
console.print(f"[yellow]无法加载 Agent '{agent_name}': {e}[/yellow]")
|
|
170
|
+
console.print("[dim]使用默认 Agent[/dim]")
|
|
171
|
+
return DefaultChatAgent()
|
|
172
|
+
|
|
173
|
+
# TODO: 从注册表查找已注册的 Agent
|
|
174
|
+
console.print(f"[yellow]未找到 Agent '{agent_name}',使用默认 Agent[/yellow]")
|
|
175
|
+
return DefaultChatAgent()
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
async def _handle_command(
|
|
179
|
+
command: str,
|
|
180
|
+
session: Session,
|
|
181
|
+
context: InvocationContext,
|
|
182
|
+
verbose: bool,
|
|
183
|
+
) -> bool:
|
|
184
|
+
"""
|
|
185
|
+
处理斜杠命令。
|
|
186
|
+
|
|
187
|
+
Returns:
|
|
188
|
+
bool: 是否继续对话
|
|
189
|
+
"""
|
|
190
|
+
cmd = command.strip().lower()
|
|
191
|
+
|
|
192
|
+
if cmd in ["/quit", "/exit", "/q"]:
|
|
193
|
+
return False
|
|
194
|
+
|
|
195
|
+
elif cmd == "/help":
|
|
196
|
+
console.print(Panel(
|
|
197
|
+
"[bold]可用命令:[/bold]\n\n"
|
|
198
|
+
"/help - 显示帮助信息\n"
|
|
199
|
+
"/quit - 退出对话\n"
|
|
200
|
+
"/clear - 清空对话历史\n"
|
|
201
|
+
"/session - 显示当前会话信息\n"
|
|
202
|
+
"/history - 显示对话历史\n"
|
|
203
|
+
"/save - 保存当前会话\n"
|
|
204
|
+
"/verbose - 切换详细模式",
|
|
205
|
+
title="帮助",
|
|
206
|
+
))
|
|
207
|
+
|
|
208
|
+
elif cmd == "/clear":
|
|
209
|
+
context.history = []
|
|
210
|
+
console.print("[green]对话历史已清空[/green]")
|
|
211
|
+
|
|
212
|
+
elif cmd == "/session":
|
|
213
|
+
console.print(Panel(
|
|
214
|
+
f"会话 ID: {session.session_id}\n"
|
|
215
|
+
f"消息数: {len(context.history) if hasattr(context, 'history') else 0}",
|
|
216
|
+
title="会话信息",
|
|
217
|
+
))
|
|
218
|
+
|
|
219
|
+
elif cmd == "/history":
|
|
220
|
+
if hasattr(context, 'history') and context.history:
|
|
221
|
+
for msg in context.history[-10:]: # 最近 10 条
|
|
222
|
+
role = msg.get("role", "unknown")
|
|
223
|
+
content = msg.get("content", "")[:100]
|
|
224
|
+
console.print(f"[dim]{role}: {content}...[/dim]")
|
|
225
|
+
else:
|
|
226
|
+
console.print("[dim]暂无对话历史[/dim]")
|
|
227
|
+
|
|
228
|
+
elif cmd == "/save":
|
|
229
|
+
# TODO: 实现会话保存
|
|
230
|
+
console.print("[green]会话已保存[/green]")
|
|
231
|
+
|
|
232
|
+
elif cmd == "/verbose":
|
|
233
|
+
console.print(f"[dim]详细模式: {'开启' if verbose else '关闭'}[/dim]")
|
|
234
|
+
|
|
235
|
+
else:
|
|
236
|
+
console.print(f"[yellow]未知命令: {command}[/yellow]")
|
|
237
|
+
console.print("[dim]输入 /help 查看可用命令[/dim]")
|
|
238
|
+
|
|
239
|
+
return True
|
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
"""
|
|
2
|
+
配置管理命令
|
|
3
|
+
============
|
|
4
|
+
|
|
5
|
+
管理 Aury Agent 的配置文件。
|
|
6
|
+
"""
|
|
7
|
+
import os
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
from typing import Optional, Any
|
|
10
|
+
|
|
11
|
+
import typer
|
|
12
|
+
import yaml
|
|
13
|
+
from rich.console import Console
|
|
14
|
+
from rich.panel import Panel
|
|
15
|
+
from rich.syntax import Syntax
|
|
16
|
+
|
|
17
|
+
config_app = typer.Typer(help="配置管理命令")
|
|
18
|
+
console = Console()
|
|
19
|
+
|
|
20
|
+
# 默认配置目录
|
|
21
|
+
DEFAULT_CONFIG_DIR = Path.home() / ".aury"
|
|
22
|
+
DEFAULT_CONFIG_FILE = DEFAULT_CONFIG_DIR / "config.yaml"
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def get_default_config() -> dict:
|
|
26
|
+
"""获取默认配置。"""
|
|
27
|
+
return {
|
|
28
|
+
"agent": {
|
|
29
|
+
"default": "chat_agent",
|
|
30
|
+
"max_iterations": 20,
|
|
31
|
+
},
|
|
32
|
+
"model": {
|
|
33
|
+
"provider": "openai",
|
|
34
|
+
"model": "gpt-4",
|
|
35
|
+
"temperature": 0.7,
|
|
36
|
+
"max_tokens": 4096,
|
|
37
|
+
},
|
|
38
|
+
"session": {
|
|
39
|
+
"storage_path": str(DEFAULT_CONFIG_DIR / "sessions"),
|
|
40
|
+
"auto_save": True,
|
|
41
|
+
"max_history": 100,
|
|
42
|
+
},
|
|
43
|
+
"logging": {
|
|
44
|
+
"level": "INFO",
|
|
45
|
+
"file": None,
|
|
46
|
+
},
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def load_config(config_path: Optional[Path] = None) -> dict:
|
|
51
|
+
"""
|
|
52
|
+
加载配置文件。
|
|
53
|
+
|
|
54
|
+
Args:
|
|
55
|
+
config_path: 配置文件路径,为 None 时使用默认路径
|
|
56
|
+
|
|
57
|
+
Returns:
|
|
58
|
+
配置字典
|
|
59
|
+
"""
|
|
60
|
+
if config_path is None:
|
|
61
|
+
config_path = DEFAULT_CONFIG_FILE
|
|
62
|
+
|
|
63
|
+
config = get_default_config()
|
|
64
|
+
|
|
65
|
+
if config_path.exists():
|
|
66
|
+
try:
|
|
67
|
+
with open(config_path, "r", encoding="utf-8") as f:
|
|
68
|
+
user_config = yaml.safe_load(f) or {}
|
|
69
|
+
|
|
70
|
+
# 合并用户配置
|
|
71
|
+
_deep_merge(config, user_config)
|
|
72
|
+
except Exception as e:
|
|
73
|
+
console.print(f"[yellow]警告: 无法加载配置文件 {config_path}: {e}[/yellow]")
|
|
74
|
+
|
|
75
|
+
return config
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
def save_config(config: dict, config_path: Optional[Path] = None) -> None:
|
|
79
|
+
"""保存配置文件。"""
|
|
80
|
+
if config_path is None:
|
|
81
|
+
config_path = DEFAULT_CONFIG_FILE
|
|
82
|
+
|
|
83
|
+
# 确保目录存在
|
|
84
|
+
config_path.parent.mkdir(parents=True, exist_ok=True)
|
|
85
|
+
|
|
86
|
+
with open(config_path, "w", encoding="utf-8") as f:
|
|
87
|
+
yaml.safe_dump(config, f, allow_unicode=True, default_flow_style=False)
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def _deep_merge(base: dict, override: dict) -> None:
|
|
91
|
+
"""深度合并字典。"""
|
|
92
|
+
for key, value in override.items():
|
|
93
|
+
if key in base and isinstance(base[key], dict) and isinstance(value, dict):
|
|
94
|
+
_deep_merge(base[key], value)
|
|
95
|
+
else:
|
|
96
|
+
base[key] = value
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
@config_app.command("show")
|
|
100
|
+
def show_config(
|
|
101
|
+
config_file: Optional[Path] = typer.Option(
|
|
102
|
+
None,
|
|
103
|
+
"--file", "-f",
|
|
104
|
+
help="配置文件路径",
|
|
105
|
+
),
|
|
106
|
+
):
|
|
107
|
+
"""显示当前配置。"""
|
|
108
|
+
config = load_config(config_file)
|
|
109
|
+
|
|
110
|
+
yaml_str = yaml.safe_dump(config, allow_unicode=True, default_flow_style=False)
|
|
111
|
+
|
|
112
|
+
console.print(Panel(
|
|
113
|
+
Syntax(yaml_str, "yaml", theme="monokai"),
|
|
114
|
+
title="当前配置",
|
|
115
|
+
))
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
@config_app.command("init")
|
|
119
|
+
def init_config(
|
|
120
|
+
force: bool = typer.Option(
|
|
121
|
+
False,
|
|
122
|
+
"--force", "-f",
|
|
123
|
+
help="强制覆盖已存在的配置",
|
|
124
|
+
),
|
|
125
|
+
):
|
|
126
|
+
"""初始化默认配置文件。"""
|
|
127
|
+
if DEFAULT_CONFIG_FILE.exists() and not force:
|
|
128
|
+
console.print(f"[yellow]配置文件已存在: {DEFAULT_CONFIG_FILE}[/yellow]")
|
|
129
|
+
console.print("[dim]使用 --force 覆盖[/dim]")
|
|
130
|
+
raise typer.Exit(1)
|
|
131
|
+
|
|
132
|
+
# 创建默认配置
|
|
133
|
+
config = get_default_config()
|
|
134
|
+
save_config(config)
|
|
135
|
+
|
|
136
|
+
console.print(f"[green]配置文件已创建: {DEFAULT_CONFIG_FILE}[/green]")
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
@config_app.command("set")
|
|
140
|
+
def set_config(
|
|
141
|
+
key: str = typer.Argument(..., help="配置项键名(用点号分隔,如 model.temperature)"),
|
|
142
|
+
value: str = typer.Argument(..., help="配置项值"),
|
|
143
|
+
config_file: Optional[Path] = typer.Option(
|
|
144
|
+
None,
|
|
145
|
+
"--file", "-f",
|
|
146
|
+
help="配置文件路径",
|
|
147
|
+
),
|
|
148
|
+
):
|
|
149
|
+
"""设置配置项。"""
|
|
150
|
+
config = load_config(config_file)
|
|
151
|
+
|
|
152
|
+
# 解析键路径
|
|
153
|
+
keys = key.split(".")
|
|
154
|
+
current = config
|
|
155
|
+
|
|
156
|
+
for k in keys[:-1]:
|
|
157
|
+
if k not in current:
|
|
158
|
+
current[k] = {}
|
|
159
|
+
current = current[k]
|
|
160
|
+
|
|
161
|
+
# 尝试解析值类型
|
|
162
|
+
try:
|
|
163
|
+
# 尝试解析为数字
|
|
164
|
+
if "." in value:
|
|
165
|
+
parsed_value = float(value)
|
|
166
|
+
else:
|
|
167
|
+
parsed_value = int(value)
|
|
168
|
+
except ValueError:
|
|
169
|
+
# 尝试解析为布尔值
|
|
170
|
+
if value.lower() in ["true", "yes"]:
|
|
171
|
+
parsed_value = True
|
|
172
|
+
elif value.lower() in ["false", "no"]:
|
|
173
|
+
parsed_value = False
|
|
174
|
+
elif value.lower() == "null":
|
|
175
|
+
parsed_value = None
|
|
176
|
+
else:
|
|
177
|
+
parsed_value = value
|
|
178
|
+
|
|
179
|
+
current[keys[-1]] = parsed_value
|
|
180
|
+
|
|
181
|
+
# 保存
|
|
182
|
+
save_config(config, config_file)
|
|
183
|
+
|
|
184
|
+
console.print(f"[green]已设置 {key} = {parsed_value}[/green]")
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
@config_app.command("get")
|
|
188
|
+
def get_config(
|
|
189
|
+
key: str = typer.Argument(..., help="配置项键名"),
|
|
190
|
+
config_file: Optional[Path] = typer.Option(
|
|
191
|
+
None,
|
|
192
|
+
"--file", "-f",
|
|
193
|
+
help="配置文件路径",
|
|
194
|
+
),
|
|
195
|
+
):
|
|
196
|
+
"""获取配置项值。"""
|
|
197
|
+
config = load_config(config_file)
|
|
198
|
+
|
|
199
|
+
# 解析键路径
|
|
200
|
+
keys = key.split(".")
|
|
201
|
+
current = config
|
|
202
|
+
|
|
203
|
+
try:
|
|
204
|
+
for k in keys:
|
|
205
|
+
current = current[k]
|
|
206
|
+
console.print(f"{key} = {current}")
|
|
207
|
+
except KeyError:
|
|
208
|
+
console.print(f"[red]配置项不存在: {key}[/red]")
|
|
209
|
+
raise typer.Exit(1)
|
|
210
|
+
|
|
211
|
+
|
|
212
|
+
@config_app.command("path")
|
|
213
|
+
def config_path():
|
|
214
|
+
"""显示配置文件路径。"""
|
|
215
|
+
console.print(f"配置目录: {DEFAULT_CONFIG_DIR}")
|
|
216
|
+
console.print(f"配置文件: {DEFAULT_CONFIG_FILE}")
|
|
217
|
+
console.print(f"文件存在: {'是' if DEFAULT_CONFIG_FILE.exists() else '否'}")
|
|
218
|
+
|
|
219
|
+
|
|
220
|
+
@config_app.command("edit")
|
|
221
|
+
def edit_config():
|
|
222
|
+
"""使用默认编辑器编辑配置文件。"""
|
|
223
|
+
import subprocess
|
|
224
|
+
|
|
225
|
+
if not DEFAULT_CONFIG_FILE.exists():
|
|
226
|
+
console.print("[yellow]配置文件不存在,先创建默认配置[/yellow]")
|
|
227
|
+
config = get_default_config()
|
|
228
|
+
save_config(config)
|
|
229
|
+
|
|
230
|
+
editor = os.environ.get("EDITOR", "vim")
|
|
231
|
+
|
|
232
|
+
try:
|
|
233
|
+
subprocess.run([editor, str(DEFAULT_CONFIG_FILE)])
|
|
234
|
+
except FileNotFoundError:
|
|
235
|
+
console.print(f"[red]编辑器未找到: {editor}[/red]")
|
|
236
|
+
console.print(f"[dim]请设置 EDITOR 环境变量或手动编辑: {DEFAULT_CONFIG_FILE}[/dim]")
|