diff --git a/src/gui_2.py b/src/gui_2.py index cf533cf..6ac4e42 100644 --- a/src/gui_2.py +++ b/src/gui_2.py @@ -928,79 +928,147 @@ class App: def _render_preset_manager_content(self, is_embedded: bool = False) -> None: avail = imgui.get_content_region_avail() - imgui.begin_child("preset_list_area", imgui.ImVec2(avail.x * 0.25, avail.y), True) + if not hasattr(self, "_prompt_md_preview"): self._prompt_md_preview = False + try: - preset_names = sorted(self.controller.presets.keys()) - if imgui.button("New Preset", imgui.ImVec2(-1, 0)): - self._editing_preset_name = "" - self._editing_preset_content = "" - self._editing_preset_scope = "project" - self._editing_preset_is_new = True - imgui.separator() - for name in preset_names: - p = self.controller.presets[name] - is_sel = (name == self._editing_preset_name) - if imgui.selectable(name, is_sel)[0]: - self._editing_preset_name = name - self._editing_preset_content = p.system_prompt - self._editing_preset_is_new = False - finally: - imgui.end_child() - imgui.same_line() - imgui.begin_child("preset_edit_area", imgui.ImVec2(0, avail.y), False) - try: - p_name = self._editing_preset_name or "(New Preset)" - imgui.text_colored(C_IN, f"Editing Preset: {p_name}") - imgui.separator() - imgui.dummy(imgui.ImVec2(0, 8)) - imgui.text("Name:") - imgui.set_next_item_width(-1) - _, self._editing_preset_name = imgui.input_text("##edit_name", self._editing_preset_name) - imgui.dummy(imgui.ImVec2(0, 8)) - imgui.text("Scope:") - if imgui.radio_button("Global", self._editing_preset_scope == "global"): - self._editing_preset_scope = "global" - imgui.same_line() - if imgui.radio_button("Project", self._editing_preset_scope == "project"): - self._editing_preset_scope = "project" - imgui.dummy(imgui.ImVec2(0, 8)) - imgui.text("Content:") - _, self._editing_preset_content = imgui.input_text_multiline("##edit_content", self._editing_preset_content, imgui.ImVec2(-1, -40)) + if imgui.begin_table("prompt_main_split", 2, imgui.TableFlags_.resizable | imgui.TableFlags_.borders_inner_v): + imgui.table_setup_column("List", imgui.TableColumnFlags_.width_fixed, 200) + imgui.table_setup_column("Editor", imgui.TableColumnFlags_.width_stretch) + imgui.table_next_row() + + # Left Column: Presets + imgui.table_next_column() + imgui.begin_child("prompt_list_pane", imgui.ImVec2(0, 0), False) + if imgui.button("New Preset", imgui.ImVec2(-1, 0)): + self._editing_preset_name = "" + self._editing_preset_system_prompt = "" + self._editing_preset_temperature = 0.7 + self._editing_preset_top_p = 1.0 + self._editing_preset_max_output_tokens = 4096 + self._editing_preset_scope = "project" + self._selected_preset_idx = -1 + imgui.separator() + preset_names = sorted(self.controller.presets.keys()) + for i, name in enumerate(preset_names): + if imgui.selectable(name, self._selected_preset_idx == i)[0]: + self._selected_preset_idx = i + self._editing_preset_name = name + p = self.controller.presets[name] + self._editing_preset_system_prompt = p.system_prompt + self._editing_preset_temperature = p.temperature + self._editing_preset_top_p = p.top_p + self._editing_preset_max_output_tokens = p.max_output_tokens + self._editing_preset_scope = self.controller.preset_manager.get_preset_scope(name) + imgui.end_child() - imgui.dummy(imgui.ImVec2(0, 8)) - if imgui.button("Save", imgui.ImVec2(120, 0)): - if self._editing_preset_name.strip(): - self.controller._cb_save_preset( - self._editing_preset_name.strip(), - self._editing_preset_content, - self._editing_preset_scope - ) - self.ai_status = f"Preset '{self._editing_preset_name.strip()}' saved to {self._editing_preset_scope}" - imgui.set_item_tooltip("Save the current preset settings") - imgui.same_line() - if imgui.button("Delete", imgui.ImVec2(120, 0)): - if self._editing_preset_name.strip(): - try: - self.controller._cb_delete_preset(self._editing_preset_name.strip(), self._editing_preset_scope) - self.ai_status = f"Preset '{self._editing_preset_name}' deleted from {self._editing_preset_scope}" - self._editing_preset_name = "" - self._editing_preset_content = "" - except Exception as e: - self.ai_status = f"Error deleting: {e}" - imgui.set_item_tooltip("Delete the selected preset") - if not is_embedded: + # Right Column: Editor + imgui.table_next_column() + avail_r = imgui.get_content_region_avail() + # Subtract space for bottom buttons (approx 45px) + imgui.begin_child("prompt_edit_pane", imgui.ImVec2(0, avail_r.y - 45), False) + + p_disp = self._editing_preset_name or "(New Preset)" + imgui.text_colored(C_IN, f"Editing Preset: {p_disp}") + imgui.separator() + + if imgui.begin_table("p_meta", 2): + imgui.table_setup_column("L", imgui.TableColumnFlags_.width_fixed, 80) + imgui.table_setup_column("F", imgui.TableColumnFlags_.width_stretch) + imgui.table_next_row() + imgui.table_next_column() + imgui.text("Name:") + imgui.table_next_column() + imgui.set_next_item_width(-1) + _, self._editing_preset_name = imgui.input_text("##epn", self._editing_preset_name) + imgui.table_next_row() + imgui.table_next_column() + imgui.text("Scope:") + imgui.table_next_column() + if imgui.radio_button("Global", self._editing_preset_scope == "global"): + self._editing_preset_scope = "global" + imgui.same_line() + if imgui.radio_button("Project", self._editing_preset_scope == "project"): + self._editing_preset_scope = "project" + imgui.end_table() + + imgui.dummy(imgui.ImVec2(0, 4)) + imgui.separator() + imgui.text("Parameters:") + if imgui.begin_table("p_params", 2): + imgui.table_setup_column("L", imgui.TableColumnFlags_.width_fixed, 120) + imgui.table_setup_column("C", imgui.TableColumnFlags_.width_stretch) + imgui.table_next_row() + imgui.table_next_column() + imgui.text("Temperature:") + imgui.table_next_column() + cw = imgui.get_content_region_avail().x + imgui.set_next_item_width(cw * 0.7) + _, self._editing_preset_temperature = imgui.slider_float("##pts", self._editing_preset_temperature, 0.0, 2.0, "%.1f") + imgui.same_line() + imgui.set_next_item_width(-1) + _, self._editing_preset_temperature = imgui.input_float("##pti", self._editing_preset_temperature, 0.1, 0.1, "%.1f") + + imgui.table_next_row() + imgui.table_next_column() + imgui.text("Top-P:") + imgui.table_next_column() + imgui.set_next_item_width(cw * 0.7) + _, self._editing_preset_top_p = imgui.slider_float("##ptp_s", self._editing_preset_top_p, 0.0, 1.0, "%.2f") + imgui.same_line() + imgui.set_next_item_width(-1) + _, self._editing_preset_top_p = imgui.input_float("##ptp_i", self._editing_preset_top_p, 0.05, 0.05, "%.2f") + + imgui.table_next_row() + imgui.table_next_column() + imgui.text("Max Tokens:") + imgui.table_next_column() + imgui.set_next_item_width(-1) + _, self._editing_preset_max_output_tokens = imgui.input_int("##pmaxt", self._editing_preset_max_output_tokens) + imgui.end_table() + + imgui.dummy(imgui.ImVec2(0, 4)) + imgui.separator() + imgui.text("Prompt Content:") imgui.same_line() - if imgui.button("Close", imgui.ImVec2(120, 0)): - self.show_preset_manager_window = False + if imgui.button("MD Preview" if not self._prompt_md_preview else "Edit Mode"): + self._prompt_md_preview = not self._prompt_md_preview + + if self._prompt_md_preview: + imgui.begin_child("prompt_preview", imgui.ImVec2(-1, imgui.get_content_region_avail().y), True) + self.markdown_renderer.render(self._editing_preset_system_prompt) + imgui.end_child() + else: + _, self._editing_preset_system_prompt = imgui.input_text_multiline("##pcont", self._editing_preset_system_prompt, imgui.ImVec2(-1, imgui.get_content_region_avail().y)) + imgui.end_child() + + # Bottom Buttons + imgui.separator() + imgui.dummy(imgui.ImVec2(0, 4)) + if imgui.button("Save Preset", imgui.ImVec2(150, 0)): + if self._editing_preset_name.strip(): + p = models.Preset(name=self._editing_preset_name.strip(), system_prompt=self._editing_preset_system_prompt, temperature=self._editing_preset_temperature, top_p=self._editing_preset_top_p, max_output_tokens=self._editing_preset_max_output_tokens) + self.controller._cb_save_preset(p, self._editing_preset_scope) + self.ai_status = f"Saved: {p.name}" + imgui.same_line() + if imgui.button("Delete Preset", imgui.ImVec2(120, 0)): + if self._editing_preset_name: + self.controller._cb_delete_preset(self._editing_preset_name, self._editing_preset_scope) + self._editing_preset_name = "" + self._selected_preset_idx = -1 + if not is_embedded: + imgui.same_line() + if imgui.button("Close", imgui.ImVec2(100, 0)): + self.show_preset_manager_window = False + imgui.end_table() finally: - imgui.end_child() + pass def _render_preset_manager_window(self, is_embedded: bool = False) -> None: if not self.show_preset_manager_window and not is_embedded: return if not is_embedded: - imgui.set_next_window_size(imgui.ImVec2(800, 600), imgui.Cond_.first_use_ever) - opened, self.show_preset_manager_window = imgui.begin("Preset Manager", self.show_preset_manager_window) + imgui.set_next_window_size(imgui.ImVec2(1000, 800), imgui.Cond_.first_use_ever) + opened, self.show_preset_manager_window = imgui.begin("Prompt Presets Manager", self.show_preset_manager_window) if not opened: imgui.end() return @@ -1013,248 +1081,229 @@ class App: def _render_tool_preset_manager_content(self, is_embedded: bool = False) -> None: avail = imgui.get_content_region_avail() - # Left Column: Listbox - imgui.begin_child("tool_preset_list_area", imgui.ImVec2(avail.x * 0.25, avail.y), True) try: - if imgui.button("New Tool Preset", imgui.ImVec2(-1, 0)): - self._editing_tool_preset_name = "" - self._editing_tool_preset_categories = {cat: {} for cat in models.DEFAULT_TOOL_CATEGORIES} - self._editing_tool_preset_scope = "project" - self._selected_tool_preset_idx = -1 - if imgui.is_item_hovered(): - imgui.set_tooltip("Create a new tool preset configuration.") - - imgui.separator() - preset_names = sorted(self.controller.tool_presets.keys()) - for i, name in enumerate(preset_names): - is_selected = (self._selected_tool_preset_idx == i) - if imgui.selectable(name, is_selected)[0]: - self._selected_tool_preset_idx = i - self._editing_tool_preset_name = name - preset = self.controller.tool_presets[name] + if imgui.begin_table("tp_main_split", 2, imgui.TableFlags_.resizable | imgui.TableFlags_.borders_inner_v): + imgui.table_setup_column("List", imgui.TableColumnFlags_.width_fixed, 200) + imgui.table_setup_column("Editor", imgui.TableColumnFlags_.width_stretch) + imgui.table_next_row() + + # Left Column: Presets + imgui.table_next_column() + imgui.begin_child("tp_list_pane", imgui.ImVec2(0, 0), False) + if imgui.button("New Tool Preset", imgui.ImVec2(-1, 0)): + self._editing_tool_preset_name = "" self._editing_tool_preset_categories = {cat: {} for cat in models.DEFAULT_TOOL_CATEGORIES} - for cat, tools in preset.categories.items(): - self._editing_tool_preset_categories[cat] = copy.deepcopy(tools) - finally: - imgui.end_child() + self._editing_tool_preset_scope = "project" + self._selected_tool_preset_idx = -1 + imgui.separator() + preset_names = sorted(self.controller.tool_presets.keys()) + for i, name in enumerate(preset_names): + if imgui.selectable(name, self._selected_tool_preset_idx == i)[0]: + self._selected_tool_preset_idx = i + self._editing_tool_preset_name = name + preset = self.controller.tool_presets[name] + self._editing_tool_preset_categories = {cat: copy.deepcopy(tools) for cat, tools in preset.categories.items()} + imgui.end_child() - imgui.same_line() + # Right Column: Editor + imgui.table_next_column() + avail_r = imgui.get_content_region_avail() + # Subtract space for bottom buttons (approx 45px) + imgui.begin_child("tp_edit_pane", imgui.ImVec2(0, avail_r.y - 45), False) + + p_name = self._editing_tool_preset_name or "(New Tool Preset)" + imgui.text_colored(C_IN, f"Editing Tool Preset: {p_name}") + imgui.separator() + + if imgui.begin_table("tp_meta", 2): + imgui.table_setup_column("L", imgui.TableColumnFlags_.width_fixed, 80) + imgui.table_setup_column("F", imgui.TableColumnFlags_.width_stretch) + imgui.table_next_row() + imgui.table_next_column() + imgui.text("Name:") + imgui.table_next_column() + imgui.set_next_item_width(-1) + _, self._editing_tool_preset_name = imgui.input_text("##etpn", self._editing_tool_preset_name) + imgui.table_next_row() + imgui.table_next_column() + imgui.text("Scope:") + imgui.table_next_column() + if imgui.radio_button("Global", self._editing_tool_preset_scope == "global"): + self._editing_tool_preset_scope = "global" + imgui.same_line() + if imgui.radio_button("Project", self._editing_tool_preset_scope == "project"): + self._editing_tool_preset_scope = "project" + imgui.end_table() - # Right Column: Edit Area - imgui.begin_child("tool_preset_edit_area", imgui.ImVec2(0, avail.y), False) - try: - p_name = self._editing_tool_preset_name or "(New Tool Preset)" - imgui.text_colored(C_IN, f"Editing Tool Preset: {p_name}") - imgui.separator() - imgui.dummy(imgui.ImVec2(0, 8)) + imgui.dummy(imgui.ImVec2(0, 4)) + imgui.text("Categories & Tools:") + imgui.same_line() + cat_opts = ["All"] + sorted(list(models.DEFAULT_TOOL_CATEGORIES.keys())) + f_idx = cat_opts.index(self.ui_tool_filter_category) if self.ui_tool_filter_category in cat_opts else 0 + imgui.set_next_item_width(200) + ch_cat, next_f_idx = imgui.combo("##tp_filter", f_idx, cat_opts) + if ch_cat: + self.ui_tool_filter_category = cat_opts[next_f_idx] - imgui.text("Name:") - imgui.set_next_item_width(-1) - _, self._editing_tool_preset_name = imgui.input_text("##edit_tp_name", self._editing_tool_preset_name) - imgui.dummy(imgui.ImVec2(0, 8)) - - imgui.text("Scope:") - if imgui.radio_button("Global", self._editing_tool_preset_scope == "global"): - self._editing_tool_preset_scope = "global" - imgui.same_line() - if imgui.radio_button("Project", self._editing_tool_preset_scope == "project"): - self._editing_tool_preset_scope = "project" - imgui.dummy(imgui.ImVec2(0, 8)) - - imgui.text("Categories & Tools:") - cat_options = ["All"] + sorted(list(models.DEFAULT_TOOL_CATEGORIES.keys())) - try: - f_idx = cat_options.index(self.ui_tool_filter_category) - except ValueError: - f_idx = 0 - imgui.set_next_item_width(200) - ch_cat, next_f_idx = imgui.combo("Filter Category##tp", f_idx, cat_options) - if ch_cat: - self.ui_tool_filter_category = cat_options[next_f_idx] - - imgui.begin_child("tp_categories_scroll", imgui.ImVec2(0, 300), True) - try: + # Use ~35% of height for tool list + tool_list_h = (avail_r.y - 45) * 0.35 + imgui.begin_child("tp_scroll", imgui.ImVec2(0, tool_list_h), True) for cat_name, default_tools in models.DEFAULT_TOOL_CATEGORIES.items(): if self.ui_tool_filter_category != "All" and self.ui_tool_filter_category != cat_name: continue if imgui.tree_node(cat_name): if cat_name not in self._editing_tool_preset_categories: self._editing_tool_preset_categories[cat_name] = [] - current_cat_tools = self._editing_tool_preset_categories[cat_name] # List of Tool - - for tool_name in default_tools: - # Find existing Tool object in list - tool = next((t for t in current_cat_tools if t.name == tool_name), None) - mode = "disabled" if tool is None else tool.approval - - imgui.text(tool_name) - imgui.same_line(180) - - if imgui.radio_button(f"Off##{cat_name}_{tool_name}", mode == "disabled"): - if tool: current_cat_tools.remove(tool) - imgui.same_line() - if imgui.radio_button(f"Auto##{cat_name}_{tool_name}", mode == "auto"): - if not tool: - tool = models.Tool(name=tool_name, approval="auto") - current_cat_tools.append(tool) - else: - tool.approval = "auto" - imgui.same_line() - if imgui.radio_button(f"Ask##{cat_name}_{tool_name}", mode == "ask"): - if not tool: - tool = models.Tool(name=tool_name, approval="ask") - current_cat_tools.append(tool) - else: - tool.approval = "ask" - - if tool: - imgui.same_line(350) - imgui.set_next_item_width(100) - _, tool.weight = imgui.slider_int(f"Weight##{cat_name}_{tool_name}", tool.weight, 1, 5) + curr_cat_tools = self._editing_tool_preset_categories[cat_name] + if imgui.begin_table(f"tt_{cat_name}", 2, imgui.TableFlags_.borders_inner_v): + imgui.table_setup_column("Tool", imgui.TableColumnFlags_.width_fixed, 180) + imgui.table_setup_column("Ctrls", imgui.TableColumnFlags_.width_stretch) + for tool_name in default_tools: + tool = next((t for t in curr_cat_tools if t.name == tool_name), None) + mode = "disabled" if tool is None else tool.approval + imgui.table_next_row() + imgui.table_next_column() + imgui.text(tool_name) + imgui.table_next_column() + if imgui.radio_button(f"Off##{cat_name}_{tool_name}", mode == "disabled"): + if tool: curr_cat_tools.remove(tool) imgui.same_line() - pb_str = json.dumps(tool.parameter_bias) - imgui.set_next_item_width(150) - ch_pb, pb_new = imgui.input_text(f"Params##{cat_name}_{tool_name}", pb_str) - if ch_pb: - try: tool.parameter_bias = json.loads(pb_new) - except: pass - imgui.tree_pop() - finally: + if imgui.radio_button(f"Auto##{cat_name}_{tool_name}", mode == "auto"): + if not tool: + tool = models.Tool(name=tool_name, approval="auto") + current_cat_tools.append(tool) + else: + tool.approval = "auto" + imgui.same_line() + if imgui.radio_button(f"Ask##{cat_name}_{tool_name}", mode == "ask"): + if not tool: + tool = models.Tool(name=tool_name, approval="ask") + current_cat_tools.append(tool) + else: + tool.approval = "ask" + if tool: + imgui.same_line() + imgui.text(" W:") + imgui.same_line() + imgui.set_next_item_width(60) + _, tool.weight = imgui.slider_int(f"##w_{cat_name}_{tool_name}", tool.weight, 1, 5) + imgui.same_line() + imgui.text(" P:") + imgui.same_line() + imgui.set_next_item_width(-1) + pb_str = json.dumps(tool.parameter_bias) + ch_pb, pb_new = imgui.input_text(f"##pb_{cat_name}_{tool_name}", pb_str) + if ch_pb: + try: tool.parameter_bias = json.loads(pb_new) + except: pass + imgui.end_table() + imgui.tree_pop() imgui.end_child() - imgui.dummy(imgui.ImVec2(0, 8)) - imgui.separator() - imgui.text_colored(C_SUB, "Bias Profiles") - imgui.begin_child("bias_profiles_area", imgui.ImVec2(0, 300), True) - try: - avail_bias = imgui.get_content_region_avail() - imgui.begin_child("bias_list", imgui.ImVec2(avail_bias.x * 0.3, avail_bias.y), False) - if imgui.button("New Profile", imgui.ImVec2(-1, 0)): - self._editing_bias_profile_name = "" - self._editing_bias_profile_tool_weights = {} - self._editing_bias_profile_category_multipliers = {} - self._selected_bias_profile_idx = -1 - imgui.separator() - bnames = sorted(self.bias_profiles.keys()) - for i, bname in enumerate(bnames): - is_sel = (self._selected_bias_profile_idx == i) - if imgui.selectable(bname, is_sel)[0]: - self._selected_bias_profile_idx = i - self._editing_bias_profile_name = bname - profile = self.bias_profiles[bname] - self._editing_bias_profile_tool_weights = copy.deepcopy(profile.tool_weights) - self._editing_bias_profile_category_multipliers = copy.deepcopy(profile.category_multipliers) - imgui.end_child() - imgui.same_line() - imgui.begin_child("bias_edit", imgui.ImVec2(0, avail_bias.y), False) - imgui.text("Name:") - imgui.set_next_item_width(-1) - _, self._editing_bias_profile_name = imgui.input_text("##b_name", self._editing_bias_profile_name) - - imgui.text_colored(C_KEY, "Tool Weights:") - to_remove_tw = [] - for tw_name, tw_val in list(self._editing_bias_profile_tool_weights.items()): - imgui.text(f" {tw_name}:") - imgui.same_line(150) - imgui.set_next_item_width(100) - changed, new_val = imgui.slider_int(f"##tw_{tw_name}", tw_val, 1, 10) - if changed: self._editing_bias_profile_tool_weights[tw_name] = new_val - imgui.same_line() - if imgui.button(f"x##rem_tw_{tw_name}"): - to_remove_tw.append(tw_name) - for r in to_remove_tw: del self._editing_bias_profile_tool_weights[r] - - # Add Tool Override - imgui.set_next_item_width(150) - if imgui.begin_combo("##add_tw_combo", self._new_bias_tool_name): - for tn in models.AGENT_TOOL_NAMES: - if tn not in self._editing_bias_profile_tool_weights: - if imgui.selectable(tn, tn == self._new_bias_tool_name)[0]: - self._new_bias_tool_name = tn - imgui.end_combo() - imgui.same_line() - if imgui.button("Add Tool Override"): - self._editing_bias_profile_tool_weights[self._new_bias_tool_name] = 5 - imgui.dummy(imgui.ImVec2(0, 4)) - imgui.text_colored(C_KEY, "Category Multipliers:") - to_remove_cm = [] - for cm_name, cm_val in list(self._editing_bias_profile_category_multipliers.items()): - imgui.text(f" {cm_name}:") - imgui.same_line(150) - imgui.set_next_item_width(100) - changed, new_val = imgui.slider_float(f"##cm_{cm_name}", cm_val, 0.1, 5.0, "%.1fx") - if changed: self._editing_bias_profile_category_multipliers[cm_name] = new_val + imgui.separator() + imgui.text_colored(C_SUB, "Bias Profiles") + # Bias fills remaining space + imgui.begin_child("bias_area", imgui.ImVec2(0, imgui.get_content_region_avail().y), True) + if imgui.begin_table("bias_split", 2, imgui.TableFlags_.resizable | imgui.TableFlags_.borders_inner_v): + imgui.table_setup_column("BList", imgui.TableColumnFlags_.width_fixed, 150) + imgui.table_setup_column("BEdit", imgui.TableColumnFlags_.width_stretch) + imgui.table_next_row() + imgui.table_next_column() + imgui.begin_child("blist_pane", imgui.ImVec2(0, 0), False) + if imgui.button("New Profile", imgui.ImVec2(-1, 0)): + self._editing_bias_profile_name = "" + self._editing_bias_profile_tool_weights = {} + self._editing_bias_profile_category_multipliers = {} + self._selected_bias_profile_idx = -1 + imgui.separator() + bnames = sorted(self.bias_profiles.keys()) + for i, bname in enumerate(bnames): + if imgui.selectable(bname, self._selected_bias_profile_idx == i)[0]: + self._selected_bias_profile_idx = i + self._editing_bias_profile_name = bname + prof = self.bias_profiles[bname] + self._editing_bias_profile_tool_weights = copy.deepcopy(prof.tool_weights) + self._editing_bias_profile_category_multipliers = copy.deepcopy(prof.category_multipliers) + imgui.end_child() + imgui.table_next_column() + imgui.begin_child("bedit_pane", imgui.ImVec2(0, 0), False) + imgui.text("Name:") imgui.same_line() - if imgui.button(f"x##rem_cm_{cm_name}"): - to_remove_cm.append(cm_name) - for r in to_remove_cm: del self._editing_bias_profile_category_multipliers[r] - - # Add Category Override - imgui.set_next_item_width(150) - cat_names = sorted(list(models.DEFAULT_TOOL_CATEGORIES.keys())) - if imgui.begin_combo("##add_cm_combo", self._new_bias_category_name): - for cn in cat_names: - if cn not in self._editing_bias_profile_category_multipliers: - if imgui.selectable(cn, cn == self._new_bias_category_name)[0]: - self._new_bias_category_name = cn - imgui.end_combo() - imgui.same_line() - if imgui.button("Add Category Override"): - self._editing_bias_profile_category_multipliers[self._new_bias_category_name] = 1.0 - - imgui.dummy(imgui.ImVec2(0, 8)) - if imgui.button("Save Profile"): - try: - prof = models.BiasProfile( - name=self._editing_bias_profile_name, - tool_weights=self._editing_bias_profile_tool_weights, - category_multipliers=self._editing_bias_profile_category_multipliers - ) - self.controller._cb_save_bias_profile(prof, self._editing_tool_preset_scope) - self.ai_status = f"Bias profile '{prof.name}' saved" - except Exception as e: - self.ai_status = f"Error: {e}" - imgui.same_line() - if imgui.button("Delete Profile"): - self.controller._cb_delete_bias_profile(self._editing_bias_profile_name, self._editing_tool_preset_scope) - self.ai_status = f"Bias profile deleted" + imgui.set_next_item_width(-1) + _, self._editing_bias_profile_name = imgui.input_text("##bname", self._editing_bias_profile_name) + + imgui.text_colored(C_KEY, "Tool Weighting:") + imgui.begin_child("btool_scroll", imgui.ImVec2(0, 150), True) + for cat_name, default_tools in models.DEFAULT_TOOL_CATEGORIES.items(): + if imgui.tree_node(f"{cat_name}##b"): + if imgui.begin_table(f"bt_{cat_name}", 2): + imgui.table_setup_column("T", imgui.TableColumnFlags_.width_fixed, 150) + imgui.table_setup_column("W", imgui.TableColumnFlags_.width_stretch) + for tn in default_tools: + imgui.table_next_row() + imgui.table_next_column() + imgui.text(tn) + imgui.table_next_column() + curr_w = self._editing_bias_profile_tool_weights.get(tn, 3) + imgui.set_next_item_width(-1) + ch_w, n_w = imgui.slider_int(f"##bw_{tn}", curr_w, 1, 10) + if ch_w: + self._editing_bias_profile_tool_weights[tn] = n_w + imgui.end_table() + imgui.tree_pop() + imgui.end_child() + + imgui.text_colored(C_KEY, "Category Multipliers:") + imgui.begin_child("bcat_scroll", imgui.ImVec2(0, 100), True) + if imgui.begin_table("bcats", 2): + imgui.table_setup_column("C", imgui.TableColumnFlags_.width_fixed, 150) + imgui.table_setup_column("M", imgui.TableColumnFlags_.width_stretch) + for cn in sorted(models.DEFAULT_TOOL_CATEGORIES.keys()): + imgui.table_next_row() + imgui.table_next_column() + imgui.text(cn) + imgui.table_next_column() + curr_m = self._editing_bias_profile_category_multipliers.get(cn, 1.0) + imgui.set_next_item_width(-1) + ch_m, n_m = imgui.slider_float(f"##cm_{cn}", curr_m, 0.1, 5.0, "%.1fx") + if ch_m: + self._editing_bias_profile_category_multipliers[cn] = n_m + imgui.end_table() + imgui.end_child() + + if imgui.button("Save Profile", imgui.ImVec2(-1, 0)): + try: + p = models.BiasProfile(name=self._editing_bias_profile_name, tool_weights=self._editing_bias_profile_tool_weights, category_multipliers=self._editing_bias_profile_category_multipliers) + self.controller._cb_save_bias_profile(p, self._editing_tool_preset_scope) + self.ai_status = f"Saved: {p.name}" + except Exception as e: + self.ai_status = f"Error: {e}" + imgui.end_child() + imgui.end_table() imgui.end_child() - finally: imgui.end_child() - imgui.dummy(imgui.ImVec2(0, 8)) - if imgui.button("Save", imgui.ImVec2(100, 0)): - if self._editing_tool_preset_name.strip(): - self.controller._cb_save_tool_preset( - self._editing_tool_preset_name.strip(), - self._editing_tool_preset_categories, - self._editing_tool_preset_scope - ) - self.ai_status = f"Tool preset '{self._editing_tool_preset_name}' saved" - if imgui.is_item_hovered(): - imgui.set_tooltip("Save the current tool preset configuration.") - - imgui.same_line() - if imgui.button("Delete", imgui.ImVec2(100, 0)): - if self._editing_tool_preset_name.strip(): - self.controller._cb_delete_tool_preset( - self._editing_tool_preset_name.strip(), - self._editing_tool_preset_scope - ) - self.ai_status = f"Tool preset '{self._editing_tool_preset_name}' deleted" - self._editing_tool_preset_name = "" - self._editing_tool_preset_categories = {} - self._selected_tool_preset_idx = -1 - if imgui.is_item_hovered(): - imgui.set_tooltip("Delete this tool preset permanently.") - - if not is_embedded: + # Bottom Buttons + imgui.separator() + imgui.dummy(imgui.ImVec2(0, 4)) + if imgui.button("Save Tool Preset", imgui.ImVec2(150, 0)): + if self._editing_tool_preset_name.strip(): + self.controller._cb_save_tool_preset(self._editing_tool_preset_name.strip(), self._editing_tool_preset_categories, self._editing_tool_preset_scope) + self.ai_status = f"Saved Preset: {self._editing_tool_preset_name}" imgui.same_line() - if imgui.button("Close", imgui.ImVec2(100, 0)): - self.show_tool_preset_manager_window = False + if imgui.button("Delete Preset", imgui.ImVec2(120, 0)): + if self._editing_tool_preset_name: + self.controller._cb_delete_tool_preset(self._editing_tool_preset_name, self._editing_tool_preset_scope) + self._editing_tool_preset_name = "" + self._selected_tool_preset_idx = -1 + imgui.same_line() + if not is_embedded: + if imgui.button("Close", imgui.ImVec2(100, 0)): + self.show_tool_preset_manager_window = False + imgui.end_table() finally: - imgui.end_child() + pass def _render_tool_preset_manager_window(self, is_embedded: bool = False) -> None: if not self.show_tool_preset_manager_window and not is_embedded: return @@ -1282,22 +1331,20 @@ class App: return try: - avail = imgui.get_content_region_avail() - # Left Pane: List of Personas - imgui.begin_child("persona_list_area", imgui.ImVec2(avail.x * 0.25, avail.y), True) - try: + if imgui.begin_table("persona_main_split", 2, imgui.TableFlags_.resizable | imgui.TableFlags_.borders_inner_v): + imgui.table_setup_column("List", imgui.TableColumnFlags_.width_fixed, 200) + imgui.table_setup_column("Editor", imgui.TableColumnFlags_.width_stretch) + imgui.table_next_row() + + # --- Left Pane: List --- + imgui.table_next_column() + imgui.begin_child("persona_list_pane", imgui.ImVec2(0, 0), False) if imgui.button("New Persona", imgui.ImVec2(-1, 0)): self._editing_persona_name = "" self._editing_persona_system_prompt = "" self._editing_persona_tool_preset_id = "" self._editing_persona_bias_profile_id = "" - self._editing_persona_preferred_models_list = [{ - "provider": self.current_provider, - "model": self.current_model, - "temperature": 0.7, - "max_output_tokens": 4096, - "history_trunc_limit": 900000 - }] + self._editing_persona_preferred_models_list = [{"provider": self.current_provider, "model": self.current_model, "temperature": 0.7, "max_output_tokens": 4096, "history_trunc_limit": 900000}] self._editing_persona_scope = "project" self._editing_persona_is_new = True imgui.separator() @@ -1314,45 +1361,49 @@ class App: self._editing_persona_preferred_models_list = copy.deepcopy(p.preferred_models) if p.preferred_models else [] self._editing_persona_scope = self.controller.persona_manager.get_persona_scope(p.name) self._editing_persona_is_new = False - finally: imgui.end_child() - - imgui.same_line() - - # Right Pane: Editor - imgui.begin_child("persona_edit_area", imgui.ImVec2(0, avail.y), False) - try: + + # --- Right Pane: Editor --- + imgui.table_next_column() + avail = imgui.get_content_region_avail() + # Subtract space for bottom buttons (approx 45px) + imgui.begin_child("persona_editor_pane", imgui.ImVec2(0, avail.y - 45), False) + header = "New Persona" if getattr(self, '_editing_persona_is_new', True) else f"Editing Persona: {self._editing_persona_name}" imgui.text_colored(C_IN, header) imgui.separator() - imgui.dummy(imgui.ImVec2(0, 8)) - imgui.text("Name:") - imgui.set_next_item_width(-1) - _, self._editing_persona_name = imgui.input_text("##pname", self._editing_persona_name, 128) - imgui.dummy(imgui.ImVec2(0, 8)) - - imgui.text("Scope:") - if imgui.radio_button("Global##pscope", getattr(self, '_editing_persona_scope', 'project') == "global"): - self._editing_persona_scope = "global" - imgui.same_line() - if imgui.radio_button("Project##pscope", getattr(self, '_editing_persona_scope', 'project') == "project"): - self._editing_persona_scope = "project" - imgui.dummy(imgui.ImVec2(0, 8)) + if imgui.begin_table("p_meta", 2): + imgui.table_setup_column("L", imgui.TableColumnFlags_.width_fixed, 60) + imgui.table_setup_column("F", imgui.TableColumnFlags_.width_stretch) + imgui.table_next_row() + imgui.table_next_column() + imgui.text("Name:") + imgui.table_next_column() + imgui.set_next_item_width(-1) + _, self._editing_persona_name = imgui.input_text("##pname", self._editing_persona_name, 128) + imgui.table_next_row() + imgui.table_next_column() + imgui.text("Scope:") + imgui.table_next_column() + if imgui.radio_button("Global##pscope", getattr(self, '_editing_persona_scope', 'project') == "global"): + self._editing_persona_scope = "global" + imgui.same_line() + if imgui.radio_button("Project##pscope", getattr(self, '_editing_persona_scope', 'project') == "project"): + self._editing_persona_scope = "project" + imgui.end_table() + imgui.dummy(imgui.ImVec2(0, 4)) imgui.separator() - imgui.dummy(imgui.ImVec2(0, 8)) - imgui.text("Preferred Models:") - providers = self.controller.PROVIDERS - if not hasattr(self, '_persona_pref_models_expanded'): - self._persona_pref_models_expanded = {} - imgui.begin_child("pref_models_list", imgui.ImVec2(0, 200), True) + # Use ~35% of remaining height for models + models_h = (avail.y - 45) * 0.35 + imgui.begin_child("pref_models_scroll", imgui.ImVec2(0, models_h), True) to_remove = [] - avail_edit = imgui.get_content_region_avail() + providers = self.controller.PROVIDERS + if not hasattr(self, '_persona_pref_models_expanded'): self._persona_pref_models_expanded = {} for i, entry in enumerate(self._editing_persona_preferred_models_list): imgui.push_id(f"pref_model_{i}") - prov = entry.get("provider", "Unknown") mod = entry.get("model", "Unknown") is_expanded = self._persona_pref_models_expanded.get(i, False) @@ -1360,7 +1411,6 @@ class App: if imgui.button("-" if is_expanded else "+"): self._persona_pref_models_expanded[i] = not is_expanded imgui.same_line() - imgui.text(f"{i+1}.") imgui.same_line() imgui.text_colored(C_LBL, f"{prov}") @@ -1369,163 +1419,147 @@ class App: imgui.same_line() imgui.text_colored(C_IN, f"{mod}") + if not is_expanded: + imgui.same_line() + summary = f" (T:{entry.get('temperature', 0.7):.1f}, M:{entry.get('max_output_tokens', 0)}, H:{entry.get('history_trunc_limit', 0)})" + imgui.text_colored(C_SUB, summary) + imgui.same_line(imgui.get_content_region_avail().x - 30) if imgui.button("x"): to_remove.append(i) - + if is_expanded: imgui.indent(20) - - imgui.text("Provider:") - imgui.same_line() - imgui.set_next_item_width(150) - p_idx = providers.index(prov) + 1 if prov in providers else 0 - changed_p, p_idx = imgui.combo("##prov", p_idx, ["None"] + providers) - if changed_p: - entry["provider"] = providers[p_idx-1] if p_idx > 0 else "" - - imgui.same_line() - imgui.text("Model:") - imgui.same_line() - imgui.set_next_item_width(250) - curr_prov = entry.get("provider", "") - m_list = self.controller.all_available_models.get(curr_prov, []) - m_idx = m_list.index(mod) + 1 if mod in m_list else 0 - changed_m, m_idx = imgui.combo("##model", m_idx, ["None"] + m_list) - if changed_m: - entry["model"] = m_list[m_idx-1] if m_idx > 0 else "" - - imgui.text("Temperature:") - imgui.same_line() - imgui.set_next_item_width(avail_edit.x * 0.3) - _, entry["temperature"] = imgui.slider_float("##temp_slider", entry.get("temperature", 0.7), 0.0, 2.0, "%.1f") - imgui.same_line() - imgui.set_next_item_width(80) - _, entry["temperature"] = imgui.input_float("##temp_input", entry.get("temperature", 0.7), 0.1, 0.1, "%.1f") - - imgui.same_line() - imgui.text("Max Output Tokens:") - imgui.same_line() - imgui.set_next_item_width(100) - _, entry["max_output_tokens"] = imgui.input_int("##maxt", entry.get("max_output_tokens", 4096)) - - imgui.text("History Truncation Limit:") - imgui.same_line() - imgui.set_next_item_width(100) - _, entry["history_trunc_limit"] = imgui.input_int("##hist", entry.get("history_trunc_limit", 900000)) - + if imgui.begin_table("model_settings", 2, imgui.TableFlags_.borders_inner_v): + imgui.table_setup_column("Label", imgui.TableColumnFlags_.width_fixed, 120) + imgui.table_setup_column("Control", imgui.TableColumnFlags_.width_stretch) + + imgui.table_next_row() + imgui.table_next_column() + imgui.text("Provider:") + imgui.table_next_column() + imgui.set_next_item_width(-1) + p_idx = providers.index(prov) + 1 if prov in providers else 0 + ch_p, p_idx = imgui.combo("##prov", p_idx, ["None"] + providers) + if ch_p: entry["provider"] = providers[p_idx-1] if p_idx > 0 else "" + + imgui.table_next_row() + imgui.table_next_column() + imgui.text("Model:") + imgui.table_next_column() + imgui.set_next_item_width(-1) + m_list = self.controller.all_available_models.get(entry.get("provider", ""), []) + m_idx = m_list.index(mod) + 1 if mod in m_list else 0 + ch_m, m_idx = imgui.combo("##model", m_idx, ["None"] + m_list) + if ch_m: entry["model"] = m_list[m_idx-1] if m_idx > 0 else "" + + imgui.table_next_row() + imgui.table_next_column() + imgui.text("Temperature:") + imgui.table_next_column() + cw = imgui.get_content_region_avail().x + imgui.set_next_item_width(cw * 0.7) + _, entry["temperature"] = imgui.slider_float("##ts", entry.get("temperature", 0.7), 0.0, 2.0, "%.1f") + imgui.same_line() + imgui.set_next_item_width(-1) + _, entry["temperature"] = imgui.input_float("##ti", entry.get("temperature", 0.7), 0.1, 0.1, "%.1f") + + imgui.table_next_row() + imgui.table_next_column() + imgui.text("Max Tokens:") + imgui.table_next_column() + imgui.set_next_item_width(-1) + _, entry["max_output_tokens"] = imgui.input_int("##maxt", entry.get("max_output_tokens", 4096)) + + imgui.table_next_row() + imgui.table_next_column() + imgui.text("History Limit:") + imgui.table_next_column() + imgui.set_next_item_width(-1) + _, entry["history_trunc_limit"] = imgui.input_int("##hist", entry.get("history_trunc_limit", 900000)) + imgui.end_table() imgui.unindent(20) - imgui.dummy(imgui.ImVec2(0, 4)) imgui.pop_id() for i in reversed(to_remove): self._editing_persona_preferred_models_list.pop(i) - if i in self._persona_pref_models_expanded: - del self._persona_pref_models_expanded[i] imgui.end_child() - if imgui.button("Add Preferred Model"): - idx = len(self._editing_persona_preferred_models_list) - self._editing_persona_preferred_models_list.append({ - "provider": self.current_provider, - "model": self.current_model, - "temperature": 0.7, - "max_output_tokens": 4096, - "history_trunc_limit": 900000 - }) - self._persona_pref_models_expanded[idx] = True + if imgui.button("Add Preferred Model", imgui.ImVec2(-1, 0)): + self._editing_persona_preferred_models_list.append({"provider": self.current_provider, "model": self.current_model, "temperature": 0.7, "max_output_tokens": 4096, "history_trunc_limit": 900000}) - imgui.dummy(imgui.ImVec2(0, 8)) + imgui.dummy(imgui.ImVec2(0, 4)) imgui.separator() - imgui.dummy(imgui.ImVec2(0, 8)) + if imgui.begin_table("p_assign", 2): + imgui.table_setup_column("C1") + imgui.table_setup_column("C2") + imgui.table_next_row() + imgui.table_next_column() + imgui.text("Tool Preset:") + tn = ["None"] + sorted(self.controller.tool_presets.keys()) + t_idx = tn.index(self._editing_persona_tool_preset_id) if getattr(self, '_editing_persona_tool_preset_id', '') in tn else 0 + imgui.set_next_item_width(-1) + _, t_idx = imgui.combo("##ptp", t_idx, tn) + self._editing_persona_tool_preset_id = tn[t_idx] if t_idx > 0 else "" + imgui.table_next_column() + imgui.text("Bias Profile:") + bn = ["None"] + sorted(self.controller.bias_profiles.keys()) + b_idx = bn.index(self._editing_persona_bias_profile_id) if getattr(self, '_editing_persona_bias_profile_id', '') in bn else 0 + imgui.set_next_item_width(-1) + _, b_idx = imgui.combo("##pbp", b_idx, bn) + self._editing_persona_bias_profile_id = bn[b_idx] if b_idx > 0 else "" + imgui.end_table() - imgui.text("Tool Preset:") - imgui.same_line() - t_preset_names = ["None"] + sorted(self.controller.tool_presets.keys()) - t_idx = t_preset_names.index(self._editing_persona_tool_preset_id) if getattr(self, '_editing_persona_tool_preset_id', '') in t_preset_names else 0 - imgui.set_next_item_width(200) - _, t_idx = imgui.combo("##ptoolpreset", t_idx, t_preset_names) - self._editing_persona_tool_preset_id = t_preset_names[t_idx] if t_idx > 0 else "" - - imgui.same_line() - imgui.text("Bias Profile:") - imgui.same_line() - bias_names = ["None"] + sorted(self.controller.bias_profiles.keys()) - b_idx = bias_names.index(self._editing_persona_bias_profile_id) if getattr(self, '_editing_persona_bias_profile_id', '') in bias_names else 0 - imgui.set_next_item_width(200) - _, b_idx = imgui.combo("##pbiasprofile", b_idx, bias_names) - self._editing_persona_bias_profile_id = bias_names[b_idx] if b_idx > 0 else "" - - imgui.same_line() - if imgui.button("Manage Tools##p_tools"): + if imgui.button("Manage Tools & Biases", imgui.ImVec2(-1, 0)): self.show_tool_preset_manager_window = True - imgui.dummy(imgui.ImVec2(0, 8)) + imgui.dummy(imgui.ImVec2(0, 4)) imgui.separator() - imgui.dummy(imgui.ImVec2(0, 8)) - imgui.text("System Prompt:") - - imgui.text("Load from Preset:") + imgui.begin_child("p_prompt_header", imgui.ImVec2(0, 30), False) + imgui.text("Template:") imgui.same_line() - prompt_presets = ["Select..."] + sorted(self.controller.presets.keys()) + p_pre = ["Select..."] + sorted(self.controller.presets.keys()) if not hasattr(self, "_load_preset_idx"): self._load_preset_idx = 0 - imgui.set_next_item_width(150) - _, self._load_preset_idx = imgui.combo("##load_preset", self._load_preset_idx, prompt_presets) + imgui.set_next_item_width(200) + _, self._load_preset_idx = imgui.combo("##load_p", self._load_preset_idx, p_pre) imgui.same_line() - if imgui.button("Apply##apply_p"): + if imgui.button("Apply"): if self._load_preset_idx > 0: - pname = prompt_presets[self._load_preset_idx] - if pname in self.controller.presets: - p = self.controller.presets[pname] - self._editing_persona_system_prompt = p.system_prompt - self._load_preset_idx = 0 - + self._editing_persona_system_prompt = self.controller.presets[p_pre[self._load_preset_idx]].system_prompt imgui.same_line() - if imgui.button("Manage Prompts##p_prompts"): + if imgui.button("Manage"): self.show_preset_manager_window = True + imgui.end_child() + + # Prompt fills remaining space + _, self._editing_persona_system_prompt = imgui.input_text_multiline("##pprompt", self._editing_persona_system_prompt, imgui.ImVec2(-1, imgui.get_content_region_avail().y)) + imgui.end_child() - _, self._editing_persona_system_prompt = imgui.input_text_multiline("##pprompt", self._editing_persona_system_prompt, imgui.ImVec2(-1, 150)) - - imgui.dummy(imgui.ImVec2(0, 8)) + # --- Bottom Buttons --- imgui.separator() - imgui.dummy(imgui.ImVec2(0, 8)) - - if imgui.button("Save Persona", imgui.ImVec2(120, 0)): + imgui.dummy(imgui.ImVec2(0, 4)) + if imgui.button("Save Persona", imgui.ImVec2(150, 0)): if self._editing_persona_name.strip(): try: import copy - save_models = copy.deepcopy(self._editing_persona_preferred_models_list) - - persona = models.Persona( - name=self._editing_persona_name.strip(), - system_prompt=self._editing_persona_system_prompt, - tool_preset=self._editing_persona_tool_preset_id or None, - bias_profile=self._editing_persona_bias_profile_id or None, - preferred_models=save_models, - ) + persona = models.Persona(name=self._editing_persona_name.strip(), system_prompt=self._editing_persona_system_prompt, tool_preset=self._editing_persona_tool_preset_id or None, bias_profile=self._editing_persona_bias_profile_id or None, preferred_models=copy.deepcopy(self._editing_persona_preferred_models_list)) self.controller._cb_save_persona(persona, getattr(self, '_editing_persona_scope', 'project')) - self.ai_status = f"Saved Persona: {persona.name}" + self.ai_status = f"Saved: {persona.name}" except Exception as e: - self.ai_status = f"Error saving persona: {e}" - import traceback - traceback.print_exc() + self.ai_status = f"Error: {e}" else: self.ai_status = "Name required" - imgui.same_line() if imgui.button("Delete", imgui.ImVec2(100, 0)): if not getattr(self, '_editing_persona_is_new', True) and self._editing_persona_name: self.controller._cb_delete_persona(self._editing_persona_name, getattr(self, '_editing_persona_scope', 'project')) - self.ai_status = f"Deleted Persona: {self._editing_persona_name}" self._editing_persona_name = "" self._editing_persona_is_new = True - if not is_embedded: imgui.same_line() if imgui.button("Close", imgui.ImVec2(100, 0)): self.show_persona_editor_window = False - finally: - imgui.end_child() + imgui.end_table() finally: if not is_embedded: imgui.end()