aloop 0.1.0__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.

Potentially problematic release.


This version of aloop might be problematic. Click here for more details.

Files changed (62) hide show
  1. agent/__init__.py +0 -0
  2. agent/agent.py +182 -0
  3. agent/base.py +406 -0
  4. agent/context.py +126 -0
  5. agent/todo.py +149 -0
  6. agent/tool_executor.py +54 -0
  7. agent/verification.py +135 -0
  8. aloop-0.1.0.dist-info/METADATA +246 -0
  9. aloop-0.1.0.dist-info/RECORD +62 -0
  10. aloop-0.1.0.dist-info/WHEEL +5 -0
  11. aloop-0.1.0.dist-info/entry_points.txt +2 -0
  12. aloop-0.1.0.dist-info/licenses/LICENSE +21 -0
  13. aloop-0.1.0.dist-info/top_level.txt +9 -0
  14. cli.py +19 -0
  15. config.py +146 -0
  16. interactive.py +865 -0
  17. llm/__init__.py +51 -0
  18. llm/base.py +26 -0
  19. llm/compat.py +226 -0
  20. llm/content_utils.py +309 -0
  21. llm/litellm_adapter.py +450 -0
  22. llm/message_types.py +245 -0
  23. llm/model_manager.py +265 -0
  24. llm/retry.py +95 -0
  25. main.py +246 -0
  26. memory/__init__.py +20 -0
  27. memory/compressor.py +554 -0
  28. memory/manager.py +538 -0
  29. memory/serialization.py +82 -0
  30. memory/short_term.py +88 -0
  31. memory/token_tracker.py +203 -0
  32. memory/types.py +51 -0
  33. tools/__init__.py +6 -0
  34. tools/advanced_file_ops.py +557 -0
  35. tools/base.py +51 -0
  36. tools/calculator.py +50 -0
  37. tools/code_navigator.py +975 -0
  38. tools/explore.py +254 -0
  39. tools/file_ops.py +150 -0
  40. tools/git_tools.py +791 -0
  41. tools/notify.py +69 -0
  42. tools/parallel_execute.py +420 -0
  43. tools/session_manager.py +205 -0
  44. tools/shell.py +147 -0
  45. tools/shell_background.py +470 -0
  46. tools/smart_edit.py +491 -0
  47. tools/todo.py +130 -0
  48. tools/web_fetch.py +673 -0
  49. tools/web_search.py +61 -0
  50. utils/__init__.py +15 -0
  51. utils/logger.py +105 -0
  52. utils/model_pricing.py +49 -0
  53. utils/runtime.py +75 -0
  54. utils/terminal_ui.py +422 -0
  55. utils/tui/__init__.py +39 -0
  56. utils/tui/command_registry.py +49 -0
  57. utils/tui/components.py +306 -0
  58. utils/tui/input_handler.py +393 -0
  59. utils/tui/model_ui.py +204 -0
  60. utils/tui/progress.py +292 -0
  61. utils/tui/status_bar.py +178 -0
  62. utils/tui/theme.py +165 -0
