broken: discussion compression

This commit is contained in:
2026-05-12 18:26:39 -04:00
parent 4b78d1df02
commit 5be6ef88f8
+4 -312
View File
@@ -3799,326 +3799,18 @@ def hello():
if self.perf_profiling_enabled: self.perf_monitor.end_component("_render_discussion_panel") if self.perf_profiling_enabled: self.perf_monitor.end_component("_render_discussion_panel")
return return
if not self.is_viewing_prior_session and imgui.collapsing_header("Discussions", imgui.TreeNodeFlags_.default_open): self._render_discussion_selector()
names = self._get_discussion_names()
grouped_discussions = {}
for name in names:
base = name.split("_take_")[0]
grouped_discussions.setdefault(base, []).append(name)
active_base = self.active_discussion.split("_take_")[0]
if active_base not in grouped_discussions:
active_base = names[0] if names else ""
base_names = sorted(grouped_discussions.keys())
if imgui.begin_combo("##disc_sel", active_base):
for bname in base_names:
is_selected = (bname == active_base)
if imgui.selectable(bname, is_selected)[0]:
target = bname if bname in names else grouped_discussions[bname][0]
if target != self.active_discussion:
self._switch_discussion(target)
if is_selected:
imgui.set_item_default_focus()
imgui.end_combo()
# Sync variables in case combo selection changed self.active_discussion
active_base = self.active_discussion.split("_take_")[0]
current_takes = grouped_discussions.get(active_base, [])
if imgui.begin_tab_bar("discussion_takes_tabs"):
for take_name in current_takes:
label = "Original" if take_name == active_base else take_name.replace(f"{active_base}_", "").replace("_", " ").title()
flags = imgui.TabItemFlags_.set_selected if take_name == self.active_discussion else 0
res = imgui.begin_tab_item(f"{label}###{take_name}", None, flags)
if res[0]:
if take_name != self.active_discussion:
self._switch_discussion(take_name)
imgui.end_tab_item()
res_s = imgui.begin_tab_item("Synthesis###Synthesis")
if res_s[0]:
self._render_synthesis_panel()
imgui.end_tab_item()
imgui.end_tab_bar()
if "_take_" in self.active_discussion:
if imgui.button("Promote Take"):
base_name = self.active_discussion.split("_take_")[0]
new_name = f"{base_name}_promoted"
counter = 1
while new_name in names:
new_name = f"{base_name}_promoted_{counter}"
counter += 1
project_manager.promote_take(self.project, self.active_discussion, new_name)
self._switch_discussion(new_name)
imgui.same_line()
if self.active_track:
imgui.same_line()
changed, self._track_discussion_active = imgui.checkbox("Track Discussion", self._track_discussion_active)
if changed:
if self._track_discussion_active:
self._flush_disc_entries_to_project()
history_strings = project_manager.load_track_history(self.active_track.id, self.active_project_root)
with self._disc_entries_lock:
self.disc_entries = models.parse_history_entries(history_strings, self.disc_roles)
self.ai_status = f"track discussion: {self.active_track.id}"
else:
self._flush_disc_entries_to_project()
# Restore project discussion
self._switch_discussion(self.active_discussion)
self.ai_status = "track discussion disabled"
disc_sec = self.project.get("discussion", {})
disc_data = disc_sec.get("discussions", {}).get(self.active_discussion, {})
git_commit = disc_data.get("git_commit", "")
last_updated = disc_data.get("last_updated", "")
imgui.text_colored(C_LBL, "commit:")
imgui.same_line()
self._render_selectable_label('git_commit_val', git_commit[:12] if git_commit else '(none)', width=100, color=(C_IN if git_commit else C_LBL))
imgui.same_line()
if imgui.button("Update Commit"):
git_dir = self.ui_project_git_dir
if git_dir:
cmt = project_manager.get_git_commit(git_dir)
if cmt:
disc_data["git_commit"] = cmt
disc_data["last_updated"] = project_manager.now_ts()
self.ai_status = f"commit: {cmt[:12]}"
imgui.text_colored(C_LBL, "updated:")
imgui.same_line()
imgui.text_colored(C_SUB, last_updated if last_updated else "(never)")
ch, self.ui_disc_new_name_input = imgui.input_text("##new_disc", self.ui_disc_new_name_input)
imgui.same_line()
if imgui.button("Create"):
nm = self.ui_disc_new_name_input.strip()
if nm: self._create_discussion(nm); self.ui_disc_new_name_input = ""
imgui.same_line()
if imgui.button("Rename"):
nm = self.ui_disc_new_name_input.strip()
if nm: self._rename_discussion(self.active_discussion, nm); self.ui_disc_new_name_input = ""
imgui.same_line()
if imgui.button("Delete"):
self._delete_discussion(self.active_discussion)
if not self.is_viewing_prior_session: if not self.is_viewing_prior_session:
imgui.separator() imgui.separator()
if imgui.button("+ Entry"): self._render_discussion_entry_controls()
self.disc_entries.append({"role": self.disc_roles[0] if self.disc_roles else "User", "content": "", "collapsed": True, "ts": project_manager.now_ts()})
imgui.same_line()
if imgui.button("-All"):
for e in self.disc_entries: e["collapsed"] = True
imgui.same_line()
if imgui.button("+All"):
for e in self.disc_entries: e["collapsed"] = False
imgui.same_line()
if imgui.button("Clear All"):
self.disc_entries.clear()
imgui.same_line()
if imgui.button("Save"):
self._flush_to_project()
self._flush_to_config()
models.save_config(self.config)
self.ai_status = "discussion saved"
ch, self.ui_auto_add_history = imgui.checkbox("Auto-add message & response to history", self.ui_auto_add_history)
# Truncation controls
imgui.text("Keep Pairs:")
imgui.same_line()
imgui.set_next_item_width(80)
ch, self.ui_disc_truncate_pairs = imgui.input_int("##trunc_pairs", self.ui_disc_truncate_pairs, 1)
if self.ui_disc_truncate_pairs < 1: self.ui_disc_truncate_pairs = 1
imgui.same_line()
if imgui.button("Truncate"):
with self._disc_entries_lock:
self.disc_entries = truncate_entries(self.disc_entries, self.ui_disc_truncate_pairs)
self.ai_status = f"history truncated to {self.ui_disc_truncate_pairs} pairs"
imgui.separator() imgui.separator()
if imgui.collapsing_header("Roles"): self._render_discussion_roles()
imgui.begin_child("roles_scroll", imgui.ImVec2(0, 100), True)
for i, r in enumerate(self.disc_roles):
imgui.push_id(f"role_{i}")
if imgui.button("X"):
self.disc_roles.pop(i)
imgui.pop_id()
break
imgui.same_line()
imgui.text(r)
imgui.pop_id()
imgui.end_child()
ch, self.ui_disc_new_role_input = imgui.input_text("##new_role", self.ui_disc_new_role_input)
imgui.same_line()
if imgui.button("Add"):
r = self.ui_disc_new_role_input.strip()
if r and r not in self.disc_roles:
self.disc_roles.append(r)
self.ui_disc_new_role_input = ""
imgui.separator() imgui.separator()
imgui.begin_child("disc_scroll", imgui.ImVec2(0, 0), False) self._render_discussion_entries()
# Filter entries based on focused agent persona
display_entries = self.disc_entries
if self.ui_focus_agent:
tier_usage = self.mma_tier_usage.get(self.ui_focus_agent)
if tier_usage:
persona_name = tier_usage.get("persona")
if persona_name:
# Show User messages and the focused agent's responses
display_entries = [e for e in self.disc_entries if e.get("role") == persona_name or e.get("role") == "User"]
clipper = imgui.ListClipper()
clipper.begin(len(display_entries))
while clipper.step():
for i in range(clipper.display_start, clipper.display_end):
entry = display_entries[i]
# Use the index in the original list for ID if possible, but here i is index in display_entries
imgui.push_id(f"disc_{i}")
collapsed = entry.get("collapsed", False)
read_mode = entry.get("read_mode", False)
if imgui.button("+" if collapsed else "-"):
entry["collapsed"] = not collapsed
imgui.same_line()
self._render_text_viewer(f"Entry #{i+1}", entry["content"])
imgui.same_line()
imgui.set_next_item_width(120)
if imgui.begin_combo("##role", entry["role"]):
for r in self.disc_roles:
if imgui.selectable(r, r == entry["role"])[0]:
entry["role"] = r
imgui.end_combo()
if not collapsed:
imgui.same_line()
if imgui.button("[Edit]" if read_mode else "[Read]"):
entry["read_mode"] = not read_mode
ts_str = entry.get("ts", "")
if ts_str:
imgui.same_line()
imgui.text_colored(vec4(120, 120, 100), str(ts_str))
# Visual indicator for file injections
e_dt = project_manager.parse_ts(ts_str)
if e_dt:
e_unix = e_dt.timestamp()
next_unix = float('inf')
if i + 1 < len(self.disc_entries):
n_ts = self.disc_entries[i+1].get("ts", "")
n_dt = project_manager.parse_ts(n_ts)
if n_dt: next_unix = n_dt.timestamp()
injected_here = [f for f in self.files if hasattr(f, 'injected_at') and f.injected_at and e_unix <= f.injected_at < next_unix]
if injected_here:
imgui.same_line()
imgui.text_colored(vec4(100, 255, 100), f"[{len(injected_here)}+]")
if imgui.is_item_hovered():
tooltip = "Files injected at this point:\n" + "\n".join([f.path for f in injected_here])
imgui.set_tooltip(tooltip)
if collapsed:
imgui.same_line()
if imgui.button("Ins"):
self.disc_entries.insert(i, {"role": "User", "content": "", "collapsed": True, "ts": project_manager.now_ts()})
imgui.same_line()
if imgui.button("Del"):
self.disc_entries.pop(i)
imgui.pop_id()
break # Break from inner loop, clipper will re-step
imgui.same_line()
if imgui.button("Branch"):
self._branch_discussion(i)
imgui.same_line()
preview = entry["content"].replace("\n", " ")[:60]
if len(entry["content"]) > 60: preview += "..."
if not preview.strip() and entry.get("thinking_segments"):
preview = entry["thinking_segments"][0]["content"].replace("\n", " ")[:60]
if len(entry["thinking_segments"][0]["content"]) > 60: preview += "..."
imgui.text_colored(vec4(160, 160, 150), preview)
if not collapsed:
thinking_segments = entry.get("thinking_segments", [])
has_content = bool(entry.get("content", "").strip())
is_standalone = bool(thinking_segments) and not has_content
if thinking_segments:
self._render_thinking_trace(thinking_segments, i, is_standalone=is_standalone)
if read_mode:
content = entry["content"]
if content.strip():
if '## Retrieved Context' in content:
rag_match = re.search(r'## Retrieved Context\n\n([\s\S]*?)(?=\n\n#|\Z)', content)
if rag_match:
rag_section = rag_match.group(1)
if imgui.collapsing_header('Retrieved Context'):
chunks = re.finditer(r'### Chunk (\d+) \(Source: (.*?)\)\n([\s\S]*?)(?=\n### Chunk|\Z)', rag_section)
for chunk_match in chunks:
idx = chunk_match.group(1)
path = chunk_match.group(2)
chunk_content = chunk_match.group(3)
if imgui.collapsing_header(f'Chunk {idx}: {path}'):
if imgui.button(f'[Source]##rag_{i}_{idx}'):
res = mcp_client.read_file(path)
if res:
self.text_viewer_title = path
self.text_viewer_content = res
self.text_viewer_type = Path(path).suffix.lstrip('.') if Path(path).suffix else 'text'
self.show_text_viewer = True
imgui.text_unformatted(chunk_content)
content = content[:rag_match.start()] + content[rag_match.end():]
pattern = re.compile(r"\[Definition: (.*?) from (.*?) \(line (\d+)\)\](\s+```[\s\S]*?```)?")
matches = list(pattern.finditer(content))
is_nerv = theme.is_nerv_active()
if not matches:
if is_nerv: imgui.push_style_color(imgui.Col_.text, vec4(80, 255, 80))
markdown_helper.render(content, context_id=f'disc_{i}')
if is_nerv: imgui.pop_style_color()
else:
imgui.begin_child(f"read_content_{i}", imgui.ImVec2(0, 150), True)
if self.ui_word_wrap: imgui.push_text_wrap_pos(imgui.get_content_region_avail().x)
last_idx = 0
for m_idx, match in enumerate(matches):
before = content[last_idx:match.start()]
if before:
if is_nerv: imgui.push_style_color(imgui.Col_.text, vec4(80, 255, 80))
markdown_helper.render(before, context_id=f'disc_{i}_b_{m_idx}')
if is_nerv: imgui.pop_style_color()
header_text = match.group(0).split("\n")[0].strip()
path = match.group(2)
code_block = match.group(4)
if imgui.collapsing_header(header_text):
if imgui.button(f"[Source]##{i}_{match.start()}"):
res = mcp_client.read_file(path)
if res:
self.text_viewer_title = path
self.text_viewer_content = res
self.text_viewer_type = Path(path).suffix.lstrip('.') if Path(path).suffix else 'text'
self.show_text_viewer = True
if code_block:
if is_nerv: imgui.push_style_color(imgui.Col_.text, vec4(80, 255, 80))
markdown_helper.render(code_block, context_id=f'disc_{i}_c_{m_idx}')
if is_nerv: imgui.pop_style_color()
last_idx = match.end()
after = content[last_idx:]
if after:
if is_nerv: imgui.push_style_color(imgui.Col_.text, vec4(80, 255, 80))
markdown_helper.render(after, context_id=f'disc_{i}_a')
if is_nerv: imgui.pop_style_color()
if self.ui_word_wrap: imgui.pop_text_wrap_pos()
imgui.end_child()
else:
if not is_standalone:
ch, entry["content"] = imgui.input_text_multiline("##content", entry["content"], imgui.ImVec2(-1, 150))
imgui.separator()
imgui.pop_id()
if self._scroll_disc_to_bottom:
imgui.set_scroll_here_y(1.0)
self._scroll_disc_to_bottom = False
imgui.end_child()
if self.perf_profiling_enabled: self.perf_monitor.end_component("_render_discussion_panel") if self.perf_profiling_enabled: self.perf_monitor.end_component("_render_discussion_panel")
def _render_synthesis_panel(self) -> None: def _render_synthesis_panel(self) -> None: