From d9d00dc7bd22cc8564e77f316a912128ef4fefa2 Mon Sep 17 00:00:00 2001 From: Jeffrey Phillips Freeman Date: Tue, 9 Dec 2025 18:26:04 -0500 Subject: [PATCH] Feats: Works mostly end to end and now prints full paper content as first step in the paper_review stage --- .../scientific_paper_writer_langgraph.yaml | 161 +++++++----------- src/cleveragents/cli.py | 16 -- 2 files changed, 59 insertions(+), 118 deletions(-) diff --git a/examples/scientific_paper_writer_langgraph.yaml b/examples/scientific_paper_writer_langgraph.yaml index 37e98f6d..fea761f9 100644 --- a/examples/scientific_paper_writer_langgraph.yaml +++ b/examples/scientific_paper_writer_langgraph.yaml @@ -1079,9 +1079,14 @@ agents: - Work with the user to refine the content based on their feedback - If the user asks for changes, make them and present the revised content - IMPORTANT: When the user is satisfied with the section content, tell them to type !proofread to proceed to proofreading. + RESPONSE FORMAT (MANDATORY): + SECTION_CONTENT: + - Example ending: "Let me know if you'd like any changes. When you're satisfied, type !proofread to proceed to proofreading." + NEXT_ACTION: + Type !proofread when you are satisfied with this section. + + Do NOT include any other commentary, explanations, or conversational text. The SECTION_CONTENT block must contain only the section prose that will be stored in the paper. section_writer_saver: type: tool @@ -1092,9 +1097,20 @@ agents: path = context.get('current_section_path', 'unknown') # Only save actual content, not routing commands if input_data and not input_data.startswith('GOTO_'): - context.setdefault('section_content', {})[path] = input_data - context.setdefault('section_drafts', {})[path] = input_data - context['last_written_section_content'] = input_data + text = input_data.strip() + section_text = text + marker = 'SECTION_CONTENT:' + if marker in text: + remainder = text.split(marker, 1)[1] + next_marker = 'NEXT_ACTION:' + if next_marker in remainder: + section_text = remainder.split(next_marker, 1)[0].strip() + else: + section_text = remainder.strip() + if section_text: + context.setdefault('section_content', {})[path] = section_text + context.setdefault('section_drafts', {})[path] = section_text + context['last_written_section_content'] = section_text result = input_data section_accept_handler: @@ -1191,95 +1207,26 @@ agents: import sys print(f"DEBUG paper_review_controller", file=sys.stderr) - def looks_like_feedback(text: str) -> bool: - if not isinstance(text, str): - return False - lowered = text.lower() - markers = [ - "proofread", - "proofreading", - "grammatical errors", - "type !accept", - "command_output:", - "review feedback", - ] - return any(marker in lowered for marker in markers) - - history_messages = None - for key in ("__messages", "history", "conversation_history", "messages"): - value = context.get(key) - if isinstance(value, list): - history_messages = value - break - if history_messages is None: - graph_state = context.get('graph_state', {}) - if isinstance(graph_state, dict): - msgs = graph_state.get('messages') - if isinstance(msgs, list): - history_messages = msgs - if history_messages is None: - history_messages = [] - - writer_history = {} - current_section = None - pending_writer_output = False - for message in history_messages: - if not isinstance(message, dict): - continue - role = (message.get('role') or '').lower() - content = message.get('content') - if not isinstance(content, str): - continue - stripped = content.strip() - if role == 'assistant': - if stripped.lower().startswith('now working on section:'): - section_name = stripped.split(':', 1)[1].strip() - if section_name: - section_name = section_name.splitlines()[0].strip() - current_section = section_name or current_section - pending_writer_output = False - elif pending_writer_output and current_section: - writer_history[current_section] = content - pending_writer_output = False - elif role == 'user': - if stripped.lower().startswith('!write'): - pending_writer_output = True - else: - pending_writer_output = False - section_content = context.get('section_content', {}) if not section_content: result = "ERROR: No sections found. Please complete section writing stage first." else: - section_drafts = context.get('section_drafts', {}) - # Build full paper text paper_topic = context.get('paper_details', {}).get('topic', 'Scientific Paper') full_text = f"# {paper_topic}\n\n" - # Add sections in order, repairing content when necessary section_paths = context.get('section_paths', []) for path in section_paths: text = section_content.get(path) - if not text: - replacement = section_drafts.get(path) or writer_history.get(path) - if replacement: - section_content[path] = replacement - text = replacement - elif looks_like_feedback(text): - replacement = section_drafts.get(path) or writer_history.get(path) - if replacement: - section_content[path] = replacement - text = replacement - if text: - full_text += f"## {path}\n\n{text}\n\n" + if isinstance(text, str) and text.strip(): + full_text += f"## {path}\n\n{text.strip()}\n\n" - context['assembled_paper'] = full_text + assembled = full_text.strip() + context['assembled_paper'] = assembled - # Display the complete paper to the user before invoking the reviewer LLM - preview_header = "# Full Paper\n" - preview_body = full_text.strip() - preview_output = f"{preview_header}\n{preview_body}" if preview_body else preview_header + preview_output = ( + f"# Full Paper\n\n{assembled}" if assembled else "# Full Paper\n\n(No content available.)" + ) print(preview_output, flush=True) instruction = ( @@ -1298,28 +1245,27 @@ agents: max_history: 20 system_prompt: | You are reviewing the complete assembled paper. - - Here is the full paper content: - - {{ context.get('assembled_paper', '') }} - + + Here is the full paper content: + + {{ context.get('assembled_paper', '') }} + The system already displayed the full paper verbatim before your turn. DO NOT repeat or summarize the paper content. Focus solely on analysis. Structure your response under a single top-level heading "Review Feedback" with clear bullets or subheadings. - - When providing feedback, review the paper for: - - Consistency and logical flow between sections - - Grammar, spelling, and style - - Proper citation format and usage - - Logical coherence and argument strength - - Transitions between sections - - Provide specific, actionable feedback tied to the relevant sections. The user can: - - Discuss refinements to specific sections - - Make changes across the document - - Use !accept or !next when satisfied to proceed to LaTeX generation + When providing feedback, review the paper for: + - Consistency and logical flow between sections + - Grammar, spelling, and style + - Proper citation format and usage + - Logical coherence and argument strength + - Transitions between sections + + Provide specific, actionable feedback tied to the relevant sections. The user can: + - Discuss refinements to specific sections + - Make changes across the document + - Use !accept or !next when satisfied to proceed to LaTeX generation paper_review_saver: type: tool @@ -1330,6 +1276,7 @@ agents: context['reviewed_paper'] = input_data result = input_data + # ============================================ # LATEX GENERATION STAGE # ============================================ @@ -1699,10 +1646,11 @@ routes: target: paper_review extract_message: true separator: ":" - - + + - match_type: prefix pattern: "GOTO_LATEX_GENERATION" + target: latex_generation extract_message: true separator: ":" @@ -1985,18 +1933,19 @@ routes: paper_review: type: AGENT agent: paper_review_controller - + paper_review_agent: type: AGENT agent: paper_review_agent metadata: max_history_messages: 10 max_history_chars: 6000 - + paper_review_saver: type: AGENT agent: paper_review_saver + latex_generation: type: AGENT agent: latex_controller @@ -2208,6 +2157,13 @@ routes: value: section_accept_handler # Paper review sub-routing + - source: router + target: paper_review + condition: + type: context_value + key: next_node + value: paper_review + - source: router target: paper_review_agent condition: @@ -2215,6 +2171,7 @@ routes: key: next_node value: paper_review_agent + # LaTeX generation sub-routing - source: router target: latex_structure_gen diff --git a/src/cleveragents/cli.py b/src/cleveragents/cli.py index 6d205c15..df4b4964 100644 --- a/src/cleveragents/cli.py +++ b/src/cleveragents/cli.py @@ -141,9 +141,6 @@ def run( context_manager = ContextManager(context, context_dir) context_manager.import_context(load_context) - if app.config: - app.config.global_context["__messages"] = list(context_manager.messages) - # Restore the global context to the app saved_global_context = context_manager.get_global_context() if saved_global_context and app.config: @@ -173,11 +170,6 @@ def run( with open(load_context, "r", encoding="utf-8") as f: context_data = json.load(f) - if app.config: - app.config.global_context["__messages"] = list( - context_data.get("messages", []) - ) - # Apply global context to app if present if context_data.get("global_context") and app.config: app.config.global_context.update(context_data["global_context"]) @@ -189,9 +181,6 @@ def run( # Use named context (existing behavior) context_manager = ContextManager(context, context_dir) - if app.config: - app.config.global_context["__messages"] = list(context_manager.messages) - # If context exists, restore the global context to the app if context_manager.exists(): saved_global_context = context_manager.get_global_context() @@ -312,11 +301,6 @@ def interactive( # Import the context from file temp_ctx_manager.import_context(load_context) - if app.config: - app.config.global_context["__messages"] = list( - temp_ctx_manager.messages - ) - # Apply global context to app if present saved_global_context = temp_ctx_manager.get_global_context() if saved_global_context and app.config: