LiamKhoaLe commited on
Commit
ed4acef
·
1 Parent(s): 40c0e33

Upd appendMessage utils in static. Upd mermaid rendering. Upd coder and namer agents

Browse files
Files changed (5) hide show
  1. .DS_Store +0 -0
  2. .gitignore +2 -1
  3. routes/reports.py +126 -1
  4. static/script.js +45 -54
  5. static/sessions.js +14 -33
.DS_Store CHANGED
Binary files a/.DS_Store and b/.DS_Store differ
 
.gitignore CHANGED
@@ -1 +1,2 @@
1
- .env
 
 
1
+ .env
2
+ diagram
routes/reports.py CHANGED
@@ -459,6 +459,23 @@ async def execute_detailed_subtasks(cot_plan: Dict[str, Any], context_text: str,
459
  expected_output, quality_checks, context_text, web_context, filename,
460
  agent_context, nvidia_rotator, gemini_rotator
461
  )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
462
 
463
  # Store agent context for next agents
464
  agent_context[f"{section_id}.{subtask_number}"] = {
@@ -533,7 +550,7 @@ async def analyze_subtask_with_cot_references(subsection_id: str, task: str, rea
533
  sub_actions_text = "\n".join([f"- {action}" for action in sub_actions]) if sub_actions else "No specific sub-actions defined"
534
  quality_checks_text = "\n".join([f"- {check}" for check in quality_checks]) if quality_checks else "No specific quality checks defined"
535
 
536
- sys_prompt = f"""You are an expert analyst performing comprehensive research as part of a hierarchical report generation system.
537
 
538
  SUBSECTION ID: {subsection_id}
539
  TASK: {task}
@@ -566,6 +583,19 @@ CRITICAL REQUIREMENTS:
566
  - Integrate findings with previous work through CoT references
567
  - Create detailed, comprehensive content (not just summaries)
568
 
 
 
 
 
 
 
 
 
 
 
 
 
 
569
  Return only the comprehensive analysis, no meta-commentary or introductory phrases."""
570
 
571
  user_prompt = f"""SUBSECTION {subsection_id}: {task}
@@ -642,6 +672,14 @@ CRITICAL REQUIREMENTS:
642
  - Create detailed, comprehensive content (not summaries)
643
  - Use proper hierarchical numbering and structure
644
 
 
 
 
 
 
 
 
 
645
  Return only the synthesized analysis with proper hierarchical structure, no meta-commentary."""
646
 
647
  user_prompt = f"""SECTION {section_id}: {section_title}
@@ -760,6 +798,16 @@ HIERARCHICAL STRUCTURE REQUIREMENTS:
760
  - Professional, analytical tone
761
  - Chain of Thought integration showing how sections build upon each other
762
 
 
 
 
 
 
 
 
 
 
 
763
  CRITICAL: Start the report immediately with substantive content. Do NOT include meta-commentary like "Here is a comprehensive report..." or "Of course, here is...". Begin directly with the report title and content."""
764
 
765
  user_prompt = f"""USER REQUEST: {instructions}
@@ -789,6 +837,16 @@ Create a comprehensive, authoritative report with proper hierarchical structure
789
  # Post-process to remove any remaining meta-commentary and ensure proper formatting
790
  report = remove_meta_commentary(report)
791
  report = ensure_hierarchical_structure(report)
 
 
 
 
 
 
 
 
 
 
792
 
793
  logger.info(f"[REPORT] Comprehensive hierarchical report synthesized, length: {len(report)} characters")
794
  return report
@@ -801,6 +859,73 @@ Create a comprehensive, authoritative report with proper hierarchical structure
801
  fallback_report += f"\n\n## Conclusion\n\n{instructions}"
802
  return fallback_report
803
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
804
 
805
  def build_cot_references(agent_context: Dict[str, Any], current_subsection_id: str) -> str:
806
  """Build Chain of Thought references from previous agents' work."""
 
459
  expected_output, quality_checks, context_text, web_context, filename,
460
  agent_context, nvidia_rotator, gemini_rotator
461
  )
462
+
463
+ # If the subtask implies coding, generate code artifacts and explanations
464
+ if any(kw in (task.lower() + " " + reasoning.lower()) for kw in ["implement", "code", "function", "class", "api", "script", "module", "endpoint"]):
465
+ try:
466
+ code_markdown = await generate_code_artifacts(
467
+ subsection_id=subsection_id,
468
+ task=task,
469
+ reasoning=reasoning,
470
+ context_text=context_text,
471
+ web_context=web_context,
472
+ gemini_rotator=gemini_rotator,
473
+ nvidia_rotator=nvidia_rotator
474
+ )
475
+ # Append code and explanation beneath the analysis
476
+ subtask_result = subtask_result + "\n\n" + code_markdown
477
+ except Exception as ce:
478
+ logger.warning(f"[REPORT] Code generation failed for {subsection_id}: {ce}")
479
 
480
  # Store agent context for next agents
481
  agent_context[f"{section_id}.{subtask_number}"] = {
 
550
  sub_actions_text = "\n".join([f"- {action}" for action in sub_actions]) if sub_actions else "No specific sub-actions defined"
551
  quality_checks_text = "\n".join([f"- {check}" for check in quality_checks]) if quality_checks else "No specific quality checks defined"
552
 
553
+ sys_prompt = f"""You are an expert analyst performing comprehensive research as part of a hierarchical report generation system.
554
 
555
  SUBSECTION ID: {subsection_id}
556
  TASK: {task}
 
583
  - Integrate findings with previous work through CoT references
584
  - Create detailed, comprehensive content (not just summaries)
585
 
586
+ WHEN THE TASK IMPLIES IMPLEMENTATION OR CODING:
587
+ - Propose concrete code solutions using fenced code blocks with proper language tags
588
+ - Organize code by files and paths (e.g., ```python
589
+ # File: utils/service/new_module.py
590
+ ...
591
+ ```)
592
+ - Keep code self-contained and runnable; include imports and minimal scaffolding if needed
593
+ - Add brief explanation after each code block (what/why), linking back to requirements
594
+
595
+ WHEN VISUAL STRUCTURE HELPS (flows/architecture/timelines):
596
+ - Include a Mermaid diagram as a fenced ```mermaid block when genuinely helpful (not always)
597
+ - Prefer sequence diagrams for flows, class diagrams for structure, graph diagrams for dependencies
598
+
599
  Return only the comprehensive analysis, no meta-commentary or introductory phrases."""
600
 
601
  user_prompt = f"""SUBSECTION {subsection_id}: {task}
 
672
  - Create detailed, comprehensive content (not summaries)
673
  - Use proper hierarchical numbering and structure
674
 
675
+ CODING ARTIFACTS:
676
+ - If subsections include code, consolidate into a coherent file-by-file set
677
+ - Ensure consistent naming, imports, and integration across files
678
+ - Provide short rationale per file and note relationships (e.g., section 2.1 uses helper from 1.2)
679
+
680
+ DIAGRAMS:
681
+ - If helpful, include a single Mermaid diagram summarizing relationships across subsections
682
+
683
  Return only the synthesized analysis with proper hierarchical structure, no meta-commentary."""
684
 
685
  user_prompt = f"""SECTION {section_id}: {section_title}
 
798
  - Professional, analytical tone
799
  - Chain of Thought integration showing how sections build upon each other
800
 
801
+ CODING GENERATION (WHEN REQUESTED/IMPLIED):
802
+ - Produce file-by-file code blocks with file path headers
803
+ - Ensure code compiles and integrates across files (imports, shared types)
804
+ - For each file, include a short explanation and why this design
805
+ - Link code back to the report sections/subsections where it originates
806
+
807
+ DIAGRAMS (WHEN HELPFUL):
808
+ - Include concise Mermaid diagrams to visualize flows/architecture
809
+ - Use fenced ```mermaid blocks so the UI can render them
810
+
811
  CRITICAL: Start the report immediately with substantive content. Do NOT include meta-commentary like "Here is a comprehensive report..." or "Of course, here is...". Begin directly with the report title and content."""
812
 
813
  user_prompt = f"""USER REQUEST: {instructions}
 
837
  # Post-process to remove any remaining meta-commentary and ensure proper formatting
838
  report = remove_meta_commentary(report)
839
  report = ensure_hierarchical_structure(report)
840
+
841
+ # Optionally enrich with Mermaid diagrams when useful
842
+ try:
843
+ if should_generate_mermaid(instructions, report):
844
+ diagram = await generate_mermaid_diagram(instructions, detailed_analysis, gemini_rotator, nvidia_rotator)
845
+ if diagram:
846
+ # Prepend a Diagrams section
847
+ report = ("## Diagrams\n\n" + "```mermaid\n" + diagram.strip() + "\n```\n\n" + report)
848
+ except Exception as me:
849
+ logger.warning(f"[REPORT] Mermaid generation skipped: {me}")
850
 
851
  logger.info(f"[REPORT] Comprehensive hierarchical report synthesized, length: {len(report)} characters")
852
  return report
 
859
  fallback_report += f"\n\n## Conclusion\n\n{instructions}"
860
  return fallback_report
861
 
862
+ async def generate_code_artifacts(subsection_id: str, task: str, reasoning: str, context_text: str, web_context: str, gemini_rotator, nvidia_rotator) -> str:
863
+ """Generate code (files-by-files) with explanations using Gemini Pro, grounded in context."""
864
+ system_prompt = (
865
+ "You are a senior software engineer. Generate production-quality code that fulfills the TASK,\n"
866
+ "grounded strictly in the provided CONTEXT.\n"
867
+ "Rules:\n"
868
+ "- Output Markdown with multiple code blocks by file, each preceded by a short heading 'File: path'.\n"
869
+ "- Prefer clear, minimal dependencies.\n"
870
+ "- After each code block, add a concise explanation of design decisions.\n"
871
+ "- If APIs/endpoints are referenced, ensure coherent naming across files.\n"
872
+ "- Do not include meta text like 'Here is the code'. Start with the first file heading."
873
+ )
874
+ user_prompt = (
875
+ f"SUBSECTION {subsection_id}\nTASK: {task}\nREASONING: {reasoning}\n\n"
876
+ f"CONTEXT (DOCUMENT):\n{trim_text(context_text, 6000)}\n\n"
877
+ f"CONTEXT (WEB):\n{trim_text(web_context, 3000)}\n\n"
878
+ "Produce the code files and explanations as specified."
879
+ )
880
+ selection = {"provider": "gemini", "model": "gemini-2.5-pro"}
881
+ code_md = await generate_answer_with_model(selection, system_prompt, user_prompt, gemini_rotator, nvidia_rotator)
882
+ return code_md.strip()
883
+
884
+ def should_generate_mermaid(instructions: str, report_text: str) -> bool:
885
+ """Heuristic to decide whether to include a Mermaid diagram."""
886
+ intent = (instructions or "") + " " + (report_text or "")
887
+ keywords = ("architecture", "workflow", "data flow", "sequence", "state machine", "er diagram", "dependency", "pipeline", "diagram")
888
+ if any(k in intent.lower() for k in keywords):
889
+ return True
890
+ return False
891
+
892
+ async def generate_mermaid_diagram(instructions: str, detailed_analysis: Dict[str, Any], gemini_rotator, nvidia_rotator) -> str:
893
+ """Use NVIDIA_LARGE (GPT-OSS) to synthesize a concise Mermaid diagram when helpful."""
894
+ try:
895
+ # Build a compact overview context from section titles and key insights
896
+ overview = []
897
+ for title, data in detailed_analysis.items():
898
+ section_id = data.get("section_id", "")
899
+ insights = extract_key_insights(data.get("section_synthesis", ""))
900
+ overview.append(f"{section_id} {title}: " + "; ".join(insights))
901
+ context_overview = "\n".join(overview)
902
+
903
+ sys_prompt = (
904
+ "You are an expert technical illustrator. Create a single concise Mermaid diagram that best conveys the core structure\n"
905
+ "(e.g., flowchart, sequence, class, state, or ER) based on the provided CONTEXT.\n"
906
+ "Rules:\n"
907
+ "- Return Mermaid code only (no backticks, no explanations).\n"
908
+ "- Prefer flowchart or sequence if uncertain.\n"
909
+ "- Keep node labels short but meaningful.\n"
910
+ "- Ensure Mermaid syntax is valid."
911
+ )
912
+ user_prompt = f"INSTRUCTIONS:\n{instructions}\n\nCONTEXT OVERVIEW:\n{context_overview}"
913
+
914
+ # Use NVIDIA_LARGE for diagram synthesis
915
+ selection = {"provider": "nvidia_large", "model": os.getenv("NVIDIA_LARGE", "openai/gpt-oss-120b")}
916
+ diagram = await generate_answer_with_model(selection, sys_prompt, user_prompt, gemini_rotator, nvidia_rotator)
917
+ # Strip accidental code fences
918
+ diagram = diagram.strip()
919
+ if diagram.startswith("```"):
920
+ diagram = diagram.strip('`')
921
+ # Attempt to remove language header if present
922
+ if diagram.lower().startswith("mermaid"):
923
+ diagram = "\n".join(diagram.splitlines()[1:])
924
+ return diagram
925
+ except Exception as e:
926
+ logger.warning(f"[REPORT] Mermaid generation error: {e}")
927
+ return ""
928
+
929
 
930
  def build_cot_references(agent_context: Dict[str, Any], current_subsection_id: str) -> str:
931
  """Build Chain of Thought references from previous agents' work."""
static/script.js CHANGED
@@ -738,40 +738,52 @@
738
  }
739
  }
740
 
741
- function appendMessage(role, text, isReport = false) {
742
- const messageDiv = document.createElement('div');
743
- messageDiv.className = `msg ${role}`;
744
-
745
- // Render Markdown for assistant messages
746
- if (role === 'assistant') {
747
- try {
748
- // Use marked library to convert Markdown to HTML
749
- const htmlContent = marked.parse(text);
750
- messageDiv.innerHTML = htmlContent;
751
-
752
- // Add copy buttons to code blocks
753
- addCopyButtonsToCodeBlocks(messageDiv);
754
-
755
- // Add download PDF button for reports
756
- if (isReport) {
757
- addDownloadPdfButton(messageDiv, text);
758
- }
759
- } catch (e) {
760
- // Fallback to plain text if Markdown parsing fails
761
- messageDiv.textContent = text;
762
- }
763
- } else {
764
- messageDiv.textContent = text;
765
  }
766
-
767
- messages.appendChild(messageDiv);
768
-
769
- // Scroll to bottom
770
- requestAnimationFrame(() => {
771
- messageDiv.scrollIntoView({ behavior: 'smooth', block: 'end' });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
772
  });
773
-
774
- return messageDiv;
775
  }
776
 
777
  // Expose markdown-aware appenders for use after refresh (projects.js)
@@ -1161,7 +1173,6 @@
1161
  function appendMessage(role, text, isReport = false) {
1162
  const messageDiv = document.createElement('div');
1163
  messageDiv.className = `msg ${role}`;
1164
-
1165
  if (role === 'thinking') {
1166
  messageDiv.innerHTML = `
1167
  <div class="thinking-container">
@@ -1172,34 +1183,14 @@
1172
  </div>
1173
  `;
1174
  } else if (role === 'assistant') {
1175
- // Render Markdown for assistant messages
1176
- try {
1177
- // Use marked library to convert Markdown to HTML
1178
- const htmlContent = marked.parse(text);
1179
- messageDiv.innerHTML = htmlContent;
1180
-
1181
- // Add copy buttons to code blocks
1182
- addCopyButtonsToCodeBlocks(messageDiv);
1183
-
1184
- // Add download PDF button for reports
1185
- if (isReport) {
1186
- addDownloadPdfButton(messageDiv, text);
1187
- }
1188
- } catch (e) {
1189
- // Fallback to plain text if Markdown parsing fails
1190
- messageDiv.textContent = text;
1191
- }
1192
  } else {
1193
  messageDiv.textContent = text;
1194
  }
1195
-
1196
  messages.appendChild(messageDiv);
1197
-
1198
- // Scroll to bottom
1199
  requestAnimationFrame(() => {
1200
  messageDiv.scrollIntoView({ behavior: 'smooth', block: 'end' });
1201
  });
1202
-
1203
  return messageDiv;
1204
  }
1205
  })();
 
738
  }
739
  }
740
 
741
+ function renderAssistantMarkdown(container, markdown, isReport) {
742
+ try {
743
+ const htmlContent = marked.parse(markdown);
744
+ container.innerHTML = htmlContent;
745
+ // Render Mermaid if present
746
+ renderMermaidInElement(container);
747
+ // Add copy buttons to code blocks
748
+ addCopyButtonsToCodeBlocks(container);
749
+ // Add download PDF button for reports
750
+ if (isReport) addDownloadPdfButton(container, markdown);
751
+ } catch (e) {
752
+ container.textContent = markdown;
 
 
 
 
 
 
 
 
 
 
 
 
753
  }
754
+ }
755
+
756
+ // Dynamically load Mermaid and render mermaid code blocks
757
+ async function ensureMermaidLoaded() {
758
+ if (window.mermaid && window.mermaid.initialize) return true;
759
+ return new Promise((resolve) => {
760
+ const existing = document.querySelector('script[data-sb-mermaid]');
761
+ if (existing) { existing.addEventListener('load', () => resolve(true)); return; }
762
+ const s = document.createElement('script');
763
+ s.src = 'https://cdn.jsdelivr.net/npm/mermaid@10/dist/mermaid.min.js';
764
+ s.async = true;
765
+ s.dataset.sbMermaid = '1';
766
+ s.onload = () => {
767
+ try { window.mermaid.initialize({ startOnLoad: false, securityLevel: 'loose', theme: 'default' }); } catch {}
768
+ resolve(true);
769
+ };
770
+ document.head.appendChild(s);
771
+ });
772
+ }
773
+
774
+ async function renderMermaidInElement(el) {
775
+ const mermaidBlocks = el.querySelectorAll('code.language-mermaid, pre code.language-mermaid');
776
+ if (!mermaidBlocks.length) return;
777
+ await ensureMermaidLoaded();
778
+ mermaidBlocks.forEach((codeBlock, idx) => {
779
+ const graph = codeBlock.textContent;
780
+ const wrapper = document.createElement('div');
781
+ const id = `mermaid-${Date.now()}-${idx}`;
782
+ wrapper.className = 'mermaid';
783
+ wrapper.id = id;
784
+ codeBlock.parentElement.replaceWith(wrapper);
785
+ try { window.mermaid.render(id + '-svg', graph, (svg) => { wrapper.innerHTML = svg; }); } catch {}
786
  });
 
 
787
  }
788
 
789
  // Expose markdown-aware appenders for use after refresh (projects.js)
 
1173
  function appendMessage(role, text, isReport = false) {
1174
  const messageDiv = document.createElement('div');
1175
  messageDiv.className = `msg ${role}`;
 
1176
  if (role === 'thinking') {
1177
  messageDiv.innerHTML = `
1178
  <div class="thinking-container">
 
1183
  </div>
1184
  `;
1185
  } else if (role === 'assistant') {
1186
+ renderAssistantMarkdown(messageDiv, text, isReport);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1187
  } else {
1188
  messageDiv.textContent = text;
1189
  }
 
1190
  messages.appendChild(messageDiv);
 
 
1191
  requestAnimationFrame(() => {
1192
  messageDiv.scrollIntoView({ behavior: 'smooth', block: 'end' });
1193
  });
 
1194
  return messageDiv;
1195
  }
1196
  })();
static/sessions.js CHANGED
@@ -334,11 +334,20 @@
334
  const response = await fetch(`/chat/history?user_id=${encodeURIComponent(user.user_id)}&project_id=${encodeURIComponent(currentProject.project_id)}&session_id=${encodeURIComponent(currentSessionId)}`);
335
  if (response.ok) {
336
  const data = await response.json();
337
- const messages = document.getElementById('messages');
338
- if (messages && data.messages) {
339
- messages.innerHTML = '';
 
340
  data.messages.forEach(message => {
341
- appendMessage(message.role, message.content, message.sources);
 
 
 
 
 
 
 
 
342
  });
343
  }
344
  }
