code-lm 0.3.0__tar.gz → 0.3.2__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.
- {code_lm-0.3.0/src/code_lm.egg-info → code_lm-0.3.2}/PKG-INFO +20 -8
- {code_lm-0.3.0 → code_lm-0.3.2}/README.md +19 -7
- {code_lm-0.3.0 → code_lm-0.3.2}/pyproject.toml +1 -1
- {code_lm-0.3.0 → code_lm-0.3.2/src/code_lm.egg-info}/PKG-INFO +20 -8
- {code_lm-0.3.0 → code_lm-0.3.2}/src/code_lm.egg-info/SOURCES.txt +1 -1
- {code_lm-0.3.0 → code_lm-0.3.2}/src/lm_code/main.py +240 -2
- code_lm-0.3.2/src/lm_code/session.py +283 -0
- {code_lm-0.3.0 → code_lm-0.3.2}/src/lm_code/utils.py +1 -3
- code_lm-0.3.0/src/lm_code/models/gemini.py +0 -43
- {code_lm-0.3.0 → code_lm-0.3.2}/MANIFEST.in +0 -0
- {code_lm-0.3.0 → code_lm-0.3.2}/setup.cfg +0 -0
- {code_lm-0.3.0 → code_lm-0.3.2}/setup.py +0 -0
- {code_lm-0.3.0 → code_lm-0.3.2}/src/code_lm.egg-info/dependency_links.txt +0 -0
- {code_lm-0.3.0 → code_lm-0.3.2}/src/code_lm.egg-info/entry_points.txt +0 -0
- {code_lm-0.3.0 → code_lm-0.3.2}/src/code_lm.egg-info/requires.txt +0 -0
- {code_lm-0.3.0 → code_lm-0.3.2}/src/code_lm.egg-info/top_level.txt +0 -0
- {code_lm-0.3.0 → code_lm-0.3.2}/src/lm_code/__init__.py +0 -0
- {code_lm-0.3.0 → code_lm-0.3.2}/src/lm_code/config.py +0 -0
- {code_lm-0.3.0 → code_lm-0.3.2}/src/lm_code/models/__init__.py +0 -0
- {code_lm-0.3.0 → code_lm-0.3.2}/src/lm_code/models/openrouter.py +0 -0
- {code_lm-0.3.0 → code_lm-0.3.2}/src/lm_code/tools/__init__.py +0 -0
- {code_lm-0.3.0 → code_lm-0.3.2}/src/lm_code/tools/base.py +0 -0
- {code_lm-0.3.0 → code_lm-0.3.2}/src/lm_code/tools/directory_tools.py +0 -0
- {code_lm-0.3.0 → code_lm-0.3.2}/src/lm_code/tools/file_tools.py +0 -0
- {code_lm-0.3.0 → code_lm-0.3.2}/src/lm_code/tools/quality_tools.py +0 -0
- {code_lm-0.3.0 → code_lm-0.3.2}/src/lm_code/tools/summarizer_tool.py +0 -0
- {code_lm-0.3.0 → code_lm-0.3.2}/src/lm_code/tools/system_tools.py +0 -0
- {code_lm-0.3.0 → code_lm-0.3.2}/src/lm_code/tools/task_complete_tool.py +0 -0
- {code_lm-0.3.0 → code_lm-0.3.2}/src/lm_code/tools/test_runner.py +0 -0
- {code_lm-0.3.0 → code_lm-0.3.2}/src/lm_code/tools/tree_tool.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: code-lm
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.2
|
|
4
4
|
Summary: An AI coding assistant using various LLM models.
|
|
5
5
|
Home-page: https://github.com/Panagiotis897/lm-code
|
|
6
6
|
Author: Panagiotis897
|
|
@@ -39,6 +39,10 @@ LM Code is a powerful AI coding assistant for your terminal supporting 17 free m
|
|
|
39
39
|
- Markdown rendering for improved readability.
|
|
40
40
|
- **17 Free Models via OpenRouter**:
|
|
41
41
|
- NVIDIA Nemotron, Qwen, OpenAI, Meta Llama, Mistral, and more.
|
|
42
|
+
- **Session Persistence**:
|
|
43
|
+
- Conversations are saved per project directory.
|
|
44
|
+
- Resume previous sessions automatically or manually with `/load`.
|
|
45
|
+
- Auto-saves after each exchange.
|
|
42
46
|
- **Automated Tool Usage**:
|
|
43
47
|
- File operations: `view`, `edit`, `grep`, `glob`.
|
|
44
48
|
- Directory operations: `ls`, `tree`, `create_directory`.
|
|
@@ -132,8 +136,12 @@ lmcode list-models
|
|
|
132
136
|
|
|
133
137
|
During an interactive session:
|
|
134
138
|
|
|
135
|
-
- **`/exit`**:
|
|
139
|
+
- **`/exit`**: Save session and exit.
|
|
136
140
|
- **`/help`**: Display help information.
|
|
141
|
+
- **`/sessions`**: List all sessions for the current project.
|
|
142
|
+
- **`/load <id>`**: Load a session by ID.
|
|
143
|
+
- **`/new`**: Start a new session (saves current first).
|
|
144
|
+
- **`/save`**: Manually save the current session.
|
|
137
145
|
|
|
138
146
|
---
|
|
139
147
|
|
|
@@ -153,10 +161,16 @@ LM Code is under active development. Contributions, feature requests, and feedba
|
|
|
153
161
|
|
|
154
162
|
### Changelog
|
|
155
163
|
|
|
164
|
+
#### v0.3.2
|
|
165
|
+
- Added session persistence: conversations are saved per project directory.
|
|
166
|
+
- Sessions auto-save after each exchange and on exit.
|
|
167
|
+
- New commands: `/sessions`, `/load <id>`, `/new`, `/save`.
|
|
168
|
+
- On startup, offers to resume previous sessions for the current project.
|
|
169
|
+
|
|
156
170
|
#### v0.3.0
|
|
157
171
|
- Updated default model to NVIDIA Nemotron 3 Super 120B.
|
|
158
172
|
- Added 17 free models from OpenRouter (previously 6).
|
|
159
|
-
- Fixed `ModuleNotFoundError: No module named 'gemini_cli'` from stale entry point.
|
|
173
|
+
- Fixed `ModuleNotFoundError: No module named 'gemini_cli'` from stale entry point after package rename.
|
|
160
174
|
- Fixed `UnicodeDecodeError` on Windows (cp1253) for all subprocess commands.
|
|
161
175
|
- Fixed API URL (`/chat/completions` was missing) causing HTML response errors.
|
|
162
176
|
- Improved API error handling for empty/invalid responses.
|
|
@@ -168,20 +182,18 @@ LM Code is under active development. Contributions, feature requests, and feedba
|
|
|
168
182
|
#### v0.2.5
|
|
169
183
|
- Added more models to the model list.
|
|
170
184
|
- Fixed crucial bugs from previous versions.
|
|
171
|
-
- Removed Gemini
|
|
185
|
+
- Removed legacy Gemini module.
|
|
172
186
|
- Updated models to latest versions.
|
|
173
187
|
|
|
174
188
|
#### v0.1.0
|
|
175
|
-
- Rebranded
|
|
189
|
+
- Rebranded to LM Code.
|
|
176
190
|
- Integrated OpenRouter as the default provider.
|
|
177
191
|
- Added multi-model support.
|
|
178
|
-
- Overhauled CLI commands
|
|
192
|
+
- Overhauled CLI commands.
|
|
179
193
|
|
|
180
194
|
---
|
|
181
195
|
|
|
182
196
|
## Future Plans
|
|
183
|
-
|
|
184
|
-
- Pricing with appropriate rate limits.
|
|
185
197
|
- Non-free model support.
|
|
186
198
|
- MCP Server integration.
|
|
187
199
|
- Additional providers.
|
|
@@ -11,6 +11,10 @@ LM Code is a powerful AI coding assistant for your terminal supporting 17 free m
|
|
|
11
11
|
- Markdown rendering for improved readability.
|
|
12
12
|
- **17 Free Models via OpenRouter**:
|
|
13
13
|
- NVIDIA Nemotron, Qwen, OpenAI, Meta Llama, Mistral, and more.
|
|
14
|
+
- **Session Persistence**:
|
|
15
|
+
- Conversations are saved per project directory.
|
|
16
|
+
- Resume previous sessions automatically or manually with `/load`.
|
|
17
|
+
- Auto-saves after each exchange.
|
|
14
18
|
- **Automated Tool Usage**:
|
|
15
19
|
- File operations: `view`, `edit`, `grep`, `glob`.
|
|
16
20
|
- Directory operations: `ls`, `tree`, `create_directory`.
|
|
@@ -104,8 +108,12 @@ lmcode list-models
|
|
|
104
108
|
|
|
105
109
|
During an interactive session:
|
|
106
110
|
|
|
107
|
-
- **`/exit`**:
|
|
111
|
+
- **`/exit`**: Save session and exit.
|
|
108
112
|
- **`/help`**: Display help information.
|
|
113
|
+
- **`/sessions`**: List all sessions for the current project.
|
|
114
|
+
- **`/load <id>`**: Load a session by ID.
|
|
115
|
+
- **`/new`**: Start a new session (saves current first).
|
|
116
|
+
- **`/save`**: Manually save the current session.
|
|
109
117
|
|
|
110
118
|
---
|
|
111
119
|
|
|
@@ -125,10 +133,16 @@ LM Code is under active development. Contributions, feature requests, and feedba
|
|
|
125
133
|
|
|
126
134
|
### Changelog
|
|
127
135
|
|
|
136
|
+
#### v0.3.2
|
|
137
|
+
- Added session persistence: conversations are saved per project directory.
|
|
138
|
+
- Sessions auto-save after each exchange and on exit.
|
|
139
|
+
- New commands: `/sessions`, `/load <id>`, `/new`, `/save`.
|
|
140
|
+
- On startup, offers to resume previous sessions for the current project.
|
|
141
|
+
|
|
128
142
|
#### v0.3.0
|
|
129
143
|
- Updated default model to NVIDIA Nemotron 3 Super 120B.
|
|
130
144
|
- Added 17 free models from OpenRouter (previously 6).
|
|
131
|
-
- Fixed `ModuleNotFoundError: No module named 'gemini_cli'` from stale entry point.
|
|
145
|
+
- Fixed `ModuleNotFoundError: No module named 'gemini_cli'` from stale entry point after package rename.
|
|
132
146
|
- Fixed `UnicodeDecodeError` on Windows (cp1253) for all subprocess commands.
|
|
133
147
|
- Fixed API URL (`/chat/completions` was missing) causing HTML response errors.
|
|
134
148
|
- Improved API error handling for empty/invalid responses.
|
|
@@ -140,20 +154,18 @@ LM Code is under active development. Contributions, feature requests, and feedba
|
|
|
140
154
|
#### v0.2.5
|
|
141
155
|
- Added more models to the model list.
|
|
142
156
|
- Fixed crucial bugs from previous versions.
|
|
143
|
-
- Removed Gemini
|
|
157
|
+
- Removed legacy Gemini module.
|
|
144
158
|
- Updated models to latest versions.
|
|
145
159
|
|
|
146
160
|
#### v0.1.0
|
|
147
|
-
- Rebranded
|
|
161
|
+
- Rebranded to LM Code.
|
|
148
162
|
- Integrated OpenRouter as the default provider.
|
|
149
163
|
- Added multi-model support.
|
|
150
|
-
- Overhauled CLI commands
|
|
164
|
+
- Overhauled CLI commands.
|
|
151
165
|
|
|
152
166
|
---
|
|
153
167
|
|
|
154
168
|
## Future Plans
|
|
155
|
-
|
|
156
|
-
- Pricing with appropriate rate limits.
|
|
157
169
|
- Non-free model support.
|
|
158
170
|
- MCP Server integration.
|
|
159
171
|
- Additional providers.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: code-lm
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.2
|
|
4
4
|
Summary: An AI coding assistant using various LLM models.
|
|
5
5
|
Home-page: https://github.com/Panagiotis897/lm-code
|
|
6
6
|
Author: Panagiotis897
|
|
@@ -39,6 +39,10 @@ LM Code is a powerful AI coding assistant for your terminal supporting 17 free m
|
|
|
39
39
|
- Markdown rendering for improved readability.
|
|
40
40
|
- **17 Free Models via OpenRouter**:
|
|
41
41
|
- NVIDIA Nemotron, Qwen, OpenAI, Meta Llama, Mistral, and more.
|
|
42
|
+
- **Session Persistence**:
|
|
43
|
+
- Conversations are saved per project directory.
|
|
44
|
+
- Resume previous sessions automatically or manually with `/load`.
|
|
45
|
+
- Auto-saves after each exchange.
|
|
42
46
|
- **Automated Tool Usage**:
|
|
43
47
|
- File operations: `view`, `edit`, `grep`, `glob`.
|
|
44
48
|
- Directory operations: `ls`, `tree`, `create_directory`.
|
|
@@ -132,8 +136,12 @@ lmcode list-models
|
|
|
132
136
|
|
|
133
137
|
During an interactive session:
|
|
134
138
|
|
|
135
|
-
- **`/exit`**:
|
|
139
|
+
- **`/exit`**: Save session and exit.
|
|
136
140
|
- **`/help`**: Display help information.
|
|
141
|
+
- **`/sessions`**: List all sessions for the current project.
|
|
142
|
+
- **`/load <id>`**: Load a session by ID.
|
|
143
|
+
- **`/new`**: Start a new session (saves current first).
|
|
144
|
+
- **`/save`**: Manually save the current session.
|
|
137
145
|
|
|
138
146
|
---
|
|
139
147
|
|
|
@@ -153,10 +161,16 @@ LM Code is under active development. Contributions, feature requests, and feedba
|
|
|
153
161
|
|
|
154
162
|
### Changelog
|
|
155
163
|
|
|
164
|
+
#### v0.3.2
|
|
165
|
+
- Added session persistence: conversations are saved per project directory.
|
|
166
|
+
- Sessions auto-save after each exchange and on exit.
|
|
167
|
+
- New commands: `/sessions`, `/load <id>`, `/new`, `/save`.
|
|
168
|
+
- On startup, offers to resume previous sessions for the current project.
|
|
169
|
+
|
|
156
170
|
#### v0.3.0
|
|
157
171
|
- Updated default model to NVIDIA Nemotron 3 Super 120B.
|
|
158
172
|
- Added 17 free models from OpenRouter (previously 6).
|
|
159
|
-
- Fixed `ModuleNotFoundError: No module named 'gemini_cli'` from stale entry point.
|
|
173
|
+
- Fixed `ModuleNotFoundError: No module named 'gemini_cli'` from stale entry point after package rename.
|
|
160
174
|
- Fixed `UnicodeDecodeError` on Windows (cp1253) for all subprocess commands.
|
|
161
175
|
- Fixed API URL (`/chat/completions` was missing) causing HTML response errors.
|
|
162
176
|
- Improved API error handling for empty/invalid responses.
|
|
@@ -168,20 +182,18 @@ LM Code is under active development. Contributions, feature requests, and feedba
|
|
|
168
182
|
#### v0.2.5
|
|
169
183
|
- Added more models to the model list.
|
|
170
184
|
- Fixed crucial bugs from previous versions.
|
|
171
|
-
- Removed Gemini
|
|
185
|
+
- Removed legacy Gemini module.
|
|
172
186
|
- Updated models to latest versions.
|
|
173
187
|
|
|
174
188
|
#### v0.1.0
|
|
175
|
-
- Rebranded
|
|
189
|
+
- Rebranded to LM Code.
|
|
176
190
|
- Integrated OpenRouter as the default provider.
|
|
177
191
|
- Added multi-model support.
|
|
178
|
-
- Overhauled CLI commands
|
|
192
|
+
- Overhauled CLI commands.
|
|
179
193
|
|
|
180
194
|
---
|
|
181
195
|
|
|
182
196
|
## Future Plans
|
|
183
|
-
|
|
184
|
-
- Pricing with appropriate rate limits.
|
|
185
197
|
- Non-free model support.
|
|
186
198
|
- MCP Server integration.
|
|
187
199
|
- Additional providers.
|
|
@@ -11,9 +11,9 @@ src/code_lm.egg-info/top_level.txt
|
|
|
11
11
|
src/lm_code/__init__.py
|
|
12
12
|
src/lm_code/config.py
|
|
13
13
|
src/lm_code/main.py
|
|
14
|
+
src/lm_code/session.py
|
|
14
15
|
src/lm_code/utils.py
|
|
15
16
|
src/lm_code/models/__init__.py
|
|
16
|
-
src/lm_code/models/gemini.py
|
|
17
17
|
src/lm_code/models/openrouter.py
|
|
18
18
|
src/lm_code/tools/__init__.py
|
|
19
19
|
src/lm_code/tools/base.py
|
|
@@ -10,14 +10,17 @@ from rich.console import Console
|
|
|
10
10
|
from rich.markdown import Markdown
|
|
11
11
|
from rich.panel import Panel
|
|
12
12
|
from pathlib import Path
|
|
13
|
+
from typing import Optional, Dict, Any
|
|
13
14
|
import yaml
|
|
14
15
|
import logging
|
|
15
16
|
import time
|
|
17
|
+
import questionary
|
|
16
18
|
|
|
17
19
|
from .models.openrouter import OpenRouterModel, list_available_models
|
|
18
20
|
from .config import Config
|
|
19
21
|
from .utils import count_tokens
|
|
20
22
|
from .tools import AVAILABLE_TOOLS
|
|
23
|
+
from .session import SessionManager
|
|
21
24
|
|
|
22
25
|
# Setup console and config
|
|
23
26
|
console = Console(
|
|
@@ -259,6 +262,13 @@ def start_interactive_session(model_name: str, console: Console):
|
|
|
259
262
|
)
|
|
260
263
|
return
|
|
261
264
|
|
|
265
|
+
# --- Session Persistence Setup ---
|
|
266
|
+
session_manager = SessionManager()
|
|
267
|
+
project_dir = os.getcwd()
|
|
268
|
+
current_session_id = None
|
|
269
|
+
current_model_name = model_name
|
|
270
|
+
# ---
|
|
271
|
+
|
|
262
272
|
try:
|
|
263
273
|
console.print(f"\nInitializing model [bold]{model_name}[/bold]...")
|
|
264
274
|
# Pass the console object to OpenRouterModel constructor
|
|
@@ -275,6 +285,63 @@ def start_interactive_session(model_name: str, console: Console):
|
|
|
275
285
|
)
|
|
276
286
|
return
|
|
277
287
|
|
|
288
|
+
# --- Check for existing sessions and offer to resume ---
|
|
289
|
+
existing_sessions = session_manager.list_sessions(project_dir)
|
|
290
|
+
if existing_sessions:
|
|
291
|
+
console.print(
|
|
292
|
+
f"\n[yellow]Found {len(existing_sessions)} previous session(s) for this project:[/yellow]"
|
|
293
|
+
)
|
|
294
|
+
for i, session in enumerate(existing_sessions[:5], 1):
|
|
295
|
+
updated = session.get("updated_at", "")[:19].replace("T", " ")
|
|
296
|
+
messages = session.get("message_count", 0)
|
|
297
|
+
model = session.get("model_name", "unknown")
|
|
298
|
+
console.print(f" {i}. [{updated}] {messages} messages (model: {model})")
|
|
299
|
+
|
|
300
|
+
resume_choice = questionary.select(
|
|
301
|
+
"Resume a previous session?",
|
|
302
|
+
choices=["Start new session", "Resume latest", "Choose session"],
|
|
303
|
+
default="Start new session",
|
|
304
|
+
).ask()
|
|
305
|
+
|
|
306
|
+
if resume_choice == "Resume latest":
|
|
307
|
+
latest_session = session_manager.get_latest_session(project_dir)
|
|
308
|
+
if latest_session:
|
|
309
|
+
model.chat_history = latest_session["chat_history"]
|
|
310
|
+
current_session_id = latest_session["id"]
|
|
311
|
+
current_model_name = latest_session.get("model_name", model_name)
|
|
312
|
+
console.print(
|
|
313
|
+
f"[green]Resumed session {current_session_id} ({len(model.chat_history)} messages)[/green]\n"
|
|
314
|
+
)
|
|
315
|
+
else:
|
|
316
|
+
console.print("[yellow]No session to resume. Starting new.[/yellow]\n")
|
|
317
|
+
elif resume_choice == "Choose session":
|
|
318
|
+
session_choices = [
|
|
319
|
+
f"{s['id']} ({s['message_count']} msgs, {s['updated_at'][:16]})"
|
|
320
|
+
for s in existing_sessions[:10]
|
|
321
|
+
]
|
|
322
|
+
session_choices.append("Cancel - start new session")
|
|
323
|
+
chosen = questionary.select(
|
|
324
|
+
"Select a session to resume:",
|
|
325
|
+
choices=session_choices,
|
|
326
|
+
).ask()
|
|
327
|
+
if chosen and chosen != "Cancel - start new session":
|
|
328
|
+
chosen_id = chosen.split(" (")[0]
|
|
329
|
+
loaded_session = session_manager.load_session(project_dir, chosen_id)
|
|
330
|
+
if loaded_session:
|
|
331
|
+
model.chat_history = loaded_session["chat_history"]
|
|
332
|
+
current_session_id = loaded_session["id"]
|
|
333
|
+
current_model_name = loaded_session.get("model_name", model_name)
|
|
334
|
+
console.print(
|
|
335
|
+
f"[green]Resumed session {current_session_id} ({len(model.chat_history)} messages)[/green]\n"
|
|
336
|
+
)
|
|
337
|
+
else:
|
|
338
|
+
console.print("[red]Failed to load session. Starting new.[/red]\n")
|
|
339
|
+
else:
|
|
340
|
+
console.print("[yellow]Starting new session.[/yellow]\n")
|
|
341
|
+
else:
|
|
342
|
+
console.print("[yellow]Starting new session.[/yellow]\n")
|
|
343
|
+
# --- End Session Resume Check ---
|
|
344
|
+
|
|
278
345
|
# --- Session Start Message ---
|
|
279
346
|
console.print("Type '/help' for commands, '/exit' or Ctrl+C to quit.")
|
|
280
347
|
|
|
@@ -283,10 +350,64 @@ def start_interactive_session(model_name: str, console: Console):
|
|
|
283
350
|
user_input = console.input("[bold green]You:[/bold green] ")
|
|
284
351
|
|
|
285
352
|
if user_input.lower() == "/exit":
|
|
353
|
+
# Save session before exiting
|
|
354
|
+
_save_current_session(
|
|
355
|
+
session_manager,
|
|
356
|
+
project_dir,
|
|
357
|
+
model,
|
|
358
|
+
current_model_name,
|
|
359
|
+
current_session_id,
|
|
360
|
+
console,
|
|
361
|
+
)
|
|
286
362
|
break
|
|
287
363
|
elif user_input.lower() == "/help":
|
|
288
364
|
show_help()
|
|
289
365
|
continue
|
|
366
|
+
elif user_input.lower() == "/sessions":
|
|
367
|
+
_show_sessions(session_manager, project_dir, console)
|
|
368
|
+
continue
|
|
369
|
+
elif user_input.lower().startswith("/load "):
|
|
370
|
+
session_id = user_input[6:].strip()
|
|
371
|
+
loaded = _load_session_by_id(
|
|
372
|
+
session_manager, project_dir, session_id, model, console
|
|
373
|
+
)
|
|
374
|
+
if loaded:
|
|
375
|
+
current_session_id = loaded["id"]
|
|
376
|
+
current_model_name = loaded.get("model_name", model_name)
|
|
377
|
+
continue
|
|
378
|
+
elif user_input.lower() == "/new":
|
|
379
|
+
# Save current session first
|
|
380
|
+
_save_current_session(
|
|
381
|
+
session_manager,
|
|
382
|
+
project_dir,
|
|
383
|
+
model,
|
|
384
|
+
current_model_name,
|
|
385
|
+
current_session_id,
|
|
386
|
+
console,
|
|
387
|
+
)
|
|
388
|
+
# Reset chat history
|
|
389
|
+
model.chat_history = [
|
|
390
|
+
{"role": "system", "content": model.system_instruction},
|
|
391
|
+
{
|
|
392
|
+
"role": "assistant",
|
|
393
|
+
"content": "Okay, I'm ready. Provide the directory context and your request.",
|
|
394
|
+
},
|
|
395
|
+
]
|
|
396
|
+
current_session_id = None
|
|
397
|
+
console.print("[green]Started new session.[/green]\n")
|
|
398
|
+
continue
|
|
399
|
+
elif user_input.lower() == "/save":
|
|
400
|
+
session_id = _save_current_session(
|
|
401
|
+
session_manager,
|
|
402
|
+
project_dir,
|
|
403
|
+
model,
|
|
404
|
+
current_model_name,
|
|
405
|
+
current_session_id,
|
|
406
|
+
console,
|
|
407
|
+
)
|
|
408
|
+
if session_id:
|
|
409
|
+
current_session_id = session_id
|
|
410
|
+
continue
|
|
290
411
|
|
|
291
412
|
# Display initial "thinking" status - generate handles intermediate ones
|
|
292
413
|
response_text = model.generate(user_input)
|
|
@@ -302,7 +423,26 @@ def start_interactive_session(model_name: str, console: Console):
|
|
|
302
423
|
console.print("[bold green]Assistant:[/bold green]")
|
|
303
424
|
console.print(Markdown(response_text), highlight=True)
|
|
304
425
|
|
|
426
|
+
# Auto-save after each exchange
|
|
427
|
+
current_session_id = _auto_save_session(
|
|
428
|
+
session_manager,
|
|
429
|
+
project_dir,
|
|
430
|
+
model,
|
|
431
|
+
current_model_name,
|
|
432
|
+
current_session_id,
|
|
433
|
+
console,
|
|
434
|
+
)
|
|
435
|
+
|
|
305
436
|
except KeyboardInterrupt:
|
|
437
|
+
# Save session before exiting
|
|
438
|
+
_save_current_session(
|
|
439
|
+
session_manager,
|
|
440
|
+
project_dir,
|
|
441
|
+
model,
|
|
442
|
+
current_model_name,
|
|
443
|
+
current_session_id,
|
|
444
|
+
console,
|
|
445
|
+
)
|
|
306
446
|
console.print("\n[yellow]Session interrupted. Exiting.[/yellow]")
|
|
307
447
|
break
|
|
308
448
|
except Exception as e:
|
|
@@ -312,6 +452,96 @@ def start_interactive_session(model_name: str, console: Console):
|
|
|
312
452
|
log.error("Error during interactive loop", exc_info=True)
|
|
313
453
|
|
|
314
454
|
|
|
455
|
+
def _save_current_session(
|
|
456
|
+
session_manager: SessionManager,
|
|
457
|
+
project_dir: str,
|
|
458
|
+
model: OpenRouterModel,
|
|
459
|
+
model_name: str,
|
|
460
|
+
session_id: Optional[str],
|
|
461
|
+
console: Console,
|
|
462
|
+
) -> Optional[str]:
|
|
463
|
+
"""Save the current session. Returns the session ID if saved."""
|
|
464
|
+
# Don't save if there are no user messages
|
|
465
|
+
user_messages = [msg for msg in model.chat_history if msg.get("role") == "user"]
|
|
466
|
+
if not user_messages:
|
|
467
|
+
return None
|
|
468
|
+
|
|
469
|
+
try:
|
|
470
|
+
saved_id = session_manager.save_session(
|
|
471
|
+
project_dir=project_dir,
|
|
472
|
+
chat_history=model.chat_history,
|
|
473
|
+
model_name=model_name,
|
|
474
|
+
session_id=session_id,
|
|
475
|
+
)
|
|
476
|
+
console.print(f"[dim]Session saved: {saved_id}[/dim]")
|
|
477
|
+
return saved_id
|
|
478
|
+
except Exception as e:
|
|
479
|
+
log.error(f"Failed to save session: {e}")
|
|
480
|
+
console.print(f"[red]Warning: Failed to save session: {e}[/red]")
|
|
481
|
+
return None
|
|
482
|
+
|
|
483
|
+
|
|
484
|
+
def _auto_save_session(
|
|
485
|
+
session_manager: SessionManager,
|
|
486
|
+
project_dir: str,
|
|
487
|
+
model: OpenRouterModel,
|
|
488
|
+
model_name: str,
|
|
489
|
+
session_id: Optional[str],
|
|
490
|
+
console: Console,
|
|
491
|
+
) -> Optional[str]:
|
|
492
|
+
"""Auto-save session silently. Returns the session ID."""
|
|
493
|
+
try:
|
|
494
|
+
saved_id = session_manager.save_session(
|
|
495
|
+
project_dir=project_dir,
|
|
496
|
+
chat_history=model.chat_history,
|
|
497
|
+
model_name=model_name,
|
|
498
|
+
session_id=session_id,
|
|
499
|
+
)
|
|
500
|
+
return saved_id
|
|
501
|
+
except Exception as e:
|
|
502
|
+
log.error(f"Auto-save failed: {e}")
|
|
503
|
+
return session_id
|
|
504
|
+
|
|
505
|
+
|
|
506
|
+
def _show_sessions(session_manager: SessionManager, project_dir: str, console: Console):
|
|
507
|
+
"""Display sessions for the current project."""
|
|
508
|
+
sessions = session_manager.list_sessions(project_dir)
|
|
509
|
+
if not sessions:
|
|
510
|
+
console.print("[yellow]No sessions found for this project.[/yellow]")
|
|
511
|
+
return
|
|
512
|
+
|
|
513
|
+
console.print(f"\n[bold cyan]Sessions for current project:[/bold cyan]")
|
|
514
|
+
for i, session in enumerate(sessions, 1):
|
|
515
|
+
updated = session.get("updated_at", "")[:19].replace("T", " ")
|
|
516
|
+
messages = session.get("message_count", 0)
|
|
517
|
+
model = session.get("model_name", "unknown")
|
|
518
|
+
console.print(
|
|
519
|
+
f" {i}. ID: [green]{session['id']}[/green] | {updated} | {messages} msgs | {model}"
|
|
520
|
+
)
|
|
521
|
+
console.print("\nUse '/load <session_id>' to resume a session.\n")
|
|
522
|
+
|
|
523
|
+
|
|
524
|
+
def _load_session_by_id(
|
|
525
|
+
session_manager: SessionManager,
|
|
526
|
+
project_dir: str,
|
|
527
|
+
session_id: str,
|
|
528
|
+
model: OpenRouterModel,
|
|
529
|
+
console: Console,
|
|
530
|
+
) -> Optional[Dict[str, Any]]:
|
|
531
|
+
"""Load a session by ID. Returns session data or None."""
|
|
532
|
+
loaded_session = session_manager.load_session(project_dir, session_id)
|
|
533
|
+
if loaded_session:
|
|
534
|
+
model.chat_history = loaded_session["chat_history"]
|
|
535
|
+
console.print(
|
|
536
|
+
f"[green]Loaded session {session_id} ({len(model.chat_history)} messages)[/green]\n"
|
|
537
|
+
)
|
|
538
|
+
return loaded_session
|
|
539
|
+
else:
|
|
540
|
+
console.print(f"[red]Session '{session_id}' not found.[/red]")
|
|
541
|
+
console.print("[yellow]Use '/sessions' to list available sessions.[/yellow]\n")
|
|
542
|
+
return None
|
|
543
|
+
|
|
544
|
+
|
|
315
545
|
def show_help():
|
|
316
546
|
"""Show help information for interactive mode."""
|
|
317
547
|
tool_list_formatted = ""
|
|
@@ -327,8 +557,12 @@ def show_help():
|
|
|
327
557
|
help_text = f""" [bold]Help[/bold]
|
|
328
558
|
|
|
329
559
|
[cyan]Interactive Commands:[/cyan]
|
|
330
|
-
/exit
|
|
331
|
-
/help
|
|
560
|
+
/exit - Save session and exit
|
|
561
|
+
/help - Show this help message
|
|
562
|
+
/sessions - List sessions for current project
|
|
563
|
+
/load <id> - Load a session by ID
|
|
564
|
+
/new - Start a new session (saves current first)
|
|
565
|
+
/save - Manually save current session
|
|
332
566
|
|
|
333
567
|
[cyan]CLI Commands:[/cyan]
|
|
334
568
|
lmcode setup KEY
|
|
@@ -338,6 +572,10 @@ def show_help():
|
|
|
338
572
|
|
|
339
573
|
[cyan]Workflow Hint:[/cyan] Analyze -> Plan -> Execute -> Verify -> Summarize
|
|
340
574
|
|
|
575
|
+
[cyan]Session Persistence:[/cyan]
|
|
576
|
+
Sessions are automatically saved per project directory.
|
|
577
|
+
Resume previous sessions with /load or on startup.
|
|
578
|
+
|
|
341
579
|
[cyan]Available Tools:[/cyan]
|
|
342
580
|
{tool_list_formatted}
|
|
343
581
|
"""
|
|
@@ -0,0 +1,283 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Session persistence for LM Code CLI.
|
|
3
|
+
Saves and loads conversation history per project directory.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
import json
|
|
7
|
+
import hashlib
|
|
8
|
+
import os
|
|
9
|
+
import logging
|
|
10
|
+
from datetime import datetime
|
|
11
|
+
from pathlib import Path
|
|
12
|
+
from typing import List, Dict, Optional, Any
|
|
13
|
+
|
|
14
|
+
log = logging.getLogger(__name__)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class SessionManager:
|
|
18
|
+
"""Manages session persistence for conversation history."""
|
|
19
|
+
|
|
20
|
+
def __init__(self, sessions_dir: Optional[str] = None):
|
|
21
|
+
self.sessions_dir = (
|
|
22
|
+
Path(sessions_dir)
|
|
23
|
+
if sessions_dir
|
|
24
|
+
else Path.home() / ".config" / "lm-code" / "sessions"
|
|
25
|
+
)
|
|
26
|
+
self.sessions_dir.mkdir(parents=True, exist_ok=True)
|
|
27
|
+
self.current_session_id = None
|
|
28
|
+
self.current_project_dir = None
|
|
29
|
+
|
|
30
|
+
def _get_project_hash(self, project_dir: str) -> str:
|
|
31
|
+
"""Generate a unique hash for a project directory path."""
|
|
32
|
+
normalized = os.path.normpath(os.path.abspath(project_dir))
|
|
33
|
+
return hashlib.sha256(normalized.encode()).hexdigest()[:16]
|
|
34
|
+
|
|
35
|
+
def _get_project_sessions_dir(self, project_dir: str) -> Path:
|
|
36
|
+
"""Get the sessions directory for a specific project."""
|
|
37
|
+
project_hash = self._get_project_hash(project_dir)
|
|
38
|
+
project_sessions_dir = self.sessions_dir / project_hash
|
|
39
|
+
project_sessions_dir.mkdir(parents=True, exist_ok=True)
|
|
40
|
+
return project_sessions_dir
|
|
41
|
+
|
|
42
|
+
def save_session(
|
|
43
|
+
self,
|
|
44
|
+
project_dir: str,
|
|
45
|
+
chat_history: List[Dict[str, Any]],
|
|
46
|
+
model_name: str,
|
|
47
|
+
session_id: Optional[str] = None,
|
|
48
|
+
) -> str:
|
|
49
|
+
"""
|
|
50
|
+
Save a session to disk.
|
|
51
|
+
|
|
52
|
+
Args:
|
|
53
|
+
project_dir: The project directory path
|
|
54
|
+
chat_history: The chat history list from OpenRouterModel
|
|
55
|
+
model_name: The model being used
|
|
56
|
+
session_id: Optional existing session ID to overwrite
|
|
57
|
+
|
|
58
|
+
Returns:
|
|
59
|
+
The session ID of the saved session
|
|
60
|
+
"""
|
|
61
|
+
if session_id is None:
|
|
62
|
+
session_id = datetime.now().strftime("%Y%m%d_%H%M%S")
|
|
63
|
+
|
|
64
|
+
project_sessions_dir = self._get_project_sessions_dir(project_dir)
|
|
65
|
+
|
|
66
|
+
session_data = {
|
|
67
|
+
"id": session_id,
|
|
68
|
+
"project_dir": os.path.abspath(project_dir),
|
|
69
|
+
"model_name": model_name,
|
|
70
|
+
"created_at": datetime.now().isoformat(),
|
|
71
|
+
"updated_at": datetime.now().isoformat(),
|
|
72
|
+
"chat_history": chat_history,
|
|
73
|
+
"message_count": len(
|
|
74
|
+
[
|
|
75
|
+
msg
|
|
76
|
+
for msg in chat_history
|
|
77
|
+
if msg.get("role") in ("user", "assistant")
|
|
78
|
+
]
|
|
79
|
+
),
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
session_file = project_sessions_dir / f"{session_id}.json"
|
|
83
|
+
|
|
84
|
+
try:
|
|
85
|
+
with open(session_file, "w", encoding="utf-8") as f:
|
|
86
|
+
json.dump(session_data, f, indent=2, ensure_ascii=False)
|
|
87
|
+
log.info(f"Session saved: {session_id} for project {project_dir}")
|
|
88
|
+
self.current_session_id = session_id
|
|
89
|
+
self.current_project_dir = project_dir
|
|
90
|
+
return session_id
|
|
91
|
+
except Exception as e:
|
|
92
|
+
log.error(f"Failed to save session: {e}", exc_info=True)
|
|
93
|
+
raise
|
|
94
|
+
|
|
95
|
+
def load_session(
|
|
96
|
+
self, project_dir: str, session_id: str
|
|
97
|
+
) -> Optional[Dict[str, Any]]:
|
|
98
|
+
"""
|
|
99
|
+
Load a session from disk.
|
|
100
|
+
|
|
101
|
+
Args:
|
|
102
|
+
project_dir: The project directory path
|
|
103
|
+
session_id: The session ID to load
|
|
104
|
+
|
|
105
|
+
Returns:
|
|
106
|
+
Session data dict or None if not found
|
|
107
|
+
"""
|
|
108
|
+
project_sessions_dir = self._get_project_sessions_dir(project_dir)
|
|
109
|
+
session_file = project_sessions_dir / f"{session_id}.json"
|
|
110
|
+
|
|
111
|
+
if not session_file.exists():
|
|
112
|
+
log.warning(f"Session file not found: {session_file}")
|
|
113
|
+
return None
|
|
114
|
+
|
|
115
|
+
try:
|
|
116
|
+
with open(session_file, "r", encoding="utf-8") as f:
|
|
117
|
+
session_data = json.load(f)
|
|
118
|
+
log.info(f"Session loaded: {session_id}")
|
|
119
|
+
self.current_session_id = session_id
|
|
120
|
+
self.current_project_dir = project_dir
|
|
121
|
+
return session_data
|
|
122
|
+
except Exception as e:
|
|
123
|
+
log.error(f"Failed to load session: {e}", exc_info=True)
|
|
124
|
+
return None
|
|
125
|
+
|
|
126
|
+
def list_sessions(self, project_dir: str) -> List[Dict[str, Any]]:
|
|
127
|
+
"""
|
|
128
|
+
List all sessions for a project directory.
|
|
129
|
+
|
|
130
|
+
Args:
|
|
131
|
+
project_dir: The project directory path
|
|
132
|
+
|
|
133
|
+
Returns:
|
|
134
|
+
List of session metadata dicts, sorted by most recent first
|
|
135
|
+
"""
|
|
136
|
+
project_sessions_dir = self._get_project_sessions_dir(project_dir)
|
|
137
|
+
sessions = []
|
|
138
|
+
|
|
139
|
+
for session_file in project_sessions_dir.glob("*.json"):
|
|
140
|
+
try:
|
|
141
|
+
with open(session_file, "r", encoding="utf-8") as f:
|
|
142
|
+
session_data = json.load(f)
|
|
143
|
+
sessions.append(
|
|
144
|
+
{
|
|
145
|
+
"id": session_data.get("id", session_file.stem),
|
|
146
|
+
"project_dir": session_data.get("project_dir", project_dir),
|
|
147
|
+
"model_name": session_data.get("model_name", "unknown"),
|
|
148
|
+
"created_at": session_data.get("created_at", ""),
|
|
149
|
+
"updated_at": session_data.get("updated_at", ""),
|
|
150
|
+
"message_count": session_data.get("message_count", 0),
|
|
151
|
+
}
|
|
152
|
+
)
|
|
153
|
+
except Exception as e:
|
|
154
|
+
log.warning(f"Failed to read session file {session_file}: {e}")
|
|
155
|
+
continue
|
|
156
|
+
|
|
157
|
+
# Sort by most recent first
|
|
158
|
+
sessions.sort(key=lambda s: s.get("updated_at", ""), reverse=True)
|
|
159
|
+
return sessions
|
|
160
|
+
|
|
161
|
+
def list_all_sessions(self) -> List[Dict[str, Any]]:
|
|
162
|
+
"""
|
|
163
|
+
List all sessions across all projects.
|
|
164
|
+
|
|
165
|
+
Returns:
|
|
166
|
+
List of session metadata dicts, sorted by most recent first
|
|
167
|
+
"""
|
|
168
|
+
all_sessions = []
|
|
169
|
+
|
|
170
|
+
for project_hash_dir in self.sessions_dir.iterdir():
|
|
171
|
+
if not project_hash_dir.is_dir():
|
|
172
|
+
continue
|
|
173
|
+
|
|
174
|
+
for session_file in project_hash_dir.glob("*.json"):
|
|
175
|
+
try:
|
|
176
|
+
with open(session_file, "r", encoding="utf-8") as f:
|
|
177
|
+
session_data = json.load(f)
|
|
178
|
+
all_sessions.append(
|
|
179
|
+
{
|
|
180
|
+
"id": session_data.get("id", session_file.stem),
|
|
181
|
+
"project_dir": session_data.get("project_dir", "unknown"),
|
|
182
|
+
"model_name": session_data.get("model_name", "unknown"),
|
|
183
|
+
"created_at": session_data.get("created_at", ""),
|
|
184
|
+
"updated_at": session_data.get("updated_at", ""),
|
|
185
|
+
"message_count": session_data.get("message_count", 0),
|
|
186
|
+
}
|
|
187
|
+
)
|
|
188
|
+
except Exception as e:
|
|
189
|
+
log.warning(f"Failed to read session file {session_file}: {e}")
|
|
190
|
+
continue
|
|
191
|
+
|
|
192
|
+
# Sort by most recent first
|
|
193
|
+
all_sessions.sort(key=lambda s: s.get("updated_at", ""), reverse=True)
|
|
194
|
+
return all_sessions
|
|
195
|
+
|
|
196
|
+
def get_latest_session(self, project_dir: str) -> Optional[Dict[str, Any]]:
|
|
197
|
+
"""
|
|
198
|
+
Get the most recent session for a project directory.
|
|
199
|
+
|
|
200
|
+
Args:
|
|
201
|
+
project_dir: The project directory path
|
|
202
|
+
|
|
203
|
+
Returns:
|
|
204
|
+
Session data dict or None if no sessions exist
|
|
205
|
+
"""
|
|
206
|
+
sessions = self.list_sessions(project_dir)
|
|
207
|
+
if not sessions:
|
|
208
|
+
return None
|
|
209
|
+
|
|
210
|
+
latest_id = sessions[0]["id"]
|
|
211
|
+
return self.load_session(project_dir, latest_id)
|
|
212
|
+
|
|
213
|
+
def delete_session(self, project_dir: str, session_id: str) -> bool:
|
|
214
|
+
"""
|
|
215
|
+
Delete a session.
|
|
216
|
+
|
|
217
|
+
Args:
|
|
218
|
+
project_dir: The project directory path
|
|
219
|
+
session_id: The session ID to delete
|
|
220
|
+
|
|
221
|
+
Returns:
|
|
222
|
+
True if deleted successfully, False otherwise
|
|
223
|
+
"""
|
|
224
|
+
project_sessions_dir = self._get_project_sessions_dir(project_dir)
|
|
225
|
+
session_file = project_sessions_dir / f"{session_id}.json"
|
|
226
|
+
|
|
227
|
+
if not session_file.exists():
|
|
228
|
+
log.warning(f"Session file not found for deletion: {session_file}")
|
|
229
|
+
return False
|
|
230
|
+
|
|
231
|
+
try:
|
|
232
|
+
session_file.unlink()
|
|
233
|
+
log.info(f"Session deleted: {session_id}")
|
|
234
|
+
if self.current_session_id == session_id:
|
|
235
|
+
self.current_session_id = None
|
|
236
|
+
return True
|
|
237
|
+
except Exception as e:
|
|
238
|
+
log.error(f"Failed to delete session: {e}", exc_info=True)
|
|
239
|
+
return False
|
|
240
|
+
|
|
241
|
+
def update_session(
|
|
242
|
+
self, project_dir: str, session_id: str, chat_history: List[Dict[str, Any]]
|
|
243
|
+
) -> bool:
|
|
244
|
+
"""
|
|
245
|
+
Update an existing session with new chat history.
|
|
246
|
+
|
|
247
|
+
Args:
|
|
248
|
+
project_dir: The project directory path
|
|
249
|
+
session_id: The session ID to update
|
|
250
|
+
chat_history: The updated chat history
|
|
251
|
+
|
|
252
|
+
Returns:
|
|
253
|
+
True if updated successfully, False otherwise
|
|
254
|
+
"""
|
|
255
|
+
project_sessions_dir = self._get_project_sessions_dir(project_dir)
|
|
256
|
+
session_file = project_sessions_dir / f"{session_id}.json"
|
|
257
|
+
|
|
258
|
+
if not session_file.exists():
|
|
259
|
+
log.warning(f"Session file not found for update: {session_file}")
|
|
260
|
+
return False
|
|
261
|
+
|
|
262
|
+
try:
|
|
263
|
+
with open(session_file, "r", encoding="utf-8") as f:
|
|
264
|
+
session_data = json.load(f)
|
|
265
|
+
|
|
266
|
+
session_data["chat_history"] = chat_history
|
|
267
|
+
session_data["updated_at"] = datetime.now().isoformat()
|
|
268
|
+
session_data["message_count"] = len(
|
|
269
|
+
[
|
|
270
|
+
msg
|
|
271
|
+
for msg in chat_history
|
|
272
|
+
if msg.get("role") in ("user", "assistant")
|
|
273
|
+
]
|
|
274
|
+
)
|
|
275
|
+
|
|
276
|
+
with open(session_file, "w", encoding="utf-8") as f:
|
|
277
|
+
json.dump(session_data, f, indent=2, ensure_ascii=False)
|
|
278
|
+
|
|
279
|
+
log.info(f"Session updated: {session_id}")
|
|
280
|
+
return True
|
|
281
|
+
except Exception as e:
|
|
282
|
+
log.error(f"Failed to update session: {e}", exc_info=True)
|
|
283
|
+
return False
|
|
@@ -9,9 +9,7 @@ import json
|
|
|
9
9
|
def count_tokens(text):
|
|
10
10
|
"""
|
|
11
11
|
Count the number of tokens in a text string.
|
|
12
|
-
|
|
13
|
-
This is a rough estimate for Gemini 2.5 Pro, using GPT-4 tokenizer as a proxy.
|
|
14
|
-
For production, you'd want to use model-specific token counting.
|
|
12
|
+
Uses GPT-4 tokenizer as a proxy.
|
|
15
13
|
"""
|
|
16
14
|
try:
|
|
17
15
|
encoding = tiktoken.encoding_for_model("gpt-4")
|
|
@@ -1,43 +0,0 @@
|
|
|
1
|
-
"""
|
|
2
|
-
Gemini model integration for the CLI tool.
|
|
3
|
-
"""
|
|
4
|
-
|
|
5
|
-
import logging
|
|
6
|
-
import time
|
|
7
|
-
from rich.console import Console
|
|
8
|
-
from rich.panel import Panel
|
|
9
|
-
import questionary
|
|
10
|
-
|
|
11
|
-
from ..utils import count_tokens
|
|
12
|
-
from ..tools import get_tool, AVAILABLE_TOOLS
|
|
13
|
-
|
|
14
|
-
# Setup logging (basic config, consider moving to main.py)
|
|
15
|
-
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - [%(filename)s:%(lineno)d] - %(message)s')
|
|
16
|
-
log = logging.getLogger(__name__)
|
|
17
|
-
|
|
18
|
-
MAX_AGENT_ITERATIONS = 10
|
|
19
|
-
CONTEXT_TRUNCATION_THRESHOLD_TOKENS = 800000 # Example token limit
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
class GeminiModel:
|
|
23
|
-
"""Interface for Gemini models using native function calling agentic loop."""
|
|
24
|
-
|
|
25
|
-
def __init__(self, console: Console):
|
|
26
|
-
"""Initialize the Gemini model interface."""
|
|
27
|
-
self.console = console
|
|
28
|
-
|
|
29
|
-
# --- Tool Definition ---
|
|
30
|
-
self.function_declarations = None # Tools have been removed
|
|
31
|
-
# ---
|
|
32
|
-
|
|
33
|
-
# --- System Prompt (Native Functions & Planning) ---
|
|
34
|
-
self.system_instruction = "Initialize system prompt."
|
|
35
|
-
# ---
|
|
36
|
-
|
|
37
|
-
# --- Initialize Persistent History ---
|
|
38
|
-
self.chat_history = [
|
|
39
|
-
{'role': 'user', 'parts': [self.system_instruction]},
|
|
40
|
-
{'role': 'model', 'parts': ["Okay, I'm ready. Provide the directory context and your request."]}
|
|
41
|
-
]
|
|
42
|
-
log.info("Initialized persistent chat history.")
|
|
43
|
-
# ---
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|