aloop 0.1.0__tar.gz
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.
Potentially problematic release.
This version of aloop might be problematic. Click here for more details.
- aloop-0.1.0/LICENSE +21 -0
- aloop-0.1.0/MANIFEST.in +5 -0
- aloop-0.1.0/PKG-INFO +246 -0
- aloop-0.1.0/README.md +197 -0
- aloop-0.1.0/agent/__init__.py +0 -0
- aloop-0.1.0/agent/agent.py +182 -0
- aloop-0.1.0/agent/base.py +406 -0
- aloop-0.1.0/agent/context.py +126 -0
- aloop-0.1.0/agent/todo.py +149 -0
- aloop-0.1.0/agent/tool_executor.py +54 -0
- aloop-0.1.0/agent/verification.py +135 -0
- aloop-0.1.0/aloop.egg-info/PKG-INFO +246 -0
- aloop-0.1.0/aloop.egg-info/SOURCES.txt +90 -0
- aloop-0.1.0/aloop.egg-info/dependency_links.txt +1 -0
- aloop-0.1.0/aloop.egg-info/entry_points.txt +2 -0
- aloop-0.1.0/aloop.egg-info/requires.txt +26 -0
- aloop-0.1.0/aloop.egg-info/top_level.txt +9 -0
- aloop-0.1.0/cli.py +19 -0
- aloop-0.1.0/config.py +146 -0
- aloop-0.1.0/docs/configuration.md +124 -0
- aloop-0.1.0/docs/examples.md +161 -0
- aloop-0.1.0/docs/extending.md +159 -0
- aloop-0.1.0/docs/memory-management.md +245 -0
- aloop-0.1.0/docs/packaging.md +104 -0
- aloop-0.1.0/examples/agent_example.py +75 -0
- aloop-0.1.0/examples/memory_persistence_demo.py +132 -0
- aloop-0.1.0/examples/web_fetch_example.py +59 -0
- aloop-0.1.0/interactive.py +865 -0
- aloop-0.1.0/llm/__init__.py +51 -0
- aloop-0.1.0/llm/base.py +26 -0
- aloop-0.1.0/llm/compat.py +226 -0
- aloop-0.1.0/llm/content_utils.py +309 -0
- aloop-0.1.0/llm/litellm_adapter.py +450 -0
- aloop-0.1.0/llm/message_types.py +245 -0
- aloop-0.1.0/llm/model_manager.py +265 -0
- aloop-0.1.0/llm/retry.py +95 -0
- aloop-0.1.0/main.py +246 -0
- aloop-0.1.0/memory/__init__.py +20 -0
- aloop-0.1.0/memory/compressor.py +554 -0
- aloop-0.1.0/memory/manager.py +538 -0
- aloop-0.1.0/memory/serialization.py +82 -0
- aloop-0.1.0/memory/short_term.py +88 -0
- aloop-0.1.0/memory/token_tracker.py +203 -0
- aloop-0.1.0/memory/types.py +51 -0
- aloop-0.1.0/pyproject.toml +107 -0
- aloop-0.1.0/setup.cfg +4 -0
- aloop-0.1.0/test/test_basic.py +45 -0
- aloop-0.1.0/test/test_code_navigator.py +770 -0
- aloop-0.1.0/test/test_explore_tool.py +139 -0
- aloop-0.1.0/test/test_litellm_adapter.py +204 -0
- aloop-0.1.0/test/test_memory.py +110 -0
- aloop-0.1.0/test/test_model_setup_session.py +74 -0
- aloop-0.1.0/test/test_notify_integration.py +32 -0
- aloop-0.1.0/test/test_notify_tool.py +127 -0
- aloop-0.1.0/test/test_parallel_execute_tool.py +256 -0
- aloop-0.1.0/test/test_ralph_loop.py +285 -0
- aloop-0.1.0/test/test_shell_background.py +348 -0
- aloop-0.1.0/test/test_slash_command_autocomplete.py +72 -0
- aloop-0.1.0/test/test_smart_edit.py +344 -0
- aloop-0.1.0/test/test_smart_edit_integration.py +93 -0
- aloop-0.1.0/test/test_tool_size_limits.py +154 -0
- aloop-0.1.0/test/test_web_fetch.py +139 -0
- aloop-0.1.0/tools/__init__.py +6 -0
- aloop-0.1.0/tools/advanced_file_ops.py +557 -0
- aloop-0.1.0/tools/base.py +51 -0
- aloop-0.1.0/tools/calculator.py +50 -0
- aloop-0.1.0/tools/code_navigator.py +975 -0
- aloop-0.1.0/tools/explore.py +254 -0
- aloop-0.1.0/tools/file_ops.py +150 -0
- aloop-0.1.0/tools/git_tools.py +791 -0
- aloop-0.1.0/tools/notify.py +69 -0
- aloop-0.1.0/tools/parallel_execute.py +420 -0
- aloop-0.1.0/tools/session_manager.py +205 -0
- aloop-0.1.0/tools/shell.py +147 -0
- aloop-0.1.0/tools/shell_background.py +470 -0
- aloop-0.1.0/tools/smart_edit.py +491 -0
- aloop-0.1.0/tools/todo.py +130 -0
- aloop-0.1.0/tools/web_fetch.py +673 -0
- aloop-0.1.0/tools/web_search.py +61 -0
- aloop-0.1.0/utils/__init__.py +15 -0
- aloop-0.1.0/utils/logger.py +105 -0
- aloop-0.1.0/utils/model_pricing.py +49 -0
- aloop-0.1.0/utils/runtime.py +75 -0
- aloop-0.1.0/utils/terminal_ui.py +422 -0
- aloop-0.1.0/utils/tui/__init__.py +39 -0
- aloop-0.1.0/utils/tui/command_registry.py +49 -0
- aloop-0.1.0/utils/tui/components.py +306 -0
- aloop-0.1.0/utils/tui/input_handler.py +393 -0
- aloop-0.1.0/utils/tui/model_ui.py +204 -0
- aloop-0.1.0/utils/tui/progress.py +292 -0
- aloop-0.1.0/utils/tui/status_bar.py +178 -0
- aloop-0.1.0/utils/tui/theme.py +165 -0
aloop-0.1.0/LICENSE
ADDED
|
@@ -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.
|
aloop-0.1.0/MANIFEST.in
ADDED
aloop-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: aloop
|
|
3
|
+
Version: 0.1.0
|
|
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+ and [uv](https://github.com/astral-sh/uv).
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
git clone https://github.com/luohaha/aloop.git
|
|
70
|
+
cd aloop
|
|
71
|
+
./scripts/bootstrap.sh
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
## Quick Start
|
|
75
|
+
|
|
76
|
+
### 1. Configure Models
|
|
77
|
+
|
|
78
|
+
On first run, `~/.aloop/models.yaml` is created with a template. Edit it to add your provider and API key:
|
|
79
|
+
|
|
80
|
+
```yaml
|
|
81
|
+
models:
|
|
82
|
+
openai/gpt-4o:
|
|
83
|
+
api_key: sk-...
|
|
84
|
+
|
|
85
|
+
anthropic/claude-3-5-sonnet-20241022:
|
|
86
|
+
api_key: sk-ant-...
|
|
87
|
+
|
|
88
|
+
ollama/llama2:
|
|
89
|
+
api_base: http://localhost:11434
|
|
90
|
+
|
|
91
|
+
default: openai/gpt-4o
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
See [LiteLLM Providers](https://docs.litellm.ai/docs/providers) for the full list.
|
|
95
|
+
|
|
96
|
+
### 2. Run
|
|
97
|
+
|
|
98
|
+
```bash
|
|
99
|
+
# Interactive mode
|
|
100
|
+
aloop
|
|
101
|
+
|
|
102
|
+
# Single task (returns raw result)
|
|
103
|
+
aloop --task "Calculate 123 * 456"
|
|
104
|
+
|
|
105
|
+
# Resume last session
|
|
106
|
+
aloop --resume
|
|
107
|
+
|
|
108
|
+
# Resume specific session (ID prefix)
|
|
109
|
+
aloop --resume a1b2c3d4
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
## CLI Reference
|
|
113
|
+
|
|
114
|
+
| Flag | Short | Description |
|
|
115
|
+
|------|-------|-------------|
|
|
116
|
+
| `--task TEXT` | `-t` | Run a single task and exit |
|
|
117
|
+
| `--model ID` | `-m` | LiteLLM model ID to use |
|
|
118
|
+
| `--resume [ID]` | `-r` | Resume a session (`latest` if no ID given) |
|
|
119
|
+
| `--verbose` | `-v` | Enable verbose logging to `~/.aloop/logs/` |
|
|
120
|
+
|
|
121
|
+
## Interactive Commands
|
|
122
|
+
|
|
123
|
+
### Slash Commands
|
|
124
|
+
|
|
125
|
+
| Command | Description |
|
|
126
|
+
|---------|-------------|
|
|
127
|
+
| `/help` | Show help |
|
|
128
|
+
| `/clear` | Clear conversation and start fresh |
|
|
129
|
+
| `/stats` | Show memory and token usage statistics |
|
|
130
|
+
| `/resume [id]` | List or resume a previous session |
|
|
131
|
+
| `/model` | Pick a model (arrow keys + Enter) |
|
|
132
|
+
| `/model edit` | Open `~/.aloop/models.yaml` in editor (auto-reload on save) |
|
|
133
|
+
| `/theme` | Toggle dark/light theme |
|
|
134
|
+
| `/verbose` | Toggle thinking display |
|
|
135
|
+
| `/compact` | Toggle compact output |
|
|
136
|
+
| `/exit` | Exit (also `/quit`) |
|
|
137
|
+
|
|
138
|
+
### Keyboard Shortcuts
|
|
139
|
+
|
|
140
|
+
| Key | Action |
|
|
141
|
+
|-----|--------|
|
|
142
|
+
| `/` | Command autocomplete |
|
|
143
|
+
| `Ctrl+C` | Cancel current operation |
|
|
144
|
+
| `Ctrl+L` | Clear screen |
|
|
145
|
+
| `Ctrl+T` | Toggle thinking display |
|
|
146
|
+
| `Ctrl+S` | Show quick stats |
|
|
147
|
+
| Up/Down | Navigate command history |
|
|
148
|
+
|
|
149
|
+
## How It Works
|
|
150
|
+
|
|
151
|
+
**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.
|
|
152
|
+
|
|
153
|
+
**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).
|
|
154
|
+
|
|
155
|
+
**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`.
|
|
156
|
+
|
|
157
|
+
**Session persistence**: Conversations are saved as YAML files under `~/.aloop/sessions/`. Resume with `--resume` or `/resume`.
|
|
158
|
+
|
|
159
|
+
## Tools
|
|
160
|
+
|
|
161
|
+
| Tool | Description |
|
|
162
|
+
|------|-------------|
|
|
163
|
+
| `read_file` | Read file contents |
|
|
164
|
+
| `write_file` | Write content to a file |
|
|
165
|
+
| `search_files` | Search for files by name |
|
|
166
|
+
| `edit_file` | Exact string replacement in files |
|
|
167
|
+
| `smart_edit` | LLM-assisted file editing |
|
|
168
|
+
| `glob_files` | Glob pattern file matching |
|
|
169
|
+
| `grep_content` | Regex search in file contents |
|
|
170
|
+
| `code_navigator` | AST-based code navigation (tree-sitter) |
|
|
171
|
+
| `calculate` | Evaluate expressions / run Python code |
|
|
172
|
+
| `shell` | Execute shell commands |
|
|
173
|
+
| `shell_task_status` | Check background shell task status |
|
|
174
|
+
| `web_search` | Web search (DuckDuckGo) |
|
|
175
|
+
| `web_fetch` | Fetch and extract web page content |
|
|
176
|
+
| `explore_context` | Explore project structure and context |
|
|
177
|
+
| `parallel_execute` | Run multiple tool calls in parallel |
|
|
178
|
+
| `notify` | Send email notifications (Resend) |
|
|
179
|
+
| `manage_todo_list` | Manage a task/todo list |
|
|
180
|
+
|
|
181
|
+
## Project Structure
|
|
182
|
+
|
|
183
|
+
```
|
|
184
|
+
aloop/
|
|
185
|
+
├── main.py # Entry point (argparse)
|
|
186
|
+
├── cli.py # CLI wrapper (`aloop` entry point)
|
|
187
|
+
├── interactive.py # Interactive session, model setup, TUI
|
|
188
|
+
├── config.py # Runtime config (~/.aloop/config)
|
|
189
|
+
├── agent/
|
|
190
|
+
│ ├── base.py # BaseAgent (ReAct + Ralph loops)
|
|
191
|
+
│ ├── agent.py # LoopAgent
|
|
192
|
+
│ ├── verification.py # LLMVerifier for Ralph loop
|
|
193
|
+
│ ├── context.py # Context injection (cwd, platform, date)
|
|
194
|
+
│ ├── tool_executor.py # Tool execution engine
|
|
195
|
+
│ └── todo.py # Todo list data structure
|
|
196
|
+
├── llm/
|
|
197
|
+
│ ├── litellm_adapter.py # LiteLLM adapter (100+ providers)
|
|
198
|
+
│ ├── model_manager.py # Model config from ~/.aloop/models.yaml
|
|
199
|
+
│ ├── retry.py # Retry with exponential backoff
|
|
200
|
+
│ └── message_types.py # LLMMessage, LLMResponse, ToolCall
|
|
201
|
+
├── memory/
|
|
202
|
+
│ ├── manager.py # Memory orchestrator + persistence
|
|
203
|
+
│ ├── compressor.py # LLM-driven compression
|
|
204
|
+
│ ├── short_term.py # Short-term memory (sliding window)
|
|
205
|
+
│ ├── token_tracker.py # Token counting + cost tracking
|
|
206
|
+
│ ├── types.py # Core data structures
|
|
207
|
+
│ └── store/
|
|
208
|
+
│ └── yaml_file_memory_store.py # YAML session persistence
|
|
209
|
+
├── tools/ # 18 tool implementations
|
|
210
|
+
├── utils/
|
|
211
|
+
│ ├── tui/ # TUI components (input, themes, status bar)
|
|
212
|
+
│ ├── logger.py # Logging setup
|
|
213
|
+
│ └── model_pricing.py # Model pricing data
|
|
214
|
+
├── docs/ # Documentation
|
|
215
|
+
├── test/ # Tests
|
|
216
|
+
├── scripts/ # Dev scripts (bootstrap.sh, dev.sh)
|
|
217
|
+
└── rfc/ # RFC design documents
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
## Configuration
|
|
221
|
+
|
|
222
|
+
Runtime settings live in `~/.aloop/config` (auto-created). Key settings:
|
|
223
|
+
|
|
224
|
+
| Setting | Default | Description |
|
|
225
|
+
|---------|---------|-------------|
|
|
226
|
+
| `MAX_ITERATIONS` | `1000` | Maximum agent loop iterations |
|
|
227
|
+
| `TOOL_TIMEOUT` | `600` | Tool execution timeout (seconds) |
|
|
228
|
+
| `RALPH_LOOP_MAX_ITERATIONS` | `3` | Max verification attempts |
|
|
229
|
+
| `MEMORY_ENABLED` | `true` | Enable memory management |
|
|
230
|
+
| `MEMORY_COMPRESSION_THRESHOLD` | `60000` | Token threshold for compression |
|
|
231
|
+
| `MEMORY_SHORT_TERM_SIZE` | `100` | Messages kept at full fidelity |
|
|
232
|
+
| `RETRY_MAX_ATTEMPTS` | `3` | Rate-limit retry attempts |
|
|
233
|
+
|
|
234
|
+
See [Configuration Guide](docs/configuration.md) for all settings.
|
|
235
|
+
|
|
236
|
+
## Documentation
|
|
237
|
+
|
|
238
|
+
- [Configuration](docs/configuration.md) -- model setup, runtime settings, custom endpoints
|
|
239
|
+
- [Examples](docs/examples.md) -- usage patterns and programmatic API
|
|
240
|
+
- [Memory Management](docs/memory-management.md) -- compression, persistence, token tracking
|
|
241
|
+
- [Extending](docs/extending.md) -- adding tools, agents, LLM providers
|
|
242
|
+
- [Packaging](docs/packaging.md) -- building, publishing, Docker
|
|
243
|
+
|
|
244
|
+
## License
|
|
245
|
+
|
|
246
|
+
MIT License
|
aloop-0.1.0/README.md
ADDED
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
<div align="center">
|
|
2
|
+
<picture>
|
|
3
|
+
<img alt="ALOOP" src="docs/assets/logo.svg" width="440">
|
|
4
|
+
</picture>
|
|
5
|
+
|
|
6
|
+
**One loop is all you need.**
|
|
7
|
+
|
|
8
|
+
aloop is an AI agent built on a single, unified loop. Planning, parallel sub-agents,
|
|
9
|
+
self-verification — everything folds into the same loop, chosen autonomously by the
|
|
10
|
+
agent itself, not by a hardcoded workflow. Simple architecture, emergent capability.
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
</div>
|
|
14
|
+
|
|
15
|
+
## Installation
|
|
16
|
+
|
|
17
|
+
Prerequisites: Python 3.12+ and [uv](https://github.com/astral-sh/uv).
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
git clone https://github.com/luohaha/aloop.git
|
|
21
|
+
cd aloop
|
|
22
|
+
./scripts/bootstrap.sh
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## Quick Start
|
|
26
|
+
|
|
27
|
+
### 1. Configure Models
|
|
28
|
+
|
|
29
|
+
On first run, `~/.aloop/models.yaml` is created with a template. Edit it to add your provider and API key:
|
|
30
|
+
|
|
31
|
+
```yaml
|
|
32
|
+
models:
|
|
33
|
+
openai/gpt-4o:
|
|
34
|
+
api_key: sk-...
|
|
35
|
+
|
|
36
|
+
anthropic/claude-3-5-sonnet-20241022:
|
|
37
|
+
api_key: sk-ant-...
|
|
38
|
+
|
|
39
|
+
ollama/llama2:
|
|
40
|
+
api_base: http://localhost:11434
|
|
41
|
+
|
|
42
|
+
default: openai/gpt-4o
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
See [LiteLLM Providers](https://docs.litellm.ai/docs/providers) for the full list.
|
|
46
|
+
|
|
47
|
+
### 2. Run
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
# Interactive mode
|
|
51
|
+
aloop
|
|
52
|
+
|
|
53
|
+
# Single task (returns raw result)
|
|
54
|
+
aloop --task "Calculate 123 * 456"
|
|
55
|
+
|
|
56
|
+
# Resume last session
|
|
57
|
+
aloop --resume
|
|
58
|
+
|
|
59
|
+
# Resume specific session (ID prefix)
|
|
60
|
+
aloop --resume a1b2c3d4
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
## CLI Reference
|
|
64
|
+
|
|
65
|
+
| Flag | Short | Description |
|
|
66
|
+
|------|-------|-------------|
|
|
67
|
+
| `--task TEXT` | `-t` | Run a single task and exit |
|
|
68
|
+
| `--model ID` | `-m` | LiteLLM model ID to use |
|
|
69
|
+
| `--resume [ID]` | `-r` | Resume a session (`latest` if no ID given) |
|
|
70
|
+
| `--verbose` | `-v` | Enable verbose logging to `~/.aloop/logs/` |
|
|
71
|
+
|
|
72
|
+
## Interactive Commands
|
|
73
|
+
|
|
74
|
+
### Slash Commands
|
|
75
|
+
|
|
76
|
+
| Command | Description |
|
|
77
|
+
|---------|-------------|
|
|
78
|
+
| `/help` | Show help |
|
|
79
|
+
| `/clear` | Clear conversation and start fresh |
|
|
80
|
+
| `/stats` | Show memory and token usage statistics |
|
|
81
|
+
| `/resume [id]` | List or resume a previous session |
|
|
82
|
+
| `/model` | Pick a model (arrow keys + Enter) |
|
|
83
|
+
| `/model edit` | Open `~/.aloop/models.yaml` in editor (auto-reload on save) |
|
|
84
|
+
| `/theme` | Toggle dark/light theme |
|
|
85
|
+
| `/verbose` | Toggle thinking display |
|
|
86
|
+
| `/compact` | Toggle compact output |
|
|
87
|
+
| `/exit` | Exit (also `/quit`) |
|
|
88
|
+
|
|
89
|
+
### Keyboard Shortcuts
|
|
90
|
+
|
|
91
|
+
| Key | Action |
|
|
92
|
+
|-----|--------|
|
|
93
|
+
| `/` | Command autocomplete |
|
|
94
|
+
| `Ctrl+C` | Cancel current operation |
|
|
95
|
+
| `Ctrl+L` | Clear screen |
|
|
96
|
+
| `Ctrl+T` | Toggle thinking display |
|
|
97
|
+
| `Ctrl+S` | Show quick stats |
|
|
98
|
+
| Up/Down | Navigate command history |
|
|
99
|
+
|
|
100
|
+
## How It Works
|
|
101
|
+
|
|
102
|
+
**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.
|
|
103
|
+
|
|
104
|
+
**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).
|
|
105
|
+
|
|
106
|
+
**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`.
|
|
107
|
+
|
|
108
|
+
**Session persistence**: Conversations are saved as YAML files under `~/.aloop/sessions/`. Resume with `--resume` or `/resume`.
|
|
109
|
+
|
|
110
|
+
## Tools
|
|
111
|
+
|
|
112
|
+
| Tool | Description |
|
|
113
|
+
|------|-------------|
|
|
114
|
+
| `read_file` | Read file contents |
|
|
115
|
+
| `write_file` | Write content to a file |
|
|
116
|
+
| `search_files` | Search for files by name |
|
|
117
|
+
| `edit_file` | Exact string replacement in files |
|
|
118
|
+
| `smart_edit` | LLM-assisted file editing |
|
|
119
|
+
| `glob_files` | Glob pattern file matching |
|
|
120
|
+
| `grep_content` | Regex search in file contents |
|
|
121
|
+
| `code_navigator` | AST-based code navigation (tree-sitter) |
|
|
122
|
+
| `calculate` | Evaluate expressions / run Python code |
|
|
123
|
+
| `shell` | Execute shell commands |
|
|
124
|
+
| `shell_task_status` | Check background shell task status |
|
|
125
|
+
| `web_search` | Web search (DuckDuckGo) |
|
|
126
|
+
| `web_fetch` | Fetch and extract web page content |
|
|
127
|
+
| `explore_context` | Explore project structure and context |
|
|
128
|
+
| `parallel_execute` | Run multiple tool calls in parallel |
|
|
129
|
+
| `notify` | Send email notifications (Resend) |
|
|
130
|
+
| `manage_todo_list` | Manage a task/todo list |
|
|
131
|
+
|
|
132
|
+
## Project Structure
|
|
133
|
+
|
|
134
|
+
```
|
|
135
|
+
aloop/
|
|
136
|
+
├── main.py # Entry point (argparse)
|
|
137
|
+
├── cli.py # CLI wrapper (`aloop` entry point)
|
|
138
|
+
├── interactive.py # Interactive session, model setup, TUI
|
|
139
|
+
├── config.py # Runtime config (~/.aloop/config)
|
|
140
|
+
├── agent/
|
|
141
|
+
│ ├── base.py # BaseAgent (ReAct + Ralph loops)
|
|
142
|
+
│ ├── agent.py # LoopAgent
|
|
143
|
+
│ ├── verification.py # LLMVerifier for Ralph loop
|
|
144
|
+
│ ├── context.py # Context injection (cwd, platform, date)
|
|
145
|
+
│ ├── tool_executor.py # Tool execution engine
|
|
146
|
+
│ └── todo.py # Todo list data structure
|
|
147
|
+
├── llm/
|
|
148
|
+
│ ├── litellm_adapter.py # LiteLLM adapter (100+ providers)
|
|
149
|
+
│ ├── model_manager.py # Model config from ~/.aloop/models.yaml
|
|
150
|
+
│ ├── retry.py # Retry with exponential backoff
|
|
151
|
+
│ └── message_types.py # LLMMessage, LLMResponse, ToolCall
|
|
152
|
+
├── memory/
|
|
153
|
+
│ ├── manager.py # Memory orchestrator + persistence
|
|
154
|
+
│ ├── compressor.py # LLM-driven compression
|
|
155
|
+
│ ├── short_term.py # Short-term memory (sliding window)
|
|
156
|
+
│ ├── token_tracker.py # Token counting + cost tracking
|
|
157
|
+
│ ├── types.py # Core data structures
|
|
158
|
+
│ └── store/
|
|
159
|
+
│ └── yaml_file_memory_store.py # YAML session persistence
|
|
160
|
+
├── tools/ # 18 tool implementations
|
|
161
|
+
├── utils/
|
|
162
|
+
│ ├── tui/ # TUI components (input, themes, status bar)
|
|
163
|
+
│ ├── logger.py # Logging setup
|
|
164
|
+
│ └── model_pricing.py # Model pricing data
|
|
165
|
+
├── docs/ # Documentation
|
|
166
|
+
├── test/ # Tests
|
|
167
|
+
├── scripts/ # Dev scripts (bootstrap.sh, dev.sh)
|
|
168
|
+
└── rfc/ # RFC design documents
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
## Configuration
|
|
172
|
+
|
|
173
|
+
Runtime settings live in `~/.aloop/config` (auto-created). Key settings:
|
|
174
|
+
|
|
175
|
+
| Setting | Default | Description |
|
|
176
|
+
|---------|---------|-------------|
|
|
177
|
+
| `MAX_ITERATIONS` | `1000` | Maximum agent loop iterations |
|
|
178
|
+
| `TOOL_TIMEOUT` | `600` | Tool execution timeout (seconds) |
|
|
179
|
+
| `RALPH_LOOP_MAX_ITERATIONS` | `3` | Max verification attempts |
|
|
180
|
+
| `MEMORY_ENABLED` | `true` | Enable memory management |
|
|
181
|
+
| `MEMORY_COMPRESSION_THRESHOLD` | `60000` | Token threshold for compression |
|
|
182
|
+
| `MEMORY_SHORT_TERM_SIZE` | `100` | Messages kept at full fidelity |
|
|
183
|
+
| `RETRY_MAX_ATTEMPTS` | `3` | Rate-limit retry attempts |
|
|
184
|
+
|
|
185
|
+
See [Configuration Guide](docs/configuration.md) for all settings.
|
|
186
|
+
|
|
187
|
+
## Documentation
|
|
188
|
+
|
|
189
|
+
- [Configuration](docs/configuration.md) -- model setup, runtime settings, custom endpoints
|
|
190
|
+
- [Examples](docs/examples.md) -- usage patterns and programmatic API
|
|
191
|
+
- [Memory Management](docs/memory-management.md) -- compression, persistence, token tracking
|
|
192
|
+
- [Extending](docs/extending.md) -- adding tools, agents, LLM providers
|
|
193
|
+
- [Packaging](docs/packaging.md) -- building, publishing, Docker
|
|
194
|
+
|
|
195
|
+
## License
|
|
196
|
+
|
|
197
|
+
MIT License
|
|
File without changes
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
"""Loop agent implementation."""
|
|
2
|
+
|
|
3
|
+
from config import Config
|
|
4
|
+
from llm import LLMMessage
|
|
5
|
+
from utils import terminal_ui
|
|
6
|
+
|
|
7
|
+
from .base import BaseAgent
|
|
8
|
+
from .context import format_context_prompt
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class LoopAgent(BaseAgent):
|
|
12
|
+
"""Primary agent implementation — one unified loop for all tasks."""
|
|
13
|
+
|
|
14
|
+
SYSTEM_PROMPT = """<role>
|
|
15
|
+
You are a helpful AI assistant that uses tools to accomplish tasks efficiently and reliably.
|
|
16
|
+
</role>
|
|
17
|
+
|
|
18
|
+
<critical_rules>
|
|
19
|
+
IMPORTANT: Always think before acting
|
|
20
|
+
IMPORTANT: Use the most efficient tool for each operation
|
|
21
|
+
IMPORTANT: Manage todo lists for complex multi-step tasks
|
|
22
|
+
IMPORTANT: Mark tasks completed IMMEDIATELY after finishing them
|
|
23
|
+
</critical_rules>
|
|
24
|
+
|
|
25
|
+
<task_management>
|
|
26
|
+
Use the manage_todo_list tool for complex tasks to prevent forgetting steps.
|
|
27
|
+
|
|
28
|
+
WHEN TO USE TODO LISTS:
|
|
29
|
+
- Tasks with 3+ distinct steps
|
|
30
|
+
- Multi-file operations
|
|
31
|
+
- Complex workflows requiring planning
|
|
32
|
+
- Any task where tracking progress helps
|
|
33
|
+
|
|
34
|
+
TODO LIST RULES:
|
|
35
|
+
- Create todos BEFORE starting complex work
|
|
36
|
+
- Exactly ONE task must be in_progress at any time
|
|
37
|
+
- Mark tasks completed IMMEDIATELY after finishing
|
|
38
|
+
- Update status as you work through the list
|
|
39
|
+
|
|
40
|
+
<good_example>
|
|
41
|
+
User: Create a data pipeline that reads CSV, processes it, and generates report
|
|
42
|
+
Assistant: I'll use the todo list to track this multi-step task.
|
|
43
|
+
[Calls manage_todo_list with operation="add" for each step]
|
|
44
|
+
[Marks first task as in_progress before starting]
|
|
45
|
+
[Uses read_file tool]
|
|
46
|
+
[Marks as completed, moves to next task]
|
|
47
|
+
</good_example>
|
|
48
|
+
|
|
49
|
+
<bad_example>
|
|
50
|
+
User: Create a data pipeline that reads CSV, processes it, and generates report
|
|
51
|
+
Assistant: [Immediately starts without planning, forgets steps halfway through]
|
|
52
|
+
</bad_example>
|
|
53
|
+
</task_management>
|
|
54
|
+
|
|
55
|
+
<tool_usage_guidelines>
|
|
56
|
+
For file operations:
|
|
57
|
+
- Use glob_files to find files by pattern (fast, efficient)
|
|
58
|
+
- Use code_navigator to find function/class definitions (10x faster than grep, AST-based)
|
|
59
|
+
- Use grep_content for text search only (not for finding code structure)
|
|
60
|
+
- Use read_file only when you need full contents (avoid reading multiple large files at once)
|
|
61
|
+
- Use smart_edit for code edits (fuzzy match, auto backup, diff preview)
|
|
62
|
+
- Use edit_file for simple append/insert operations only
|
|
63
|
+
- Use write_file only for creating new files or complete rewrites
|
|
64
|
+
|
|
65
|
+
CRITICAL: Never read multiple large files in a single iteration - this causes context overflow!
|
|
66
|
+
Instead: Use code_navigator or grep_content to find specific information, then read only what you need.
|
|
67
|
+
|
|
68
|
+
For complex tasks:
|
|
69
|
+
- Use manage_todo_list to track progress
|
|
70
|
+
- Break into smaller, manageable steps
|
|
71
|
+
- Mark tasks completed as you go
|
|
72
|
+
- Keep exactly ONE task in_progress at a time
|
|
73
|
+
|
|
74
|
+
<good_example>
|
|
75
|
+
Task: Find all Python files that import 'requests'
|
|
76
|
+
Approach:
|
|
77
|
+
1. Use glob_files with pattern "**/*.py" to find Python files
|
|
78
|
+
2. Use grep_content with pattern "^import requests|^from requests" to search
|
|
79
|
+
Result: Efficient, minimal tokens used
|
|
80
|
+
</good_example>
|
|
81
|
+
|
|
82
|
+
<bad_example>
|
|
83
|
+
Task: Find all Python files that import 'requests'
|
|
84
|
+
Approach:
|
|
85
|
+
1. Use read_file on every Python file one by one
|
|
86
|
+
2. Manually search through content
|
|
87
|
+
Result: Wasteful, uses 100x more tokens
|
|
88
|
+
</bad_example>
|
|
89
|
+
</tool_usage_guidelines>
|
|
90
|
+
|
|
91
|
+
<workflow>
|
|
92
|
+
For each user request, follow this ReAct pattern:
|
|
93
|
+
1. THINK: Analyze what's needed, choose best tools
|
|
94
|
+
2. ACT: Execute with appropriate tools
|
|
95
|
+
3. OBSERVE: Check results and learn from them
|
|
96
|
+
4. REPEAT or COMPLETE: Continue the loop or provide final answer
|
|
97
|
+
|
|
98
|
+
When you have enough information, provide your final answer directly without using more tools.
|
|
99
|
+
</workflow>
|
|
100
|
+
|
|
101
|
+
<complex_task_strategy>
|
|
102
|
+
For complex tasks, combine tools to achieve an explore-plan-execute workflow:
|
|
103
|
+
|
|
104
|
+
1. **EXPLORE**: Gather context before acting
|
|
105
|
+
- Use explore_context for parallel information gathering (code structure, web research)
|
|
106
|
+
|
|
107
|
+
2. **PLAN**: Structure your approach
|
|
108
|
+
- Use manage_todo_list to break down the task into trackable steps
|
|
109
|
+
- Identify dependencies between steps
|
|
110
|
+
|
|
111
|
+
3. **EXECUTE**: Carry out the plan
|
|
112
|
+
- Use parallel_execute for multiple independent/semi-dependent tasks
|
|
113
|
+
- Use regular tools for simple sequential operations
|
|
114
|
+
|
|
115
|
+
When to use each approach:
|
|
116
|
+
- Simple task (1-2 steps) → Use tools directly
|
|
117
|
+
- Medium task (3-5 steps) → Use todo list + sequential execution
|
|
118
|
+
- Complex task (needs research) → Explore → Plan → Execute
|
|
119
|
+
- Parallel workload → parallel_execute
|
|
120
|
+
</complex_task_strategy>"""
|
|
121
|
+
|
|
122
|
+
async def run(self, task: str, verify: bool = True) -> str:
|
|
123
|
+
"""Execute ReAct loop until task is complete.
|
|
124
|
+
|
|
125
|
+
Args:
|
|
126
|
+
task: The task to complete
|
|
127
|
+
verify: If True, use ralph loop (outer verification). If False, use
|
|
128
|
+
plain react loop (suitable for interactive multi-turn sessions).
|
|
129
|
+
|
|
130
|
+
Returns:
|
|
131
|
+
Final answer as a string
|
|
132
|
+
"""
|
|
133
|
+
# Build system message with context (only if not already in memory)
|
|
134
|
+
# This allows multi-turn conversations to reuse the same system message
|
|
135
|
+
if not self.memory.system_messages:
|
|
136
|
+
system_content = self.SYSTEM_PROMPT
|
|
137
|
+
try:
|
|
138
|
+
context = await format_context_prompt()
|
|
139
|
+
system_content = context + "\n" + system_content
|
|
140
|
+
except Exception:
|
|
141
|
+
# If context gathering fails, continue without it
|
|
142
|
+
pass
|
|
143
|
+
|
|
144
|
+
# Add system message only on first turn
|
|
145
|
+
await self.memory.add_message(LLMMessage(role="system", content=system_content))
|
|
146
|
+
|
|
147
|
+
# Add user task/message
|
|
148
|
+
await self.memory.add_message(LLMMessage(role="user", content=task))
|
|
149
|
+
|
|
150
|
+
tools = self.tool_executor.get_tool_schemas()
|
|
151
|
+
|
|
152
|
+
if verify:
|
|
153
|
+
# Use ralph loop (outer verification wrapping the inner ReAct loop)
|
|
154
|
+
result = await self._ralph_loop(
|
|
155
|
+
messages=[], # Not used when use_memory=True
|
|
156
|
+
tools=tools,
|
|
157
|
+
use_memory=True,
|
|
158
|
+
save_to_memory=True,
|
|
159
|
+
task=task,
|
|
160
|
+
max_iterations=Config.RALPH_LOOP_MAX_ITERATIONS,
|
|
161
|
+
)
|
|
162
|
+
else:
|
|
163
|
+
# Plain react loop without verification
|
|
164
|
+
result = await self._react_loop(
|
|
165
|
+
messages=[],
|
|
166
|
+
tools=tools,
|
|
167
|
+
use_memory=True,
|
|
168
|
+
save_to_memory=True,
|
|
169
|
+
task=task,
|
|
170
|
+
)
|
|
171
|
+
|
|
172
|
+
self._print_memory_stats()
|
|
173
|
+
|
|
174
|
+
# Save memory state to database after task completion
|
|
175
|
+
await self.memory.save_memory()
|
|
176
|
+
|
|
177
|
+
return result
|
|
178
|
+
|
|
179
|
+
def _print_memory_stats(self):
|
|
180
|
+
"""Print memory usage statistics."""
|
|
181
|
+
stats = self.memory.get_stats()
|
|
182
|
+
terminal_ui.print_memory_stats(stats)
|