aloop 0.1.1__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.
- agent/__init__.py +0 -0
- agent/agent.py +182 -0
- agent/base.py +406 -0
- agent/context.py +126 -0
- agent/prompts/__init__.py +1 -0
- agent/todo.py +149 -0
- agent/tool_executor.py +54 -0
- agent/verification.py +135 -0
- aloop-0.1.1.dist-info/METADATA +252 -0
- aloop-0.1.1.dist-info/RECORD +66 -0
- aloop-0.1.1.dist-info/WHEEL +5 -0
- aloop-0.1.1.dist-info/entry_points.txt +2 -0
- aloop-0.1.1.dist-info/licenses/LICENSE +21 -0
- aloop-0.1.1.dist-info/top_level.txt +9 -0
- cli.py +19 -0
- config.py +146 -0
- interactive.py +865 -0
- llm/__init__.py +51 -0
- llm/base.py +26 -0
- llm/compat.py +226 -0
- llm/content_utils.py +309 -0
- llm/litellm_adapter.py +450 -0
- llm/message_types.py +245 -0
- llm/model_manager.py +265 -0
- llm/retry.py +95 -0
- main.py +246 -0
- memory/__init__.py +20 -0
- memory/compressor.py +554 -0
- memory/manager.py +538 -0
- memory/serialization.py +82 -0
- memory/short_term.py +88 -0
- memory/store/__init__.py +6 -0
- memory/store/memory_store.py +100 -0
- memory/store/yaml_file_memory_store.py +414 -0
- memory/token_tracker.py +203 -0
- memory/types.py +51 -0
- tools/__init__.py +6 -0
- tools/advanced_file_ops.py +557 -0
- tools/base.py +51 -0
- tools/calculator.py +50 -0
- tools/code_navigator.py +975 -0
- tools/explore.py +254 -0
- tools/file_ops.py +150 -0
- tools/git_tools.py +791 -0
- tools/notify.py +69 -0
- tools/parallel_execute.py +420 -0
- tools/session_manager.py +205 -0
- tools/shell.py +147 -0
- tools/shell_background.py +470 -0
- tools/smart_edit.py +491 -0
- tools/todo.py +130 -0
- tools/web_fetch.py +673 -0
- tools/web_search.py +61 -0
- utils/__init__.py +15 -0
- utils/logger.py +105 -0
- utils/model_pricing.py +49 -0
- utils/runtime.py +75 -0
- utils/terminal_ui.py +422 -0
- utils/tui/__init__.py +39 -0
- utils/tui/command_registry.py +49 -0
- utils/tui/components.py +306 -0
- utils/tui/input_handler.py +393 -0
- utils/tui/model_ui.py +204 -0
- utils/tui/progress.py +292 -0
- utils/tui/status_bar.py +178 -0
- utils/tui/theme.py +165 -0
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: aloop
|
|
3
|
+
Version: 0.1.1
|
|
4
|
+
Summary: General AI Agent System
|
|
5
|
+
Author-email: luohaha <luoyixin6688@gmail.com>
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/luohaha/aloop
|
|
8
|
+
Project-URL: Documentation, https://github.com/luohaha/aloop#readme
|
|
9
|
+
Project-URL: Repository, https://github.com/luohaha/aloop
|
|
10
|
+
Project-URL: Issues, https://github.com/luohaha/aloop/issues
|
|
11
|
+
Keywords: ai,agent,llm,claude,gpt,gemini,loop,planning
|
|
12
|
+
Classifier: Development Status :: 4 - Beta
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
18
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
19
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
20
|
+
Requires-Python: >=3.12
|
|
21
|
+
Description-Content-Type: text/markdown
|
|
22
|
+
License-File: LICENSE
|
|
23
|
+
Requires-Dist: ddgs>=1.0.0
|
|
24
|
+
Requires-Dist: rich>=13.0.0
|
|
25
|
+
Requires-Dist: prompt_toolkit>=3.0.0
|
|
26
|
+
Requires-Dist: httpx>=0.28.0
|
|
27
|
+
Requires-Dist: trafilatura>=2.0.0
|
|
28
|
+
Requires-Dist: lxml>=5.0.0
|
|
29
|
+
Requires-Dist: aiofiles>=24.1.0
|
|
30
|
+
Requires-Dist: litellm>=1.30.0
|
|
31
|
+
Requires-Dist: PyYAML>=6.0
|
|
32
|
+
Requires-Dist: tiktoken>=0.5.0
|
|
33
|
+
Requires-Dist: tenacity>=8.2.0
|
|
34
|
+
Requires-Dist: tree-sitter<0.22.0,>=0.21.0
|
|
35
|
+
Requires-Dist: tree-sitter-languages<1.11.0,>=1.8.0
|
|
36
|
+
Requires-Dist: croniter>=1.3.0
|
|
37
|
+
Provides-Extra: dev
|
|
38
|
+
Requires-Dist: pytest>=7.0.0; extra == "dev"
|
|
39
|
+
Requires-Dist: pytest-asyncio>=0.24.0; extra == "dev"
|
|
40
|
+
Requires-Dist: black>=23.0.0; extra == "dev"
|
|
41
|
+
Requires-Dist: isort>=5.12.0; extra == "dev"
|
|
42
|
+
Requires-Dist: mypy>=1.0.0; extra == "dev"
|
|
43
|
+
Requires-Dist: ruff>=0.6.0; extra == "dev"
|
|
44
|
+
Requires-Dist: pre-commit>=3.0.0; extra == "dev"
|
|
45
|
+
Requires-Dist: types-aiofiles>=25.1.0; extra == "dev"
|
|
46
|
+
Requires-Dist: types-croniter>=1.3.0; extra == "dev"
|
|
47
|
+
Requires-Dist: types-PyYAML>=6.0; extra == "dev"
|
|
48
|
+
Dynamic: license-file
|
|
49
|
+
|
|
50
|
+
<div align="center">
|
|
51
|
+
<picture>
|
|
52
|
+
<img alt="ALOOP" src="docs/assets/logo.svg" width="440">
|
|
53
|
+
</picture>
|
|
54
|
+
|
|
55
|
+
**One loop is all you need.**
|
|
56
|
+
|
|
57
|
+
aloop is an AI agent built on a single, unified loop. Planning, parallel sub-agents,
|
|
58
|
+
self-verification — everything folds into the same loop, chosen autonomously by the
|
|
59
|
+
agent itself, not by a hardcoded workflow. Simple architecture, emergent capability.
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
</div>
|
|
63
|
+
|
|
64
|
+
## Installation
|
|
65
|
+
|
|
66
|
+
Prerequisites: Python 3.12+.
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
pip install aloop
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
Or install from source (for development):
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
git clone https://github.com/luohaha/aloop.git
|
|
76
|
+
cd aloop
|
|
77
|
+
./scripts/bootstrap.sh # requires uv
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
## Quick Start
|
|
81
|
+
|
|
82
|
+
### 1. Configure Models
|
|
83
|
+
|
|
84
|
+
On first run, `~/.aloop/models.yaml` is created with a template. Edit it to add your provider and API key:
|
|
85
|
+
|
|
86
|
+
```yaml
|
|
87
|
+
models:
|
|
88
|
+
openai/gpt-4o:
|
|
89
|
+
api_key: sk-...
|
|
90
|
+
|
|
91
|
+
anthropic/claude-3-5-sonnet-20241022:
|
|
92
|
+
api_key: sk-ant-...
|
|
93
|
+
|
|
94
|
+
ollama/llama2:
|
|
95
|
+
api_base: http://localhost:11434
|
|
96
|
+
|
|
97
|
+
default: openai/gpt-4o
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
See [LiteLLM Providers](https://docs.litellm.ai/docs/providers) for the full list.
|
|
101
|
+
|
|
102
|
+
### 2. Run
|
|
103
|
+
|
|
104
|
+
```bash
|
|
105
|
+
# Interactive mode
|
|
106
|
+
aloop
|
|
107
|
+
|
|
108
|
+
# Single task (returns raw result)
|
|
109
|
+
aloop --task "Calculate 123 * 456"
|
|
110
|
+
|
|
111
|
+
# Resume last session
|
|
112
|
+
aloop --resume
|
|
113
|
+
|
|
114
|
+
# Resume specific session (ID prefix)
|
|
115
|
+
aloop --resume a1b2c3d4
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
## CLI Reference
|
|
119
|
+
|
|
120
|
+
| Flag | Short | Description |
|
|
121
|
+
|------|-------|-------------|
|
|
122
|
+
| `--task TEXT` | `-t` | Run a single task and exit |
|
|
123
|
+
| `--model ID` | `-m` | LiteLLM model ID to use |
|
|
124
|
+
| `--resume [ID]` | `-r` | Resume a session (`latest` if no ID given) |
|
|
125
|
+
| `--verbose` | `-v` | Enable verbose logging to `~/.aloop/logs/` |
|
|
126
|
+
|
|
127
|
+
## Interactive Commands
|
|
128
|
+
|
|
129
|
+
### Slash Commands
|
|
130
|
+
|
|
131
|
+
| Command | Description |
|
|
132
|
+
|---------|-------------|
|
|
133
|
+
| `/help` | Show help |
|
|
134
|
+
| `/clear` | Clear conversation and start fresh |
|
|
135
|
+
| `/stats` | Show memory and token usage statistics |
|
|
136
|
+
| `/resume [id]` | List or resume a previous session |
|
|
137
|
+
| `/model` | Pick a model (arrow keys + Enter) |
|
|
138
|
+
| `/model edit` | Open `~/.aloop/models.yaml` in editor (auto-reload on save) |
|
|
139
|
+
| `/theme` | Toggle dark/light theme |
|
|
140
|
+
| `/verbose` | Toggle thinking display |
|
|
141
|
+
| `/compact` | Toggle compact output |
|
|
142
|
+
| `/exit` | Exit (also `/quit`) |
|
|
143
|
+
|
|
144
|
+
### Keyboard Shortcuts
|
|
145
|
+
|
|
146
|
+
| Key | Action |
|
|
147
|
+
|-----|--------|
|
|
148
|
+
| `/` | Command autocomplete |
|
|
149
|
+
| `Ctrl+C` | Cancel current operation |
|
|
150
|
+
| `Ctrl+L` | Clear screen |
|
|
151
|
+
| `Ctrl+T` | Toggle thinking display |
|
|
152
|
+
| `Ctrl+S` | Show quick stats |
|
|
153
|
+
| Up/Down | Navigate command history |
|
|
154
|
+
|
|
155
|
+
## How It Works
|
|
156
|
+
|
|
157
|
+
**Agent loop**: The agent follows a Think-Act-Observe cycle. It reasons about the task, selects a tool, observes the result, and repeats until it has an answer. Planning, sub-agent dispatch, and tool use all happen inside this single loop.
|
|
158
|
+
|
|
159
|
+
**Ralph verification**: For single tasks (`--task`), an outer loop verifies the agent's answer against the original task. If incomplete, feedback is injected and the agent loop re-enters. Configurable via `RALPH_LOOP_MAX_ITERATIONS` (default: 3).
|
|
160
|
+
|
|
161
|
+
**Memory compression**: When context grows past a token threshold, older messages are compressed via LLM summarization. Recent messages are kept at full fidelity. Strategies: `sliding_window` (default), `selective`, `deletion`.
|
|
162
|
+
|
|
163
|
+
**Session persistence**: Conversations are saved as YAML files under `~/.aloop/sessions/`. Resume with `--resume` or `/resume`.
|
|
164
|
+
|
|
165
|
+
## Tools
|
|
166
|
+
|
|
167
|
+
| Tool | Description |
|
|
168
|
+
|------|-------------|
|
|
169
|
+
| `read_file` | Read file contents |
|
|
170
|
+
| `write_file` | Write content to a file |
|
|
171
|
+
| `search_files` | Search for files by name |
|
|
172
|
+
| `edit_file` | Exact string replacement in files |
|
|
173
|
+
| `smart_edit` | LLM-assisted file editing |
|
|
174
|
+
| `glob_files` | Glob pattern file matching |
|
|
175
|
+
| `grep_content` | Regex search in file contents |
|
|
176
|
+
| `code_navigator` | AST-based code navigation (tree-sitter) |
|
|
177
|
+
| `calculate` | Evaluate expressions / run Python code |
|
|
178
|
+
| `shell` | Execute shell commands |
|
|
179
|
+
| `shell_task_status` | Check background shell task status |
|
|
180
|
+
| `web_search` | Web search (DuckDuckGo) |
|
|
181
|
+
| `web_fetch` | Fetch and extract web page content |
|
|
182
|
+
| `explore_context` | Explore project structure and context |
|
|
183
|
+
| `parallel_execute` | Run multiple tool calls in parallel |
|
|
184
|
+
| `notify` | Send email notifications (Resend) |
|
|
185
|
+
| `manage_todo_list` | Manage a task/todo list |
|
|
186
|
+
|
|
187
|
+
## Project Structure
|
|
188
|
+
|
|
189
|
+
```
|
|
190
|
+
aloop/
|
|
191
|
+
├── main.py # Entry point (argparse)
|
|
192
|
+
├── cli.py # CLI wrapper (`aloop` entry point)
|
|
193
|
+
├── interactive.py # Interactive session, model setup, TUI
|
|
194
|
+
├── config.py # Runtime config (~/.aloop/config)
|
|
195
|
+
├── agent/
|
|
196
|
+
│ ├── base.py # BaseAgent (ReAct + Ralph loops)
|
|
197
|
+
│ ├── agent.py # LoopAgent
|
|
198
|
+
│ ├── verification.py # LLMVerifier for Ralph loop
|
|
199
|
+
│ ├── context.py # Context injection (cwd, platform, date)
|
|
200
|
+
│ ├── tool_executor.py # Tool execution engine
|
|
201
|
+
│ └── todo.py # Todo list data structure
|
|
202
|
+
├── llm/
|
|
203
|
+
│ ├── litellm_adapter.py # LiteLLM adapter (100+ providers)
|
|
204
|
+
│ ├── model_manager.py # Model config from ~/.aloop/models.yaml
|
|
205
|
+
│ ├── retry.py # Retry with exponential backoff
|
|
206
|
+
│ └── message_types.py # LLMMessage, LLMResponse, ToolCall
|
|
207
|
+
├── memory/
|
|
208
|
+
│ ├── manager.py # Memory orchestrator + persistence
|
|
209
|
+
│ ├── compressor.py # LLM-driven compression
|
|
210
|
+
│ ├── short_term.py # Short-term memory (sliding window)
|
|
211
|
+
│ ├── token_tracker.py # Token counting + cost tracking
|
|
212
|
+
│ ├── types.py # Core data structures
|
|
213
|
+
│ └── store/
|
|
214
|
+
│ └── yaml_file_memory_store.py # YAML session persistence
|
|
215
|
+
├── tools/ # 18 tool implementations
|
|
216
|
+
├── utils/
|
|
217
|
+
│ ├── tui/ # TUI components (input, themes, status bar)
|
|
218
|
+
│ ├── logger.py # Logging setup
|
|
219
|
+
│ └── model_pricing.py # Model pricing data
|
|
220
|
+
├── docs/ # Documentation
|
|
221
|
+
├── test/ # Tests
|
|
222
|
+
├── scripts/ # Dev scripts (bootstrap.sh, dev.sh)
|
|
223
|
+
└── rfc/ # RFC design documents
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
## Configuration
|
|
227
|
+
|
|
228
|
+
Runtime settings live in `~/.aloop/config` (auto-created). Key settings:
|
|
229
|
+
|
|
230
|
+
| Setting | Default | Description |
|
|
231
|
+
|---------|---------|-------------|
|
|
232
|
+
| `MAX_ITERATIONS` | `1000` | Maximum agent loop iterations |
|
|
233
|
+
| `TOOL_TIMEOUT` | `600` | Tool execution timeout (seconds) |
|
|
234
|
+
| `RALPH_LOOP_MAX_ITERATIONS` | `3` | Max verification attempts |
|
|
235
|
+
| `MEMORY_ENABLED` | `true` | Enable memory management |
|
|
236
|
+
| `MEMORY_COMPRESSION_THRESHOLD` | `60000` | Token threshold for compression |
|
|
237
|
+
| `MEMORY_SHORT_TERM_SIZE` | `100` | Messages kept at full fidelity |
|
|
238
|
+
| `RETRY_MAX_ATTEMPTS` | `3` | Rate-limit retry attempts |
|
|
239
|
+
|
|
240
|
+
See [Configuration Guide](docs/configuration.md) for all settings.
|
|
241
|
+
|
|
242
|
+
## Documentation
|
|
243
|
+
|
|
244
|
+
- [Configuration](docs/configuration.md) -- model setup, runtime settings, custom endpoints
|
|
245
|
+
- [Examples](docs/examples.md) -- usage patterns and programmatic API
|
|
246
|
+
- [Memory Management](docs/memory-management.md) -- compression, persistence, token tracking
|
|
247
|
+
- [Extending](docs/extending.md) -- adding tools, agents, LLM providers
|
|
248
|
+
- [Packaging](docs/packaging.md) -- building, publishing, Docker
|
|
249
|
+
|
|
250
|
+
## License
|
|
251
|
+
|
|
252
|
+
MIT License
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
cli.py,sha256=j3B6la8ArJ5BAvR_vCw-_oDp8wyX9D25mKRJN96b8U8,338
|
|
2
|
+
config.py,sha256=1D2VjNtZidj4NuA2AN013djKVFqxstc53zUd-Ppz2U4,4980
|
|
3
|
+
interactive.py,sha256=TSoBsVuxsX_RxGFKRmqHEnQL7KOVL7s_GnCF7E3t6xc,34369
|
|
4
|
+
main.py,sha256=P_96WocjwBkcBVj0zGy2r-S9FZbu8qpFXOdAvIaXkio,7710
|
|
5
|
+
agent/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
6
|
+
agent/agent.py,sha256=VfSQYKnLHiKU3mXJ9Ri8sHwYJpI7Ug14GgH3XqhoNO0,6597
|
|
7
|
+
agent/base.py,sha256=WerzJeHo0zqw7Gllenf3cp3c2fqjzfu027p9js5jy1Q,15643
|
|
8
|
+
agent/context.py,sha256=paxoP5KRqNMnEEREa9Mj8QVIdpgtpj9FafwtvuTe1I8,3793
|
|
9
|
+
agent/todo.py,sha256=dM6DSCeV9QbatDVx1HEUK46b_XEdcPCf_1uYYZN4Fsg,5272
|
|
10
|
+
agent/tool_executor.py,sha256=uvxwhiqQh4MkSZAhHbcUkFHRpuiM7XfMQOh_WCFMI_c,1951
|
|
11
|
+
agent/verification.py,sha256=kFOLUJfd2iK5P_OIctacRNdGhwzNXTd_cdcXdv1xGqQ,4268
|
|
12
|
+
agent/prompts/__init__.py,sha256=bnOLBsNS6jXAS6hQirULAoJ6gs1tC2suZ501i4KRRzA,36
|
|
13
|
+
aloop-0.1.1.dist-info/licenses/LICENSE,sha256=QWrc--GLlkEskZCiT0qjbtLKXYA5It4gp345uIltMew,1066
|
|
14
|
+
llm/__init__.py,sha256=GhTKAf36pjBV6fuijXTCBcLk4XqmUYeCbrZATvb_mW8,1149
|
|
15
|
+
llm/base.py,sha256=mESX6l3dbZPfh9-l9txVvvVJWpgvNIchm3tD5MkAFn8,551
|
|
16
|
+
llm/compat.py,sha256=tl5De-u86VuWLbeTmPrBVRosBtyPu3kM504fAwARFeI,6396
|
|
17
|
+
llm/content_utils.py,sha256=tcwNRlMRpFEVeCsjyCcGrcy1WxtqLiK1kRXZw0_IL4U,8262
|
|
18
|
+
llm/litellm_adapter.py,sha256=Zm4OSNC4oacLunf1Bkjkdnwwe4F9n0GNUeXbFcQeCFA,16934
|
|
19
|
+
llm/message_types.py,sha256=sjcjsJGy1JtWMtjfqrrgpyVWntg6z47unxAHVID3QIQ,7117
|
|
20
|
+
llm/model_manager.py,sha256=ms5MNAjlRWWV5RYbq60Qb0BbWrYfjpMkgphMu82RNJA,8704
|
|
21
|
+
llm/retry.py,sha256=0ouN1yAD2mCNbtFbrTxAwWXiQAW7p1XIUei623WcUpw,2710
|
|
22
|
+
memory/__init__.py,sha256=7iLqzMIysHkC_wqftqSPPFVF6peHlX_agtRTveWhHu4,578
|
|
23
|
+
memory/compressor.py,sha256=edV7B61CipvgQq5LGGdOwOsoRiuHL2fz_sC-MnSyKSM,23238
|
|
24
|
+
memory/manager.py,sha256=m0r3b47KXMj8ymAZeQwZ6pEFlRqHyqVdu4OlLQ-D4xc,19474
|
|
25
|
+
memory/serialization.py,sha256=whX7E2n2UyamD1iOtQS2PkKhbb-ebwwnvfu-fh-Y2tE,2194
|
|
26
|
+
memory/short_term.py,sha256=RHPvHxqAzSJq_FeepL5qk0v-zOYnTIKP6l_jtJ0jB7c,2519
|
|
27
|
+
memory/token_tracker.py,sha256=xufZ-FWjcGkTdkK5jmns3CTIqklv6ctFCmKH5yl9uiY,6953
|
|
28
|
+
memory/types.py,sha256=GLOLB0f0IN3cb7Bv-guVwtR17mJG7zlvJjRWtXlzyao,1744
|
|
29
|
+
memory/store/__init__.py,sha256=23CgsW2M-exOH0pt3XEFvtNP1CQ15AgyMVo9rMHKGsc,205
|
|
30
|
+
memory/store/memory_store.py,sha256=_R27AitWIP6GUIGN5x9QsjL6qKs2eWKfyVps8m3ekFw,2733
|
|
31
|
+
memory/store/yaml_file_memory_store.py,sha256=5At2tba8Ccch97h58hiIH0L5yqhiqctuQD_OXxq8Z8U,14447
|
|
32
|
+
tools/__init__.py,sha256=iAVcW_A4I2aVuiI3PoVMcCBe8cFGqe6TIBuaZ_cGD-Q,190
|
|
33
|
+
tools/advanced_file_ops.py,sha256=dZlgOvAVOhVRIyOgwU6tlaSNituQoEivKxiHQo9DNME,19938
|
|
34
|
+
tools/base.py,sha256=OtDzJg4c-CKtHTohPVaNDD_8x1xPKqCXE8SGJd8Xr_c,1397
|
|
35
|
+
tools/calculator.py,sha256=Sl201BysnFa-7tduxKstnOT94syAHyle2rTgztXA6Ho,1500
|
|
36
|
+
tools/code_navigator.py,sha256=Unj9mjbsTOJQNgVi980zrgeg1Msp3rHzqo-am8eqwEI,38336
|
|
37
|
+
tools/explore.py,sha256=hR2_TMjVYCrqrj9JFSmTgIiXyvlE7_vFYtrlxLtVGG4,8171
|
|
38
|
+
tools/file_ops.py,sha256=43qs42oIbCd9q7UD1scEKUAbKdRuzjukTjxw_ziJmuE,5082
|
|
39
|
+
tools/git_tools.py,sha256=a0pLYnk4OjXB7ak3jmlQLIDet9Bf_rolNGsGHS7XgqA,23521
|
|
40
|
+
tools/notify.py,sha256=gWlTx-9SSQ2bsITVkJPCUgX6Mm9ve2TtfgMHq4RksKM,2103
|
|
41
|
+
tools/parallel_execute.py,sha256=AD_VwCchLOkHLYYliJn0dGupX4wB01X1RmV0Lz3DqXM,13197
|
|
42
|
+
tools/session_manager.py,sha256=aXdRYWR9-NH5K-E1NfmjxG0txR5fvmhAX-mFjmq9N7c,6484
|
|
43
|
+
tools/shell.py,sha256=Ozn4ldWA4DzIkNHt1HjXD_F0A300Zdnj7IYYlrGqIqo,5492
|
|
44
|
+
tools/shell_background.py,sha256=aAduugXVEzs2IXFmXq62cHryZDKknKsmdxbI_0KgQK4,16070
|
|
45
|
+
tools/smart_edit.py,sha256=GU3SYMnpr7DCXNVL8HqphwoajRERF1vQT_e1LXmcohw,17804
|
|
46
|
+
tools/todo.py,sha256=nm3lKm-lE6ta0Q3g13XKLQ7L3JLATX4VuoZtUu9KvCU,4412
|
|
47
|
+
tools/web_fetch.py,sha256=wW6zk0uA6nMuH5ecs2ExgdPJv9-tn3xP1H92-tc5ni4,24827
|
|
48
|
+
tools/web_search.py,sha256=7D_86Jk8sQCOPCiMa_NtFXTpduZiXlwr7IRyREBRbIg,2025
|
|
49
|
+
utils/__init__.py,sha256=WKSnJ2XGVq5199-gTkIO0Acd4RT9LZofDQeOkbzVq-c,424
|
|
50
|
+
utils/logger.py,sha256=-8UiH5F5jm3IGbJvlcNhvkF_t8fUSKmuqnZPsLVSb8M,3072
|
|
51
|
+
utils/model_pricing.py,sha256=r5LVdkQNqYMm8_W7Kb2iXu9oHHHz7zHjHYyEqOb3wRA,2311
|
|
52
|
+
utils/runtime.py,sha256=tGl__Ss2SSEWsyMxEhklKPm2HrbbKRTzgtk7Tq5lCxU,1740
|
|
53
|
+
utils/terminal_ui.py,sha256=kOfT9O2UdZCOcwfx0TZ6MziOgGEhC1C_eB6bjv0sH_0,12302
|
|
54
|
+
utils/tui/__init__.py,sha256=bN2Iof8dzqp_zT7KRS9vFGWEb5pGsl36OQ9EtxmiOVw,915
|
|
55
|
+
utils/tui/command_registry.py,sha256=EYsZssHLYDaoaVyc_t1MEWOiaD5RjyEGZS8jflEquIQ,1597
|
|
56
|
+
utils/tui/components.py,sha256=m5FLAEfI-eO3773ug9qmTw5AXrjBtKIFyrCAb7Mibl4,9492
|
|
57
|
+
utils/tui/input_handler.py,sha256=e2PtQRWG-VfRyq8VuhsaOFkE7dE0b8o3EUt5YPbsl-M,14022
|
|
58
|
+
utils/tui/model_ui.py,sha256=Oqc-2NKGiyg-4fZNgnxO0drwrHVKrjK9EzTCwd4oXG4,6234
|
|
59
|
+
utils/tui/progress.py,sha256=Yb0bn6XSBvluLSJqgkvHt_SMXDob5LwT-OwR1b7aMDE,8082
|
|
60
|
+
utils/tui/status_bar.py,sha256=AG_zb9qGkkNGLMHdYgWBvnK8JVppmUVKjOmj-qCVLg4,5333
|
|
61
|
+
utils/tui/theme.py,sha256=x0wnQw21Ufy1cm5DGMQroJ88J_rYBcezVHlwg4-mdwY,5606
|
|
62
|
+
aloop-0.1.1.dist-info/METADATA,sha256=H3HAsDatOc5D7BiqSLTfWgBDZnHBtjvOLzTAfNGXHLQ,9232
|
|
63
|
+
aloop-0.1.1.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
|
|
64
|
+
aloop-0.1.1.dist-info/entry_points.txt,sha256=fXfq7fpc79S3RQ7TzXSSyiA2L6yedfo-3o1PXEFXs0I,35
|
|
65
|
+
aloop-0.1.1.dist-info/top_level.txt,sha256=RcMBYcKEUojok3GYEagBOce8M7DyEAelgCypkcTrSE0,57
|
|
66
|
+
aloop-0.1.1.dist-info/RECORD,,
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Yixin Luo
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
cli.py
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Command-line interface for aloop."""
|
|
3
|
+
|
|
4
|
+
import os
|
|
5
|
+
import sys
|
|
6
|
+
|
|
7
|
+
# Add current directory to path to allow imports
|
|
8
|
+
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def main():
|
|
12
|
+
"""Main CLI entry point."""
|
|
13
|
+
from main import main as run_main
|
|
14
|
+
|
|
15
|
+
run_main()
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
if __name__ == "__main__":
|
|
19
|
+
main()
|
config.py
ADDED
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
"""Configuration management for the agentic system."""
|
|
2
|
+
|
|
3
|
+
import os
|
|
4
|
+
import random
|
|
5
|
+
|
|
6
|
+
# Define path constants directly to avoid circular imports with utils
|
|
7
|
+
# (utils.terminal_ui imports Config, and utils.runtime is in the utils package)
|
|
8
|
+
_RUNTIME_DIR = os.path.join(os.path.expanduser("~"), ".aloop")
|
|
9
|
+
_CONFIG_FILE = os.path.join(_RUNTIME_DIR, "config")
|
|
10
|
+
|
|
11
|
+
# Default configuration template
|
|
12
|
+
_DEFAULT_CONFIG = """\
|
|
13
|
+
# aloop Configuration
|
|
14
|
+
#
|
|
15
|
+
# NOTE: Model configuration lives in `~/.aloop/models.yaml`.
|
|
16
|
+
# This file controls non-model runtime settings only.
|
|
17
|
+
|
|
18
|
+
TOOL_TIMEOUT=600
|
|
19
|
+
MAX_ITERATIONS=1000
|
|
20
|
+
|
|
21
|
+
# Ralph Loop (outer verification loop — re-checks task completion)
|
|
22
|
+
# RALPH_LOOP_MAX_ITERATIONS=3
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def _load_config(path: str) -> dict[str, str]:
|
|
27
|
+
"""Parse a KEY=VALUE config file, skipping comments and blank lines."""
|
|
28
|
+
cfg: dict[str, str] = {}
|
|
29
|
+
if not os.path.isfile(path):
|
|
30
|
+
return cfg
|
|
31
|
+
with open(path, encoding="utf-8") as f:
|
|
32
|
+
for line in f:
|
|
33
|
+
line = line.strip()
|
|
34
|
+
if not line or line.startswith("#"):
|
|
35
|
+
continue
|
|
36
|
+
if "=" not in line:
|
|
37
|
+
continue
|
|
38
|
+
key, _, value = line.partition("=")
|
|
39
|
+
# Strip inline comments (# ...) from the value
|
|
40
|
+
if "#" in value:
|
|
41
|
+
value = value[: value.index("#")]
|
|
42
|
+
cfg[key.strip()] = value.strip()
|
|
43
|
+
return cfg
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def _ensure_config():
|
|
47
|
+
"""Ensure ~/.aloop/config exists, create with defaults if not."""
|
|
48
|
+
if not os.path.exists(_CONFIG_FILE):
|
|
49
|
+
os.makedirs(_RUNTIME_DIR, exist_ok=True)
|
|
50
|
+
with open(_CONFIG_FILE, "w", encoding="utf-8") as f:
|
|
51
|
+
f.write(_DEFAULT_CONFIG)
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
# Ensure config exists and load it
|
|
55
|
+
_ensure_config()
|
|
56
|
+
_cfg = _load_config(_CONFIG_FILE)
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def get_raw_config() -> dict[str, str]:
|
|
60
|
+
"""Get the raw config dictionary.
|
|
61
|
+
|
|
62
|
+
Returns:
|
|
63
|
+
Dictionary of config key-value pairs
|
|
64
|
+
"""
|
|
65
|
+
return _cfg.copy()
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
class Config:
|
|
69
|
+
"""Configuration for the agentic system.
|
|
70
|
+
|
|
71
|
+
All configuration is centralized here. Access config values directly via Config.XXX.
|
|
72
|
+
"""
|
|
73
|
+
|
|
74
|
+
# Model configuration is handled by `~/.aloop/models.yaml` via ModelManager.
|
|
75
|
+
# `~/.aloop/config` controls non-model runtime settings only.
|
|
76
|
+
TOOL_TIMEOUT = float(_cfg.get("TOOL_TIMEOUT", "600"))
|
|
77
|
+
|
|
78
|
+
# Agent Configuration
|
|
79
|
+
MAX_ITERATIONS = int(_cfg.get("MAX_ITERATIONS", "1000"))
|
|
80
|
+
|
|
81
|
+
# Ralph Loop (outer verification loop)
|
|
82
|
+
RALPH_LOOP_MAX_ITERATIONS = int(_cfg.get("RALPH_LOOP_MAX_ITERATIONS", "3"))
|
|
83
|
+
|
|
84
|
+
# Retry Configuration
|
|
85
|
+
RETRY_MAX_ATTEMPTS = int(_cfg.get("RETRY_MAX_ATTEMPTS", "3"))
|
|
86
|
+
RETRY_INITIAL_DELAY = float(_cfg.get("RETRY_INITIAL_DELAY", "1.0"))
|
|
87
|
+
RETRY_MAX_DELAY = float(_cfg.get("RETRY_MAX_DELAY", "60.0"))
|
|
88
|
+
RETRY_EXPONENTIAL_BASE = 2.0
|
|
89
|
+
RETRY_JITTER = True
|
|
90
|
+
|
|
91
|
+
# Memory Management Configuration
|
|
92
|
+
MEMORY_ENABLED = _cfg.get("MEMORY_ENABLED", "true").lower() == "true"
|
|
93
|
+
MEMORY_COMPRESSION_THRESHOLD = int(_cfg.get("MEMORY_COMPRESSION_THRESHOLD", "60000"))
|
|
94
|
+
MEMORY_SHORT_TERM_SIZE = int(_cfg.get("MEMORY_SHORT_TERM_SIZE", "100"))
|
|
95
|
+
MEMORY_SHORT_TERM_MIN_SIZE = int(_cfg.get("MEMORY_SHORT_TERM_MIN_SIZE", "6"))
|
|
96
|
+
MEMORY_COMPRESSION_RATIO = float(_cfg.get("MEMORY_COMPRESSION_RATIO", "0.3"))
|
|
97
|
+
MEMORY_PRESERVE_SYSTEM_PROMPTS = True
|
|
98
|
+
|
|
99
|
+
# Logging Configuration
|
|
100
|
+
# Note: Logging is now controlled via --verbose flag
|
|
101
|
+
# LOG_DIR is now ~/.aloop/logs/ (see utils.runtime)
|
|
102
|
+
LOG_LEVEL = _cfg.get("LOG_LEVEL", "DEBUG").upper()
|
|
103
|
+
|
|
104
|
+
# TUI Configuration
|
|
105
|
+
TUI_THEME = _cfg.get("TUI_THEME", "dark") # "dark" or "light"
|
|
106
|
+
TUI_SHOW_THINKING = _cfg.get("TUI_SHOW_THINKING", "true").lower() == "true"
|
|
107
|
+
TUI_THINKING_MAX_PREVIEW = int(_cfg.get("TUI_THINKING_MAX_PREVIEW", "300"))
|
|
108
|
+
TUI_STATUS_BAR = _cfg.get("TUI_STATUS_BAR", "true").lower() == "true"
|
|
109
|
+
TUI_COMPACT_MODE = _cfg.get("TUI_COMPACT_MODE", "false").lower() == "true"
|
|
110
|
+
|
|
111
|
+
# Email Notification Configuration (Resend)
|
|
112
|
+
RESEND_API_KEY = _cfg.get("RESEND_API_KEY") or ""
|
|
113
|
+
NOTIFY_EMAIL_FROM = _cfg.get("NOTIFY_EMAIL_FROM") or ""
|
|
114
|
+
|
|
115
|
+
@classmethod
|
|
116
|
+
def get_retry_delay(cls, attempt: int) -> float:
|
|
117
|
+
"""Calculate delay for a given retry attempt using exponential backoff.
|
|
118
|
+
|
|
119
|
+
Args:
|
|
120
|
+
attempt: Current attempt number (0-indexed)
|
|
121
|
+
|
|
122
|
+
Returns:
|
|
123
|
+
Delay in seconds
|
|
124
|
+
"""
|
|
125
|
+
# Calculate exponential backoff
|
|
126
|
+
delay = min(
|
|
127
|
+
cls.RETRY_INITIAL_DELAY * (cls.RETRY_EXPONENTIAL_BASE**attempt),
|
|
128
|
+
cls.RETRY_MAX_DELAY,
|
|
129
|
+
)
|
|
130
|
+
|
|
131
|
+
# Add jitter to avoid thundering herd
|
|
132
|
+
if cls.RETRY_JITTER:
|
|
133
|
+
delay = delay * (0.5 + random.random())
|
|
134
|
+
|
|
135
|
+
return delay
|
|
136
|
+
|
|
137
|
+
@classmethod
|
|
138
|
+
def validate(cls):
|
|
139
|
+
"""Validate required configuration.
|
|
140
|
+
|
|
141
|
+
Raises:
|
|
142
|
+
ValueError: If required configuration is missing
|
|
143
|
+
"""
|
|
144
|
+
# Model configuration is handled by `~/.aloop/models.yaml` via ModelManager.
|
|
145
|
+
# `~/.aloop/config` is used for non-model runtime settings only.
|
|
146
|
+
return
|