simhacli 1.0.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.
- simhacli-1.0.0/PKG-INFO +216 -0
- simhacli-1.0.0/README.md +184 -0
- simhacli-1.0.0/agent/agent.py +205 -0
- simhacli-1.0.0/agent/events.py +116 -0
- simhacli-1.0.0/agent/session.py +95 -0
- simhacli-1.0.0/agent/state.py +121 -0
- simhacli-1.0.0/client/llm_client.py +247 -0
- simhacli-1.0.0/client/response.py +95 -0
- simhacli-1.0.0/config/config.py +155 -0
- simhacli-1.0.0/config/loader.py +277 -0
- simhacli-1.0.0/context/compaction.py +92 -0
- simhacli-1.0.0/context/loop_detector.py +50 -0
- simhacli-1.0.0/context/manager.py +217 -0
- simhacli-1.0.0/hooks/hook_system.py +142 -0
- simhacli-1.0.0/main.py +502 -0
- simhacli-1.0.0/prompts/system.py +337 -0
- simhacli-1.0.0/pyproject.toml +64 -0
- simhacli-1.0.0/safety/approval.py +314 -0
- simhacli-1.0.0/setup.cfg +4 -0
- simhacli-1.0.0/simhacli.egg-info/PKG-INFO +216 -0
- simhacli-1.0.0/simhacli.egg-info/SOURCES.txt +46 -0
- simhacli-1.0.0/simhacli.egg-info/dependency_links.txt +1 -0
- simhacli-1.0.0/simhacli.egg-info/entry_points.txt +2 -0
- simhacli-1.0.0/simhacli.egg-info/requires.txt +9 -0
- simhacli-1.0.0/simhacli.egg-info/top_level.txt +11 -0
- simhacli-1.0.0/tools/base.py +197 -0
- simhacli-1.0.0/tools/builtin/__init__.py +43 -0
- simhacli-1.0.0/tools/builtin/edit_file.py +211 -0
- simhacli-1.0.0/tools/builtin/glob.py +131 -0
- simhacli-1.0.0/tools/builtin/grep.py +236 -0
- simhacli-1.0.0/tools/builtin/list_dir.py +60 -0
- simhacli-1.0.0/tools/builtin/memory.py +115 -0
- simhacli-1.0.0/tools/builtin/read_file.py +131 -0
- simhacli-1.0.0/tools/builtin/shell.py +217 -0
- simhacli-1.0.0/tools/builtin/todo.py +245 -0
- simhacli-1.0.0/tools/builtin/web_fetch.py +126 -0
- simhacli-1.0.0/tools/builtin/web_search.py +91 -0
- simhacli-1.0.0/tools/builtin/write_file.py +107 -0
- simhacli-1.0.0/tools/discovery.py +70 -0
- simhacli-1.0.0/tools/mcp/client.py +114 -0
- simhacli-1.0.0/tools/mcp/mcp_manager.py +91 -0
- simhacli-1.0.0/tools/mcp/mcp_tool.py +51 -0
- simhacli-1.0.0/tools/registry.py +163 -0
- simhacli-1.0.0/tools/subagent.py +404 -0
- simhacli-1.0.0/ui/tui.py +848 -0
- simhacli-1.0.0/utils/errors.py +49 -0
- simhacli-1.0.0/utils/paths.py +40 -0
- simhacli-1.0.0/utils/text.py +91 -0
simhacli-1.0.0/PKG-INFO
ADDED
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: simhacli
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: SimhaCLI 🦁 — AI Coding Agent that runs inside your terminal
|
|
5
|
+
Author: Narasimha Naidu Korrapti
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/naidu199/SimhaCLI
|
|
8
|
+
Project-URL: Repository, https://github.com/naidu199/SimhaCLI
|
|
9
|
+
Keywords: ai,cli,agent,coding,llm,terminal
|
|
10
|
+
Classifier: Development Status :: 4 - Beta
|
|
11
|
+
Classifier: Environment :: Console
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
14
|
+
Classifier: Operating System :: OS Independent
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
19
|
+
Classifier: Topic :: Software Development :: Code Generators
|
|
20
|
+
Classifier: Topic :: Utilities
|
|
21
|
+
Requires-Python: >=3.10
|
|
22
|
+
Description-Content-Type: text/markdown
|
|
23
|
+
Requires-Dist: click>=8.0.0
|
|
24
|
+
Requires-Dist: rich>=13.0.0
|
|
25
|
+
Requires-Dist: httpx>=0.25.0
|
|
26
|
+
Requires-Dist: anyio>=4.0.0
|
|
27
|
+
Requires-Dist: pydantic>=2.0.0
|
|
28
|
+
Requires-Dist: toml>=0.10.0
|
|
29
|
+
Requires-Dist: ddgs>=6.0.0
|
|
30
|
+
Requires-Dist: fastmcp>=2.0.0
|
|
31
|
+
Requires-Dist: diskcache>=5.0.0
|
|
32
|
+
|
|
33
|
+
<div align="center">
|
|
34
|
+
|
|
35
|
+
# 🦁 SimhaCLI
|
|
36
|
+
|
|
37
|
+
### AI-Powered Coding Agent for Your Terminal
|
|
38
|
+
|
|
39
|
+
[](https://www.python.org/downloads/)
|
|
40
|
+
[](https://opensource.org/licenses/MIT)
|
|
41
|
+
[](https://github.com/psf/black)
|
|
42
|
+
|
|
43
|
+
_Built by Narasimha Naidu Korrapti_
|
|
44
|
+
|
|
45
|
+
[Features](#-features) • [Installation](#-installation)
|
|
46
|
+
|
|
47
|
+
</div>
|
|
48
|
+
|
|
49
|
+
---
|
|
50
|
+
|
|
51
|
+
## 📖 Overview
|
|
52
|
+
|
|
53
|
+
**SimhaCLI** is a powerful terminal-based AI coding agent that brings the intelligence of Large Language Models directly into your development workflow. It seamlessly integrates with your codebase, understands context, and executes actions through a comprehensive set of builtin tools.
|
|
54
|
+
|
|
55
|
+
### Why SimhaCLI?
|
|
56
|
+
|
|
57
|
+
- 🚀 **Session-Based Architecture**: Persistent context and memory across interactions
|
|
58
|
+
- 🛠️ **11 Builtin Tools**: File operations, shell commands, web access, task management
|
|
59
|
+
- 🔒 **Safety First**: Shell command blocking prevents dangerous operations
|
|
60
|
+
- 💾 **Persistent Memory**: Remember user preferences and context
|
|
61
|
+
- 🎨 **Beautiful TUI**: Rich terminal interface with syntax highlighting
|
|
62
|
+
- ⚡ **Streaming Responses**: Real-time output as the agent thinks
|
|
63
|
+
- 🔄 **Event-Driven**: Observable agent actions with full transparency
|
|
64
|
+
|
|
65
|
+
---
|
|
66
|
+
|
|
67
|
+
## ✨ Features
|
|
68
|
+
|
|
69
|
+
### 🤖 Intelligent Agent
|
|
70
|
+
|
|
71
|
+
- **Agentic Loop**: Autonomous multi-turn conversations with tool usage
|
|
72
|
+
- **Context Management**: Tracks conversation history with token counting
|
|
73
|
+
- **Turn Tracking**: Session-based state management with UUIDs
|
|
74
|
+
- **Streaming Output**: Real-time response generation
|
|
75
|
+
|
|
76
|
+
### 🛠️ Comprehensive Toolset
|
|
77
|
+
|
|
78
|
+
11 builtin tools across 5 categories:
|
|
79
|
+
|
|
80
|
+
- **📖 Read**: `read_file`, `list_dir`, `glob`, `grep`
|
|
81
|
+
- **✏️ Write**: `write_file`, `edit_file`
|
|
82
|
+
- **🖥️ Shell**: `shell` (with 40+ blocked dangerous commands)
|
|
83
|
+
- **🌐 Web**: `web_search`, `web_fetch`
|
|
84
|
+
- **💾 Memory**: `todos` (task management), `memory` (persistent storage)
|
|
85
|
+
|
|
86
|
+
### 🔒 Safety & Security
|
|
87
|
+
|
|
88
|
+
- Command blocking for dangerous operations (rm -rf, format, etc.)
|
|
89
|
+
- Timeout protection on shell commands (120s default, 600s max)
|
|
90
|
+
- File validation and error handling
|
|
91
|
+
- Configurable working directory restrictions
|
|
92
|
+
|
|
93
|
+
### 💾 Persistent Storage
|
|
94
|
+
|
|
95
|
+
- **User Memory**: JSON-based key-value storage (`~/.simhacli/user_memory.json`)
|
|
96
|
+
- **Configuration**: System and project-level TOML configs
|
|
97
|
+
- **Session Tracking**: UUID-based session management with timestamps
|
|
98
|
+
|
|
99
|
+
### 🎨 Rich Terminal UI
|
|
100
|
+
|
|
101
|
+
- Syntax-highlighted code display
|
|
102
|
+
- Color-coded tool execution (cyan=read, yellow=write, white=shell)
|
|
103
|
+
- Live streaming text output
|
|
104
|
+
- Beautiful welcome banner and formatting
|
|
105
|
+
- Error panels with detailed information
|
|
106
|
+
|
|
107
|
+
---
|
|
108
|
+
|
|
109
|
+
## 🚀 Installation
|
|
110
|
+
|
|
111
|
+
### Prerequisites
|
|
112
|
+
|
|
113
|
+
- **Python 3.10+**
|
|
114
|
+
- **API Key** (OpenAI, Gemini, or compatible API)
|
|
115
|
+
|
|
116
|
+
### Option 1: Install from PyPI (Recommended)
|
|
117
|
+
|
|
118
|
+
```bash
|
|
119
|
+
pip install simhacli
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
After installation, you can run SimhaCLI from anywhere:
|
|
123
|
+
|
|
124
|
+
```bash
|
|
125
|
+
simhacli # Start interactive mode
|
|
126
|
+
simhacli "explain this code" # Run a single prompt
|
|
127
|
+
simhacli --help # Show help
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
### Option 2: Install from Source
|
|
131
|
+
|
|
132
|
+
1. **Clone the repository**
|
|
133
|
+
|
|
134
|
+
```bash
|
|
135
|
+
git clone https://github.com/naidu199/SimhaCLI.git
|
|
136
|
+
cd SimhaCLI
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
2. **Install globally (access from anywhere)**
|
|
140
|
+
|
|
141
|
+
```bash
|
|
142
|
+
pip install -e .
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
Or install without editable mode:
|
|
146
|
+
|
|
147
|
+
```bash
|
|
148
|
+
pip install .
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
3. **Now you can use `simhacli` from anywhere:**
|
|
152
|
+
|
|
153
|
+
```bash
|
|
154
|
+
simhacli # Start interactive mode
|
|
155
|
+
simhacli "your prompt" # Run a single command
|
|
156
|
+
simhacli --cwd /path/to/dir # Set working directory
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
### Option 3: Development Setup
|
|
160
|
+
|
|
161
|
+
1. **Clone and create virtual environment**
|
|
162
|
+
|
|
163
|
+
```bash
|
|
164
|
+
git clone https://github.com/naidu199/SimhaCLI.git
|
|
165
|
+
cd SimhaCLI
|
|
166
|
+
python -m venv venv
|
|
167
|
+
|
|
168
|
+
# Windows
|
|
169
|
+
venv\Scripts\activate
|
|
170
|
+
|
|
171
|
+
# Linux/Mac
|
|
172
|
+
source venv/bin/activate
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
2. **Install in editable mode**
|
|
176
|
+
|
|
177
|
+
```bash
|
|
178
|
+
pip install -e .
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
3. **Run SimhaCLI**
|
|
182
|
+
|
|
183
|
+
```bash
|
|
184
|
+
simhacli
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
### Configuration
|
|
188
|
+
|
|
189
|
+
Set up your API credentials using one of these methods:
|
|
190
|
+
|
|
191
|
+
**Method 1: Environment Variables**
|
|
192
|
+
|
|
193
|
+
```bash
|
|
194
|
+
# Windows (PowerShell)
|
|
195
|
+
$env:API_KEY = "your_api_key_here"
|
|
196
|
+
$env:API_BASE_URL = "https://generativelanguage.googleapis.com/v1beta/openai"
|
|
197
|
+
|
|
198
|
+
# Windows (CMD)
|
|
199
|
+
set API_KEY=your_api_key_here
|
|
200
|
+
set API_BASE_URL=https://generativelanguage.googleapis.com/v1beta/openai
|
|
201
|
+
|
|
202
|
+
# Linux/Mac
|
|
203
|
+
export API_KEY=your_api_key_here
|
|
204
|
+
export API_BASE_URL=https://generativelanguage.googleapis.com/v1beta/openai
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
**Method 2: Config File**
|
|
208
|
+
|
|
209
|
+
```bash
|
|
210
|
+
# Create config directory
|
|
211
|
+
mkdir -p ~/.simhacli
|
|
212
|
+
|
|
213
|
+
# Create/edit config.toml
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
---
|
simhacli-1.0.0/README.md
ADDED
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
<div align="center">
|
|
2
|
+
|
|
3
|
+
# 🦁 SimhaCLI
|
|
4
|
+
|
|
5
|
+
### AI-Powered Coding Agent for Your Terminal
|
|
6
|
+
|
|
7
|
+
[](https://www.python.org/downloads/)
|
|
8
|
+
[](https://opensource.org/licenses/MIT)
|
|
9
|
+
[](https://github.com/psf/black)
|
|
10
|
+
|
|
11
|
+
_Built by Narasimha Naidu Korrapti_
|
|
12
|
+
|
|
13
|
+
[Features](#-features) • [Installation](#-installation)
|
|
14
|
+
|
|
15
|
+
</div>
|
|
16
|
+
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
## 📖 Overview
|
|
20
|
+
|
|
21
|
+
**SimhaCLI** is a powerful terminal-based AI coding agent that brings the intelligence of Large Language Models directly into your development workflow. It seamlessly integrates with your codebase, understands context, and executes actions through a comprehensive set of builtin tools.
|
|
22
|
+
|
|
23
|
+
### Why SimhaCLI?
|
|
24
|
+
|
|
25
|
+
- 🚀 **Session-Based Architecture**: Persistent context and memory across interactions
|
|
26
|
+
- 🛠️ **11 Builtin Tools**: File operations, shell commands, web access, task management
|
|
27
|
+
- 🔒 **Safety First**: Shell command blocking prevents dangerous operations
|
|
28
|
+
- 💾 **Persistent Memory**: Remember user preferences and context
|
|
29
|
+
- 🎨 **Beautiful TUI**: Rich terminal interface with syntax highlighting
|
|
30
|
+
- ⚡ **Streaming Responses**: Real-time output as the agent thinks
|
|
31
|
+
- 🔄 **Event-Driven**: Observable agent actions with full transparency
|
|
32
|
+
|
|
33
|
+
---
|
|
34
|
+
|
|
35
|
+
## ✨ Features
|
|
36
|
+
|
|
37
|
+
### 🤖 Intelligent Agent
|
|
38
|
+
|
|
39
|
+
- **Agentic Loop**: Autonomous multi-turn conversations with tool usage
|
|
40
|
+
- **Context Management**: Tracks conversation history with token counting
|
|
41
|
+
- **Turn Tracking**: Session-based state management with UUIDs
|
|
42
|
+
- **Streaming Output**: Real-time response generation
|
|
43
|
+
|
|
44
|
+
### 🛠️ Comprehensive Toolset
|
|
45
|
+
|
|
46
|
+
11 builtin tools across 5 categories:
|
|
47
|
+
|
|
48
|
+
- **📖 Read**: `read_file`, `list_dir`, `glob`, `grep`
|
|
49
|
+
- **✏️ Write**: `write_file`, `edit_file`
|
|
50
|
+
- **🖥️ Shell**: `shell` (with 40+ blocked dangerous commands)
|
|
51
|
+
- **🌐 Web**: `web_search`, `web_fetch`
|
|
52
|
+
- **💾 Memory**: `todos` (task management), `memory` (persistent storage)
|
|
53
|
+
|
|
54
|
+
### 🔒 Safety & Security
|
|
55
|
+
|
|
56
|
+
- Command blocking for dangerous operations (rm -rf, format, etc.)
|
|
57
|
+
- Timeout protection on shell commands (120s default, 600s max)
|
|
58
|
+
- File validation and error handling
|
|
59
|
+
- Configurable working directory restrictions
|
|
60
|
+
|
|
61
|
+
### 💾 Persistent Storage
|
|
62
|
+
|
|
63
|
+
- **User Memory**: JSON-based key-value storage (`~/.simhacli/user_memory.json`)
|
|
64
|
+
- **Configuration**: System and project-level TOML configs
|
|
65
|
+
- **Session Tracking**: UUID-based session management with timestamps
|
|
66
|
+
|
|
67
|
+
### 🎨 Rich Terminal UI
|
|
68
|
+
|
|
69
|
+
- Syntax-highlighted code display
|
|
70
|
+
- Color-coded tool execution (cyan=read, yellow=write, white=shell)
|
|
71
|
+
- Live streaming text output
|
|
72
|
+
- Beautiful welcome banner and formatting
|
|
73
|
+
- Error panels with detailed information
|
|
74
|
+
|
|
75
|
+
---
|
|
76
|
+
|
|
77
|
+
## 🚀 Installation
|
|
78
|
+
|
|
79
|
+
### Prerequisites
|
|
80
|
+
|
|
81
|
+
- **Python 3.10+**
|
|
82
|
+
- **API Key** (OpenAI, Gemini, or compatible API)
|
|
83
|
+
|
|
84
|
+
### Option 1: Install from PyPI (Recommended)
|
|
85
|
+
|
|
86
|
+
```bash
|
|
87
|
+
pip install simhacli
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
After installation, you can run SimhaCLI from anywhere:
|
|
91
|
+
|
|
92
|
+
```bash
|
|
93
|
+
simhacli # Start interactive mode
|
|
94
|
+
simhacli "explain this code" # Run a single prompt
|
|
95
|
+
simhacli --help # Show help
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
### Option 2: Install from Source
|
|
99
|
+
|
|
100
|
+
1. **Clone the repository**
|
|
101
|
+
|
|
102
|
+
```bash
|
|
103
|
+
git clone https://github.com/naidu199/SimhaCLI.git
|
|
104
|
+
cd SimhaCLI
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
2. **Install globally (access from anywhere)**
|
|
108
|
+
|
|
109
|
+
```bash
|
|
110
|
+
pip install -e .
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
Or install without editable mode:
|
|
114
|
+
|
|
115
|
+
```bash
|
|
116
|
+
pip install .
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
3. **Now you can use `simhacli` from anywhere:**
|
|
120
|
+
|
|
121
|
+
```bash
|
|
122
|
+
simhacli # Start interactive mode
|
|
123
|
+
simhacli "your prompt" # Run a single command
|
|
124
|
+
simhacli --cwd /path/to/dir # Set working directory
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
### Option 3: Development Setup
|
|
128
|
+
|
|
129
|
+
1. **Clone and create virtual environment**
|
|
130
|
+
|
|
131
|
+
```bash
|
|
132
|
+
git clone https://github.com/naidu199/SimhaCLI.git
|
|
133
|
+
cd SimhaCLI
|
|
134
|
+
python -m venv venv
|
|
135
|
+
|
|
136
|
+
# Windows
|
|
137
|
+
venv\Scripts\activate
|
|
138
|
+
|
|
139
|
+
# Linux/Mac
|
|
140
|
+
source venv/bin/activate
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
2. **Install in editable mode**
|
|
144
|
+
|
|
145
|
+
```bash
|
|
146
|
+
pip install -e .
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
3. **Run SimhaCLI**
|
|
150
|
+
|
|
151
|
+
```bash
|
|
152
|
+
simhacli
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
### Configuration
|
|
156
|
+
|
|
157
|
+
Set up your API credentials using one of these methods:
|
|
158
|
+
|
|
159
|
+
**Method 1: Environment Variables**
|
|
160
|
+
|
|
161
|
+
```bash
|
|
162
|
+
# Windows (PowerShell)
|
|
163
|
+
$env:API_KEY = "your_api_key_here"
|
|
164
|
+
$env:API_BASE_URL = "https://generativelanguage.googleapis.com/v1beta/openai"
|
|
165
|
+
|
|
166
|
+
# Windows (CMD)
|
|
167
|
+
set API_KEY=your_api_key_here
|
|
168
|
+
set API_BASE_URL=https://generativelanguage.googleapis.com/v1beta/openai
|
|
169
|
+
|
|
170
|
+
# Linux/Mac
|
|
171
|
+
export API_KEY=your_api_key_here
|
|
172
|
+
export API_BASE_URL=https://generativelanguage.googleapis.com/v1beta/openai
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
**Method 2: Config File**
|
|
176
|
+
|
|
177
|
+
```bash
|
|
178
|
+
# Create config directory
|
|
179
|
+
mkdir -p ~/.simhacli
|
|
180
|
+
|
|
181
|
+
# Create/edit config.toml
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
---
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import AsyncGenerator, Awaitable, Callable
|
|
4
|
+
|
|
5
|
+
from agent.events import AgentEvent, AgentEventType
|
|
6
|
+
from agent.session import Session
|
|
7
|
+
from client.response import (
|
|
8
|
+
StreamEventType,
|
|
9
|
+
TokenUsage,
|
|
10
|
+
ToolCall,
|
|
11
|
+
ToolResultMessage,
|
|
12
|
+
parse_tool_call_arguments,
|
|
13
|
+
)
|
|
14
|
+
from config.config import Config
|
|
15
|
+
from prompts.system import create_loop_breaker_prompt
|
|
16
|
+
from tools.base import ToolConfirmation
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class Agent:
|
|
20
|
+
def __init__(
|
|
21
|
+
self,
|
|
22
|
+
config: Config,
|
|
23
|
+
confirmation_callback: (
|
|
24
|
+
Callable[[ToolConfirmation], Awaitable[bool]] | None
|
|
25
|
+
) = None,
|
|
26
|
+
) -> None:
|
|
27
|
+
self.config = config
|
|
28
|
+
# self.client = LLMClient(config=self.config)
|
|
29
|
+
# self.context_manager = ContextManager(config=self.config)
|
|
30
|
+
# self.tool_registry = create_default_registry(config=self.config)
|
|
31
|
+
self.session: Session | None = Session(config=self.config)
|
|
32
|
+
|
|
33
|
+
self.session.approval_manager.confirmation_callback = confirmation_callback
|
|
34
|
+
|
|
35
|
+
async def run(self, message: str) -> AsyncGenerator[AgentEvent, None]:
|
|
36
|
+
await self.session.hook_system.trigger_before_agent(message)
|
|
37
|
+
yield AgentEvent.agent_start(message=message)
|
|
38
|
+
self.session.context_manager.add_user_message(message)
|
|
39
|
+
|
|
40
|
+
# add user message to the context
|
|
41
|
+
final_message: str | None = None
|
|
42
|
+
try:
|
|
43
|
+
async for event in self._agentic_loop():
|
|
44
|
+
yield event
|
|
45
|
+
if event.type == AgentEventType.TEXT_COMPLETE:
|
|
46
|
+
final_message = event.data.get("content", "")
|
|
47
|
+
# yield AgentEvent.text_complete(content=final_message)
|
|
48
|
+
await self.session.hook_system.trigger_after_agent(
|
|
49
|
+
user_message=message,
|
|
50
|
+
agent_response=final_message or "",
|
|
51
|
+
)
|
|
52
|
+
yield AgentEvent.agent_end(response=final_message)
|
|
53
|
+
except Exception as e:
|
|
54
|
+
yield AgentEvent.agent_error(f"Agent encountered an error: {str(e)}")
|
|
55
|
+
await self.session.hook_system.trigger_on_error(e)
|
|
56
|
+
|
|
57
|
+
async def _agentic_loop(self) -> AsyncGenerator[AgentEvent, None]:
|
|
58
|
+
|
|
59
|
+
max_turns = self.config.max_turns
|
|
60
|
+
|
|
61
|
+
for turn_no in range(max_turns):
|
|
62
|
+
self.session.increment_turn_count()
|
|
63
|
+
response_text = ""
|
|
64
|
+
usage: TokenUsage | None = None
|
|
65
|
+
# checking for the context overflow and trimming if necessary
|
|
66
|
+
if self.session.context_manager.needs_compression():
|
|
67
|
+
summary, usage = await self.session.chat_compressor.compress(
|
|
68
|
+
self.session.context_manager
|
|
69
|
+
)
|
|
70
|
+
if summary:
|
|
71
|
+
self.session.context_manager.replace_with_summary(summary)
|
|
72
|
+
self.session.context_manager.set_latest_usage(usage)
|
|
73
|
+
self.session.context_manager.add_usage(usage)
|
|
74
|
+
tool_schema = self.session.tool_registry.get_schemas()
|
|
75
|
+
tool_calls: list[ToolCall] = []
|
|
76
|
+
has_error = False
|
|
77
|
+
async for event in self.session.client.chat_completion(
|
|
78
|
+
self.session.context_manager.get_messages(),
|
|
79
|
+
tools=tool_schema if tool_schema else None,
|
|
80
|
+
):
|
|
81
|
+
|
|
82
|
+
if event.type == StreamEventType.TEXT_DELTA:
|
|
83
|
+
content = event.text_delta.content if event.text_delta else ""
|
|
84
|
+
response_text += content
|
|
85
|
+
yield AgentEvent.text_delta(content)
|
|
86
|
+
elif event.type == StreamEventType.TOOL_CALL_COMPLETE:
|
|
87
|
+
if event.tool_call:
|
|
88
|
+
tool_calls.append(event.tool_call)
|
|
89
|
+
elif event.type == StreamEventType.ERROR:
|
|
90
|
+
error_msg = event.error if event.error else "Unknown error"
|
|
91
|
+
has_error = True
|
|
92
|
+
yield AgentEvent.agent_error(error_msg)
|
|
93
|
+
return # Stop processing on error
|
|
94
|
+
elif event.type == StreamEventType.MESSAGE_COMPLETE:
|
|
95
|
+
usage = event.usage
|
|
96
|
+
# Only yield text_complete once at the end with full response
|
|
97
|
+
yield AgentEvent.text_complete(content=response_text)
|
|
98
|
+
|
|
99
|
+
# Convert tool_calls to the format expected by the API
|
|
100
|
+
tool_calls_dict = (
|
|
101
|
+
[
|
|
102
|
+
{
|
|
103
|
+
"id": tc.call_id,
|
|
104
|
+
"type": "function",
|
|
105
|
+
"function": {
|
|
106
|
+
"name": tc.name,
|
|
107
|
+
"arguments": tc.arguments,
|
|
108
|
+
},
|
|
109
|
+
}
|
|
110
|
+
for tc in tool_calls
|
|
111
|
+
]
|
|
112
|
+
if tool_calls
|
|
113
|
+
else None
|
|
114
|
+
)
|
|
115
|
+
|
|
116
|
+
self.session.context_manager.add_assistant_message(
|
|
117
|
+
response_text or "", tool_calls=tool_calls_dict
|
|
118
|
+
)
|
|
119
|
+
|
|
120
|
+
if response_text:
|
|
121
|
+
yield AgentEvent.text_complete(response_text)
|
|
122
|
+
self.session.loop_detector.record_action(
|
|
123
|
+
"response",
|
|
124
|
+
text=response_text,
|
|
125
|
+
)
|
|
126
|
+
|
|
127
|
+
if not tool_calls:
|
|
128
|
+
if usage:
|
|
129
|
+
self.session.context_manager.set_latest_usage(usage)
|
|
130
|
+
self.session.context_manager.add_usage(usage)
|
|
131
|
+
|
|
132
|
+
self.session.context_manager.prune_tool_outputs()
|
|
133
|
+
|
|
134
|
+
return # No tool calls, end the loop
|
|
135
|
+
|
|
136
|
+
tool_call_results: list[ToolResultMessage] = []
|
|
137
|
+
|
|
138
|
+
for tool_call in tool_calls:
|
|
139
|
+
parsed_args = parse_tool_call_arguments(tool_call.arguments or "")
|
|
140
|
+
|
|
141
|
+
yield AgentEvent.tool_call_start(
|
|
142
|
+
call_id=tool_call.call_id,
|
|
143
|
+
name=tool_call.name,
|
|
144
|
+
arguments=parsed_args,
|
|
145
|
+
)
|
|
146
|
+
|
|
147
|
+
self.session.loop_detector.record_action(
|
|
148
|
+
"tool_call",
|
|
149
|
+
tool_name=tool_call.name,
|
|
150
|
+
args=parse_tool_call_arguments(tool_call.arguments or ""),
|
|
151
|
+
)
|
|
152
|
+
|
|
153
|
+
result = await self.session.tool_registry.invoke(
|
|
154
|
+
tool_call.name or "",
|
|
155
|
+
parsed_args,
|
|
156
|
+
self.config.cwd,
|
|
157
|
+
self.session.approval_manager,
|
|
158
|
+
self.session.hook_system,
|
|
159
|
+
)
|
|
160
|
+
|
|
161
|
+
yield AgentEvent.tool_call_complete(
|
|
162
|
+
tool_call.call_id,
|
|
163
|
+
tool_call.name,
|
|
164
|
+
result,
|
|
165
|
+
)
|
|
166
|
+
tool_call_results.append(
|
|
167
|
+
ToolResultMessage(
|
|
168
|
+
tool_call_id=tool_call.call_id,
|
|
169
|
+
content=result.to_model_output(),
|
|
170
|
+
is_error=not result.success,
|
|
171
|
+
)
|
|
172
|
+
)
|
|
173
|
+
|
|
174
|
+
for tool_result in tool_call_results:
|
|
175
|
+
self.session.context_manager.add_tool_result(
|
|
176
|
+
tool_result.tool_call_id,
|
|
177
|
+
tool_result.content,
|
|
178
|
+
)
|
|
179
|
+
loop_detector_value = self.session.loop_detector.check_for_loop()
|
|
180
|
+
if loop_detector_value:
|
|
181
|
+
loop_prompt = create_loop_breaker_prompt(
|
|
182
|
+
loop_description=loop_detector_value,
|
|
183
|
+
)
|
|
184
|
+
self.session.context_manager.add_user_message(loop_prompt)
|
|
185
|
+
continue # Skip executing the tool and continue the loop
|
|
186
|
+
if usage:
|
|
187
|
+
self.session.context_manager.set_latest_usage(usage)
|
|
188
|
+
self.session.context_manager.add_usage(usage)
|
|
189
|
+
|
|
190
|
+
self.session.context_manager.prune_tool_outputs()
|
|
191
|
+
|
|
192
|
+
yield AgentEvent.agent_error(
|
|
193
|
+
f"Maximum number of turns {self.config.max_turns} reached."
|
|
194
|
+
)
|
|
195
|
+
|
|
196
|
+
async def __aenter__(self) -> Agent:
|
|
197
|
+
await self.session.initialize()
|
|
198
|
+
return self
|
|
199
|
+
|
|
200
|
+
async def __aexit__(self, exc_type, exc, tb) -> None:
|
|
201
|
+
if self.session and self.session.client and self.session.mcp_manager:
|
|
202
|
+
await self.session.client.close_client()
|
|
203
|
+
await self.session.mcp_manager.shutdown_mcp()
|
|
204
|
+
self.session.client = None
|
|
205
|
+
self.session = None
|