fixes
This commit is contained in:
@@ -24,11 +24,11 @@
|
||||
- **Config** - namespace, output dir, save
|
||||
- **Files** - base_dir, scrollable path list with remove, add file(s), add wildcard
|
||||
- **Screenshots** - base_dir, scrollable path list with remove, add screenshot(s)
|
||||
- **Discussion History** - multiline text box, `---` as separator between excerpts, save splits on `---` back into toml array
|
||||
- **Discussion History** - structured block editor; each entry has a role combo (User/AI/Vendor API/System) and a multiline content field; buttons: Insert Before, Remove per entry; global buttons: + Entry, Clear All, Save; `-> History` buttons on Message and Response panels append the current message/response as a new entry
|
||||
- **Provider** - provider combo (gemini/anthropic), model listbox populated from API, fetch models button
|
||||
- **Message** - multiline input, Gen+Send button, MD Only button, Reset session button
|
||||
- **Response** - readonly multiline displaying last AI response
|
||||
- **Tool Calls** - scrollable log of every PowerShell tool call the AI made, showing script and result; Clear button
|
||||
- **Tool Calls** - scrollable log of every PowerShell tool call the AI made; shows first line of script + result (script body omitted from display, full script saved to `.ps1` file via session_logger); Clear button
|
||||
- **Comms History** - rich structured live log of every API interaction; status line at top; colour legend; Clear button; each entry rendered with kind-specific layout rather than raw JSON
|
||||
|
||||
**Layout persistence:**
|
||||
@@ -88,7 +88,7 @@ Status line and colour legend live at the top of the Comms History window (above
|
||||
**Session Logger (session_logger.py):**
|
||||
- `open_session()` called once at GUI startup; creates `logs/` and `scripts/generated/` directories; opens `logs/comms_<ts>.log` and `logs/toolcalls_<ts>.log` (line-buffered)
|
||||
- `log_comms(entry)` appends each comms entry as a JSON-L line to the comms log; called from `App._on_comms_entry` (background thread); thread-safe via GIL + line buffering
|
||||
- `log_tool_call(script, result, script_path)` appends a markdown-formatted tool-call record to the toolcalls log and writes the script to `scripts/generated/<ts>_<seq:04d>.ps1`; uses a `threading.Lock` for the sequence counter
|
||||
- `log_tool_call(script, result, script_path)` writes the script to `scripts/generated/<ts>_<seq:04d>.ps1` and appends a markdown record to the toolcalls log **without** the script body (just the file path + result), keeping the log readable; uses a `threading.Lock` for the sequence counter
|
||||
- `close_session()` flushes and closes both file handles; called just before `dpg.destroy_context()`
|
||||
- `_on_tool_log` in `App` is wired to `ai_client.tool_log_callback` and calls `session_logger.log_tool_call`
|
||||
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -286,7 +286,7 @@ Collapsed=0
|
||||
|
||||
[Window][###104]
|
||||
Pos=1525,1246
|
||||
Size=2315,607
|
||||
Size=2315,621
|
||||
Collapsed=0
|
||||
DockId=0x00000026,0
|
||||
|
||||
@@ -309,8 +309,8 @@ Collapsed=0
|
||||
DockId=0x00000012,1
|
||||
|
||||
[Window][###97]
|
||||
Pos=1525,1855
|
||||
Size=2315,282
|
||||
Pos=1525,1869
|
||||
Size=2315,268
|
||||
Collapsed=0
|
||||
DockId=0x00000020,0
|
||||
|
||||
@@ -369,6 +369,31 @@ Pos=1578,868
|
||||
Size=700,440
|
||||
Collapsed=0
|
||||
|
||||
[Window][###717]
|
||||
Pos=1578,868
|
||||
Size=700,440
|
||||
Collapsed=0
|
||||
|
||||
[Window][###849]
|
||||
Pos=1578,868
|
||||
Size=700,440
|
||||
Collapsed=0
|
||||
|
||||
[Window][###987]
|
||||
Pos=1578,868
|
||||
Size=700,440
|
||||
Collapsed=0
|
||||
|
||||
[Window][###1131]
|
||||
Pos=1578,868
|
||||
Size=700,440
|
||||
Collapsed=0
|
||||
|
||||
[Window][###1281]
|
||||
Pos=1578,868
|
||||
Size=700,440
|
||||
Collapsed=0
|
||||
|
||||
[Docking][Data]
|
||||
DockSpace ID=0x7C6B3D9B Window=0xA87D555D Pos=0,0 Size=3840,2137 Split=X Selected=0x40484D8F
|
||||
DockNode ID=0x00000003 Parent=0x7C6B3D9B SizeRef=599,1161 Split=Y Selected=0xEE087978
|
||||
@@ -395,10 +420,10 @@ DockSpace ID=0x7C6B3D9B Window=0xA87D555D Pos=0,0 Size=3840,
|
||||
DockNode ID=0x00000018 Parent=0x00000013 SizeRef=1309,1749 Split=Y Selected=0x88A8C2FF
|
||||
DockNode ID=0x00000019 Parent=0x00000018 SizeRef=2440,1412 Split=X Selected=0x88A8C2FF
|
||||
DockNode ID=0x00000023 Parent=0x00000019 SizeRef=1160,737 Split=Y Selected=0x4F935A1E
|
||||
DockNode ID=0x0000001F Parent=0x00000023 SizeRef=2315,1853 Split=Y Selected=0x4F935A1E
|
||||
DockNode ID=0x0000001F Parent=0x00000023 SizeRef=2315,1867 Split=Y Selected=0x4F935A1E
|
||||
DockNode ID=0x00000025 Parent=0x0000001F SizeRef=2315,1244 CentralNode=1 Selected=0x4F935A1E
|
||||
DockNode ID=0x00000026 Parent=0x0000001F SizeRef=2315,607 Selected=0x7D28643F
|
||||
DockNode ID=0x00000020 Parent=0x00000023 SizeRef=2315,282 Selected=0x4C2F06CB
|
||||
DockNode ID=0x00000026 Parent=0x0000001F SizeRef=2315,621 Selected=0x7D28643F
|
||||
DockNode ID=0x00000020 Parent=0x00000023 SizeRef=2315,268 Selected=0x4C2F06CB
|
||||
DockNode ID=0x00000024 Parent=0x00000019 SizeRef=1153,737 Selected=0x88A8C2FF
|
||||
DockNode ID=0x0000001A Parent=0x00000018 SizeRef=2440,723 Selected=0x3A881EEF
|
||||
DockNode ID=0x00000014 Parent=0x00000010 SizeRef=1967,445 Selected=0xC36FF36B
|
||||
|
||||
143
gui.py
143
gui.py
@@ -426,15 +426,8 @@ class App:
|
||||
dpg.delete_item("tool_log_scroll", children_only=True)
|
||||
for i, (script, result) in enumerate(self._tool_log, 1):
|
||||
with dpg.group(parent="tool_log_scroll"):
|
||||
dpg.add_text(f"Call #{i}", color=(140, 200, 255))
|
||||
dpg.add_input_text(
|
||||
default_value=script,
|
||||
multiline=True,
|
||||
readonly=True,
|
||||
width=-1,
|
||||
height=72,
|
||||
)
|
||||
dpg.add_text("Result:", color=(180, 255, 180))
|
||||
first_line = script.strip().splitlines()[0][:80] if script.strip() else "(empty)"
|
||||
dpg.add_text(f"Call #{i}: {first_line}", color=(140, 200, 255))
|
||||
dpg.add_input_text(
|
||||
default_value=result,
|
||||
multiline=True,
|
||||
@@ -455,8 +448,12 @@ class App:
|
||||
self.config["screenshots"]["base_dir"] = dpg.get_value("shots_base_dir")
|
||||
self.config["files"]["paths"] = self.files
|
||||
self.config["screenshots"]["paths"] = self.screenshots
|
||||
raw = dpg.get_value("discussion_box")
|
||||
self.history = [s.strip() for s in raw.split("---") if s.strip()]
|
||||
# Pull latest content edits from disc widgets
|
||||
for i, entry in enumerate(self.disc_entries):
|
||||
tag = f"disc_content_{i}"
|
||||
if dpg.does_item_exist(tag):
|
||||
entry["content"] = dpg.get_value(tag)
|
||||
self.history = self._disc_serialize()
|
||||
self.config["discussion"] = {"history": self.history}
|
||||
self.config["ai"] = {
|
||||
"provider": self.current_provider,
|
||||
@@ -672,6 +669,7 @@ class App:
|
||||
self.available_models = []
|
||||
self._rebuild_models_list()
|
||||
self._fetch_models(self.current_provider)
|
||||
self._rebuild_disc_list()
|
||||
|
||||
def cb_model_changed(self, sender, app_data):
|
||||
if app_data:
|
||||
@@ -697,6 +695,103 @@ class App:
|
||||
|
||||
# ---------------------------------------------------------------- build ui
|
||||
|
||||
# ------------------------------------------------------------ disc history
|
||||
|
||||
def _disc_serialize(self) -> list[str]:
|
||||
"""Flatten disc_entries back to a single TOML history string per logical block."""
|
||||
lines = []
|
||||
for e in self.disc_entries:
|
||||
lines.append(f"{e['role']}:\n{e['content']}")
|
||||
return lines
|
||||
|
||||
def _rebuild_disc_list(self):
|
||||
if not dpg.does_item_exist("disc_scroll"):
|
||||
return
|
||||
dpg.delete_item("disc_scroll", children_only=True)
|
||||
for i, entry in enumerate(self.disc_entries):
|
||||
with dpg.group(parent="disc_scroll"):
|
||||
with dpg.group(horizontal=True):
|
||||
dpg.add_combo(
|
||||
tag=f"disc_role_{i}",
|
||||
items=DISC_ROLES,
|
||||
default_value=entry["role"],
|
||||
width=120,
|
||||
callback=self._make_disc_role_cb(i),
|
||||
)
|
||||
dpg.add_button(
|
||||
label="Insert Before",
|
||||
callback=self._make_disc_insert_cb(i),
|
||||
)
|
||||
dpg.add_button(
|
||||
label="Remove",
|
||||
callback=self._make_disc_remove_cb(i),
|
||||
)
|
||||
dpg.add_input_text(
|
||||
tag=f"disc_content_{i}",
|
||||
default_value=entry["content"],
|
||||
multiline=True,
|
||||
width=-1,
|
||||
height=100,
|
||||
callback=self._make_disc_content_cb(i),
|
||||
on_enter=False,
|
||||
)
|
||||
dpg.add_separator()
|
||||
|
||||
def _make_disc_role_cb(self, idx: int):
|
||||
def cb(sender, app_data):
|
||||
if idx < len(self.disc_entries):
|
||||
self.disc_entries[idx]["role"] = app_data
|
||||
return cb
|
||||
|
||||
def _make_disc_content_cb(self, idx: int):
|
||||
def cb(sender, app_data):
|
||||
if idx < len(self.disc_entries):
|
||||
self.disc_entries[idx]["content"] = app_data
|
||||
return cb
|
||||
|
||||
def _make_disc_insert_cb(self, idx: int):
|
||||
def cb():
|
||||
self.disc_entries.insert(idx, {"role": "User", "content": ""})
|
||||
self._rebuild_disc_list()
|
||||
return cb
|
||||
|
||||
def _make_disc_remove_cb(self, idx: int):
|
||||
def cb():
|
||||
if idx < len(self.disc_entries):
|
||||
self.disc_entries.pop(idx)
|
||||
self._rebuild_disc_list()
|
||||
return cb
|
||||
|
||||
def cb_disc_append_entry(self):
|
||||
self.disc_entries.append({"role": "User", "content": ""})
|
||||
self._rebuild_disc_list()
|
||||
|
||||
def cb_disc_clear(self):
|
||||
self.disc_entries.clear()
|
||||
self._rebuild_disc_list()
|
||||
|
||||
def cb_disc_save(self):
|
||||
# Pull any in-progress edits from widgets into disc_entries
|
||||
for i, entry in enumerate(self.disc_entries):
|
||||
tag = f"disc_content_{i}"
|
||||
if dpg.does_item_exist(tag):
|
||||
entry["content"] = dpg.get_value(tag)
|
||||
self._flush_to_config()
|
||||
save_config(self.config)
|
||||
self._update_status("discussion saved")
|
||||
|
||||
def cb_append_message_to_history(self):
|
||||
msg = dpg.get_value("ai_input")
|
||||
if msg:
|
||||
self.disc_entries.append({"role": "User", "content": msg})
|
||||
self._rebuild_disc_list()
|
||||
|
||||
def cb_append_response_to_history(self):
|
||||
resp = self.ai_response
|
||||
if resp:
|
||||
self.disc_entries.append({"role": "AI", "content": resp})
|
||||
self._rebuild_disc_list()
|
||||
|
||||
def _build_ui(self):
|
||||
|
||||
with dpg.window(
|
||||
@@ -781,22 +876,17 @@ class App:
|
||||
label="Discussion History",
|
||||
tag="win_discussion",
|
||||
pos=(824, 8),
|
||||
width=400,
|
||||
height=500,
|
||||
width=420,
|
||||
height=600,
|
||||
no_close=True,
|
||||
):
|
||||
dpg.add_input_text(
|
||||
tag="discussion_box",
|
||||
default_value="\n---\n".join(self.history),
|
||||
multiline=True,
|
||||
width=-1,
|
||||
height=-64,
|
||||
)
|
||||
dpg.add_separator()
|
||||
with dpg.group(horizontal=True):
|
||||
dpg.add_button(label="Add Separator", callback=self.cb_add_excerpt)
|
||||
dpg.add_button(label="Clear", callback=self.cb_clear_discussion)
|
||||
dpg.add_button(label="Save", callback=self.cb_save_discussion)
|
||||
dpg.add_button(label="+ Entry", callback=self.cb_disc_append_entry)
|
||||
dpg.add_button(label="Clear All", callback=self.cb_disc_clear)
|
||||
dpg.add_button(label="Save", callback=self.cb_disc_save)
|
||||
dpg.add_separator()
|
||||
with dpg.child_window(tag="disc_scroll", height=-1, border=False):
|
||||
pass
|
||||
|
||||
with dpg.window(
|
||||
label="Provider",
|
||||
@@ -846,6 +936,7 @@ class App:
|
||||
dpg.add_button(label="Gen + Send", callback=self.cb_generate_send)
|
||||
dpg.add_button(label="MD Only", callback=self.cb_md_only)
|
||||
dpg.add_button(label="Reset", callback=self.cb_reset_session)
|
||||
dpg.add_button(label="-> History", callback=self.cb_append_message_to_history)
|
||||
|
||||
with dpg.window(
|
||||
label="Response",
|
||||
@@ -860,8 +951,10 @@ class App:
|
||||
multiline=True,
|
||||
readonly=True,
|
||||
width=-1,
|
||||
height=-1,
|
||||
height=-48,
|
||||
)
|
||||
dpg.add_separator()
|
||||
dpg.add_button(label="-> History", callback=self.cb_append_response_to_history)
|
||||
|
||||
with dpg.window(
|
||||
label="Tool Calls",
|
||||
|
||||
@@ -108,12 +108,11 @@ def log_tool_call(script: str, result: str, script_path: str | None):
|
||||
ps1_path = None
|
||||
ps1_name = f"(write error: {exc})"
|
||||
|
||||
# Append to the tool-call sequence log
|
||||
# Append to the tool-call sequence log (script body omitted - see .ps1 file)
|
||||
try:
|
||||
_tool_fh.write(
|
||||
f"## Call #{seq} [{ts_entry}]\n"
|
||||
f"Script file: {ps1_path}\n\n"
|
||||
f"```powershell\n{script}\n```\n\n"
|
||||
f"### Result\n\n"
|
||||
f"```\n{result}\n```\n\n"
|
||||
f"---\n\n"
|
||||
|
||||
Reference in New Issue
Block a user