| |
|
|
| from typing import Any, Dict, Generator |
|
|
| from langchain.callbacks.base import BaseCallbackHandler |
| from langchain.chains import LLMChain |
| from langchain.prompts import PromptTemplate |
| from langchain_openai import OpenAI |
|
|
| from streaming_config import get_chunk_size, is_yield_enabled |
| import os |
|
|
| """ |
| LangChain LLMChain for writing a draft based on the outline and research notes with yield support. |
| """ |
| DRAFT_WRITER_PROMPT = """ |
| You are an expert academic writer. Given a research paper outline and research notes, write a comprehensive draft of the paper. Use clear academic language and expand on each section of the outline. |
| |
| Outline: |
| {outline} |
| |
| Research notes: |
| {research_notes} |
| |
| Instructions: |
| - Write a comprehensive draft that fully develops each section of the outline |
| - Use section headings and include inline citations like [1], [2] where appropriate |
| - Do not include a bibliography section |
| - Ensure the draft is complete and covers all points from the outline |
| |
| |
| Draft: |
| """ |
|
|
| def get_draft_writer_chain(): |
| """ |
| Returns a LangChain LLMChain for draft writing. |
| """ |
| prompt = PromptTemplate( |
| input_variables=["outline", "research_notes"], |
| template=DRAFT_WRITER_PROMPT |
| ) |
| |
| |
| llm = OpenAI( |
| temperature=0.3, |
| openai_api_key=os.getenv("OPENAI_API_KEY"), |
| streaming=True |
| ) |
| |
| return LLMChain(llm=llm, prompt=prompt, output_key="draft") |
|
|
| def yield_draft_writing(outline: str, research_notes: str, preset: str = None) -> Generator[str, None, None]: |
| """ |
| Yield draft writing results progressively |
| |
| Args: |
| outline: The research paper outline |
| research_notes: The research notes |
| preset: Optional streaming preset |
| |
| Yields: |
| str: Progressive draft content |
| """ |
| if not is_yield_enabled(preset): |
| |
| chain = get_draft_writer_chain() |
| result = chain.run(outline=outline, research_notes=research_notes) |
| yield result |
| return |
| |
| try: |
| |
| from langchain.prompts import PromptTemplate |
| from langchain_openai import ChatOpenAI |
|
|
| import os |
|
|
| |
| prompt = PromptTemplate( |
| input_variables=["outline", "research_notes"], |
| template=DRAFT_WRITER_PROMPT |
| ) |
| |
| |
| llm = ChatOpenAI( |
| temperature=0.3, |
| openai_api_key=os.getenv("OPENAI_API_KEY"), |
| streaming=True |
| ) |
| |
| |
| formatted_prompt = prompt.format(outline=outline, research_notes=research_notes) |
| |
| |
| chunk_size = get_chunk_size("draft_writer", preset) |
| |
| |
| result = llm.invoke(formatted_prompt) |
| content = result.content |
| |
| |
| for i in range(0, len(content), chunk_size): |
| chunk = content[i:i + chunk_size] |
| yield chunk |
| |
| except Exception as e: |
| yield f"Error in draft writing: {str(e)}" |
|
|
| def yield_draft_by_sections(outline: str, research_notes: str) -> Generator[str, None, None]: |
| """ |
| Yield draft writing organized by sections |
| |
| Args: |
| outline: The research paper outline |
| research_notes: The research notes |
| |
| Yields: |
| str: Progressive draft content by section |
| """ |
| |
| sections = [] |
| lines = outline.split('\n') |
| current_section = "" |
| |
| for line in lines: |
| line = line.strip() |
| if line and (line.startswith('#') or line.startswith('1.') or line.startswith('2.') or |
| line.startswith('3.') or line.startswith('4.') or line.startswith('5.')): |
| current_section = line |
| sections.append(current_section) |
| |
| if not sections: |
| |
| yield "Writing complete draft..." |
| for chunk in yield_draft_writing(outline, research_notes): |
| yield chunk |
| return |
| |
| yield f"Writing draft with {len(sections)} sections..." |
| |
| for i, section in enumerate(sections, 1): |
| yield f"\n--- Section {i}: {section} ---" |
| |
| |
| section_prompt = f""" |
| Write the content for this specific section of the research paper: |
| |
| Section: {section} |
| Full Outline: {outline} |
| Research Notes: {research_notes} |
| |
| Focus on developing this section comprehensively. |
| """ |
| |
| |
| for chunk in yield_draft_writing(section_prompt, research_notes): |
| yield chunk |
| |
| yield f"\n--- Section {i} Complete ---\n" |
|
|
| def yield_draft_with_style(outline: str, research_notes: str, style: str = "academic") -> Generator[str, None, None]: |
| """ |
| Yield draft writing with specific style using yield generators |
| |
| Args: |
| outline: The research paper outline |
| research_notes: The research notes |
| style: Writing style (academic, technical, accessible, etc.) |
| |
| Yields: |
| str: Progressive draft content with specified style |
| """ |
| style_instructions = { |
| "academic": "Use formal academic language with proper citations and scholarly tone.", |
| "technical": "Focus on technical details and methodology with precise terminology.", |
| "accessible": "Use clear, accessible language suitable for broader audiences.", |
| "concise": "Write in a concise, direct manner with minimal elaboration." |
| } |
| |
| style_instruction = style_instructions.get(style, style_instructions["academic"]) |
| |
| yield f"Writing draft in {style} style..." |
| |
| |
| enhanced_prompt = f""" |
| You are an expert academic writer. Given a research paper outline and research notes, write a comprehensive draft of the paper. |
| |
| Style requirement: {style_instruction} |
| |
| Outline: |
| {outline} |
| |
| Research notes: |
| {research_notes} |
| |
| Instructions: |
| - Write a comprehensive draft that fully develops each section of the outline |
| - Use section headings and include inline citations like [1], [2] where appropriate |
| - Do not include a bibliography section |
| - Ensure the draft is complete and covers all points from the outline |
| - Follow the specified style: {style} |
| |
| Draft: |
| """ |
| |
| |
| for chunk in yield_draft_writing(enhanced_prompt, ""): |
| yield chunk |
|
|
| def process_draft_with_revisions(outline: str, research_notes: str, revisions: list = None) -> Generator[str, None, None]: |
| """ |
| Process draft writing with optional revision requests using yield generators |
| |
| Args: |
| outline: The research paper outline |
| research_notes: The research notes |
| revisions: Optional list of revision requests |
| |
| Yields: |
| str: Progressive draft content with revisions |
| """ |
| if revisions: |
| yield f"Applying {len(revisions)} revision requests..." |
| |
| |
| revision_text = "\n".join([f"- {rev}" for rev in revisions]) |
| enhanced_prompt = f""" |
| Outline: {outline} |
| Research notes: {research_notes} |
| |
| Revision requests: |
| {revision_text} |
| |
| Please incorporate these revision requests into the draft. |
| """ |
| else: |
| enhanced_prompt = f"Outline: {outline}\nResearch notes: {research_notes}" |
| |
| |
| for chunk in yield_draft_writing(enhanced_prompt, ""): |
| yield chunk |
|
|