github-actions[bot] commited on
Commit
caa2d9c
·
1 Parent(s): f57aa27

Auto-sync from demo at Fri Jan 16 06:05:51 UTC 2026

Browse files
app.py CHANGED
@@ -524,6 +524,7 @@ with gr.Blocks(title="GraphGen Demo", theme=gr.themes.Glass(), css=css) as demo:
524
  "multi_choice",
525
  "multi_answer",
526
  "fill_in_blank",
 
527
  ],
528
  label=_("Mode"),
529
  value="aggregated",
 
524
  "multi_choice",
525
  "multi_answer",
526
  "fill_in_blank",
527
+ "true_false",
528
  ],
529
  label=_("Mode"),
530
  value="aggregated",
graphgen/models/__init__.py CHANGED
@@ -16,6 +16,7 @@ from .generator import (
16
  MultiChoiceGenerator,
17
  MultiHopGenerator,
18
  QuizGenerator,
 
19
  VQAGenerator,
20
  )
21
  from .kg_builder import LightRAGKGBuilder, MMKGBuilder
 
16
  MultiChoiceGenerator,
17
  MultiHopGenerator,
18
  QuizGenerator,
19
+ TrueFalseGenerator,
20
  VQAGenerator,
21
  )
22
  from .kg_builder import LightRAGKGBuilder, MMKGBuilder
graphgen/models/generator/__init__.py CHANGED
@@ -6,4 +6,5 @@ from .multi_answer_generator import MultiAnswerGenerator
6
  from .multi_choice_generator import MultiChoiceGenerator
7
  from .multi_hop_generator import MultiHopGenerator
8
  from .quiz_generator import QuizGenerator
 
9
  from .vqa_generator import VQAGenerator
 
6
  from .multi_choice_generator import MultiChoiceGenerator
7
  from .multi_hop_generator import MultiHopGenerator
8
  from .quiz_generator import QuizGenerator
9
+ from .true_false_generator import TrueFalseGenerator
10
  from .vqa_generator import VQAGenerator
graphgen/models/generator/true_false_generator.py ADDED
@@ -0,0 +1,91 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import re
2
+ from typing import Any
3
+
4
+ from graphgen.bases import BaseGenerator
5
+ from graphgen.templates import TF_GENERATION_PROMPT
6
+ from graphgen.utils import compute_content_hash, detect_main_language, logger
7
+
8
+
9
+ class TrueFalseGenerator(BaseGenerator):
10
+ def __init__(self, llm_client, num_of_questions) -> None:
11
+ super().__init__(llm_client)
12
+ self.num_of_questions = num_of_questions
13
+
14
+ @staticmethod
15
+ def parse_response(response: str) -> Any:
16
+ """
17
+ Parse true/false QA pairs from the LLM response.
18
+ Each QA pair contains a statement question and True/False answer.
19
+
20
+ :param response: The LLM response containing XML-formatted QA pairs
21
+ :return: Dictionary mapping question hash to question data, where each
22
+ value is a dict with "question", "options", and "answer" keys
23
+ """
24
+ qa_pairs: dict[str, dict[str, Any]] = {}
25
+
26
+ # Extract all QA pair blocks
27
+ qa_blocks = re.findall(r"<qa_pair>(.*?)</qa_pair>", response, re.DOTALL)
28
+
29
+ if not qa_blocks:
30
+ logger.warning("No QA pairs found in response: %s", response)
31
+ return {}
32
+
33
+ for block in qa_blocks:
34
+ # Extract and clean question text
35
+ q_match = re.search(r"<question>(.*?)</question>", block, re.DOTALL)
36
+ if not q_match:
37
+ logger.warning("Failed to parse question from block: %s", block)
38
+ continue
39
+ question = q_match.group(1).strip().strip('"').strip("'")
40
+
41
+ # Extract and validate answer
42
+ ans_match = re.search(r"<answer>(.*?)</answer>", block, re.DOTALL)
43
+ if not ans_match:
44
+ logger.warning("Failed to parse answer from block: %s", block)
45
+ continue
46
+ answer = ans_match.group(1).strip().strip('"').strip("'")
47
+
48
+ # Ensure answer exists in options
49
+ if answer.lower() not in ["true", "false"]:
50
+ logger.warning("Invalid answer '%s' in block: %s", answer, block)
51
+ continue
52
+
53
+ # Build result entry with question hash as key
54
+ question_hash = compute_content_hash(question)
55
+ qa_pairs[question_hash] = {
56
+ "question": question,
57
+ "answer": answer, # "True" or "False"
58
+ }
59
+
60
+ logger.debug("Successfully parsed TF question: %s", question[:50])
61
+
62
+ if not qa_pairs:
63
+ logger.error("Failed to parse any valid true/false pairs from response")
64
+
65
+ return qa_pairs
66
+
67
+ # pylint: disable=W0221
68
+ def build_prompt(
69
+ self, batch: tuple[list[tuple[str, dict]], list[tuple[Any, Any, dict]]]
70
+ ) -> str:
71
+ nodes, edges = batch
72
+ entities_str = "\n".join(
73
+ [
74
+ f"{index + 1}. {node[0]}: {node[1]['description']}"
75
+ for index, node in enumerate(nodes)
76
+ ]
77
+ )
78
+
79
+ relationships_str = "\n".join(
80
+ [
81
+ f"{index + 1}. {edge[0]} -- {edge[1]}: {edge[2]['description']}"
82
+ for index, edge in enumerate(edges)
83
+ ]
84
+ )
85
+ context = entities_str + "\n" + relationships_str
86
+ language = detect_main_language(entities_str + relationships_str)
87
+ prompt = TF_GENERATION_PROMPT[language].format(
88
+ context=context,
89
+ num_of_questions=self.num_of_questions,
90
+ )
91
+ return prompt
graphgen/operators/generate/generate_service.py CHANGED
@@ -64,6 +64,13 @@ class GenerateService(BaseOperator):
64
  self.llm_client,
