Coverage for src / local_deep_research / advanced_search_system / questions / standard_question.py: 42%
41 statements
« prev ^ index » next coverage.py v7.12.0, created at 2026-01-11 00:51 +0000
« prev ^ index » next coverage.py v7.12.0, created at 2026-01-11 00:51 +0000
1"""
2Standard question generation implementation.
3"""
5from datetime import datetime, UTC
6from typing import List
8from loguru import logger
10from .base_question import BaseQuestionGenerator
13class StandardQuestionGenerator(BaseQuestionGenerator):
14 """Standard question generator."""
16 def generate_questions(
17 self,
18 current_knowledge: str,
19 query: str,
20 questions_per_iteration: int = 2,
21 questions_by_iteration: dict = None,
22 ) -> List[str]:
23 """Generate follow-up questions based on current knowledge."""
24 now = datetime.now(UTC)
25 current_time = now.strftime("%Y-%m-%d")
26 questions_by_iteration = questions_by_iteration or {}
28 logger.info("Generating follow-up questions...")
30 if questions_by_iteration: 30 ↛ 31line 30 didn't jump to line 31 because the condition on line 30 was never true
31 prompt = f"""Critically reflect current knowledge (e.g., timeliness), what {questions_per_iteration} high-quality internet search questions remain unanswered to exactly answer the query?
32 Query: {query}
33 Today: {current_time}
34 Past questions: {questions_by_iteration!s}
35 Knowledge: {current_knowledge}
36 Include questions that critically reflect current knowledge.
37 \n\n\nFormat: One question per line, e.g. \n Q: question1 \n Q: question2\n\n"""
38 else:
39 prompt = f" You will have follow up questions. First, identify if your knowledge is outdated (high chance). Today: {current_time}. Generate {questions_per_iteration} high-quality internet search questions to exactly answer: {query}\n\n\nFormat: One question per line, e.g. \n Q: question1 \n Q: question2\n\n"
41 response = self.model.invoke(prompt)
43 # Handle both string responses and responses with .content attribute
44 response_text = ""
45 if hasattr(response, "content"): 45 ↛ 49line 45 didn't jump to line 49 because the condition on line 45 was always true
46 response_text = response.content
47 else:
48 # Handle string responses
49 response_text = str(response)
51 questions = [
52 q.replace("Q:", "").strip()
53 for q in response_text.split("\n")
54 if q.strip().startswith("Q:")
55 ][:questions_per_iteration]
57 logger.info(f"Generated {len(questions)} follow-up questions")
59 return questions
61 def generate_sub_questions(
62 self, query: str, context: str = ""
63 ) -> List[str]:
64 """
65 Generate sub-questions from a main query.
67 Args:
68 query: The main query to break down
69 context: Additional context for question generation
71 Returns:
72 List[str]: List of generated sub-questions
73 """
74 prompt = f"""You are an expert at breaking down complex questions into simpler sub-questions.
76Original Question: {query}
78{context}
80Break down the original question into 2-5 simpler sub-questions that would help answer the original question when answered in sequence.
81Follow these guidelines:
821. Each sub-question should be specific and answerable on its own
832. Sub-questions should build towards answering the original question
843. For multi-hop or complex queries, identify the individual facts or entities needed
854. Ensure the sub-questions can be answered with separate searches
87Format your response as a numbered list with ONLY the sub-questions, one per line:
881. First sub-question
892. Second sub-question
90...
92Only provide the numbered sub-questions, nothing else."""
94 try:
95 response = self.model.invoke(prompt)
97 # Handle both string responses and responses with .content attribute
98 content = ""
99 if hasattr(response, "content"):
100 content = response.content
101 else:
102 # Handle string responses
103 content = str(response)
105 # Parse sub-questions from the response
106 sub_questions = []
107 for line in content.strip().split("\n"):
108 line = line.strip()
109 if line and (line[0].isdigit() or line.startswith("-")):
110 # Extract sub-question from numbered or bulleted list
111 parts = (
112 line.split(".", 1)
113 if "." in line
114 else line.split(" ", 1)
115 )
116 if len(parts) > 1:
117 sub_question = parts[1].strip()
118 sub_questions.append(sub_question)
120 # Limit to at most 5 sub-questions
121 return sub_questions[:5]
122 except Exception as e:
123 logger.exception(f"Error generating sub-questions: {e!s}")
124 return []