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.
Files changed (149) hide show
  1. aury/__init__.py +2 -0
  2. aury/agents/__init__.py +55 -0
  3. aury/agents/a2a/__init__.py +168 -0
  4. aury/agents/backends/__init__.py +196 -0
  5. aury/agents/backends/artifact/__init__.py +9 -0
  6. aury/agents/backends/artifact/memory.py +130 -0
  7. aury/agents/backends/artifact/types.py +133 -0
  8. aury/agents/backends/code/__init__.py +65 -0
  9. aury/agents/backends/file/__init__.py +11 -0
  10. aury/agents/backends/file/local.py +66 -0
  11. aury/agents/backends/file/types.py +40 -0
  12. aury/agents/backends/invocation/__init__.py +8 -0
  13. aury/agents/backends/invocation/memory.py +81 -0
  14. aury/agents/backends/invocation/types.py +110 -0
  15. aury/agents/backends/memory/__init__.py +8 -0
  16. aury/agents/backends/memory/memory.py +179 -0
  17. aury/agents/backends/memory/types.py +136 -0
  18. aury/agents/backends/message/__init__.py +9 -0
  19. aury/agents/backends/message/memory.py +122 -0
  20. aury/agents/backends/message/types.py +124 -0
  21. aury/agents/backends/sandbox.py +275 -0
  22. aury/agents/backends/session/__init__.py +8 -0
  23. aury/agents/backends/session/memory.py +93 -0
  24. aury/agents/backends/session/types.py +124 -0
  25. aury/agents/backends/shell/__init__.py +11 -0
  26. aury/agents/backends/shell/local.py +110 -0
  27. aury/agents/backends/shell/types.py +55 -0
  28. aury/agents/backends/shell.py +209 -0
  29. aury/agents/backends/snapshot/__init__.py +19 -0
  30. aury/agents/backends/snapshot/git.py +95 -0
  31. aury/agents/backends/snapshot/hybrid.py +125 -0
  32. aury/agents/backends/snapshot/memory.py +86 -0
  33. aury/agents/backends/snapshot/types.py +59 -0
  34. aury/agents/backends/state/__init__.py +29 -0
  35. aury/agents/backends/state/composite.py +49 -0
  36. aury/agents/backends/state/file.py +57 -0
  37. aury/agents/backends/state/memory.py +52 -0
  38. aury/agents/backends/state/sqlite.py +262 -0
  39. aury/agents/backends/state/types.py +178 -0
  40. aury/agents/backends/subagent/__init__.py +165 -0
  41. aury/agents/cli/__init__.py +41 -0
  42. aury/agents/cli/chat.py +239 -0
  43. aury/agents/cli/config.py +236 -0
  44. aury/agents/cli/extensions.py +460 -0
  45. aury/agents/cli/main.py +189 -0
  46. aury/agents/cli/session.py +337 -0
  47. aury/agents/cli/workflow.py +276 -0
  48. aury/agents/context_providers/__init__.py +66 -0
  49. aury/agents/context_providers/artifact.py +299 -0
  50. aury/agents/context_providers/base.py +177 -0
  51. aury/agents/context_providers/memory.py +70 -0
  52. aury/agents/context_providers/message.py +130 -0
  53. aury/agents/context_providers/skill.py +50 -0
  54. aury/agents/context_providers/subagent.py +46 -0
  55. aury/agents/context_providers/tool.py +68 -0
  56. aury/agents/core/__init__.py +83 -0
  57. aury/agents/core/base.py +573 -0
  58. aury/agents/core/context.py +797 -0
  59. aury/agents/core/context_builder.py +303 -0
  60. aury/agents/core/event_bus/__init__.py +15 -0
  61. aury/agents/core/event_bus/bus.py +203 -0
  62. aury/agents/core/factory.py +169 -0
  63. aury/agents/core/isolator.py +97 -0
  64. aury/agents/core/logging.py +95 -0
  65. aury/agents/core/parallel.py +194 -0
  66. aury/agents/core/runner.py +139 -0
  67. aury/agents/core/services/__init__.py +5 -0
  68. aury/agents/core/services/file_session.py +144 -0
  69. aury/agents/core/services/message.py +53 -0
  70. aury/agents/core/services/session.py +53 -0
  71. aury/agents/core/signals.py +109 -0
  72. aury/agents/core/state.py +363 -0
  73. aury/agents/core/types/__init__.py +107 -0
  74. aury/agents/core/types/action.py +176 -0
  75. aury/agents/core/types/artifact.py +135 -0
  76. aury/agents/core/types/block.py +736 -0
  77. aury/agents/core/types/message.py +350 -0
  78. aury/agents/core/types/recall.py +144 -0
  79. aury/agents/core/types/session.py +257 -0
  80. aury/agents/core/types/subagent.py +154 -0
  81. aury/agents/core/types/tool.py +205 -0
  82. aury/agents/eval/__init__.py +331 -0
  83. aury/agents/hitl/__init__.py +57 -0
  84. aury/agents/hitl/ask_user.py +242 -0
  85. aury/agents/hitl/compaction.py +230 -0
  86. aury/agents/hitl/exceptions.py +87 -0
  87. aury/agents/hitl/permission.py +617 -0
  88. aury/agents/hitl/revert.py +216 -0
  89. aury/agents/llm/__init__.py +31 -0
  90. aury/agents/llm/adapter.py +367 -0
  91. aury/agents/llm/openai.py +294 -0
  92. aury/agents/llm/provider.py +476 -0
  93. aury/agents/mcp/__init__.py +153 -0
  94. aury/agents/memory/__init__.py +46 -0
  95. aury/agents/memory/compaction.py +394 -0
  96. aury/agents/memory/manager.py +465 -0
  97. aury/agents/memory/processor.py +177 -0
  98. aury/agents/memory/store.py +187 -0
  99. aury/agents/memory/types.py +137 -0
  100. aury/agents/messages/__init__.py +40 -0
  101. aury/agents/messages/config.py +47 -0
  102. aury/agents/messages/raw_store.py +224 -0
  103. aury/agents/messages/store.py +118 -0
  104. aury/agents/messages/types.py +88 -0
  105. aury/agents/middleware/__init__.py +31 -0
  106. aury/agents/middleware/base.py +341 -0
  107. aury/agents/middleware/chain.py +342 -0
  108. aury/agents/middleware/message.py +129 -0
  109. aury/agents/middleware/message_container.py +126 -0
  110. aury/agents/middleware/raw_message.py +153 -0
  111. aury/agents/middleware/truncation.py +139 -0
  112. aury/agents/middleware/types.py +81 -0
  113. aury/agents/plugin.py +162 -0
  114. aury/agents/react/__init__.py +4 -0
  115. aury/agents/react/agent.py +1923 -0
  116. aury/agents/sandbox/__init__.py +23 -0
  117. aury/agents/sandbox/local.py +239 -0
  118. aury/agents/sandbox/remote.py +200 -0
  119. aury/agents/sandbox/types.py +115 -0
  120. aury/agents/skill/__init__.py +16 -0
  121. aury/agents/skill/loader.py +180 -0
  122. aury/agents/skill/types.py +83 -0
  123. aury/agents/tool/__init__.py +39 -0
  124. aury/agents/tool/builtin/__init__.py +23 -0
  125. aury/agents/tool/builtin/ask_user.py +155 -0
  126. aury/agents/tool/builtin/bash.py +107 -0
  127. aury/agents/tool/builtin/delegate.py +726 -0
  128. aury/agents/tool/builtin/edit.py +121 -0
  129. aury/agents/tool/builtin/plan.py +277 -0
  130. aury/agents/tool/builtin/read.py +91 -0
  131. aury/agents/tool/builtin/thinking.py +111 -0
  132. aury/agents/tool/builtin/yield_result.py +130 -0
  133. aury/agents/tool/decorator.py +252 -0
  134. aury/agents/tool/set.py +204 -0
  135. aury/agents/usage/__init__.py +12 -0
  136. aury/agents/usage/tracker.py +236 -0
  137. aury/agents/workflow/__init__.py +85 -0
  138. aury/agents/workflow/adapter.py +268 -0
  139. aury/agents/workflow/dag.py +116 -0
  140. aury/agents/workflow/dsl.py +575 -0
  141. aury/agents/workflow/executor.py +659 -0
  142. aury/agents/workflow/expression.py +136 -0
  143. aury/agents/workflow/parser.py +182 -0
  144. aury/agents/workflow/state.py +145 -0
  145. aury/agents/workflow/types.py +86 -0
  146. aury_agent-0.0.4.dist-info/METADATA +90 -0
  147. aury_agent-0.0.4.dist-info/RECORD +149 -0
  148. aury_agent-0.0.4.dist-info/WHEEL +4 -0
  149. aury_agent-0.0.4.dist-info/entry_points.txt +2 -0
@@ -0,0 +1,460 @@
1
+ """CLI Extension System for custom Agents and Tools.
2
+
3
+ This module provides a plugin/extension system that allows users to register
4
+ custom Agents and Tools that can be used with the CLI commands.
5
+
6
+ Usage:
7
+ 1. Create a configuration file (e.g., aury.extensions.toml):
8
+
9
+ [agents]
10
+ my_agent = "path.to.module:MyAgentClass"
11
+
12
+ [tools]
13
+ my_tool = "path.to.module:my_tool_function"
14
+
15
+ 2. Or programmatically:
16
+
17
+ from aury.agents.cli.extensions import ExtensionRegistry
18
+ registry = ExtensionRegistry()
19
+ registry.register_agent("my_agent", MyAgentClass)
20
+ registry.register_tool("my_tool", my_tool_function)
21
+ """
22
+ from __future__ import annotations
23
+
24
+ import importlib
25
+ import sys
26
+ from dataclasses import dataclass, field
27
+ from pathlib import Path
28
+ from typing import Any, Callable, TYPE_CHECKING
29
+
30
+ from rich.console import Console
31
+
32
+ if TYPE_CHECKING:
33
+ from aury.agents.core.base import BaseAgent
34
+ from aury.agents.tools import BaseTool
35
+
36
+ console = Console()
37
+
38
+
39
+ @dataclass
40
+ class ExtensionInfo:
41
+ """Information about a registered extension."""
42
+ name: str
43
+ module_path: str | None
44
+ class_or_func: Any
45
+ description: str = ""
46
+ source: str = "programmatic" # programmatic, config, entry_point
47
+
48
+
49
+ class ExtensionRegistry:
50
+ """Registry for custom Agents and Tools.
51
+
52
+ This class provides a central registry for custom extensions that can be
53
+ used with the CLI commands. Extensions can be registered:
54
+
55
+ 1. Programmatically via register_agent() / register_tool()
56
+ 2. From configuration files (TOML/YAML)
57
+ 3. From Python entry points
58
+
59
+ Example:
60
+ >>> registry = ExtensionRegistry()
61
+ >>> registry.register_agent("my_agent", MyAgent)
62
+ >>> registry.register_tool("my_tool", my_tool_func)
63
+ >>>
64
+ >>> # Load from config
65
+ >>> registry.load_from_config("~/.aury/extensions.toml")
66
+ >>>
67
+ >>> # Get registered extensions
68
+ >>> agent_cls = registry.get_agent("my_agent")
69
+ >>> tool = registry.get_tool("my_tool")
70
+ """
71
+
72
+ _instance: "ExtensionRegistry | None" = None
73
+
74
+ def __new__(cls) -> "ExtensionRegistry":
75
+ """Singleton pattern for global registry."""
76
+ if cls._instance is None:
77
+ cls._instance = super().__new__(cls)
78
+ cls._instance._init()
79
+ return cls._instance
80
+
81
+ def _init(self) -> None:
82
+ """Initialize the registry."""
83
+ self._agents: dict[str, ExtensionInfo] = {}
84
+ self._tools: dict[str, ExtensionInfo] = {}
85
+ self._loaded_configs: set[str] = set()
86
+
87
+ # ========== Agent Registration ==========
88
+
89
+ def register_agent(
90
+ self,
91
+ name: str,
92
+ agent_class: type["BaseAgent"],
93
+ description: str = "",
94
+ ) -> None:
95
+ """Register a custom Agent class.
96
+
97
+ Args:
98
+ name: Unique name for the agent
99
+ agent_class: The Agent class (must inherit from BaseAgent)
100
+ description: Optional description
101
+ """
102
+ self._agents[name] = ExtensionInfo(
103
+ name=name,
104
+ module_path=f"{agent_class.__module__}:{agent_class.__name__}",
105
+ class_or_func=agent_class,
106
+ description=description or getattr(agent_class, "description", ""),
107
+ source="programmatic",
108
+ )
109
+
110
+ def get_agent(self, name: str) -> type["BaseAgent"] | None:
111
+ """Get a registered Agent class by name."""
112
+ info = self._agents.get(name)
113
+ if info:
114
+ return info.class_or_func
115
+ return None
116
+
117
+ def list_agents(self) -> list[ExtensionInfo]:
118
+ """List all registered Agents."""
119
+ return list(self._agents.values())
120
+
121
+ # ========== Tool Registration ==========
122
+
123
+ def register_tool(
124
+ self,
125
+ name: str,
126
+ tool: "BaseTool | Callable",
127
+ description: str = "",
128
+ ) -> None:
129
+ """Register a custom Tool.
130
+
131
+ Args:
132
+ name: Unique name for the tool
133
+ tool: Tool instance or function decorated with @tool
134
+ description: Optional description
135
+ """
136
+ self._tools[name] = ExtensionInfo(
137
+ name=name,
138
+ module_path=self._get_module_path(tool),
139
+ class_or_func=tool,
140
+ description=description or getattr(tool, "description", ""),
141
+ source="programmatic",
142
+ )
143
+
144
+ def get_tool(self, name: str) -> "BaseTool | Callable | None":
145
+ """Get a registered Tool by name."""
146
+ info = self._tools.get(name)
147
+ if info:
148
+ return info.class_or_func
149
+ return None
150
+
151
+ def list_tools(self) -> list[ExtensionInfo]:
152
+ """List all registered Tools."""
153
+ return list(self._tools.values())
154
+
155
+ # ========== Config Loading ==========
156
+
157
+ def load_from_config(self, config_path: str | Path) -> int:
158
+ """Load extensions from a configuration file.
159
+
160
+ Supports TOML format:
161
+
162
+ [agents]
163
+ my_agent = "mymodule.agents:MyAgent"
164
+
165
+ [tools]
166
+ my_tool = "mymodule.tools:my_tool"
167
+
168
+ Args:
169
+ config_path: Path to configuration file
170
+
171
+ Returns:
172
+ Number of extensions loaded
173
+ """
174
+ config_path = Path(config_path).expanduser()
175
+
176
+ if not config_path.exists():
177
+ return 0
178
+
179
+ config_key = str(config_path.resolve())
180
+ if config_key in self._loaded_configs:
181
+ return 0
182
+
183
+ self._loaded_configs.add(config_key)
184
+
185
+ count = 0
186
+
187
+ try:
188
+ import tomllib
189
+ except ImportError:
190
+ import tomli as tomllib
191
+
192
+ try:
193
+ with open(config_path, "rb") as f:
194
+ config = tomllib.load(f)
195
+
196
+ # Load agents
197
+ for name, path in config.get("agents", {}).items():
198
+ try:
199
+ cls = self._import_from_string(path)
200
+ self._agents[name] = ExtensionInfo(
201
+ name=name,
202
+ module_path=path,
203
+ class_or_func=cls,
204
+ source="config",
205
+ )
206
+ count += 1
207
+ except Exception as e:
208
+ console.print(f"[yellow]Warning: Failed to load agent '{name}': {e}[/yellow]")
209
+
210
+ # Load tools
211
+ for name, path in config.get("tools", {}).items():
212
+ try:
213
+ func = self._import_from_string(path)
214
+ self._tools[name] = ExtensionInfo(
215
+ name=name,
216
+ module_path=path,
217
+ class_or_func=func,
218
+ source="config",
219
+ )
220
+ count += 1
221
+ except Exception as e:
222
+ console.print(f"[yellow]Warning: Failed to load tool '{name}': {e}[/yellow]")
223
+
224
+ except Exception as e:
225
+ console.print(f"[yellow]Warning: Failed to load config {config_path}: {e}[/yellow]")
226
+
227
+ return count
228
+
229
+ def load_from_directory(self, directory: str | Path) -> int:
230
+ """Load extensions from Python files in a directory.
231
+
232
+ Scans for .py files and looks for:
233
+ - Classes with `register_as_agent = True`
234
+ - Functions decorated with `@tool`
235
+
236
+ Args:
237
+ directory: Directory to scan
238
+
239
+ Returns:
240
+ Number of extensions loaded
241
+ """
242
+ directory = Path(directory).expanduser()
243
+
244
+ if not directory.is_dir():
245
+ return 0
246
+
247
+ count = 0
248
+
249
+ for py_file in directory.glob("*.py"):
250
+ if py_file.name.startswith("_"):
251
+ continue
252
+
253
+ try:
254
+ count += self._load_from_file(py_file)
255
+ except Exception as e:
256
+ console.print(f"[yellow]Warning: Failed to load {py_file}: {e}[/yellow]")
257
+
258
+ return count
259
+
260
+ def load_from_entry_points(self, group: str = "aury.extensions") -> int:
261
+ """Load extensions from Python entry points.
262
+
263
+ This allows packages to declare extensions in their pyproject.toml:
264
+
265
+ [project.entry-points."aury.extensions"]
266
+ my_agent = "mypackage:MyAgent"
267
+ my_tool = "mypackage:my_tool"
268
+
269
+ Args:
270
+ group: Entry point group name
271
+
272
+ Returns:
273
+ Number of extensions loaded
274
+ """
275
+ count = 0
276
+
277
+ try:
278
+ from importlib.metadata import entry_points
279
+
280
+ # Python 3.10+ syntax
281
+ eps = entry_points(group=group)
282
+
283
+ for ep in eps:
284
+ try:
285
+ obj = ep.load()
286
+
287
+ # Determine if it's an Agent or Tool
288
+ if self._is_agent_class(obj):
289
+ self._agents[ep.name] = ExtensionInfo(
290
+ name=ep.name,
291
+ module_path=f"{ep.value}",
292
+ class_or_func=obj,
293
+ source="entry_point",
294
+ )
295
+ count += 1
296
+ elif self._is_tool(obj):
297
+ self._tools[ep.name] = ExtensionInfo(
298
+ name=ep.name,
299
+ module_path=f"{ep.value}",
300
+ class_or_func=obj,
301
+ source="entry_point",
302
+ )
303
+ count += 1
304
+ except Exception as e:
305
+ console.print(f"[yellow]Warning: Failed to load entry point '{ep.name}': {e}[/yellow]")
306
+
307
+ except Exception:
308
+ pass # Entry points not available
309
+
310
+ return count
311
+
312
+ def auto_discover(self) -> int:
313
+ """Auto-discover extensions from common locations.
314
+
315
+ Searches:
316
+ - ~/.aury/extensions.toml
317
+ - ./aury.extensions.toml
318
+ - ~/.aury/extensions/
319
+ - ./extensions/
320
+ - Python entry points
321
+
322
+ Returns:
323
+ Total number of extensions loaded
324
+ """
325
+ count = 0
326
+
327
+ # Config files
328
+ count += self.load_from_config(Path.home() / ".aury" / "extensions.toml")
329
+ count += self.load_from_config(Path.cwd() / "aury.extensions.toml")
330
+
331
+ # Extension directories
332
+ count += self.load_from_directory(Path.home() / ".aury" / "extensions")
333
+ count += self.load_from_directory(Path.cwd() / "extensions")
334
+
335
+ # Entry points
336
+ count += self.load_from_entry_points()
337
+
338
+ return count
339
+
340
+ # ========== Helper Methods ==========
341
+
342
+ def _import_from_string(self, path: str) -> Any:
343
+ """Import a class or function from a string like 'module.path:ClassName'."""
344
+ if ":" in path:
345
+ module_path, attr_name = path.rsplit(":", 1)
346
+ else:
347
+ # Assume it's just a module with a default export
348
+ module_path = path
349
+ attr_name = None
350
+
351
+ module = importlib.import_module(module_path)
352
+
353
+ if attr_name:
354
+ return getattr(module, attr_name)
355
+ return module
356
+
357
+ def _get_module_path(self, obj: Any) -> str:
358
+ """Get module path string for an object."""
359
+ if hasattr(obj, "__module__") and hasattr(obj, "__name__"):
360
+ return f"{obj.__module__}:{obj.__name__}"
361
+ elif hasattr(obj, "__module__") and hasattr(obj, "__class__"):
362
+ return f"{obj.__module__}:{obj.__class__.__name__}"
363
+ return "unknown"
364
+
365
+ def _is_agent_class(self, obj: Any) -> bool:
366
+ """Check if object is an Agent class."""
367
+ from aury.agents.core.base import BaseAgent
368
+ return isinstance(obj, type) and issubclass(obj, BaseAgent)
369
+
370
+ def _is_tool(self, obj: Any) -> bool:
371
+ """Check if object is a Tool."""
372
+ from aury.agents.tools import BaseTool
373
+ # Check if it's a BaseTool instance or has tool decorator attributes
374
+ return (
375
+ isinstance(obj, BaseTool) or
376
+ hasattr(obj, "_tool_name") or
377
+ hasattr(obj, "name") and hasattr(obj, "execute")
378
+ )
379
+
380
+ def _load_from_file(self, path: Path) -> int:
381
+ """Load extensions from a Python file."""
382
+ count = 0
383
+
384
+ # Add parent directory to path
385
+ sys.path.insert(0, str(path.parent))
386
+
387
+ try:
388
+ spec = importlib.util.spec_from_file_location(path.stem, path)
389
+ module = importlib.util.module_from_spec(spec)
390
+ spec.loader.exec_module(module)
391
+
392
+ # Find agents and tools
393
+ for name in dir(module):
394
+ if name.startswith("_"):
395
+ continue
396
+
397
+ obj = getattr(module, name)
398
+
399
+ if self._is_agent_class(obj):
400
+ agent_name = getattr(obj, "name", name.lower())
401
+ self._agents[agent_name] = ExtensionInfo(
402
+ name=agent_name,
403
+ module_path=f"{path}:{name}",
404
+ class_or_func=obj,
405
+ description=getattr(obj, "description", ""),
406
+ source="file",
407
+ )
408
+ count += 1
409
+
410
+ elif self._is_tool(obj):
411
+ tool_name = getattr(obj, "name", getattr(obj, "_tool_name", name.lower()))
412
+ self._tools[tool_name] = ExtensionInfo(
413
+ name=tool_name,
414
+ module_path=f"{path}:{name}",
415
+ class_or_func=obj,
416
+ description=getattr(obj, "description", ""),
417
+ source="file",
418
+ )
419
+ count += 1
420
+
421
+ finally:
422
+ sys.path.pop(0)
423
+
424
+ return count
425
+
426
+ def clear(self) -> None:
427
+ """Clear all registered extensions."""
428
+ self._agents.clear()
429
+ self._tools.clear()
430
+ self._loaded_configs.clear()
431
+
432
+
433
+ # Global registry instance
434
+ registry = ExtensionRegistry()
435
+
436
+
437
+ # Convenience functions
438
+ def register_agent(name: str, agent_class: type["BaseAgent"], description: str = "") -> None:
439
+ """Register a custom Agent class globally."""
440
+ registry.register_agent(name, agent_class, description)
441
+
442
+
443
+ def register_tool(name: str, tool: "BaseTool | Callable", description: str = "") -> None:
444
+ """Register a custom Tool globally."""
445
+ registry.register_tool(name, tool, description)
446
+
447
+
448
+ def get_agent(name: str) -> type["BaseAgent"] | None:
449
+ """Get a registered Agent by name."""
450
+ return registry.get_agent(name)
451
+
452
+
453
+ def get_tool(name: str) -> "BaseTool | Callable | None":
454
+ """Get a registered Tool by name."""
455
+ return registry.get_tool(name)
456
+
457
+
458
+ def auto_discover() -> int:
459
+ """Auto-discover all extensions."""
460
+ return registry.auto_discover()
@@ -0,0 +1,189 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Aury Agent CLI 主入口
4
+ ======================
5
+
6
+ 使用方式:
7
+ aury-agent chat # 启动交互式对话
8
+ aury-agent workflow run # 运行 Workflow
9
+ aury-agent session list # 列出会话
10
+ aury-agent config show # 显示配置
11
+ """
12
+ import asyncio
13
+ import sys
14
+ from pathlib import Path
15
+ from typing import Optional
16
+
17
+ import typer
18
+ from rich.console import Console
19
+ from rich.panel import Panel
20
+
21
+ from aury.agents.cli.chat import chat_command
22
+ from aury.agents.cli.workflow import workflow_app
23
+ from aury.agents.cli.session import session_app
24
+ from aury.agents.cli.config import config_app, load_config
25
+
26
+ # 创建主应用
27
+ app = typer.Typer(
28
+ name="aury-agent",
29
+ help="Aury Agent 命令行工具 - AI Agent 框架",
30
+ add_completion=True,
31
+ )
32
+
33
+ # 注册子命令
34
+ app.add_typer(workflow_app, name="workflow", help="Workflow 相关命令")
35
+ app.add_typer(session_app, name="session", help="会话管理命令")
36
+ app.add_typer(config_app, name="config", help="配置管理命令")
37
+
38
+ # Rich console 用于美化输出
39
+ console = Console()
40
+
41
+
42
+ @app.command()
43
+ def chat(
44
+ agent: Optional[str] = typer.Option(
45
+ None,
46
+ "--agent", "-a",
47
+ help="要使用的 Agent 名称或路径",
48
+ ),
49
+ session_id: Optional[str] = typer.Option(
50
+ None,
51
+ "--session", "-s",
52
+ help="会话 ID,用于恢复之前的对话",
53
+ ),
54
+ config_file: Optional[Path] = typer.Option(
55
+ None,
56
+ "--config", "-c",
57
+ help="配置文件路径",
58
+ ),
59
+ verbose: bool = typer.Option(
60
+ False,
61
+ "--verbose", "-v",
62
+ help="显示详细输出",
63
+ ),
64
+ ):
65
+ """
66
+ 启动交互式对话。
67
+
68
+ 示例:
69
+ aury-agent chat
70
+ aury-agent chat --agent my_agent
71
+ aury-agent chat --session session-123 --verbose
72
+ """
73
+ # 加载配置
74
+ cfg = load_config(config_file)
75
+
76
+ # 运行交互式对话
77
+ asyncio.run(chat_command(
78
+ agent_name=agent,
79
+ session_id=session_id,
80
+ config=cfg,
81
+ verbose=verbose,
82
+ ))
83
+
84
+
85
+ @app.command()
86
+ def run(
87
+ script: Path = typer.Argument(
88
+ ...,
89
+ help="要运行的 Agent 脚本路径",
90
+ ),
91
+ input_data: Optional[str] = typer.Option(
92
+ None,
93
+ "--input", "-i",
94
+ help="输入数据(JSON 格式)",
95
+ ),
96
+ config_file: Optional[Path] = typer.Option(
97
+ None,
98
+ "--config", "-c",
99
+ help="配置文件路径",
100
+ ),
101
+ ):
102
+ """
103
+ 运行 Agent 脚本。
104
+
105
+ 示例:
106
+ aury-agent run agent.py
107
+ aury-agent run agent.py --input '{"query": "hello"}'
108
+ """
109
+ import json
110
+ import importlib.util
111
+
112
+ # 加载配置
113
+ cfg = load_config(config_file)
114
+
115
+ # 加载脚本
116
+ if not script.exists():
117
+ console.print(f"[red]错误: 脚本文件不存在: {script}[/red]")
118
+ raise typer.Exit(1)
119
+
120
+ # 解析输入
121
+ input_dict = {}
122
+ if input_data:
123
+ try:
124
+ input_dict = json.loads(input_data)
125
+ except json.JSONDecodeError as e:
126
+ console.print(f"[red]错误: 无效的 JSON 输入: {e}[/red]")
127
+ raise typer.Exit(1)
128
+
129
+ console.print(f"[green]运行脚本: {script}[/green]")
130
+
131
+ # 动态导入并运行
132
+ spec = importlib.util.spec_from_file_location("agent_script", script)
133
+ module = importlib.util.module_from_spec(spec)
134
+ spec.loader.exec_module(module)
135
+
136
+ # 查找并运行 main 函数
137
+ if hasattr(module, "main"):
138
+ asyncio.run(module.main())
139
+ else:
140
+ console.print("[yellow]警告: 脚本中没有 main 函数[/yellow]")
141
+
142
+
143
+ @app.command()
144
+ def version():
145
+ """显示版本信息。"""
146
+ from aury.agents import __version__
147
+
148
+ console.print(Panel(
149
+ f"[bold blue]Aury Agent Framework[/bold blue]\n"
150
+ f"版本: {__version__}",
151
+ title="版本信息",
152
+ ))
153
+
154
+
155
+ @app.command()
156
+ def info():
157
+ """显示系统信息和配置状态。"""
158
+ import platform
159
+
160
+ console.print(Panel(
161
+ f"[bold]系统信息[/bold]\n"
162
+ f"Python: {platform.python_version()}\n"
163
+ f"平台: {platform.platform()}\n\n"
164
+ f"[bold]Aury Agent[/bold]\n"
165
+ f"配置目录: ~/.aury/\n"
166
+ f"会话目录: ~/.aury/sessions/",
167
+ title="系统信息",
168
+ ))
169
+
170
+
171
+ @app.callback()
172
+ def main_callback(
173
+ ctx: typer.Context,
174
+ ):
175
+ """
176
+ Aury Agent 命令行工具。
177
+
178
+ 使用 --help 查看各子命令的帮助信息。
179
+ """
180
+ pass
181
+
182
+
183
+ def main():
184
+ """CLI 入口点。"""
185
+ app()
186
+
187
+
188
+ if __name__ == "__main__":
189
+ main()