@@ -347,35 +356,7 @@
347
  }
348
  }
349
 
350
- function appendMessage(role, content, sources = []) {
351
- const messages = document.getElementById('messages');
352
- if (!messages) return;
353
-
354
- const messageDiv = document.createElement('div');
355
- messageDiv.className = `message ${role}`;
356
-
357
- const contentDiv = document.createElement('div');
358
- contentDiv.className = 'message-content';
359
-
360
- if (role === 'assistant') {
361
- contentDiv.innerHTML = marked.parse(content);
362
- } else {
363
- contentDiv.textContent = content;
364
- }
365
-
366
- messageDiv.appendChild(contentDiv);
367
-
368
- // Add sources if available
369
- if (sources && sources.length > 0) {
370
- const sourcesDiv = document.createElement('div');
371
- sourcesDiv.className = 'message-sources';
372
- sourcesDiv.innerHTML = '<strong>Sources:</strong> ' + sources.map(s => s.filename || s.url || 'Unknown').join(', ');
373
- messageDiv.appendChild(sourcesDiv);
374
- }
375
-
376
- messages.appendChild(messageDiv);
377
- messages.scrollTop = messages.scrollHeight;
378
- }
379
 
380
  // Function to update session name in UI immediately
381
  function updateSessionName(sessionId, newName) {
 
334
  const response = await fetch(`/chat/history?user_id=${encodeURIComponent(user.user_id)}&project_id=${encodeURIComponent(currentProject.project_id)}&session_id=${encodeURIComponent(currentSessionId)}`);
335
  if (response.ok) {
336
  const data = await response.json();
337
+ const msgContainer = document.getElementById('messages');
338
+ if (msgContainer && data.messages) {
339
+ msgContainer.innerHTML = '';
340
+ // Use unified renderer from script.js to ensure consistent UX
341
  data.messages.forEach(message => {
342
+ const isReport = !!message.is_report;
343
+ // Render the message
344
+ if (window.appendMessage) {
345
+ window.appendMessage(message.role, message.content, isReport);
346
+ }
347
+ // Render sources separately to avoid mixing with content
348
+ if (message.sources && message.sources.length && window.appendSources) {
349
+ window.appendSources(message.sources);
350
+ }
351
  });
352
  }
353
  }
 
356
  }
357
  }
358
 
359
+ // Remove local appendMessage in favor of the unified version from script.js
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
360
 
361
  // Function to update session name in UI immediately
362
  function updateSessionName(sessionId, newName) {