@@ -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
@@ -0,0 +1,62 @@
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
+ aloop-0.1.0.dist-info/licenses/LICENSE,sha256=QWrc--GLlkEskZCiT0qjbtLKXYA5It4gp345uIltMew,1066
13
+ llm/__init__.py,sha256=GhTKAf36pjBV6fuijXTCBcLk4XqmUYeCbrZATvb_mW8,1149
14
+ llm/base.py,sha256=mESX6l3dbZPfh9-l9txVvvVJWpgvNIchm3tD5MkAFn8,551
15
+ llm/compat.py,sha256=tl5De-u86VuWLbeTmPrBVRosBtyPu3kM504fAwARFeI,6396
16
+ llm/content_utils.py,sha256=tcwNRlMRpFEVeCsjyCcGrcy1WxtqLiK1kRXZw0_IL4U,8262
17
+ llm/litellm_adapter.py,sha256=Zm4OSNC4oacLunf1Bkjkdnwwe4F9n0GNUeXbFcQeCFA,16934
18
+ llm/message_types.py,sha256=sjcjsJGy1JtWMtjfqrrgpyVWntg6z47unxAHVID3QIQ,7117
19
+ llm/model_manager.py,sha256=ms5MNAjlRWWV5RYbq60Qb0BbWrYfjpMkgphMu82RNJA,8704
20
+ llm/retry.py,sha256=0ouN1yAD2mCNbtFbrTxAwWXiQAW7p1XIUei623WcUpw,2710
21
+ memory/__init__.py,sha256=7iLqzMIysHkC_wqftqSPPFVF6peHlX_agtRTveWhHu4,578
22
+ memory/compressor.py,sha256=edV7B61CipvgQq5LGGdOwOsoRiuHL2fz_sC-MnSyKSM,23238
23
+ memory/manager.py,sha256=m0r3b47KXMj8ymAZeQwZ6pEFlRqHyqVdu4OlLQ-D4xc,19474
24
+ memory/serialization.py,sha256=whX7E2n2UyamD1iOtQS2PkKhbb-ebwwnvfu-fh-Y2tE,2194
25
+ memory/short_term.py,sha256=RHPvHxqAzSJq_FeepL5qk0v-zOYnTIKP6l_jtJ0jB7c,2519
26
+ memory/token_tracker.py,sha256=xufZ-FWjcGkTdkK5jmns3CTIqklv6ctFCmKH5yl9uiY,6953
27
+ memory/types.py,sha256=GLOLB0f0IN3cb7Bv-guVwtR17mJG7zlvJjRWtXlzyao,1744
28
+ tools/__init__.py,sha256=iAVcW_A4I2aVuiI3PoVMcCBe8cFGqe6TIBuaZ_cGD-Q,190
29
+ tools/advanced_file_ops.py,sha256=dZlgOvAVOhVRIyOgwU6tlaSNituQoEivKxiHQo9DNME,19938
30
+ tools/base.py,sha256=OtDzJg4c-CKtHTohPVaNDD_8x1xPKqCXE8SGJd8Xr_c,1397
31
+ tools/calculator.py,sha256=Sl201BysnFa-7tduxKstnOT94syAHyle2rTgztXA6Ho,1500
32
+ tools/code_navigator.py,sha256=Unj9mjbsTOJQNgVi980zrgeg1Msp3rHzqo-am8eqwEI,38336
33
+ tools/explore.py,sha256=hR2_TMjVYCrqrj9JFSmTgIiXyvlE7_vFYtrlxLtVGG4,8171
34
+ tools/file_ops.py,sha256=43qs42oIbCd9q7UD1scEKUAbKdRuzjukTjxw_ziJmuE,5082
35
+ tools/git_tools.py,sha256=a0pLYnk4OjXB7ak3jmlQLIDet9Bf_rolNGsGHS7XgqA,23521
36
+ tools/notify.py,sha256=gWlTx-9SSQ2bsITVkJPCUgX6Mm9ve2TtfgMHq4RksKM,2103
37
+ tools/parallel_execute.py,sha256=AD_VwCchLOkHLYYliJn0dGupX4wB01X1RmV0Lz3DqXM,13197
38
+ tools/session_manager.py,sha256=aXdRYWR9-NH5K-E1NfmjxG0txR5fvmhAX-mFjmq9N7c,6484
39
+ tools/shell.py,sha256=Ozn4ldWA4DzIkNHt1HjXD_F0A300Zdnj7IYYlrGqIqo,5492
40
+ tools/shell_background.py,sha256=aAduugXVEzs2IXFmXq62cHryZDKknKsmdxbI_0KgQK4,16070
41
+ tools/smart_edit.py,sha256=GU3SYMnpr7DCXNVL8HqphwoajRERF1vQT_e1LXmcohw,17804
42
+ tools/todo.py,sha256=nm3lKm-lE6ta0Q3g13XKLQ7L3JLATX4VuoZtUu9KvCU,4412
43
+ tools/web_fetch.py,sha256=wW6zk0uA6nMuH5ecs2ExgdPJv9-tn3xP1H92-tc5ni4,24827
44
+ tools/web_search.py,sha256=7D_86Jk8sQCOPCiMa_NtFXTpduZiXlwr7IRyREBRbIg,2025
45
+ utils/__init__.py,sha256=WKSnJ2XGVq5199-gTkIO0Acd4RT9LZofDQeOkbzVq-c,424
46
+ utils/logger.py,sha256=-8UiH5F5jm3IGbJvlcNhvkF_t8fUSKmuqnZPsLVSb8M,3072
47
+ utils/model_pricing.py,sha256=r5LVdkQNqYMm8_W7Kb2iXu9oHHHz7zHjHYyEqOb3wRA,2311
48
+ utils/runtime.py,sha256=tGl__Ss2SSEWsyMxEhklKPm2HrbbKRTzgtk7Tq5lCxU,1740
49
+ utils/terminal_ui.py,sha256=kOfT9O2UdZCOcwfx0TZ6MziOgGEhC1C_eB6bjv0sH_0,12302
50
+ utils/tui/__init__.py,sha256=bN2Iof8dzqp_zT7KRS9vFGWEb5pGsl36OQ9EtxmiOVw,915
51
+ utils/tui/command_registry.py,sha256=EYsZssHLYDaoaVyc_t1MEWOiaD5RjyEGZS8jflEquIQ,1597
52
+ utils/tui/components.py,sha256=m5FLAEfI-eO3773ug9qmTw5AXrjBtKIFyrCAb7Mibl4,9492
53
+ utils/tui/input_handler.py,sha256=e2PtQRWG-VfRyq8VuhsaOFkE7dE0b8o3EUt5YPbsl-M,14022
54
+ utils/tui/model_ui.py,sha256=Oqc-2NKGiyg-4fZNgnxO0drwrHVKrjK9EzTCwd4oXG4,6234
55
+ utils/tui/progress.py,sha256=Yb0bn6XSBvluLSJqgkvHt_SMXDob5LwT-OwR1b7aMDE,8082
56
+ utils/tui/status_bar.py,sha256=AG_zb9qGkkNGLMHdYgWBvnK8JVppmUVKjOmj-qCVLg4,5333
57
+ utils/tui/theme.py,sha256=x0wnQw21Ufy1cm5DGMQroJ88J_rYBcezVHlwg4-mdwY,5606
58
+ aloop-0.1.0.dist-info/METADATA,sha256=ys5QYs_mwM7zNEOwNSSueCzoOwLhOMQ60J6E8PHWJ3w,9184
59
+ aloop-0.1.0.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
60
+ aloop-0.1.0.dist-info/entry_points.txt,sha256=fXfq7fpc79S3RQ7TzXSSyiA2L6yedfo-3o1PXEFXs0I,35
61
+ aloop-0.1.0.dist-info/top_level.txt,sha256=RcMBYcKEUojok3GYEagBOce8M7DyEAelgCypkcTrSE0,57
62
+ aloop-0.1.0.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (80.10.2)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ aloop = cli:main
@@ -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.
@@ -0,0 +1,9 @@
1
+ agent
2
+ cli
3
+ config
4
+ interactive
5
+ llm
6
+ main
7
+ memory
8
+ tools
9
+ utils
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