broken: discussion compression
This commit is contained in:
+4
-312
@@ -3799,326 +3799,18 @@ def hello():
|
||||
if self.perf_profiling_enabled: self.perf_monitor.end_component("_render_discussion_panel")
|
||||
return
|
||||
|
||||
if not self.is_viewing_prior_session and imgui.collapsing_header("Discussions", imgui.TreeNodeFlags_.default_open):
|
||||
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)
|
||||
self._render_discussion_selector()
|
||||
|
||||
if not self.is_viewing_prior_session:
|
||||
imgui.separator()
|
||||
if imgui.button("+ Entry"):
|
||||
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"
|
||||
self._render_discussion_entry_controls()
|
||||
|
||||
imgui.separator()
|
||||
if imgui.collapsing_header("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 = ""
|
||||
self._render_discussion_roles()
|
||||
|
||||
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")
|
||||
|
||||
def _render_synthesis_panel(self) -> None:
|
||||
|
||||
Reference in New Issue
Block a user