anushkap01patidar commited on
Commit
7de6e4d
·
1 Parent(s): ff2498b

Add agents directory with citation, draft, outline, and topic modules

Browse files
agents/__init__.py ADDED
@@ -0,0 +1 @@
 
 
1
+ # Agents package for Research Paper Generator
agents/citation_formatter.py ADDED
@@ -0,0 +1,53 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ LangChain LLMChain for generating bibliography from research notes and draft.
3
+ """
4
+ from langchain.callbacks.base import BaseCallbackHandler
5
+ from langchain.chains import LLMChain
6
+ from langchain.prompts import PromptTemplate
7
+ from langchain_community.llms import OpenAI
8
+
9
+ from config import get_openai_api_key
10
+
11
+ # Prompt template for bibliography generation
12
+ BIBLIOGRAPHY_PROMPT = """
13
+ You are an expert academic writer. Given research notes and a draft, generate a bibliography in APA format with a maximum of 10 references.
14
+
15
+ Research notes:
16
+ {research_notes}
17
+
18
+ Draft:
19
+ {draft}
20
+
21
+ Instructions:
22
+ - Analyze the research notes and draft to identify the most important sources and references
23
+ - Extract information about studies, papers, authors, and findings mentioned
24
+ - Create proper APA format citations for each source
25
+ - Include author names, publication years, titles, and sources where possible
26
+ - If specific details are missing, create reasonable citations based on the content
27
+ - Limit the bibliography to a maximum of 10 references
28
+ - Choose the most relevant and important sources only
29
+ - Number the references if they are cited in the draft with [1], [2], etc.
30
+
31
+ Example format:
32
+ [1] Smith, J., & Johnson, A. (2023). Title of the study. Journal Name, 45(2), 123-145.
33
+
34
+ Bibliography (APA format, maximum 10 references):
35
+ """
36
+
37
+ def get_bibliography_chain():
38
+ """
39
+ Returns a LangChain LLMChain for bibliography generation.
40
+ """
41
+ prompt = PromptTemplate(
42
+ input_variables=["research_notes", "draft"],
43
+ template=BIBLIOGRAPHY_PROMPT
44
+ )
45
+
46
+ # Always enable streaming in the LLM - callbacks will be passed during execution
47
+ llm = OpenAI(
48
+ temperature=0.1,
49
+ openai_api_key=get_openai_api_key(),
50
+ streaming=True # Always enable streaming
51
+ )
52
+
53
+ return LLMChain(llm=llm, prompt=prompt, output_key="bibliography")
agents/draft_writer.py ADDED
@@ -0,0 +1,242 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # agents/draft_writer.py
2
+
3
+ from typing import Any, Dict, Generator
4
+
5
+ from langchain.callbacks.base import BaseCallbackHandler
6
+ from langchain.chains import LLMChain
7
+ from langchain.prompts import PromptTemplate
8
+ from langchain_community.llms import OpenAI
9
+
10
+ from config import get_openai_api_key
11
+ from streaming_config import get_chunk_size, is_yield_enabled
12
+
13
+ """
14
+ LangChain LLMChain for writing a draft based on the outline and research notes with yield support.
15
+ """
16
+ DRAFT_WRITER_PROMPT = """
17
+ 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.
18
+
19
+ Outline:
20
+ {outline}
21
+
22
+ Research notes:
23
+ {research_notes}
24
+
25
+ Instructions:
26
+ - Write a comprehensive draft that fully develops each section of the outline
27
+ - Use section headings and include inline citations like [1], [2] where appropriate
28
+ - Do not include a bibliography section
29
+ - Ensure the draft is complete and covers all points from the outline
30
+
31
+
32
+ Draft:
33
+ """
34
+
35
+ def get_draft_writer_chain():
36
+ """
37
+ Returns a LangChain LLMChain for draft writing.
38
+ """
39
+ prompt = PromptTemplate(
40
+ input_variables=["outline", "research_notes"],
41
+ template=DRAFT_WRITER_PROMPT
42
+ )
43
+
44
+ # Always enable streaming in the LLM - callbacks will be passed during execution
45
+ llm = OpenAI(
46
+ temperature=0.3,
47
+ openai_api_key=get_openai_api_key(),
48
+ streaming=True # Always enable streaming
49
+ )
50
+
51
+ return LLMChain(llm=llm, prompt=prompt, output_key="draft")
52
+
53
+ def yield_draft_writing(outline: str, research_notes: str, preset: str = None) -> Generator[str, None, None]:
54
+ """
55
+ Yield draft writing results progressively
56
+
57
+ Args:
58
+ outline: The research paper outline
59
+ research_notes: The research notes
60
+ preset: Optional streaming preset
61
+
62
+ Yields:
63
+ str: Progressive draft content
64
+ """
65
+ if not is_yield_enabled(preset):
66
+ # Fallback to non-yield processing
67
+ chain = get_draft_writer_chain()
68
+ result = chain.run(outline=outline, research_notes=research_notes)
69
+ yield result
70
+ return
71
+
72
+ try:
73
+ # Import required modules
74
+ from langchain.prompts import PromptTemplate
75
+ from langchain_openai import ChatOpenAI
76
+
77
+ from config import get_openai_api_key
78
+
79
+ # Create prompt template
80
+ prompt = PromptTemplate(
81
+ input_variables=["outline", "research_notes"],
82
+ template=DRAFT_WRITER_PROMPT
83
+ )
84
+
85
+ # Create LLM with streaming
86
+ llm = ChatOpenAI(
87
+ temperature=0.3,
88
+ openai_api_key=get_openai_api_key(),
89
+ streaming=True
90
+ )
91
+
92
+ # Format the prompt
93
+ formatted_prompt = prompt.format(outline=outline, research_notes=research_notes)
94
+
95
+ # Get chunk size for this step
96
+ chunk_size = get_chunk_size("draft_writer", preset)
97
+
98
+ # Call LLM and yield results progressively
99
+ result = llm.invoke(formatted_prompt)
100
+ content = result.content
101
+
102
+ # Yield content in chunks
103
+ for i in range(0, len(content), chunk_size):
104
+ chunk = content[i:i + chunk_size]
105
+ yield chunk
106
+
107
+ except Exception as e:
108
+ yield f"Error in draft writing: {str(e)}"
109
+
110
+ def yield_draft_by_sections(outline: str, research_notes: str) -> Generator[str, None, None]:
111
+ """
112
+ Yield draft writing organized by sections
113
+
114
+ Args:
115
+ outline: The research paper outline
116
+ research_notes: The research notes
117
+
118
+ Yields:
119
+ str: Progressive draft content by section
120
+ """
121
+ # Parse outline to extract sections
122
+ sections = []
123
+ lines = outline.split('\n')
124
+ current_section = ""
125
+
126
+ for line in lines:
127
+ line = line.strip()
128
+ if line and (line.startswith('#') or line.startswith('1.') or line.startswith('2.') or
129
+ line.startswith('3.') or line.startswith('4.') or line.startswith('5.')):
130
+ current_section = line
131
+ sections.append(current_section)
132
+
133
+ if not sections:
134
+ # Fallback to single section
135
+ yield "Writing complete draft..."
136
+ for chunk in yield_draft_writing(outline, research_notes):
137
+ yield chunk
138
+ return
139
+
140
+ yield f"Writing draft with {len(sections)} sections..."
141
+
142
+ for i, section in enumerate(sections, 1):
143
+ yield f"\n--- Section {i}: {section} ---"
144
+
145
+ # Create section-specific prompt
146
+ section_prompt = f"""
147
+ Write the content for this specific section of the research paper:
148
+
149
+ Section: {section}
150
+ Full Outline: {outline}
151
+ Research Notes: {research_notes}
152
+
153
+ Focus on developing this section comprehensively.
154
+ """
155
+
156
+ # Process section with yield
157
+ for chunk in yield_draft_writing(section_prompt, research_notes):
158
+ yield chunk
159
+
160
+ yield f"\n--- Section {i} Complete ---\n"
161
+
162
+ def yield_draft_with_style(outline: str, research_notes: str, style: str = "academic") -> Generator[str, None, None]:
163
+ """
164
+ Yield draft writing with specific style using yield generators
165
+
166
+ Args:
167
+ outline: The research paper outline
168
+ research_notes: The research notes
169
+ style: Writing style (academic, technical, accessible, etc.)
170
+
171
+ Yields:
172
+ str: Progressive draft content with specified style
173
+ """
174
+ style_instructions = {
175
+ "academic": "Use formal academic language with proper citations and scholarly tone.",
176
+ "technical": "Focus on technical details and methodology with precise terminology.",
177
+ "accessible": "Use clear, accessible language suitable for broader audiences.",
178
+ "concise": "Write in a concise, direct manner with minimal elaboration."
179
+ }
180
+
181
+ style_instruction = style_instructions.get(style, style_instructions["academic"])
182
+
183
+ yield f"Writing draft in {style} style..."
184
+
185
+ # Create style-enhanced prompt
186
+ enhanced_prompt = f"""
187
+ You are an expert academic writer. Given a research paper outline and research notes, write a comprehensive draft of the paper.
188
+
189
+ Style requirement: {style_instruction}
190
+
191
+ Outline:
192
+ {outline}
193
+
194
+ Research notes:
195
+ {research_notes}
196
+
197
+ Instructions:
198
+ - Write a comprehensive draft that fully develops each section of the outline
199
+ - Use section headings and include inline citations like [1], [2] where appropriate
200
+ - Do not include a bibliography section
201
+ - Ensure the draft is complete and covers all points from the outline
202
+ - Follow the specified style: {style}
203
+
204
+ Draft:
205
+ """
206
+
207
+ # Yield the enhanced draft writing
208
+ for chunk in yield_draft_writing(enhanced_prompt, ""):
209
+ yield chunk
210
+
211
+ def process_draft_with_revisions(outline: str, research_notes: str, revisions: list = None) -> Generator[str, None, None]:
212
+ """
213
+ Process draft writing with optional revision requests using yield generators
214
+
215
+ Args:
216
+ outline: The research paper outline
217
+ research_notes: The research notes
218
+ revisions: Optional list of revision requests
219
+
220
+ Yields:
221
+ str: Progressive draft content with revisions
222
+ """
223
+ if revisions:
224
+ yield f"Applying {len(revisions)} revision requests..."
225
+
226
+ # Apply revisions to the prompt
227
+ revision_text = "\n".join([f"- {rev}" for rev in revisions])
228
+ enhanced_prompt = f"""
229
+ Outline: {outline}
230
+ Research notes: {research_notes}
231
+
232
+ Revision requests:
233
+ {revision_text}
234
+
235
+ Please incorporate these revision requests into the draft.
236
+ """
237
+ else:
238
+ enhanced_prompt = f"Outline: {outline}\nResearch notes: {research_notes}"
239
+
240
+ # Yield the enhanced draft writing
241
+ for chunk in yield_draft_writing(enhanced_prompt, ""):
242
+ yield chunk
agents/outline_builder.py ADDED
@@ -0,0 +1,127 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ LangChain LLMChain for building research paper outlines.
3
+ """
4
+ from langchain.callbacks.base import BaseCallbackHandler
5
+ from langchain.chains import LLMChain
6
+ from langchain.prompts import PromptTemplate
7
+ from langchain_community.llms import OpenAI
8
+
9
+ from config import get_openai_api_key
10
+
11
+ # Prompt template for initial outline building
12
+ OUTLINE_BUILDER_PROMPT = """
13
+ You are an expert academic writer. Given a refined research topic and research notes, create a comprehensive outline for a research paper.
14
+
15
+ Refined topic: {refined_topic}
16
+
17
+ Research notes:
18
+ {research_notes}
19
+
20
+ Instructions:
21
+ - Create a detailed, structured outline with main sections and subsections
22
+ - Include introduction, literature review, methodology, results, discussion, and conclusion
23
+ - Use clear hierarchical structure with numbers and letters
24
+ - Make sure response should be generated in 1500 characters including spaces.
25
+
26
+ Research paper outline:
27
+ """
28
+
29
+ # Prompt template for outline revision based on feedback
30
+ OUTLINE_REVISION_PROMPT = """
31
+ You are an expert academic writer. Given a current outline, feedback for revision, and research notes, revise the outline accordingly.
32
+
33
+ Current outline:
34
+ {current_outline}
35
+
36
+ Feedback for revision:
37
+ {feedback}
38
+
39
+ Research notes:
40
+ {research_notes}
41
+
42
+ Instructions:
43
+ - Revise the outline based on the provided feedback
44
+ - Maintain the academic structure and flow
45
+ - Ensure all sections are properly organized
46
+ - Make sure response should be generated in 1500 characters including spaces.
47
+
48
+ Revised research paper outline:
49
+ """
50
+
51
+ def get_outline_builder_chain():
52
+ """
53
+ Returns a LangChain LLMChain for initial outline building.
54
+ """
55
+ prompt = PromptTemplate(
56
+ input_variables=["refined_topic", "research_notes"],
57
+ template=OUTLINE_BUILDER_PROMPT
58
+ )
59
+
60
+ # Always enable streaming in the LLM - callbacks will be passed during execution
61
+ llm = OpenAI(
62
+ temperature=0.2,
63
+ openai_api_key=get_openai_api_key(),
64
+ streaming=True # Always enable streaming
65
+ )
66
+
67
+ return LLMChain(llm=llm, prompt=prompt, output_key="outline")
68
+
69
+ def get_outline_revision_chain():
70
+ """
71
+ Returns a LangChain LLMChain for outline revision based on feedback.
72
+ """
73
+ prompt = PromptTemplate(
74
+ input_variables=["current_outline", "feedback", "research_notes"],
75
+ template=OUTLINE_REVISION_PROMPT
76
+ )
77
+
78
+ # Always enable streaming in the LLM - callbacks will be passed during execution
79
+ llm = OpenAI(
80
+ temperature=0.7,
81
+ openai_api_key=get_openai_api_key(),
82
+ streaming=True # Always enable streaming
83
+ )
84
+
85
+ return LLMChain(llm=llm, prompt=prompt, output_key="outline")
86
+
87
+ def outline_builder_node(state: dict) -> dict:
88
+ """
89
+ Wrapper node for initial outline building.
90
+ """
91
+ # Create the chain
92
+ chain = get_outline_builder_chain()
93
+
94
+ # Call the chain with required inputs
95
+ result = chain({
96
+ "refined_topic": state["refined_topic"],
97
+ "research_notes": state["research_notes"]
98
+ })
99
+
100
+ # Update state with the result
101
+ state.update(result)
102
+ return state
103
+
104
+ def outline_revision_node(state: dict) -> dict:
105
+ """
106
+ Wrapper node for outline revision based on feedback.
107
+ """
108
+ # Get feedback, default to empty string if missing
109
+ feedback = state.get("feedback", "")
110
+
111
+
112
+
113
+ # Create the revision chain
114
+ chain = get_outline_revision_chain()
115
+
116
+ # Call the chain with only the required inputs (matching the prompt template)
117
+ result = chain({
118
+ "current_outline": state["outline"],
119
+ "feedback": feedback,
120
+ "research_notes": state["research_notes"]
121
+ })
122
+
123
+ # Update state with the revised outline
124
+ state.update(result)
125
+ return state
126
+
127
+
agents/research_retriever.py ADDED
@@ -0,0 +1,163 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ LangChain LLMChain for retrieving research notes based on a refined topic with yield support.
3
+ """
4
+ from typing import Any, Dict, Generator
5
+
6
+ from langchain.callbacks.base import BaseCallbackHandler
7
+ from langchain.chains import LLMChain
8
+ from langchain.prompts import PromptTemplate
9
+ from langchain_community.llms import OpenAI
10
+
11
+ from config import get_openai_api_key
12
+ from streaming_config import get_chunk_size, is_yield_enabled
13
+
14
+ # Prompt template for research retrieval
15
+ RESEARCH_RETRIEVER_PROMPT = """
16
+ You are an expert research assistant. Given a refined research topic, retrieve or summarize the most relevant and recent academic research, findings, and key points. Present the information as concise bullet points.
17
+
18
+ Refined topic: {refined_topic}
19
+
20
+ Instructions:
21
+ - Provide comprehensive but concise research notes
22
+ - Use clear bullet points for easy reading
23
+ - Focus on the most important findings and key points
24
+ - Make sure response should be generated in 2000 characters including spaces.
25
+
26
+ Research notes (bullet points):
27
+ """
28
+
29
+ def get_research_retriever_chain():
30
+ """
31
+ Returns a LangChain LLMChain for research retrieval.
32
+ """
33
+ prompt = PromptTemplate(
34
+ input_variables=["refined_topic"],
35
+ template=RESEARCH_RETRIEVER_PROMPT
36
+ )
37
+
38
+ # Always enable streaming in the LLM - callbacks will be passed during execution
39
+ llm = OpenAI(
40
+ temperature=0.1,
41
+ openai_api_key=get_openai_api_key(),
42
+ streaming=True # Always enable streaming
43
+ )
44
+
45
+ return LLMChain(llm=llm, prompt=prompt, output_key="research_notes")
46
+
47
+ def yield_research_retrieval(refined_topic: str, preset: str = None) -> Generator[str, None, None]:
48
+ """
49
+ Yield research retrieval results progressively
50
+
51
+ Args:
52
+ refined_topic: The refined research topic
53
+ preset: Optional streaming preset
54
+
55
+ Yields:
56
+ str: Progressive research notes
57
+ """
58
+ if not is_yield_enabled(preset):
59
+ # Fallback to non-yield processing
60
+ chain = get_research_retriever_chain()
61
+ result = chain.run(refined_topic=refined_topic)
62
+ yield result
63
+ return
64
+
65
+ try:
66
+ # Import required modules
67
+ from langchain.prompts import PromptTemplate
68
+ from langchain_openai import ChatOpenAI
69
+
70
+ from config import get_openai_api_key
71
+
72
+ # Create prompt template
73
+ prompt = PromptTemplate(
74
+ input_variables=["refined_topic"],
75
+ template=RESEARCH_RETRIEVER_PROMPT
76
+ )
77
+
78
+ # Create LLM with streaming
79
+ llm = ChatOpenAI(
80
+ temperature=0.1,
81
+ openai_api_key=get_openai_api_key(),
82
+ streaming=True
83
+ )
84
+
85
+ # Format the prompt
86
+ formatted_prompt = prompt.format(refined_topic=refined_topic)
87
+
88
+ # Get chunk size for this step
89
+ chunk_size = get_chunk_size("research_retriever", preset)
90
+
91
+ # Call LLM and yield results progressively
92
+ result = llm.invoke(formatted_prompt)
93
+ content = result.content
94
+
95
+ # Yield content in chunks
96
+ for i in range(0, len(content), chunk_size):
97
+ chunk = content[i:i + chunk_size]
98
+ yield chunk
99
+
100
+ except Exception as e:
101
+ yield f"Error in research retrieval: {str(e)}"
102
+
103
+ def yield_research_by_sections(refined_topic: str, sections: list = None) -> Generator[str, None, None]:
104
+ """
105
+ Yield research retrieval organized by sections
106
+
107
+ Args:
108
+ refined_topic: The refined research topic
109
+ sections: List of research sections to focus on
110
+
111
+ Yields:
112
+ str: Progressive research notes by section
113
+ """
114
+ if not sections:
115
+ sections = ["Background", "Key Findings", "Methodology", "Conclusions"]
116
+
117
+ yield f"Retrieving research for: {refined_topic}"
118
+
119
+ for section in sections:
120
+ yield f"\n--- {section} ---"
121
+
122
+ # Create section-specific prompt
123
+ section_prompt = f"""
124
+ Focus on the {section.lower()} aspects of research related to: {refined_topic}
125
+
126
+ Provide key points and findings for the {section} section.
127
+ """
128
+
129
+ # Process section with yield
130
+ for chunk in yield_research_retrieval(section_prompt):
131
+ yield chunk
132
+
133
+ yield "\n"
134
+
135
+ def process_research_with_filters(refined_topic: str, filters: dict = None) -> Generator[str, None, None]:
136
+ """
137
+ Process research retrieval with optional filters using yield generators
138
+
139
+ Args:
140
+ refined_topic: The refined research topic
141
+ filters: Optional filters for research focus
142
+
143
+ Yields:
144
+ str: Progressive filtered research results
145
+ """
146
+ if filters:
147
+ yield f"Applying research filters: {list(filters.keys())}"
148
+
149
+ # Apply filters to the prompt
150
+ filter_text = ", ".join([f"{k}: {v}" for k, v in filters.items()])
151
+ enhanced_prompt = f"""
152
+ Refined topic: {refined_topic}
153
+ Research filters: {filter_text}
154
+
155
+ Please focus the research retrieval on these specific aspects.
156
+ """
157
+ else:
158
+ enhanced_prompt = f"Refined topic: {refined_topic}"
159
+
160
+ # Yield the enhanced research retrieval
161
+ for chunk in yield_research_retrieval(enhanced_prompt):
162
+ yield chunk
163
+
agents/topic_analyzer.py ADDED
@@ -0,0 +1,125 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ LangChain LLMChain for analyzing and refining a research topic with yield support.
3
+ """
4
+ from langchain.chains import LLMChain
5
+ from langchain.prompts import PromptTemplate
6
+ from langchain_community.llms import OpenAI
7
+ from langchain.callbacks.base import BaseCallbackHandler
8
+ from config import get_openai_api_key
9
+ from typing import Dict, Any, Generator
10
+ from streaming_config import get_chunk_size, is_yield_enabled
11
+
12
+ # Prompt template for topic analysis
13
+ TOPIC_ANALYZER_PROMPT = """
14
+ You are an expert research assistant. Given a user-provided research topic, refine it for clarity, focus, and academic rigor.
15
+
16
+ Original topic: {topic}
17
+
18
+ Instructions:
19
+ - Refine the topic into a clear, focused research question
20
+ - Keep the response concise and academic
21
+ - Make sure response should be generated in 500 characters including spaces.
22
+
23
+ Refined topic (one sentence):
24
+ """
25
+
26
+ def get_topic_analyzer_chain():
27
+ """
28
+ Returns a LangChain LLMChain for topic analysis.
29
+ """
30
+ prompt = PromptTemplate(
31
+ input_variables=["topic"],
32
+ template=TOPIC_ANALYZER_PROMPT
33
+ )
34
+
35
+ # Always enable streaming in the LLM - callbacks will be passed during execution
36
+ llm = OpenAI(
37
+ temperature=0.3,
38
+ openai_api_key=get_openai_api_key(),
39
+ streaming=True # Always enable streaming
40
+ )
41
+
42
+ return LLMChain(llm=llm, prompt=prompt, output_key="refined_topic")
43
+
44
+ def yield_topic_analysis(topic: str, preset: str = None) -> Generator[str, None, None]:
45
+ """
46
+ Yield topic analysis results progressively
47
+
48
+ Args:
49
+ topic: The research topic to analyze
50
+ preset: Optional streaming preset
51
+
52
+ Yields:
53
+ str: Progressive analysis results
54
+ """
55
+ if not is_yield_enabled(preset):
56
+ # Fallback to non-yield processing
57
+ chain = get_topic_analyzer_chain()
58
+ result = chain.run(topic=topic)
59
+ yield result
60
+ return
61
+
62
+ try:
63
+ # Import required modules
64
+ from langchain_openai import ChatOpenAI
65
+ from langchain.prompts import PromptTemplate
66
+ from config import get_openai_api_key
67
+
68
+ # Create prompt template
69
+ prompt = PromptTemplate(
70
+ input_variables=["topic"],
71
+ template=TOPIC_ANALYZER_PROMPT
72
+ )
73
+
74
+ # Create LLM with streaming
75
+ llm = ChatOpenAI(
76
+ temperature=0.3,
77
+ openai_api_key=get_openai_api_key(),
78
+ streaming=True
79
+ )
80
+
81
+ # Format the prompt
82
+ formatted_prompt = prompt.format(topic=topic)
83
+
84
+ # Get chunk size for this step
85
+ chunk_size = get_chunk_size("topic_analyzer", preset)
86
+
87
+ # Call LLM and yield results progressively
88
+ result = llm.invoke(formatted_prompt)
89
+ content = result.content
90
+
91
+ # Yield content in chunks
92
+ for i in range(0, len(content), chunk_size):
93
+ chunk = content[i:i + chunk_size]
94
+ yield chunk
95
+
96
+ except Exception as e:
97
+ yield f"Error in topic analysis: {str(e)}"
98
+
99
+ def process_topic_with_feedback(topic: str, feedback: str = None) -> Generator[str, None, None]:
100
+ """
101
+ Process topic analysis with optional feedback using yield generators
102
+
103
+ Args:
104
+ topic: The research topic
105
+ feedback: Optional feedback for refinement
106
+
107
+ Yields:
108
+ str: Progressive processing results
109
+ """
110
+ if feedback:
111
+ # Include feedback in processing
112
+ enhanced_prompt = f"""
113
+ Original topic: {topic}
114
+ Feedback: {feedback}
115
+
116
+ Please refine the topic considering the feedback provided.
117
+ """
118
+ yield "Processing topic with feedback..."
119
+ else:
120
+ enhanced_prompt = f"Original topic: {topic}"
121
+ yield "Processing topic analysis..."
122
+
123
+ # Yield the enhanced prompt for processing
124
+ for chunk in yield_topic_analysis(enhanced_prompt):
125
+ yield chunk