Compare commits
2 Commits
6ae8737c1a
...
26e03d2c9f
| Author | SHA1 | Date | |
|---|---|---|---|
| 26e03d2c9f | |||
| 6da3d95c0e |
@@ -300,7 +300,9 @@ class AppController:
|
|||||||
self._inject_mode: str = "skeleton"
|
self._inject_mode: str = "skeleton"
|
||||||
self._inject_preview: str = ""
|
self._inject_preview: str = ""
|
||||||
self._show_inject_modal: bool = False
|
self._show_inject_modal: bool = False
|
||||||
self.show_preset_manager_modal: bool = False
|
self.show_preset_manager_window: bool = False
|
||||||
|
self.show_tool_preset_manager_window: bool = False
|
||||||
|
self.show_persona_editor_window: bool = False
|
||||||
self._editing_preset_name: str = ""
|
self._editing_preset_name: str = ""
|
||||||
self._editing_preset_content: str = ""
|
self._editing_preset_content: str = ""
|
||||||
self._editing_preset_temperature: float = 0.0
|
self._editing_preset_temperature: float = 0.0
|
||||||
@@ -342,7 +344,9 @@ class AppController:
|
|||||||
'ui_active_tool_preset': 'ui_active_tool_preset',
|
'ui_active_tool_preset': 'ui_active_tool_preset',
|
||||||
'temperature': 'temperature',
|
'temperature': 'temperature',
|
||||||
'max_tokens': 'max_tokens',
|
'max_tokens': 'max_tokens',
|
||||||
'show_preset_manager_modal': 'show_preset_manager_modal',
|
'show_preset_manager_window': 'show_preset_manager_window',
|
||||||
|
'show_tool_preset_manager_window': 'show_tool_preset_manager_window',
|
||||||
|
'show_persona_editor_window': 'show_persona_editor_window',
|
||||||
'_editing_preset_name': '_editing_preset_name',
|
'_editing_preset_name': '_editing_preset_name',
|
||||||
'_editing_preset_content': '_editing_preset_content',
|
'_editing_preset_content': '_editing_preset_content',
|
||||||
'_editing_preset_temperature': '_editing_preset_temperature',
|
'_editing_preset_temperature': '_editing_preset_temperature',
|
||||||
@@ -390,7 +394,9 @@ class AppController:
|
|||||||
'ui_active_tool_preset': 'ui_active_tool_preset',
|
'ui_active_tool_preset': 'ui_active_tool_preset',
|
||||||
'temperature': 'temperature',
|
'temperature': 'temperature',
|
||||||
'max_tokens': 'max_tokens',
|
'max_tokens': 'max_tokens',
|
||||||
'show_preset_manager_modal': 'show_preset_manager_modal',
|
'show_preset_manager_window': 'show_preset_manager_window',
|
||||||
|
'show_tool_preset_manager_window': 'show_tool_preset_manager_window',
|
||||||
|
'show_persona_editor_window': 'show_persona_editor_window',
|
||||||
'_editing_preset_name': '_editing_preset_name',
|
'_editing_preset_name': '_editing_preset_name',
|
||||||
'_editing_preset_content': '_editing_preset_content',
|
'_editing_preset_content': '_editing_preset_content',
|
||||||
'_editing_preset_temperature': '_editing_preset_temperature',
|
'_editing_preset_temperature': '_editing_preset_temperature',
|
||||||
@@ -2567,3 +2573,4 @@ class AppController:
|
|||||||
tasks=self.active_track.tickets
|
tasks=self.active_track.tickets
|
||||||
)
|
)
|
||||||
project_manager.save_track_state(self.active_track.id, state, self.ui_files_base_dir)
|
project_manager.save_track_state(self.active_track.id, state, self.ui_files_base_dir)
|
||||||
|
|
||||||
|
|||||||
244
src/gui_2.py
244
src/gui_2.py
@@ -96,9 +96,9 @@ class App:
|
|||||||
self.controller.init_state()
|
self.controller.init_state()
|
||||||
self.show_windows.setdefault("Diagnostics", False)
|
self.show_windows.setdefault("Diagnostics", False)
|
||||||
self.controller.start_services(self)
|
self.controller.start_services(self)
|
||||||
self.show_preset_manager_modal = False
|
self.show_preset_manager_window = False
|
||||||
self.show_tool_preset_manager_modal = False
|
self.show_tool_preset_manager_window = False
|
||||||
self.show_persona_editor_modal = False
|
self.show_persona_editor_window = False
|
||||||
self.ui_active_tool_preset = ""
|
self.ui_active_tool_preset = ""
|
||||||
self.ui_active_bias_profile = ""
|
self.ui_active_bias_profile = ""
|
||||||
self.ui_active_persona = ""
|
self.ui_active_persona = ""
|
||||||
@@ -111,7 +111,7 @@ class App:
|
|||||||
self._editing_persona_max_tokens = 4096
|
self._editing_persona_max_tokens = 4096
|
||||||
self._editing_persona_tool_preset_id = ""
|
self._editing_persona_tool_preset_id = ""
|
||||||
self._editing_persona_bias_profile_id = ""
|
self._editing_persona_bias_profile_id = ""
|
||||||
self._editing_persona_preferred_models_list: list[str] = []
|
self._editing_persona_preferred_models_list: list[dict] = []
|
||||||
self._editing_persona_scope = "project"
|
self._editing_persona_scope = "project"
|
||||||
self._editing_persona_is_new = True
|
self._editing_persona_is_new = True
|
||||||
self._persona_editor_opened = False
|
self._persona_editor_opened = False
|
||||||
@@ -384,9 +384,9 @@ class App:
|
|||||||
self._render_track_proposal_modal()
|
self._render_track_proposal_modal()
|
||||||
self._render_patch_modal()
|
self._render_patch_modal()
|
||||||
self._render_save_preset_modal()
|
self._render_save_preset_modal()
|
||||||
self._render_preset_manager_modal()
|
self._render_preset_manager_window()
|
||||||
self._render_tool_preset_manager_modal()
|
self._render_tool_preset_manager_window()
|
||||||
self._render_persona_editor_modal()
|
self._render_persona_editor_window()
|
||||||
# Auto-save (every 60s)
|
# Auto-save (every 60s)
|
||||||
now = time.time()
|
now = time.time()
|
||||||
if now - self._last_autosave >= self._autosave_interval:
|
if now - self._last_autosave >= self._autosave_interval:
|
||||||
@@ -923,12 +923,7 @@ class App:
|
|||||||
imgui.close_current_popup()
|
imgui.close_current_popup()
|
||||||
imgui.end_popup()
|
imgui.end_popup()
|
||||||
|
|
||||||
def _render_preset_manager_modal(self) -> None:
|
def _render_preset_manager_content(self, is_embedded: bool = False) -> None:
|
||||||
if not self.show_preset_manager_modal: return
|
|
||||||
imgui.open_popup("Preset Manager")
|
|
||||||
opened, self.show_preset_manager_modal = imgui.begin_popup_modal("Preset Manager", self.show_preset_manager_modal)
|
|
||||||
if opened:
|
|
||||||
try:
|
|
||||||
avail = imgui.get_content_region_avail()
|
avail = imgui.get_content_region_avail()
|
||||||
imgui.begin_child("preset_list_area", imgui.ImVec2(250, avail.y), True)
|
imgui.begin_child("preset_list_area", imgui.ImVec2(250, avail.y), True)
|
||||||
try:
|
try:
|
||||||
@@ -985,21 +980,30 @@ class App:
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.ai_status = f"Error deleting: {e}"
|
self.ai_status = f"Error deleting: {e}"
|
||||||
imgui.set_item_tooltip("Delete the selected preset")
|
imgui.set_item_tooltip("Delete the selected preset")
|
||||||
|
if not is_embedded:
|
||||||
imgui.same_line()
|
imgui.same_line()
|
||||||
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_window = False
|
||||||
imgui.close_current_popup()
|
|
||||||
finally:
|
finally:
|
||||||
imgui.end_child()
|
imgui.end_child()
|
||||||
finally:
|
|
||||||
imgui.end_popup()
|
|
||||||
|
|
||||||
def _render_tool_preset_manager_modal(self) -> None:
|
def _render_preset_manager_window(self, is_embedded: bool = False) -> None:
|
||||||
if not self.show_tool_preset_manager_modal: return
|
if not self.show_preset_manager_window and not is_embedded: return
|
||||||
imgui.open_popup("Tool Preset Manager")
|
|
||||||
opened, self.show_tool_preset_manager_modal = imgui.begin_popup_modal("Tool Preset Manager", self.show_tool_preset_manager_modal)
|
if not is_embedded:
|
||||||
if opened:
|
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)
|
||||||
|
if not opened:
|
||||||
|
imgui.end()
|
||||||
|
return
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
self._render_preset_manager_content(is_embedded=is_embedded)
|
||||||
|
finally:
|
||||||
|
if not is_embedded:
|
||||||
|
imgui.end()
|
||||||
|
|
||||||
|
def _render_tool_preset_manager_content(self, is_embedded: bool = False) -> None:
|
||||||
avail = imgui.get_content_region_avail()
|
avail = imgui.get_content_region_avail()
|
||||||
# Left Column: Listbox
|
# Left Column: Listbox
|
||||||
imgui.begin_child("tool_preset_list_area", imgui.ImVec2(250, avail.y), True)
|
imgui.begin_child("tool_preset_list_area", imgui.ImVec2(250, avail.y), True)
|
||||||
@@ -1221,21 +1225,38 @@ class App:
|
|||||||
if imgui.is_item_hovered():
|
if imgui.is_item_hovered():
|
||||||
imgui.set_tooltip("Delete this tool preset permanently.")
|
imgui.set_tooltip("Delete this tool preset permanently.")
|
||||||
|
|
||||||
|
if not is_embedded:
|
||||||
imgui.same_line()
|
imgui.same_line()
|
||||||
if imgui.button("Close", imgui.ImVec2(100, 0)):
|
if imgui.button("Close", imgui.ImVec2(100, 0)):
|
||||||
self.show_tool_preset_manager_modal = False
|
self.show_tool_preset_manager_window = False
|
||||||
imgui.close_current_popup()
|
|
||||||
finally:
|
finally:
|
||||||
imgui.end_child()
|
imgui.end_child()
|
||||||
finally:
|
|
||||||
imgui.end_popup()
|
|
||||||
|
|
||||||
def _render_persona_editor_modal(self) -> None:
|
def _render_tool_preset_manager_window(self, is_embedded: bool = False) -> None:
|
||||||
if not self.show_persona_editor_modal: return
|
if not self.show_tool_preset_manager_window and not is_embedded: return
|
||||||
imgui.open_popup("Persona Editor")
|
|
||||||
imgui.set_next_window_size(imgui.ImVec2(800, 600), imgui.Cond_.first_use_ever)
|
if not is_embedded:
|
||||||
opened, self.show_persona_editor_modal = imgui.begin_popup_modal("Persona Editor", self.show_persona_editor_modal)
|
imgui.set_next_window_size(imgui.ImVec2(1000, 800), imgui.Cond_.first_use_ever)
|
||||||
if opened:
|
opened, self.show_tool_preset_manager_window = imgui.begin("Tool Preset Manager", self.show_tool_preset_manager_window)
|
||||||
|
if not opened:
|
||||||
|
imgui.end()
|
||||||
|
return
|
||||||
|
|
||||||
|
try:
|
||||||
|
self._render_tool_preset_manager_content(is_embedded=is_embedded)
|
||||||
|
finally:
|
||||||
|
if not is_embedded:
|
||||||
|
imgui.end()
|
||||||
|
def _render_persona_editor_window(self, is_embedded: bool = False) -> None:
|
||||||
|
if not self.show_persona_editor_window and not is_embedded: return
|
||||||
|
|
||||||
|
if not is_embedded:
|
||||||
|
imgui.set_next_window_size(imgui.ImVec2(1000, 800), imgui.Cond_.first_use_ever)
|
||||||
|
opened, self.show_persona_editor_window = imgui.begin("Persona Editor", self.show_persona_editor_window)
|
||||||
|
if not opened:
|
||||||
|
imgui.end()
|
||||||
|
return
|
||||||
|
|
||||||
try:
|
try:
|
||||||
avail = imgui.get_content_region_avail()
|
avail = imgui.get_content_region_avail()
|
||||||
# Left Pane: List of Personas
|
# Left Pane: List of Personas
|
||||||
@@ -1243,14 +1264,15 @@ class App:
|
|||||||
try:
|
try:
|
||||||
if imgui.button("New Persona", imgui.ImVec2(-1, 0)):
|
if imgui.button("New Persona", imgui.ImVec2(-1, 0)):
|
||||||
self._editing_persona_name = ""
|
self._editing_persona_name = ""
|
||||||
self._editing_persona_provider = self.current_provider
|
|
||||||
self._editing_persona_model = self.current_model
|
|
||||||
self._editing_persona_system_prompt = ""
|
self._editing_persona_system_prompt = ""
|
||||||
self._editing_persona_temperature = 0.7
|
|
||||||
self._editing_persona_max_tokens = 4096
|
|
||||||
self._editing_persona_tool_preset_id = ""
|
self._editing_persona_tool_preset_id = ""
|
||||||
self._editing_persona_bias_profile_id = ""
|
self._editing_persona_bias_profile_id = ""
|
||||||
self._editing_persona_preferred_models_list = []
|
self._editing_persona_preferred_models_list = [{
|
||||||
|
"provider": self.current_provider,
|
||||||
|
"model": self.current_model,
|
||||||
|
"temperature": 0.7,
|
||||||
|
"max_output_tokens": 4096
|
||||||
|
}]
|
||||||
self._editing_persona_scope = "project"
|
self._editing_persona_scope = "project"
|
||||||
self._editing_persona_is_new = True
|
self._editing_persona_is_new = True
|
||||||
imgui.separator()
|
imgui.separator()
|
||||||
@@ -1260,14 +1282,11 @@ class App:
|
|||||||
if imgui.selectable(name, is_sel)[0]:
|
if imgui.selectable(name, is_sel)[0]:
|
||||||
p = personas[name]
|
p = personas[name]
|
||||||
self._editing_persona_name = p.name
|
self._editing_persona_name = p.name
|
||||||
self._editing_persona_provider = p.provider or ""
|
|
||||||
self._editing_persona_model = p.model or ""
|
|
||||||
self._editing_persona_system_prompt = p.system_prompt or ""
|
self._editing_persona_system_prompt = p.system_prompt or ""
|
||||||
self._editing_persona_temperature = p.temperature if p.temperature is not None else 0.7
|
|
||||||
self._editing_persona_max_tokens = p.max_output_tokens if p.max_output_tokens is not None else 4096
|
|
||||||
self._editing_persona_tool_preset_id = p.tool_preset or ""
|
self._editing_persona_tool_preset_id = p.tool_preset or ""
|
||||||
self._editing_persona_bias_profile_id = p.bias_profile or ""
|
self._editing_persona_bias_profile_id = p.bias_profile or ""
|
||||||
self._editing_persona_preferred_models_list = list(p.preferred_models) if p.preferred_models else []
|
import copy
|
||||||
|
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_scope = self.controller.persona_manager.get_persona_scope(p.name)
|
||||||
self._editing_persona_is_new = False
|
self._editing_persona_is_new = False
|
||||||
finally:
|
finally:
|
||||||
@@ -1278,7 +1297,7 @@ class App:
|
|||||||
# Right Pane: Editor
|
# Right Pane: Editor
|
||||||
imgui.begin_child("persona_edit_area", imgui.ImVec2(0, avail.y), False)
|
imgui.begin_child("persona_edit_area", imgui.ImVec2(0, avail.y), False)
|
||||||
try:
|
try:
|
||||||
header = "New Persona" if self._editing_persona_is_new else f"Editing Persona: {self._editing_persona_name}"
|
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.text_colored(C_IN, header)
|
||||||
imgui.separator()
|
imgui.separator()
|
||||||
|
|
||||||
@@ -1295,34 +1314,61 @@ class App:
|
|||||||
|
|
||||||
imgui.separator()
|
imgui.separator()
|
||||||
|
|
||||||
imgui.text("Provider:")
|
imgui.text("Preferred Models:")
|
||||||
imgui.same_line()
|
|
||||||
providers = self.controller.PROVIDERS
|
providers = self.controller.PROVIDERS
|
||||||
p_idx = providers.index(self._editing_persona_provider) + 1 if self._editing_persona_provider in providers else 0
|
imgui.begin_child("pref_models_list", imgui.ImVec2(0, 150), True)
|
||||||
imgui.push_item_width(150)
|
to_remove = []
|
||||||
_, p_idx = imgui.combo("##pprov", p_idx, ["None"] + providers)
|
for i, entry in enumerate(self._editing_persona_preferred_models_list):
|
||||||
self._editing_persona_provider = providers[p_idx - 1] if p_idx > 0 else ""
|
imgui.push_id(f"pref_model_{i}")
|
||||||
imgui.pop_item_width()
|
imgui.text(f"{i+1}.")
|
||||||
|
|
||||||
imgui.text("Model:")
|
|
||||||
imgui.same_line()
|
imgui.same_line()
|
||||||
all_models = self.controller.all_available_models.get(self._editing_persona_provider, [])
|
|
||||||
if not all_models and self._editing_persona_model:
|
|
||||||
all_models = [self._editing_persona_model]
|
|
||||||
m_idx = all_models.index(self._editing_persona_model) + 1 if self._editing_persona_model in all_models else 0
|
|
||||||
imgui.push_item_width(200)
|
|
||||||
_, m_idx = imgui.combo("##pmodel", m_idx, ["None"] + all_models)
|
|
||||||
self._editing_persona_model = all_models[m_idx - 1] if m_idx > 0 else ""
|
|
||||||
imgui.pop_item_width()
|
|
||||||
|
|
||||||
imgui.text("Temp:")
|
imgui.set_next_item_width(120)
|
||||||
|
prov = entry.get("provider", "")
|
||||||
|
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.same_line()
|
||||||
_, self._editing_persona_temperature = imgui.slider_float("##ptemp", self._editing_persona_temperature, 0.0, 2.0)
|
imgui.set_next_item_width(200)
|
||||||
|
curr_prov = entry.get("provider", "")
|
||||||
|
m_list = self.controller.all_available_models.get(curr_prov, [])
|
||||||
|
model = entry.get("model", "")
|
||||||
|
m_idx = m_list.index(model) + 1 if model 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("Max Output Tokens:")
|
|
||||||
imgui.same_line()
|
imgui.same_line()
|
||||||
_, self._editing_persona_max_tokens = imgui.input_int("##pmaxt", self._editing_persona_max_tokens)
|
imgui.text("T:")
|
||||||
|
imgui.same_line()
|
||||||
|
imgui.set_next_item_width(60)
|
||||||
|
_, entry["temperature"] = imgui.input_float("##temp", entry.get("temperature", 0.7), 0.1, 0.1, "%.1f")
|
||||||
|
|
||||||
|
imgui.same_line()
|
||||||
|
imgui.text("Max:")
|
||||||
|
imgui.same_line()
|
||||||
|
imgui.set_next_item_width(80)
|
||||||
|
_, entry["max_output_tokens"] = imgui.input_int("##maxt", entry.get("max_output_tokens", 4096))
|
||||||
|
|
||||||
|
imgui.same_line()
|
||||||
|
if imgui.button("x"):
|
||||||
|
to_remove.append(i)
|
||||||
|
imgui.pop_id()
|
||||||
|
for i in reversed(to_remove):
|
||||||
|
self._editing_persona_preferred_models_list.pop(i)
|
||||||
|
imgui.end_child()
|
||||||
|
|
||||||
|
if imgui.button("Add Preferred Model"):
|
||||||
|
self._editing_persona_preferred_models_list.append({
|
||||||
|
"provider": self.current_provider,
|
||||||
|
"model": self.current_model,
|
||||||
|
"temperature": 0.7,
|
||||||
|
"max_output_tokens": 4096
|
||||||
|
})
|
||||||
|
|
||||||
|
imgui.separator()
|
||||||
imgui.text("Tool Preset:")
|
imgui.text("Tool Preset:")
|
||||||
imgui.same_line()
|
imgui.same_line()
|
||||||
t_preset_names = ["None"] + sorted(self.controller.tool_presets.keys())
|
t_preset_names = ["None"] + sorted(self.controller.tool_presets.keys())
|
||||||
@@ -1332,6 +1378,7 @@ class App:
|
|||||||
self._editing_persona_tool_preset_id = t_preset_names[t_idx] if t_idx > 0 else ""
|
self._editing_persona_tool_preset_id = t_preset_names[t_idx] if t_idx > 0 else ""
|
||||||
imgui.pop_item_width()
|
imgui.pop_item_width()
|
||||||
|
|
||||||
|
imgui.same_line()
|
||||||
imgui.text("Bias Profile:")
|
imgui.text("Bias Profile:")
|
||||||
imgui.same_line()
|
imgui.same_line()
|
||||||
bias_names = ["None"] + sorted(self.controller.bias_profiles.keys())
|
bias_names = ["None"] + sorted(self.controller.bias_profiles.keys())
|
||||||
@@ -1341,37 +1388,14 @@ class App:
|
|||||||
self._editing_persona_bias_profile_id = bias_names[b_idx] if b_idx > 0 else ""
|
self._editing_persona_bias_profile_id = bias_names[b_idx] if b_idx > 0 else ""
|
||||||
imgui.pop_item_width()
|
imgui.pop_item_width()
|
||||||
|
|
||||||
imgui.separator()
|
if imgui.collapsing_header("Manage Tool Presets & Biases"):
|
||||||
imgui.text("Preferred Models:")
|
imgui.begin_child("tool_preset_embedded", imgui.ImVec2(0, 300), True)
|
||||||
to_remove = []
|
self._render_tool_preset_manager_content(is_embedded=True)
|
||||||
for i, model in enumerate(self._editing_persona_preferred_models_list):
|
imgui.end_child()
|
||||||
imgui.text(f"- {model}")
|
|
||||||
imgui.same_line()
|
|
||||||
if imgui.button(f"x##pref_rem_{i}"):
|
|
||||||
to_remove.append(i)
|
|
||||||
for i in reversed(to_remove):
|
|
||||||
self._editing_persona_preferred_models_list.pop(i)
|
|
||||||
|
|
||||||
# Add Preferred Model
|
|
||||||
all_possible_models = []
|
|
||||||
for prov_models in self.controller.all_available_models.values():
|
|
||||||
all_possible_models.extend(prov_models)
|
|
||||||
all_possible_models = sorted(list(set(all_possible_models)))
|
|
||||||
|
|
||||||
if not hasattr(self, "_add_pref_model_idx"): self._add_pref_model_idx = 0
|
|
||||||
imgui.push_item_width(200)
|
|
||||||
_, self._add_pref_model_idx = imgui.combo("Add Preferred Model##add_pref", self._add_pref_model_idx, ["Select Model..."] + all_possible_models)
|
|
||||||
imgui.pop_item_width()
|
|
||||||
if self._add_pref_model_idx > 0:
|
|
||||||
new_m = all_possible_models[self._add_pref_model_idx - 1]
|
|
||||||
if new_m not in self._editing_persona_preferred_models_list:
|
|
||||||
self._editing_persona_preferred_models_list.append(new_m)
|
|
||||||
self._add_pref_model_idx = 0
|
|
||||||
|
|
||||||
imgui.separator()
|
imgui.separator()
|
||||||
imgui.text("System Prompt:")
|
imgui.text("System Prompt:")
|
||||||
|
|
||||||
# Load Prompt Preset
|
|
||||||
imgui.text("Load from Preset:")
|
imgui.text("Load from Preset:")
|
||||||
imgui.same_line()
|
imgui.same_line()
|
||||||
prompt_presets = ["Select..."] + sorted(self.controller.presets.keys())
|
prompt_presets = ["Select..."] + sorted(self.controller.presets.keys())
|
||||||
@@ -1386,52 +1410,55 @@ class App:
|
|||||||
if pname in self.controller.presets:
|
if pname in self.controller.presets:
|
||||||
p = self.controller.presets[pname]
|
p = self.controller.presets[pname]
|
||||||
self._editing_persona_system_prompt = p.system_prompt
|
self._editing_persona_system_prompt = p.system_prompt
|
||||||
if p.temperature is not None: self._editing_persona_temperature = p.temperature
|
|
||||||
if p.max_output_tokens is not None: self._editing_persona_max_tokens = p.max_output_tokens
|
|
||||||
self._load_preset_idx = 0
|
self._load_preset_idx = 0
|
||||||
|
|
||||||
|
if imgui.collapsing_header("Manage Prompt Presets"):
|
||||||
|
imgui.begin_child("prompt_preset_embedded", imgui.ImVec2(0, 200), True)
|
||||||
|
self._render_preset_manager_content(is_embedded=True)
|
||||||
|
imgui.end_child()
|
||||||
|
|
||||||
_, self._editing_persona_system_prompt = imgui.input_text_multiline("##pprompt", self._editing_persona_system_prompt, imgui.ImVec2(-1, 150))
|
_, self._editing_persona_system_prompt = imgui.input_text_multiline("##pprompt", self._editing_persona_system_prompt, imgui.ImVec2(-1, 150))
|
||||||
|
|
||||||
imgui.separator()
|
imgui.separator()
|
||||||
if imgui.button("Save Persona", imgui.ImVec2(120, 0)):
|
if imgui.button("Save Persona", imgui.ImVec2(120, 0)):
|
||||||
if self._editing_persona_name.strip():
|
if self._editing_persona_name.strip():
|
||||||
try:
|
try:
|
||||||
|
import copy
|
||||||
|
save_models = copy.deepcopy(self._editing_persona_preferred_models_list)
|
||||||
|
|
||||||
persona = models.Persona(
|
persona = models.Persona(
|
||||||
name=self._editing_persona_name.strip(),
|
name=self._editing_persona_name.strip(),
|
||||||
provider=self._editing_persona_provider or None,
|
|
||||||
model=self._editing_persona_model or None,
|
|
||||||
system_prompt=self._editing_persona_system_prompt,
|
system_prompt=self._editing_persona_system_prompt,
|
||||||
temperature=self._editing_persona_temperature,
|
|
||||||
max_output_tokens=self._editing_persona_max_tokens,
|
|
||||||
tool_preset=self._editing_persona_tool_preset_id or None,
|
tool_preset=self._editing_persona_tool_preset_id or None,
|
||||||
bias_profile=self._editing_persona_bias_profile_id or None,
|
bias_profile=self._editing_persona_bias_profile_id or None,
|
||||||
preferred_models=self._editing_persona_preferred_models_list,
|
preferred_models=save_models,
|
||||||
)
|
)
|
||||||
self.controller._cb_save_persona(persona, self._editing_persona_scope)
|
self.controller._cb_save_persona(persona, self._editing_persona_scope)
|
||||||
self.ai_status = f"Saved Persona: {persona.name}"
|
self.ai_status = f"Saved Persona: {persona.name}"
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.ai_status = f"Error saving persona: {e}"
|
self.ai_status = f"Error saving persona: {e}"
|
||||||
|
import traceback
|
||||||
|
traceback.print_exc()
|
||||||
else:
|
else:
|
||||||
self.ai_status = "Name required"
|
self.ai_status = "Name required"
|
||||||
|
|
||||||
imgui.same_line()
|
imgui.same_line()
|
||||||
if imgui.button("Delete", imgui.ImVec2(100, 0)):
|
if imgui.button("Delete", imgui.ImVec2(100, 0)):
|
||||||
if not self._editing_persona_is_new and self._editing_persona_name:
|
if not getattr(self, '_editing_persona_is_new', True) and self._editing_persona_name:
|
||||||
self.controller._cb_delete_persona(self._editing_persona_name, self._editing_persona_scope)
|
self.controller._cb_delete_persona(self._editing_persona_name, self._editing_persona_scope)
|
||||||
self.ai_status = f"Deleted Persona: {self._editing_persona_name}"
|
self.ai_status = f"Deleted Persona: {self._editing_persona_name}"
|
||||||
self._editing_persona_name = ""
|
self._editing_persona_name = ""
|
||||||
self._editing_persona_is_new = True
|
self._editing_persona_is_new = True
|
||||||
|
|
||||||
|
if not is_embedded:
|
||||||
imgui.same_line()
|
imgui.same_line()
|
||||||
if imgui.button("Close", imgui.ImVec2(100, 0)):
|
if imgui.button("Close", imgui.ImVec2(100, 0)):
|
||||||
self.show_persona_editor_modal = False
|
self.show_persona_editor_window = False
|
||||||
imgui.close_current_popup()
|
|
||||||
finally:
|
finally:
|
||||||
imgui.end_child()
|
imgui.end_child()
|
||||||
finally:
|
finally:
|
||||||
imgui.end_popup()
|
if not is_embedded:
|
||||||
|
imgui.end()
|
||||||
|
|
||||||
def _render_projects_panel(self) -> None:
|
def _render_projects_panel(self) -> None:
|
||||||
if self.perf_profiling_enabled: self.perf_monitor.start_component("_render_projects_panel")
|
if self.perf_profiling_enabled: self.perf_monitor.start_component("_render_projects_panel")
|
||||||
proj_name = self.project.get("project", {}).get("name", Path(self.active_project_path).stem)
|
proj_name = self.project.get("project", {}).get("name", Path(self.active_project_path).stem)
|
||||||
@@ -2274,7 +2301,7 @@ def hello():
|
|||||||
imgui.end_combo()
|
imgui.end_combo()
|
||||||
imgui.same_line()
|
imgui.same_line()
|
||||||
if imgui.button("Manage Personas"):
|
if imgui.button("Manage Personas"):
|
||||||
self.show_persona_editor_modal = True
|
self.show_persona_editor_window = True
|
||||||
if self.ui_active_persona and self.ui_active_persona in personas:
|
if self.ui_active_persona and self.ui_active_persona in personas:
|
||||||
persona = personas[self.ui_active_persona]
|
persona = personas[self.ui_active_persona]
|
||||||
self._editing_persona_name = persona.name
|
self._editing_persona_name = persona.name
|
||||||
@@ -3589,7 +3616,7 @@ def hello():
|
|||||||
imgui.end_combo()
|
imgui.end_combo()
|
||||||
imgui.same_line(0, 8)
|
imgui.same_line(0, 8)
|
||||||
if imgui.button("Manage Presets##global"):
|
if imgui.button("Manage Presets##global"):
|
||||||
self.show_preset_manager_modal = True
|
self.show_preset_manager_window = True
|
||||||
imgui.set_item_tooltip("Open preset management modal")
|
imgui.set_item_tooltip("Open preset management modal")
|
||||||
ch, self.ui_global_system_prompt = imgui.input_text_multiline("##gsp", self.ui_global_system_prompt, imgui.ImVec2(-1, 100))
|
ch, self.ui_global_system_prompt = imgui.input_text_multiline("##gsp", self.ui_global_system_prompt, imgui.ImVec2(-1, 100))
|
||||||
imgui.separator()
|
imgui.separator()
|
||||||
@@ -3606,7 +3633,7 @@ def hello():
|
|||||||
imgui.end_combo()
|
imgui.end_combo()
|
||||||
imgui.same_line(0, 8)
|
imgui.same_line(0, 8)
|
||||||
if imgui.button("Manage Presets##project"):
|
if imgui.button("Manage Presets##project"):
|
||||||
self.show_preset_manager_modal = True
|
self.show_preset_manager_window = True
|
||||||
imgui.set_item_tooltip("Open preset management modal")
|
imgui.set_item_tooltip("Open preset management modal")
|
||||||
ch, self.ui_project_system_prompt = imgui.input_text_multiline("##psp", self.ui_project_system_prompt, imgui.ImVec2(-1, 100))
|
ch, self.ui_project_system_prompt = imgui.input_text_multiline("##psp", self.ui_project_system_prompt, imgui.ImVec2(-1, 100))
|
||||||
def _render_agent_tools_panel(self) -> None:
|
def _render_agent_tools_panel(self) -> None:
|
||||||
@@ -3628,7 +3655,7 @@ def hello():
|
|||||||
|
|
||||||
imgui.same_line()
|
imgui.same_line()
|
||||||
if imgui.button("Manage Presets##tools"):
|
if imgui.button("Manage Presets##tools"):
|
||||||
self.show_tool_preset_manager_modal = True
|
self.show_tool_preset_manager_window = True
|
||||||
if imgui.is_item_hovered():
|
if imgui.is_item_hovered():
|
||||||
imgui.set_tooltip("Configure tool availability and default modes.")
|
imgui.set_tooltip("Configure tool availability and default modes.")
|
||||||
|
|
||||||
@@ -3847,3 +3874,4 @@ def main() -> None:
|
|||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
main()
|
main()
|
||||||
|
|
||||||
|
|||||||
@@ -434,32 +434,48 @@ class BiasProfile:
|
|||||||
@dataclass
|
@dataclass
|
||||||
class Persona:
|
class Persona:
|
||||||
name: str
|
name: str
|
||||||
provider: Optional[str] = None
|
preferred_models: List[Dict[str, Any]] = field(default_factory=list)
|
||||||
model: Optional[str] = None
|
|
||||||
preferred_models: List[str] = field(default_factory=list)
|
|
||||||
system_prompt: str = ''
|
system_prompt: str = ''
|
||||||
temperature: Optional[float] = None
|
|
||||||
top_p: Optional[float] = None
|
|
||||||
max_output_tokens: Optional[int] = None
|
|
||||||
tool_preset: Optional[str] = None
|
tool_preset: Optional[str] = None
|
||||||
bias_profile: Optional[str] = None
|
bias_profile: Optional[str] = None
|
||||||
|
|
||||||
|
@property
|
||||||
|
def provider(self) -> Optional[str]:
|
||||||
|
if not self.preferred_models: return None
|
||||||
|
return self.preferred_models[0].get("provider")
|
||||||
|
|
||||||
|
@property
|
||||||
|
def model(self) -> Optional[str]:
|
||||||
|
if not self.preferred_models: return None
|
||||||
|
return self.preferred_models[0].get("model")
|
||||||
|
|
||||||
|
@property
|
||||||
|
def temperature(self) -> Optional[float]:
|
||||||
|
if not self.preferred_models: return None
|
||||||
|
return self.preferred_models[0].get("temperature")
|
||||||
|
|
||||||
|
@property
|
||||||
|
def top_p(self) -> Optional[float]:
|
||||||
|
if not self.preferred_models: return None
|
||||||
|
return self.preferred_models[0].get("top_p")
|
||||||
|
|
||||||
|
@property
|
||||||
|
def max_output_tokens(self) -> Optional[int]:
|
||||||
|
if not self.preferred_models: return None
|
||||||
|
return self.preferred_models[0].get("max_output_tokens")
|
||||||
|
|
||||||
def to_dict(self) -> Dict[str, Any]:
|
def to_dict(self) -> Dict[str, Any]:
|
||||||
res = {
|
res = {
|
||||||
"system_prompt": self.system_prompt,
|
"system_prompt": self.system_prompt,
|
||||||
}
|
}
|
||||||
if self.provider is not None:
|
|
||||||
res["provider"] = self.provider
|
|
||||||
if self.model is not None:
|
|
||||||
res["model"] = self.model
|
|
||||||
if self.preferred_models:
|
if self.preferred_models:
|
||||||
res["preferred_models"] = self.preferred_models
|
processed = []
|
||||||
if self.temperature is not None:
|
for m in self.preferred_models:
|
||||||
res["temperature"] = self.temperature
|
if isinstance(m, str):
|
||||||
if self.top_p is not None:
|
processed.append({"model": m})
|
||||||
res["top_p"] = self.top_p
|
else:
|
||||||
if self.max_output_tokens is not None:
|
processed.append(m)
|
||||||
res["max_output_tokens"] = self.max_output_tokens
|
res["preferred_models"] = processed
|
||||||
if self.tool_preset is not None:
|
if self.tool_preset is not None:
|
||||||
res["tool_preset"] = self.tool_preset
|
res["tool_preset"] = self.tool_preset
|
||||||
if self.bias_profile is not None:
|
if self.bias_profile is not None:
|
||||||
@@ -468,15 +484,34 @@ class Persona:
|
|||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_dict(cls, name: str, data: Dict[str, Any]) -> "Persona":
|
def from_dict(cls, name: str, data: Dict[str, Any]) -> "Persona":
|
||||||
|
raw_models = data.get("preferred_models", [])
|
||||||
|
parsed_models = []
|
||||||
|
for m in raw_models:
|
||||||
|
if isinstance(m, str):
|
||||||
|
parsed_models.append({"model": m})
|
||||||
|
else:
|
||||||
|
parsed_models.append(m)
|
||||||
|
|
||||||
|
# Migration logic: merge legacy fields if they exist
|
||||||
|
legacy = {}
|
||||||
|
for k in ["provider", "model", "temperature", "top_p", "max_output_tokens"]:
|
||||||
|
if data.get(k) is not None:
|
||||||
|
legacy[k] = data[k]
|
||||||
|
|
||||||
|
if legacy:
|
||||||
|
if not parsed_models:
|
||||||
|
parsed_models.append(legacy)
|
||||||
|
else:
|
||||||
|
# Merge into first item if it's missing these specific legacy fields
|
||||||
|
for k, v in legacy.items():
|
||||||
|
if k not in parsed_models[0] or parsed_models[0][k] is None:
|
||||||
|
parsed_models[0][k] = v
|
||||||
|
|
||||||
return cls(
|
return cls(
|
||||||
name=name,
|
name=name,
|
||||||
provider=data.get("provider"),
|
preferred_models=parsed_models,
|
||||||
model=data.get("model"),
|
|
||||||
preferred_models=data.get("preferred_models", []),
|
|
||||||
system_prompt=data.get("system_prompt", ""),
|
system_prompt=data.get("system_prompt", ""),
|
||||||
temperature=data.get("temperature"),
|
|
||||||
top_p=data.get("top_p"),
|
|
||||||
max_output_tokens=data.get("max_output_tokens"),
|
|
||||||
tool_preset=data.get("tool_preset"),
|
tool_preset=data.get("tool_preset"),
|
||||||
bias_profile=data.get("bias_profile"),
|
bias_profile=data.get("bias_profile"),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -59,7 +59,7 @@ def test_load_all_merged(temp_paths):
|
|||||||
|
|
||||||
def test_save_persona(temp_paths):
|
def test_save_persona(temp_paths):
|
||||||
manager = PersonaManager(project_root=temp_paths["project_root"])
|
manager = PersonaManager(project_root=temp_paths["project_root"])
|
||||||
persona = Persona(name="New", provider="gemini", system_prompt="Test")
|
persona = Persona(name="New", preferred_models=[{"provider": "gemini"}], system_prompt="Test")
|
||||||
|
|
||||||
manager.save_persona(persona, scope="project")
|
manager.save_persona(persona, scope="project")
|
||||||
loaded = manager.load_all()
|
loaded = manager.load_all()
|
||||||
|
|||||||
@@ -4,30 +4,38 @@ from src.models import Persona
|
|||||||
def test_persona_serialization():
|
def test_persona_serialization():
|
||||||
persona = Persona(
|
persona = Persona(
|
||||||
name="SecuritySpecialist",
|
name="SecuritySpecialist",
|
||||||
provider="anthropic",
|
preferred_models=[
|
||||||
model="claude-3-7-sonnet-20250219",
|
{"provider": "anthropic", "model": "claude-3-7-sonnet-20250219", "temperature": 0.2, "top_p": 0.9, "max_output_tokens": 4000},
|
||||||
preferred_models=["claude-3-7-sonnet-20250219", "claude-3-5-sonnet-20241022"],
|
"claude-3-5-sonnet-20241022"
|
||||||
|
],
|
||||||
system_prompt="You are a security expert.",
|
system_prompt="You are a security expert.",
|
||||||
temperature=0.2,
|
|
||||||
top_p=0.9,
|
|
||||||
max_output_tokens=4000,
|
|
||||||
tool_preset="SecurityTools",
|
tool_preset="SecurityTools",
|
||||||
bias_profile="Execution-Focused"
|
bias_profile="Execution-Focused"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
assert persona.provider == "anthropic"
|
||||||
|
assert persona.model == "claude-3-7-sonnet-20250219"
|
||||||
|
assert persona.temperature == 0.2
|
||||||
|
assert persona.top_p == 0.9
|
||||||
|
assert persona.max_output_tokens == 4000
|
||||||
|
|
||||||
data = persona.to_dict()
|
data = persona.to_dict()
|
||||||
|
|
||||||
assert data["provider"] == "anthropic"
|
# data should NOT have top-level provider/model anymore, it's in preferred_models
|
||||||
assert data["model"] == "claude-3-7-sonnet-20250219"
|
assert "provider" not in data
|
||||||
assert "claude-3-5-sonnet-20241022" in data["preferred_models"]
|
assert "model" not in data
|
||||||
|
assert data["preferred_models"][0]["provider"] == "anthropic"
|
||||||
|
assert data["preferred_models"][0]["model"] == "claude-3-7-sonnet-20250219"
|
||||||
|
assert data["preferred_models"][1] == {"model": "claude-3-5-sonnet-20241022"}
|
||||||
assert data["system_prompt"] == "You are a security expert."
|
assert data["system_prompt"] == "You are a security expert."
|
||||||
assert data["temperature"] == 0.2
|
assert data["preferred_models"][0]["temperature"] == 0.2
|
||||||
assert data["top_p"] == 0.9
|
assert data["preferred_models"][0]["top_p"] == 0.9
|
||||||
assert data["max_output_tokens"] == 4000
|
assert data["preferred_models"][0]["max_output_tokens"] == 4000
|
||||||
assert data["tool_preset"] == "SecurityTools"
|
assert data["tool_preset"] == "SecurityTools"
|
||||||
assert data["bias_profile"] == "Execution-Focused"
|
assert data["bias_profile"] == "Execution-Focused"
|
||||||
|
|
||||||
def test_persona_deserialization():
|
def test_persona_deserialization():
|
||||||
|
# Old config format (legacy)
|
||||||
data = {
|
data = {
|
||||||
"provider": "gemini",
|
"provider": "gemini",
|
||||||
"model": "gemini-2.5-flash",
|
"model": "gemini-2.5-flash",
|
||||||
@@ -45,7 +53,9 @@ def test_persona_deserialization():
|
|||||||
assert persona.name == "Assistant"
|
assert persona.name == "Assistant"
|
||||||
assert persona.provider == "gemini"
|
assert persona.provider == "gemini"
|
||||||
assert persona.model == "gemini-2.5-flash"
|
assert persona.model == "gemini-2.5-flash"
|
||||||
assert persona.preferred_models == ["gemini-2.5-flash"]
|
# Migration logic should have put legacy fields into preferred_models since it only had a string
|
||||||
|
assert persona.preferred_models[0]["provider"] == "gemini"
|
||||||
|
assert persona.preferred_models[0]["model"] == "gemini-2.5-flash"
|
||||||
assert persona.system_prompt == "You are a helpful assistant."
|
assert persona.system_prompt == "You are a helpful assistant."
|
||||||
assert persona.temperature == 0.5
|
assert persona.temperature == 0.5
|
||||||
assert persona.top_p == 1.0
|
assert persona.top_p == 1.0
|
||||||
|
|||||||
Reference in New Issue
Block a user