From 7427b7a9d792447a2926ef10a82be96cb5d9709f Mon Sep 17 00:00:00 2001 From: Ed_ Date: Sat, 21 Feb 2026 18:36:37 -0500 Subject: [PATCH] fixes --- MainContext.md | 6 +- config.toml | 2 +- dpg_layout.ini | 37 ++++++++++-- gui.py | 143 ++++++++++++++++++++++++++++++++++++++-------- session_logger.py | 3 +- 5 files changed, 154 insertions(+), 37 deletions(-) diff --git a/MainContext.md b/MainContext.md index 66c124b..9a56b3e 100644 --- a/MainContext.md +++ b/MainContext.md @@ -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_.log` and `logs/toolcalls_.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/_.ps1`; uses a `threading.Lock` for the sequence counter +- `log_tool_call(script, result, script_path)` writes the script to `scripts/generated/_.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` diff --git a/config.toml b/config.toml index 6f4a9fe..6669f04 100644 --- a/config.toml +++ b/config.toml @@ -23,7 +23,7 @@ paths = [] [discussion] history = [ - "User:\nAdd support for properly handling files that we have in config with anthropics api (your context will have documentation reguarding it). This will allow us to free up the main markdown file were sending as a message.\nWe also want to make sure the messaging of that giant markdown is chunked as anthropic does have an upper limit per messages.\nAI:\nGood. Now the big rewrite of `ai_client.py`. The strategy:\n\n- Add a new `_send_anthropic_files` path that uses `file_cache.get_file_id()` to upload files individually as `document`/`image` content blocks, keeping the context message lean\n- The fallback (and also for oversized content) uses chunked text blocks — split the markdown into ≤180k-char chunks (well under the ~200k token limit with headroom for overhead), each sent as a separate `text` content block with `cache_control` on the last one\n- `send()` now accepts an optional `file_items` parameter from aggregate\n- `reset_session()` also resets the file_cache client\nUser:\ncontinue. Also got this error when trying to run latest version of this program. However I can use an old version for now.\nAi:\nNow fix `gui.py` — patch the three 2-tuple unpack sites and thread `file_items` through to `send()`:\n[User]\ncontinue. My bad the error in comms was shown int he screenshot I linked. in the md context. Make sure to update MainContext.md when done.\nAI:\nfinished without sending a response...\nUser:\nSeems to be working...! \nMake a richer discussioon history editor, with structual support for the user/ai message nodes and buttons in the reponse and message panels for adding their entries to it on click.\nAI:\nThe existing format already uses `[User]\\n...` and `[AI]\\n...` prefixes. The new structured editor will parse those naturally and write them back in the same format. Now let me implement the full changes:\nUser:\nYou had failres in your last few called scripts. Git diff shows now changes to python files.\nI'll add the comms and tool logs to your context. \nWe should remove the script content from the tool log and just save the script name so that it doesn't bloat it. \nSame with omitting full message sent in the comms log, very expensive to send to you or read manually.\nVendor API:\nERROR: Error code: 400 - {'type': 'error', 'error': {'type': 'invalid_request_error', 'message': 'A maximum of 4 blocks with cache_control may be provided. Found 5.'}, 'request_id': 'req_011CYMxgMXvcZUVFNyFz1Lx8'}\nUser:\nI'm not able to send a message for a while now...\n(Message finally sent)\nAI:\nCompleted without a response message showing up.\nUser:\nRestarts custom prompting program.\nUser:\nOk lets try this again...\nLets do the discussion history improvmeents. I want a sort of visual editor where each entry is its own block. \nThere should be a role field and a content filed and I should have buttons for appending the current response or current message to it\n. I should be able to insert or remove anywhere as well.\nVendor API:\nERROR: Error code: 500 - {'type': 'error', 'error': {'type': 'api_error', 'message': 'Internal server error'}, 'request_id': 'req_011CYMyjgyfDn2b6JnPD6rvJ'}\nUser:\ncontinue...\nVendor API:\nERROR: Error code: 404 - {'type': 'error', 'error': {'type': 'not_found_error', 'message': 'File not found: file_011CYMyjXYsf951nBgTvQmkE'}, 'request_id': 'req_011CYMysoSaH2xPxVtZurjSc'}\nUser:\nreverting to older editor...\nOk I'm back on an older prompting editor. Lets get this working again... with latest..\nAI:\nGood. Now I'll rewrite the Discussion History panel in `gui.py`. The changes are:\n\n1. Add `_parse_history_entries()` — converts the raw TOML string array into `[{role, content}]` dicts\n2. Add `self.disc_entries` list of dicts to `App.__init__`\n3. Replace `_build_ui`'s Discussion window with a structured block editor inside a scrollable child window\n4. Add `_rebuild_disc_list()` — full redraw of the structured editor\n5. Add `_make_disc_*` callbacks for insert/remove/role-change/content-change\n6. Add `cb_append_message_to_history()` and `cb_append_response_to_history()`\n7. Add **→ History** buttons to Message and Response panels\n8. Update `_flush_to_config()` to serialise `disc_entries` back to the string array format\n9. Remove the old `cb_add_excerpt` / `cb_clear_discussion` / `cb_save_discussion` callbacks (replaced)\nUser:\nsounds good, continue!\nVendor API:\nERROR: Error code: 400 - {'type': 'error', 'error': {'type': 'invalid_request_error', 'message': 'messages.10: `tool_use` ids were found without `tool_result` blocks immediately after: toolu_01BjaPJPVFoNnNoRCLvPA8qF. Each `tool_use` block must have a corresponding `tool_result` block in the next message.'}, 'request_id': 'req_011CYMzHG47TKTPp7iLcE5NP'}\nUser:\nreverting to an even older version..\nUser:\nOk lets see if we can get it to work with this version and get everything fixed on latest...\nAI:\nThe cache_control logic looks correct already (system=1, tools=1, context_last=1 = 3 total, under the limit of 4). The error seen previously was likely from an older version. Good.\n\nNow let me fix the **comms log** (omit full message from OUT/request) and **tool log** (omit script body from `session_logger`):\nAI:\nNo response\nUser:\nOnly see changes done to gui.py...\nrestarting...\nUser:\nIs everything fixed? not sure?\nAI:\nNow I have a clear picture. The files on disk match the context documents closely. The outstanding tasks from the discussion history are:\n\n1. **`session_logger.py`** — omit the script body from the tool-call log (just save script name/path, not contents)\n2. **`ai_client.py`** — omit full message from OUT/request comms log (already mostly done — Gemini still uses `summary` key; Anthropic uses `message` with a summary. The Gemini one sends `summary` but the renderer looks for `message`. Fix that too.)\n3. **`gui.py`** — Discussion History structured block editor (the big one that kept failing)\n\nLet me tackle all three cleanly now.\n\n**First: `session_logger.py`** — remove script body from toolcalls log, just reference the `.ps1` file:\nVendor API:\nERROR: Error code: 404 - {'type': 'error', 'error': {'type': 'not_found_error', 'message': 'File not found: file_011CYN4ecyVxDg7ujLUYWeD3'}, 'request_id': 'req_011CYN4kpavNrA5KYXnMqKLq'}\nUser (Background):\nConstraining the discussion history to one entry...\nVendor API:\nERROR: Error code: 404 - {'type': 'error', 'error': {'type': 'not_found_error', 'message': 'File not found: file_011CYN4ecyVxDg7ujLUYWeD3'}, 'request_id': 'req_011CYN4rxjETKA5B9ACMkm2T'}\nUser:\nSigh, here we go again.\nAI - Observed by user:\nDoes many actions (recorded in screenshot but no direct response from them).\nVendor API:\nERROR: Error code: 404 - {'type': 'error', 'error': {'type': 'not_found_error', 'message': 'File not found: file_011CYN4wQpEzM4W4KFA48HDz'}, 'request_id': 'req_011CYN59c31BxJsgDZ6Rzqby'}\nUser (Background):\nOk definitely not...\nUser:\nAttempt to fix these error with files were getting... maybe this file usage thing with anthropic was a bad idea...\nVendor API:\nERROR: Error code: 500 - {'type': 'error', 'error': {'type': 'api_error', 'message': 'Internal server error'}, 'request_id': 'req_011CYN5PqDQLs5d1B9QS8inZ'}\nUser (Background):\nWeird it got pretty far and gutted the file_cache.py file. Not sure why that error popped up for the service...\nrestarting prompting editor to latest instance...\nVendor API:\nERROR: send() takes from 2 to 3 positional arguments but 4 were given\nUser:\nok... maybe I cant use latest...", + "User:\nAdd support for properly handling files that we have in config with anthropics api (your context will have documentation reguarding it). This will allow us to free up the main markdown file were sending as a message.\nWe also want to make sure the messaging of that giant markdown is chunked as anthropic does have an upper limit per messages.\nAI:\nGood. Now the big rewrite of `ai_client.py`. The strategy:\n\n- Add a new `_send_anthropic_files` path that uses `file_cache.get_file_id()` to upload files individually as `document`/`image` content blocks, keeping the context message lean\n- The fallback (and also for oversized content) uses chunked text blocks — split the markdown into ≤180k-char chunks (well under the ~200k token limit with headroom for overhead), each sent as a separate `text` content block with `cache_control` on the last one\n- `send()` now accepts an optional `file_items` parameter from aggregate\n- `reset_session()` also resets the file_cache client\nUser:\ncontinue. Also got this error when trying to run latest version of this program. However I can use an old version for now.\nAi:\nNow fix `gui.py` — patch the three 2-tuple unpack sites and thread `file_items` through to `send()`:\n[User]\ncontinue. My bad the error in comms was shown int he screenshot I linked. in the md context. Make sure to update MainContext.md when done.\nAI:\nfinished without sending a response...\nUser:\nSeems to be working...! \nMake a richer discussioon history editor, with structual support for the user/ai message nodes and buttons in the reponse and message panels for adding their entries to it on click.\nAI:\nThe existing format already uses `[User]\\n...` and `[AI]\\n...` prefixes. The new structured editor will parse those naturally and write them back in the same format. Now let me implement the full changes:\nUser:\nYou had failres in your last few called scripts. Git diff shows now changes to python files.\nI'll add the comms and tool logs to your context. \nWe should remove the script content from the tool log and just save the script name so that it doesn't bloat it. \nSame with omitting full message sent in the comms log, very expensive to send to you or read manually.\nVendor API:\nERROR: Error code: 400 - {'type': 'error', 'error': {'type': 'invalid_request_error', 'message': 'A maximum of 4 blocks with cache_control may be provided. Found 5.'}, 'request_id': 'req_011CYMxgMXvcZUVFNyFz1Lx8'}\nUser:\nI'm not able to send a message for a while now...\n(Message finally sent)\nAI:\nCompleted without a response message showing up.\nUser:\nRestarts custom prompting program.\nUser:\nOk lets try this again...\nLets do the discussion history improvmeents. I want a sort of visual editor where each entry is its own block. \nThere should be a role field and a content filed and I should have buttons for appending the current response or current message to it\n. I should be able to insert or remove anywhere as well.\nVendor API:\nERROR: Error code: 500 - {'type': 'error', 'error': {'type': 'api_error', 'message': 'Internal server error'}, 'request_id': 'req_011CYMyjgyfDn2b6JnPD6rvJ'}\nUser:\ncontinue...\nVendor API:\nERROR: Error code: 404 - {'type': 'error', 'error': {'type': 'not_found_error', 'message': 'File not found: file_011CYMyjXYsf951nBgTvQmkE'}, 'request_id': 'req_011CYMysoSaH2xPxVtZurjSc'}\nUser:\nreverting to older editor...\nOk I'm back on an older prompting editor. Lets get this working again... with latest..\nAI:\nGood. Now I'll rewrite the Discussion History panel in `gui.py`. The changes are:\n\n1. Add `_parse_history_entries()` — converts the raw TOML string array into `[{role, content}]` dicts\n2. Add `self.disc_entries` list of dicts to `App.__init__`\n3. Replace `_build_ui`'s Discussion window with a structured block editor inside a scrollable child window\n4. Add `_rebuild_disc_list()` — full redraw of the structured editor\n5. Add `_make_disc_*` callbacks for insert/remove/role-change/content-change\n6. Add `cb_append_message_to_history()` and `cb_append_response_to_history()`\n7. Add **→ History** buttons to Message and Response panels\n8. Update `_flush_to_config()` to serialise `disc_entries` back to the string array format\n9. Remove the old `cb_add_excerpt` / `cb_clear_discussion` / `cb_save_discussion` callbacks (replaced)\nUser:\nsounds good, continue!\nVendor API:\nERROR: Error code: 400 - {'type': 'error', 'error': {'type': 'invalid_request_error', 'message': 'messages.10: `tool_use` ids were found without `tool_result` blocks immediately after: toolu_01BjaPJPVFoNnNoRCLvPA8qF. Each `tool_use` block must have a corresponding `tool_result` block in the next message.'}, 'request_id': 'req_011CYMzHG47TKTPp7iLcE5NP'}\nUser:\nreverting to an even older version..\nUser:\nOk lets see if we can get it to work with this version and get everything fixed on latest...\nAI:\nThe cache_control logic looks correct already (system=1, tools=1, context_last=1 = 3 total, under the limit of 4). The error seen previously was likely from an older version. Good.\n\nNow let me fix the **comms log** (omit full message from OUT/request) and **tool log** (omit script body from `session_logger`):\nAI:\nNo response\nUser:\nOnly see changes done to gui.py...\nrestarting...\nUser:\nIs everything fixed? not sure?\nAI:\nNow I have a clear picture. The files on disk match the context documents closely. The outstanding tasks from the discussion history are:\n\n1. **`session_logger.py`** — omit the script body from the tool-call log (just save script name/path, not contents)\n2. **`ai_client.py`** — omit full message from OUT/request comms log (already mostly done — Gemini still uses `summary` key; Anthropic uses `message` with a summary. The Gemini one sends `summary` but the renderer looks for `message`. Fix that too.)\n3. **`gui.py`** — Discussion History structured block editor (the big one that kept failing)\n\nLet me tackle all three cleanly now.\n\n**First: `session_logger.py`** — remove script body from toolcalls log, just reference the `.ps1` file:\nVendor API:\nERROR: Error code: 404 - {'type': 'error', 'error': {'type': 'not_found_error', 'message': 'File not found: file_011CYN4ecyVxDg7ujLUYWeD3'}, 'request_id': 'req_011CYN4kpavNrA5KYXnMqKLq'}\nUser (Background):\nConstraining the discussion history to one entry...\nVendor API:\nERROR: Error code: 404 - {'type': 'error', 'error': {'type': 'not_found_error', 'message': 'File not found: file_011CYN4ecyVxDg7ujLUYWeD3'}, 'request_id': 'req_011CYN4rxjETKA5B9ACMkm2T'}\nUser:\nSigh, here we go again.\nAI - Observed by user:\nDoes many actions (recorded in screenshot but no direct response from them).\nVendor API:\nERROR: Error code: 404 - {'type': 'error', 'error': {'type': 'not_found_error', 'message': 'File not found: file_011CYN4wQpEzM4W4KFA48HDz'}, 'request_id': 'req_011CYN59c31BxJsgDZ6Rzqby'}\nUser (Background):\nOk definitely not...\nUser:\nAttempt to fix these error with files were getting... maybe this file usage thing with anthropic was a bad idea...\nVendor API:\nERROR: Error code: 500 - {'type': 'error', 'error': {'type': 'api_error', 'message': 'Internal server error'}, 'request_id': 'req_011CYN5PqDQLs5d1B9QS8inZ'}\nUser (Background):\nWeird it got pretty far and gutted the file_cache.py file. Not sure why that error popped up for the service...\nrestarting prompting editor to latest instance...\nVendor API:\nERROR: send() takes from 2 to 3 positional arguments but 4 were given\nUser:\nok... maybe I cant use latest...\nI applied a fix to gui.py line 658 was using the files arg with the send call thats no longer used.\nAI:\nThe file has `\\r\\n` line endings mixed in. I'll write the replacement using a Python one-liner to avoid PowerShell line-ending issues:", ] [ai] diff --git a/dpg_layout.ini b/dpg_layout.ini index 3dc0977..a184d42 100644 --- a/dpg_layout.ini +++ b/dpg_layout.ini @@ -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 diff --git a/gui.py b/gui.py index 0603e1a..c5b69d1 100644 --- a/gui.py +++ b/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", diff --git a/session_logger.py b/session_logger.py index c1f4d0c..58bab8d 100644 --- a/session_logger.py +++ b/session_logger.py @@ -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"