Spaces:
Running
Running
Commit ·
ed4acef
1
Parent(s): 40c0e33
Upd appendMessage utils in static. Upd mermaid rendering. Upd coder and namer agents
Browse files- .DS_Store +0 -0
- .gitignore +2 -1
- routes/reports.py +126 -1
- static/script.js +45 -54
- 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
|
| 742 |
-
|
| 743 |
-
|
| 744 |
-
|
| 745 |
-
|
| 746 |
-
|
| 747 |
-
|
| 748 |
-
|
| 749 |
-
|
| 750 |
-
|
| 751 |
-
|
| 752 |
-
|
| 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 |
-
|
| 768 |
-
|
| 769 |
-
|
| 770 |
-
|
| 771 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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 |
-
|
| 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
|
| 338 |
-
if (
|
| 339 |
-
|
|
|
|
| 340 |
data.messages.forEach(message => {
|
| 341 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 342 |
});
|
| 343 |
}
|
| 344 |
}
|
|
@@ -347,35 +356,7 @@
|
|
| 347 |
}
|
| 348 |
}
|
| 349 |
|
| 350 |
-
|
| 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) {
|