fix(conductor): Resolve FileExistsError and harden Preset Manager modal
This commit is contained in:
@@ -41,6 +41,9 @@
|
|||||||
|
|
||||||
## Phase 4: Final Integration & Polish
|
## Phase 4: Final Integration & Polish
|
||||||
- [x] Task: Ensure robust error handling for missing or malformed `.toml` files.
|
- [x] Task: Ensure robust error handling for missing or malformed `.toml` files.
|
||||||
|
- [x] Task: Bugfix: Correct `PresetManager` initialization to use project parent directory.
|
||||||
|
- [x] Task: Hardening: Wrap modal rendering in `try...finally` to prevent ImGui state corruption.
|
||||||
|
- [x] Task: Hardening: Ensure `PresetManager._save_file` validates that parent is a directory.
|
||||||
- [x] Task: Final UI polish (spacing, icons, tooltips).
|
- [x] Task: Final UI polish (spacing, icons, tooltips).
|
||||||
- [x] Task: Run full suite of relevant tests.
|
- [x] Task: Run full suite of relevant tests.
|
||||||
- [x] Task: Conductor - User Manual Verification 'Phase 4: Final Integration & Polish' (Protocol in workflow.md)
|
- [x] Task: Conductor - User Manual Verification 'Phase 4: Final Integration & Polish' (Protocol in workflow.md)
|
||||||
|
|||||||
@@ -2,9 +2,10 @@
|
|||||||
provider = "minimax"
|
provider = "minimax"
|
||||||
model = "MiniMax-M2.5"
|
model = "MiniMax-M2.5"
|
||||||
temperature = 0.0
|
temperature = 0.0
|
||||||
max_tokens = 24000
|
max_tokens = 4096
|
||||||
history_trunc_limit = 900000
|
history_trunc_limit = 900000
|
||||||
system_prompt = ""
|
active_preset = "Default"
|
||||||
|
system_prompt = "Not sure yet."
|
||||||
|
|
||||||
[projects]
|
[projects]
|
||||||
paths = [
|
paths = [
|
||||||
@@ -40,7 +41,7 @@ Response = false
|
|||||||
"Tool Calls" = false
|
"Tool Calls" = false
|
||||||
Theme = true
|
Theme = true
|
||||||
"Log Management" = true
|
"Log Management" = true
|
||||||
Diagnostics = true
|
Diagnostics = false
|
||||||
|
|
||||||
[theme]
|
[theme]
|
||||||
palette = "Nord Dark"
|
palette = "Nord Dark"
|
||||||
|
|||||||
@@ -114,14 +114,14 @@ Collapsed=0
|
|||||||
DockId=0x00000012,0
|
DockId=0x00000012,0
|
||||||
|
|
||||||
[Window][Files & Media]
|
[Window][Files & Media]
|
||||||
Pos=0,1849
|
Pos=0,2013
|
||||||
Size=762,288
|
Size=762,124
|
||||||
Collapsed=0
|
Collapsed=0
|
||||||
DockId=0x00000002,0
|
DockId=0x00000002,0
|
||||||
|
|
||||||
[Window][AI Settings]
|
[Window][AI Settings]
|
||||||
Pos=0,975
|
Pos=0,975
|
||||||
Size=762,872
|
Size=762,1036
|
||||||
Collapsed=0
|
Collapsed=0
|
||||||
DockId=0x00000001,0
|
DockId=0x00000001,0
|
||||||
|
|
||||||
@@ -321,6 +321,11 @@ Pos=755,679
|
|||||||
Size=420,966
|
Size=420,966
|
||||||
Collapsed=0
|
Collapsed=0
|
||||||
|
|
||||||
|
[Window][Preset Manager]
|
||||||
|
Pos=786,858
|
||||||
|
Size=780,650
|
||||||
|
Collapsed=0
|
||||||
|
|
||||||
[Table][0xFB6E3870,4]
|
[Table][0xFB6E3870,4]
|
||||||
RefScale=13
|
RefScale=13
|
||||||
Column 0 Width=80
|
Column 0 Width=80
|
||||||
@@ -415,8 +420,8 @@ DockSpace ID=0xAFC85805 Window=0x079D3A04 Pos=0,30 Size=3840,2107 Spli
|
|||||||
DockNode ID=0x00000007 Parent=0x0000000B SizeRef=762,858 Split=Y Selected=0x8CA2375C
|
DockNode ID=0x00000007 Parent=0x0000000B SizeRef=762,858 Split=Y Selected=0x8CA2375C
|
||||||
DockNode ID=0x00000005 Parent=0x00000007 SizeRef=295,943 Selected=0xF4139CA2
|
DockNode ID=0x00000005 Parent=0x00000007 SizeRef=295,943 Selected=0xF4139CA2
|
||||||
DockNode ID=0x00000006 Parent=0x00000007 SizeRef=295,1168 Split=Y Selected=0x7BD57D6A
|
DockNode ID=0x00000006 Parent=0x00000007 SizeRef=295,1168 Split=Y Selected=0x7BD57D6A
|
||||||
DockNode ID=0x00000001 Parent=0x00000006 SizeRef=824,872 CentralNode=1 Selected=0x7BD57D6A
|
DockNode ID=0x00000001 Parent=0x00000006 SizeRef=824,1036 CentralNode=1 Selected=0x7BD57D6A
|
||||||
DockNode ID=0x00000002 Parent=0x00000006 SizeRef=824,288 Selected=0x1DCB2623
|
DockNode ID=0x00000002 Parent=0x00000006 SizeRef=824,124 Selected=0x1DCB2623
|
||||||
DockNode ID=0x0000000E Parent=0x0000000B SizeRef=1908,858 Split=X Selected=0x418C7449
|
DockNode ID=0x0000000E Parent=0x0000000B SizeRef=1908,858 Split=X Selected=0x418C7449
|
||||||
DockNode ID=0x00000012 Parent=0x0000000E SizeRef=902,402 Selected=0x418C7449
|
DockNode ID=0x00000012 Parent=0x0000000E SizeRef=902,402 Selected=0x418C7449
|
||||||
DockNode ID=0x00000013 Parent=0x0000000E SizeRef=1004,402 Selected=0x6F2B5B04
|
DockNode ID=0x00000013 Parent=0x0000000E SizeRef=1004,402 Selected=0x6F2B5B04
|
||||||
|
|||||||
5
presets.toml
Normal file
5
presets.toml
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
[presets.Default]
|
||||||
|
system_prompt = "Not sure yet."
|
||||||
|
temperature = 0.0
|
||||||
|
top_p = 1.0
|
||||||
|
max_output_tokens = 4096
|
||||||
@@ -822,7 +822,7 @@ class AppController:
|
|||||||
self.ui_auto_add_history = disc_sec.get("auto_add", False)
|
self.ui_auto_add_history = disc_sec.get("auto_add", False)
|
||||||
self.ui_global_system_prompt = self.config.get("ai", {}).get("system_prompt", "")
|
self.ui_global_system_prompt = self.config.get("ai", {}).get("system_prompt", "")
|
||||||
|
|
||||||
self.preset_manager = presets.PresetManager(Path(self.active_project_path) if self.active_project_path else None)
|
self.preset_manager = presets.PresetManager(Path(self.active_project_path).parent if self.active_project_path else None)
|
||||||
self.presets = self.preset_manager.load_all()
|
self.presets = self.preset_manager.load_all()
|
||||||
self.ui_global_preset_name = ai_cfg.get("active_preset")
|
self.ui_global_preset_name = ai_cfg.get("active_preset")
|
||||||
self.ui_project_preset_name = proj_meta.get("active_preset")
|
self.ui_project_preset_name = proj_meta.get("active_preset")
|
||||||
@@ -1793,6 +1793,8 @@ class AppController:
|
|||||||
self.max_tokens = preset.max_output_tokens
|
self.max_tokens = preset.max_output_tokens
|
||||||
|
|
||||||
def _cb_save_preset(self, name, content, temp, top_p, max_tok, scope):
|
def _cb_save_preset(self, name, content, temp, top_p, max_tok, scope):
|
||||||
|
if not name or not name.strip():
|
||||||
|
raise ValueError("Preset name cannot be empty or whitespace.")
|
||||||
preset = models.Preset(
|
preset = models.Preset(
|
||||||
name=name,
|
name=name,
|
||||||
system_prompt=content,
|
system_prompt=content,
|
||||||
|
|||||||
18
src/gui_2.py
18
src/gui_2.py
@@ -409,13 +409,13 @@ class App:
|
|||||||
if exp:
|
if exp:
|
||||||
if imgui.collapsing_header("Provider & Model"):
|
if imgui.collapsing_header("Provider & Model"):
|
||||||
self._render_provider_panel()
|
self._render_provider_panel()
|
||||||
|
if imgui.collapsing_header("System Prompts"):
|
||||||
|
self._render_system_prompts_panel()
|
||||||
if imgui.collapsing_header("Token Budget"):
|
if imgui.collapsing_header("Token Budget"):
|
||||||
self._render_token_budget_panel()
|
self._render_token_budget_panel()
|
||||||
self._render_cache_panel()
|
self._render_cache_panel()
|
||||||
self._render_tool_analytics_panel()
|
self._render_tool_analytics_panel()
|
||||||
self._render_session_insights_panel()
|
self._render_session_insights_panel()
|
||||||
if imgui.collapsing_header("System Prompts"):
|
|
||||||
self._render_system_prompts_panel()
|
|
||||||
|
|
||||||
imgui.end()
|
imgui.end()
|
||||||
if self.show_windows.get("MMA Dashboard", False):
|
if self.show_windows.get("MMA Dashboard", False):
|
||||||
@@ -864,8 +864,12 @@ class App:
|
|||||||
def _render_preset_manager_modal(self) -> None:
|
def _render_preset_manager_modal(self) -> None:
|
||||||
if not self.show_preset_manager_modal: return
|
if not self.show_preset_manager_modal: return
|
||||||
imgui.open_popup("Preset Manager")
|
imgui.open_popup("Preset Manager")
|
||||||
if imgui.begin_popup_modal("Preset Manager", True, imgui.WindowFlags_.always_auto_resize)[0]:
|
opened, self.show_preset_manager_modal = imgui.begin_popup_modal("Preset Manager", self.show_preset_manager_modal)
|
||||||
imgui.begin_child("preset_list_area", imgui.ImVec2(250, 600), True)
|
if opened:
|
||||||
|
try:
|
||||||
|
avail = imgui.get_content_region_avail()
|
||||||
|
imgui.begin_child("preset_list_area", imgui.ImVec2(250, avail.y), True)
|
||||||
|
try:
|
||||||
preset_names = sorted(self.controller.presets.keys())
|
preset_names = sorted(self.controller.presets.keys())
|
||||||
if imgui.button("New Preset", imgui.ImVec2(-1, 0)):
|
if imgui.button("New Preset", imgui.ImVec2(-1, 0)):
|
||||||
self._editing_preset_name = ""
|
self._editing_preset_name = ""
|
||||||
@@ -886,9 +890,11 @@ class App:
|
|||||||
self._editing_preset_top_p = p.top_p if p.top_p is not None else 1.0
|
self._editing_preset_top_p = p.top_p if p.top_p is not None else 1.0
|
||||||
self._editing_preset_max_output_tokens = p.max_output_tokens if p.max_output_tokens is not None else 4096
|
self._editing_preset_max_output_tokens = p.max_output_tokens if p.max_output_tokens is not None else 4096
|
||||||
self._editing_preset_is_new = False
|
self._editing_preset_is_new = False
|
||||||
|
finally:
|
||||||
imgui.end_child()
|
imgui.end_child()
|
||||||
imgui.same_line()
|
imgui.same_line()
|
||||||
imgui.begin_child("preset_edit_area", imgui.ImVec2(500, 600), False)
|
imgui.begin_child("preset_edit_area", imgui.ImVec2(0, avail.y), False)
|
||||||
|
try:
|
||||||
p_name = self._editing_preset_name or "(New Preset)"
|
p_name = self._editing_preset_name or "(New Preset)"
|
||||||
imgui.text_colored(C_IN, f"Editing Preset: {p_name}")
|
imgui.text_colored(C_IN, f"Editing Preset: {p_name}")
|
||||||
imgui.separator()
|
imgui.separator()
|
||||||
@@ -937,7 +943,9 @@ class App:
|
|||||||
if imgui.button("Close", imgui.ImVec2(120, 0)):
|
if imgui.button("Close", imgui.ImVec2(120, 0)):
|
||||||
self.show_preset_manager_modal = False
|
self.show_preset_manager_modal = False
|
||||||
imgui.close_current_popup()
|
imgui.close_current_popup()
|
||||||
|
finally:
|
||||||
imgui.end_child()
|
imgui.end_child()
|
||||||
|
finally:
|
||||||
imgui.end_popup()
|
imgui.end_popup()
|
||||||
|
|
||||||
def _render_projects_panel(self) -> None:
|
def _render_projects_panel(self) -> None:
|
||||||
|
|||||||
@@ -84,6 +84,8 @@ class PresetManager:
|
|||||||
return {"presets": {}}
|
return {"presets": {}}
|
||||||
|
|
||||||
def _save_file(self, path: Path, data: Dict[str, Any]) -> None:
|
def _save_file(self, path: Path, data: Dict[str, Any]) -> None:
|
||||||
|
if path.parent.exists() and path.parent.is_file():
|
||||||
|
raise ValueError(f"Cannot save to {path}: Parent directory {path.parent} is a file. The project root seems to be a file.")
|
||||||
path.parent.mkdir(parents=True, exist_ok=True)
|
path.parent.mkdir(parents=True, exist_ok=True)
|
||||||
with open(path, "wb") as f:
|
with open(path, "wb") as f:
|
||||||
f.write(tomli_w.dumps(data).encode("utf-8"))
|
f.write(tomli_w.dumps(data).encode("utf-8"))
|
||||||
|
|||||||
Reference in New Issue
Block a user