65
  num_of_questions=generate_kwargs.get("num_of_questions", 5),
66
  )
 
 
 
 
 
 
 
67
  else:
68
  raise ValueError(f"Unsupported generation mode: {method}")
69
 
 
64
  self.llm_client,
65
  num_of_questions=generate_kwargs.get("num_of_questions", 5),
66
  )
67
+ elif self.method == "true_false":
68
+ from graphgen.models import TrueFalseGenerator
69
+
70
+ self.generator = TrueFalseGenerator(
71
+ self.llm_client,
72
+ num_of_questions=generate_kwargs.get("num_of_questions", 5),
73
+ )
74
  else:
75
  raise ValueError(f"Unsupported generation mode: {method}")
76
 
graphgen/templates/__init__.py CHANGED
@@ -10,6 +10,7 @@ from .generation import (
10
  MAQ_GENERATION_PROMPT,
11
  MCQ_GENERATION_PROMPT,
12
  MULTI_HOP_GENERATION_PROMPT,
 
13
  VQA_GENERATION_PROMPT,
14
  )
15
  from .kg import KG_EXTRACTION_PROMPT, KG_SUMMARIZATION_PROMPT, MMKG_EXTRACTION_PROMPT
 
10
  MAQ_GENERATION_PROMPT,
11
  MCQ_GENERATION_PROMPT,
12
  MULTI_HOP_GENERATION_PROMPT,
13
+ TF_GENERATION_PROMPT,
14
  VQA_GENERATION_PROMPT,
15
  )
16
  from .kg import KG_EXTRACTION_PROMPT, KG_SUMMARIZATION_PROMPT, MMKG_EXTRACTION_PROMPT
graphgen/templates/generation/__init__.py CHANGED
@@ -5,4 +5,5 @@ from .fill_in_blank_generation import FILL_IN_BLANK_GENERATION_PROMPT
5
  from .multi_answer_generation import MAQ_GENERATION_PROMPT
6
  from .multi_choice_generation import MCQ_GENERATION_PROMPT
7
  from .multi_hop_generation import MULTI_HOP_GENERATION_PROMPT
 
8
  from .vqa_generation import VQA_GENERATION_PROMPT
 
5
  from .multi_answer_generation import MAQ_GENERATION_PROMPT
6
  from .multi_choice_generation import MCQ_GENERATION_PROMPT
7
  from .multi_hop_generation import MULTI_HOP_GENERATION_PROMPT
8
+ from .true_false_generation import TF_GENERATION_PROMPT
9
  from .vqa_generation import VQA_GENERATION_PROMPT
graphgen/templates/generation/true_false_generation.py ADDED
@@ -0,0 +1,75 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ TEMPLATE_TF_ZH: str = """请根据上下文资料生成独立的知识判断题,每个判断题包含一个陈述句,答案只能是正确(True)或错误(False)。
2
+
3
+ 生成要求:
4
+ 1. **语言一致性**:若上下文资料为中文,则生成中文问题;若为英文,则生成英文问题
5
+ 2. **数量**:每个上下文资料生成{num_of_questions}个判断题
6
+ 3. **独立性**:每个问题必须完整独立,不依赖其他问题
7
+ 4. **准确性**:正确答案必须能从原文直接得出,陈述需有明确的判断依据
8
+
9
+ 输出格式:
10
+ <qa_pairs>
11
+ <qa_pair>
12
+ <question>陈述句文本</question>
13
+ <answer>True或False</answer>
14
+ </qa_pair>
15
+ </qa_pairs>
16
+
17
+ 示例(根据iPad Air 2生成2题):
18
+ <qa_pairs>
19
+ <qa_pair>
20
+ <question>iPad Air 2于2014年发布。</question>
21
+ <answer>True</answer>
22
+ </qa_pair>
23
+ <qa_pair>
24
+ <question>iPad Air 2搭载的是A10处理器。</question>
25
+ <answer>False</answer>
26
+ </qa_pair>
27
+ </qa_pairs>
28
+
29
+
30
+ 上下文资料:
31
+ {context}
32
+
33
+ 请为以下资料生成{num_of_questions}个判断题:
34
+ """
35
+
36
+
37
+ TEMPLATE_TF_EN: str = """Generate independent true/false questions based on the provided context. \
38
+ Each question should be a factual statement that can be clearly determined as true or false.
39
+
40
+ Requirements:
41
+ 1. **Language Consistency**: Generate in the same language as the context (Chinese/English)
42
+ 2. **Quantity**: Generate {num_of_questions} true/false questions per context
43
+ 3. **Independence**: Each question must be self-contained
44
+ 4. **Accuracy**: Correct answer must be directly derivable from the text with clear evidence
45
+
46
+ Output Format:
47
+ <qa_pairs>
48
+ <qa_pair>
49
+ <question>Statement text</question>
50
+ <answer>True or False</answer>
51
+ </qa_pair>
52
+ </qa_pairs>
53
+
54
+ Example (2 questions):
55
+ <qa_pairs>
56
+ <qa_pair>
57
+ <question>The iPad Air 2 was released in 2014.</question>
58
+ <answer>True</answer>
59
+ </qa_pair>
60
+ <qa_pair>
61
+ <question>The iPad Air 2 uses an A10 processor.</question>
62
+ <options>True
63
+ False</options>
64
+ <answer>False</answer>
65
+ </qa_pair>
66
+ </qa_pairs>
67
+
68
+ Context:
69
+ {context}
70
+
71
+ Please generate {num_of_questions} true/false questions for the following context:
72
+ """
73
+
74
+
75
+ TF_GENERATION_PROMPT = {"zh": TEMPLATE_TF_ZH, "en": TEMPLATE_TF_EN}
webui/app.py CHANGED
@@ -524,6 +524,7 @@ with gr.Blocks(title="GraphGen Demo", theme=gr.themes.Glass(), css=css) as demo:
524
  "multi_choice",
525
  "multi_answer",
526
  "fill_in_blank",
 
527
  ],
528
  label=_("Mode"),
529
  value="aggregated",
 
524
  "multi_choice",
525
  "multi_answer",
526
  "fill_in_blank",
527
+ "true_false",
528
  ],
529
  label=_("Mode"),
530
  value="aggregated",