update context
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
# Manual Slop
|
||||
# Manual Slop
|
||||
|
||||
## Baseline
|
||||
|
||||
@@ -24,21 +24,26 @@ Is a local GUI tool for manually curating and sending context to AI APIs. It agg
|
||||
- `aggregate.py` - reads config, collects files/screenshots/discussion, writes numbered `.md` files to `output_dir`
|
||||
- `shell_runner.py` - subprocess wrapper that runs PowerShell scripts sandboxed to `base_dir`, returns stdout/stderr/exit code as a string
|
||||
- `session_logger.py` - opens timestamped log files at session start; writes comms entries as JSON-L and tool calls as markdown; saves each AI-generated script as a `.ps1` file
|
||||
- `project_manager.py` - per-project .toml load/save, entry serialisation (entry_to_str/str_to_entry with @timestamp support), default_project/default_discussion factories, migrate_from_legacy_config, flat_config for aggregate.run(), git helpers (get_git_commit, get_git_log)
|
||||
- `theme.py` - palette definitions, font loading, scale, load_from_config/save_to_config
|
||||
- `gemini.py` - legacy standalone Gemini wrapper (not used by the main GUI; superseded by `ai_client.py`)
|
||||
- `config.toml` - namespace, output_dir, files paths+base_dir, screenshots paths+base_dir, discussion history array, ai provider+model
|
||||
- `file_cache.py` - stub; Anthropic Files API path removed; kept so stale imports don't break
|
||||
- `config.toml` - global-only settings: [ai] provider+model, [theme] palette+font+scale, [projects] paths array + active path
|
||||
- `manual_slop.toml` - per-project file: [project] name+git_dir, [output] namespace+output_dir, [files] base_dir+paths, [screenshots] base_dir+paths, [discussion] roles+active+[discussion.discussions.<name>] git_commit+last_updated+history
|
||||
- `credentials.toml` - gemini api_key, anthropic api_key
|
||||
- `dpg_layout.ini` - Dear PyGui window layout file (auto-saved on exit, auto-loaded on startup); gitignore this per-user
|
||||
|
||||
**GUI Panels:**
|
||||
- **Config** - namespace, output dir, save
|
||||
- **Projects** - active project name display (green), git directory input + Browse button, scrollable list of loaded project paths (click name to switch, x to remove), Add Project / New Project / Save All buttons
|
||||
- **Config** - namespace, output dir, save (these are project-level fields from the active .toml)
|
||||
- **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** - 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
|
||||
- **Discussion History** - discussion selector (collapsible header): listbox of named discussions, git commit + last_updated display, Update Commit button, Create/Rename/Delete buttons with name input; structured entry editor: each entry has collapse toggle (-/+), role combo, timestamp display, multiline content field; per-entry Ins/Del buttons when collapsed; global toolbar: + Entry, -All, +All, Clear All, Save; collapsible **Roles** sub-section; -> History buttons on Message and Response panels append current message/response as new entry with timestamp
|
||||
- **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; 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
|
||||
- **Message** - multiline input, Gen+Send button, MD Only button, Reset session button, -> History button
|
||||
- **Response** - readonly multiline displaying last AI response, -> History button
|
||||
- **Tool Calls** - scrollable log of every PowerShell tool call the AI made; Clear button
|
||||
- **Comms History** - rich structured live log of every API interaction; status line at top; colour legend; Clear button
|
||||
|
||||
**Layout persistence:**
|
||||
- `dpg.configure_app(..., init_file="dpg_layout.ini")` loads the ini at startup if it exists; DPG silently ignores a missing file
|
||||
@@ -47,6 +52,33 @@ Is a local GUI tool for manually curating and sending context to AI APIs. It agg
|
||||
- First run (no ini) uses the hardcoded `pos=` defaults in `_build_ui()`; after that the ini takes over
|
||||
- Delete `dpg_layout.ini` to reset to defaults
|
||||
|
||||
**Project management:**
|
||||
- `config.toml` is global-only: `[ai]`, `[theme]`, `[projects]` (paths list + active path). No project data lives here.
|
||||
- Each project has its own `.toml` file (e.g. `manual_slop.toml`). Multiple project tomls can be registered by path.
|
||||
- `App.__init__` loads global config, then loads the active project `.toml` via `project_manager.load_project()`. Falls back to `migrate_from_legacy_config()` if no valid project file exists, creating a new `.toml` automatically.
|
||||
- `_flush_to_project()` pulls widget values into `self.project` (the per-project dict) and serialises disc_entries into the active discussion's history list
|
||||
- `_flush_to_config()` writes global settings ([ai], [theme], [projects]) into `self.config`
|
||||
- `_save_active_project()` writes `self.project` to the active `.toml` path via `project_manager.save_project()`
|
||||
- `_do_generate()` calls both flush methods, saves both files, then uses `project_manager.flat_config()` to produce the dict that `aggregate.run()` expects — so `aggregate.py` needs zero changes
|
||||
- Switching projects: saves current project, loads new one, refreshes all GUI state, resets AI session
|
||||
- New project: file dialog for save path, creates default project structure, saves it, switches to it
|
||||
|
||||
**Discussion management (per-project):**
|
||||
- Each project `.toml` stores one or more named discussions under `[discussion.discussions.<name>]`
|
||||
- Each discussion has: `git_commit` (str), `last_updated` (ISO timestamp), `history` (list of serialised entry strings)
|
||||
- `active` key in `[discussion]` tracks which discussion is currently selected
|
||||
- Creating a discussion: adds a new empty discussion dict via `default_discussion()`, switches to it
|
||||
- Renaming: moves the dict to a new key, updates `active` if it was the current one
|
||||
- Deleting: removes the dict; cannot delete the last discussion; switches to first remaining if active was deleted
|
||||
- Switching: flushes current entries to project, loads new discussion's history, rebuilds disc list
|
||||
- Update Commit button: runs `git rev-parse HEAD` in the project's `git_dir` and stores result + timestamp in the active discussion
|
||||
- Timestamps: each disc entry carries a `ts` field (ISO datetime); shown next to the role combo; new entries from `-> History` or `+ Entry` get `now_ts()`
|
||||
|
||||
**Entry serialisation (project_manager):**
|
||||
- `entry_to_str(entry)` → `"@<ts>\n<role>:\n<content>"` (or `"<role>:\n<content>"` if no ts)
|
||||
- `str_to_entry(raw, roles)` → parses optional `@<ts>` prefix, then role line, then content; returns `{role, content, collapsed, ts}`
|
||||
- Round-trips correctly through TOML string arrays; handles legacy entries without timestamps
|
||||
|
||||
**AI Tool Use (PowerShell):**
|
||||
- Both Gemini and Anthropic are configured with a `run_powershell` tool/function declaration
|
||||
- When the AI wants to edit or create files it emits a tool call with a `script` string
|
||||
@@ -58,6 +90,12 @@ Is a local GUI tool for manually curating and sending context to AI APIs. It agg
|
||||
- Rejections return `"USER REJECTED: command was not executed"` to the AI
|
||||
- All tool calls (script + result/rejection) are appended to `_tool_log` and displayed in the Tool Calls panel
|
||||
|
||||
**Anthropic bug fixes applied (session history):**
|
||||
- Bug 1: SDK ContentBlock objects now converted to plain dicts via `_content_block_to_dict()` before storing in `_anthropic_history`; prevents re-serialisation failures on subsequent tool-use rounds
|
||||
- Bug 2: `_repair_anthropic_history` simplified to dict-only path since history always contains dicts
|
||||
- Bug 3: Gemini part.function_call access now guarded with `hasattr` check
|
||||
- Bug 4: Anthropic `b.type == "tool_use"` changed to `getattr(b, "type", None) == "tool_use"` for safe access during response processing
|
||||
|
||||
**Comms Log (ai_client.py):**
|
||||
- `_comms_log: list[dict]` accumulates every API interaction during a session
|
||||
- `_append_comms(direction, kind, payload)` called at each boundary: OUT/request before sending, IN/response after each model reply, OUT/tool_call before executing, IN/tool_result after executing, OUT/tool_result_send when returning results to the model
|
||||
@@ -65,11 +103,11 @@ Is a local GUI tool for manually curating and sending context to AI APIs. It agg
|
||||
- Anthropic responses also include `usage` (input_tokens, output_tokens, cache_creation_input_tokens, cache_read_input_tokens) and `stop_reason` in payload
|
||||
- `get_comms_log()` returns a snapshot; `clear_comms_log()` empties it
|
||||
- `comms_log_callback` (injected by gui.py) is called from the background thread with each new entry; gui queues entries in `_pending_comms` (lock-protected) and flushes them to the DPG panel each render frame
|
||||
- `MAX_FIELD_CHARS = 400` in ai_client (unused in display logic; kept as reference); `COMMS_CLAMP_CHARS = 300` in gui.py governs the display cutoff for heavy text fields
|
||||
- `COMMS_CLAMP_CHARS = 300` in gui.py governs the display cutoff for heavy text fields
|
||||
|
||||
**Comms History panel — rich structured rendering (gui.py):**
|
||||
|
||||
Rather than showing raw JSON, each comms entry is rendered using a kind-specific renderer function. Unknown kinds fall back to a generic key/value layout.
|
||||
Rather than showing raw JSON, each comms entry is rendered using a kind-specific renderer function. Unknown kinds fall back to a generic key/value layout.
|
||||
|
||||
Colour maps:
|
||||
- Direction: OUT = blue-ish `(100,200,255)`, IN = green-ish `(140,255,160)`
|
||||
@@ -92,14 +130,11 @@ Kind-specific renderers (in `_KIND_RENDERERS` dict, dispatched by `_render_comms
|
||||
|
||||
Entry layout: index + timestamp + direction + kind + provider/model header row, then payload rendered by the appropriate function, then a separator line.
|
||||
|
||||
Status line and colour legend live at the top of the Comms History window (above the scrollable child window `comms_scroll`).
|
||||
|
||||
**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)` 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
|
||||
- `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); 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`
|
||||
|
||||
**Anthropic prompt caching:**
|
||||
- System prompt sent as an array with `cache_control: ephemeral` on the text block
|
||||
@@ -108,18 +143,20 @@ Status line and colour legend live at the top of the Comms History window (above
|
||||
- Cache stats (creation tokens, read tokens) are surfaced in the comms log usage dict and displayed in the Comms History panel
|
||||
|
||||
**Data flow:**
|
||||
1. GUI edits are held in `App` state lists (`self.files`, `self.screenshots`, `self.history`) and dpg widget values
|
||||
2. `_flush_to_config()` pulls all widget values into `self.config` dict
|
||||
3. `_do_generate()` calls `_flush_to_config()`, saves `config.toml`, calls `aggregate.run(config)` which writes the md and returns `(markdown_str, path)`
|
||||
4. `cb_generate_send()` calls `_do_generate()` then threads a call to `ai_client.send(md, message, base_dir)`
|
||||
5. `ai_client.send()` prepends the md as a `<context>` block to the user message and sends via the active provider chat session
|
||||
6. If the AI responds with tool calls, the loop handles them (with GUI confirmation) before returning the final text response
|
||||
7. Sessions are stateful within a run (chat history maintained), `Reset` clears them, the tool log, and the comms log
|
||||
1. GUI edits are held in `App` state (`self.files`, `self.screenshots`, `self.disc_entries`, `self.project`) and dpg widget values
|
||||
2. `_flush_to_project()` pulls all widget values into `self.project` dict (per-project data)
|
||||
3. `_flush_to_config()` pulls global settings into `self.config` dict
|
||||
4. `_do_generate()` calls both flush methods, saves both files, calls `project_manager.flat_config(self.project, disc_name)` to produce a dict for `aggregate.run()`, which writes the md and returns `(markdown_str, path, file_items)`
|
||||
5. `cb_generate_send()` calls `_do_generate()` then threads a call to `ai_client.send(md, message, base_dir)`
|
||||
6. `ai_client.send()` prepends the md as a `<context>` block to the user message and sends via the active provider chat session
|
||||
7. If the AI responds with tool calls, the loop handles them (with GUI confirmation) before returning the final text response
|
||||
8. Sessions are stateful within a run (chat history maintained), `Reset` clears them, the tool log, and the comms log
|
||||
|
||||
**Config persistence:**
|
||||
- Every send and save writes `config.toml` with current state including selected provider and model under `[ai]`
|
||||
- Discussion history is stored as a TOML array of strings in `[discussion] history`
|
||||
- File and screenshot paths are stored as TOML arrays, support absolute paths, relative paths from base_dir, and `**/*` wildcards
|
||||
- `config.toml` — global only: `[ai]` provider+model, `[theme]` palette+font+scale, `[projects]` paths array + active path
|
||||
- `<project>.toml` — per-project: output, files, screenshots, discussion (roles, active discussion name, all named discussions with their history+metadata)
|
||||
- On every send and save, both files are written
|
||||
- On clean exit, `run()` calls `_flush_to_project()`, `_save_active_project()`, `_flush_to_config()`, `save_config()` before destroying context
|
||||
|
||||
**Threading model:**
|
||||
- DPG render loop runs on the main thread
|
||||
@@ -135,11 +172,8 @@ Status line and colour legend live at the top of the Comms History window (above
|
||||
|
||||
**Known extension points:**
|
||||
- Add more providers by adding a section to `credentials.toml`, a `_list_*` and `_send_*` function in `ai_client.py`, and the provider name to the `PROVIDERS` list in `gui.py`
|
||||
- System prompt support could be added as a field in `config.toml` and passed in `ai_client.send()`
|
||||
- System prompt support could be added as a field in the project `.toml` and passed in `ai_client.send()`
|
||||
- Discussion history excerpts could be individually toggleable for inclusion in the generated md
|
||||
- `MAX_TOOL_ROUNDS` in `ai_client.py` caps agentic loops at 5 rounds; adjustable
|
||||
- `COMMS_CLAMP_CHARS` in `gui.py` controls the character threshold for clamping heavy payload fields in the Comms History panel
|
||||
|
||||
|
||||
**Discussion History panel (updated):**
|
||||
- **Discussion History** - structured block editor; each entry has a collapse toggle (-/+), a role combo (populated from disc_roles config list), and a multiline content field; per-entry buttons: Ins (insert before), Del (remove); global toolbar buttons: + Entry, -All (collapse all), +All (expand all), Clear All, Save; collapsible **Roles** sub-section lets you add/remove role names which are persisted to config.toml [discussion] roles; -> History buttons on Message and Response panels append the current message/response as a new entry (collapsed=False)
|
||||
- Additional project metadata (description, tags, created date) could be added to `[project]` in the per-project toml
|
||||
Reference in New Issue
Block a user