chatterer 0.1.22__py3-none-any.whl → 0.1.24__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -2,15 +2,15 @@ import logging
2
2
  from pathlib import Path
3
3
  from typing import Optional
4
4
 
5
- from spargear import BaseArguments
5
+ from spargear import RunnableArguments
6
6
 
7
7
  from chatterer import CodeSnippets
8
8
 
9
9
  logger = logging.getLogger(__name__)
10
10
 
11
11
 
12
- class GetCodeSnippetsArgs(BaseArguments):
13
- input: str
12
+ class Arguments(RunnableArguments[CodeSnippets]):
13
+ PATH_OR_PACKAGE_NAME: str
14
14
  """Path to the package or file from which to extract code snippets."""
15
15
  output: Optional[str] = None
16
16
  """Output path for the extracted code snippets. If not provided, defaults to a file with the same name as the input."""
@@ -33,7 +33,7 @@ class GetCodeSnippetsArgs(BaseArguments):
33
33
  output = None
34
34
 
35
35
  cs = CodeSnippets.from_path_or_pkgname(
36
- path_or_pkgname=self.input,
36
+ path_or_pkgname=self.PATH_OR_PACKAGE_NAME,
37
37
  ban_file_patterns=self.ban_file_patterns,
38
38
  glob_patterns=self.glob_patterns,
39
39
  case_sensitive=self.case_sensitive,
@@ -41,14 +41,14 @@ class GetCodeSnippetsArgs(BaseArguments):
41
41
  if output is not None:
42
42
  output.parent.mkdir(parents=True, exist_ok=True)
43
43
  output.write_text(cs.snippets_text, encoding="utf-8")
44
- logger.info(f"Extracted code snippets from `{self.input}` and saved to `{output}`.")
44
+ logger.info(f"Extracted code snippets from `{self.PATH_OR_PACKAGE_NAME}` and saved to `{output}`.")
45
45
  else:
46
- logger.info(f"Extracted code snippets from `{self.input}`.")
46
+ logger.info(f"Extracted code snippets from `{self.PATH_OR_PACKAGE_NAME}`.")
47
47
  return cs
48
48
 
49
49
 
50
50
  def main() -> None:
51
- GetCodeSnippetsArgs().run()
51
+ Arguments().run()
52
52
 
53
53
 
54
54
  if __name__ == "__main__":
@@ -6,14 +6,14 @@ from typing import Optional, cast
6
6
 
7
7
  from openai import OpenAI
8
8
  from pydub import AudioSegment
9
- from spargear import BaseArguments
9
+ from spargear import RunnableArguments
10
10
 
11
11
  # Maximum chunk length in seconds
12
12
  MAX_CHUNK_DURATION = 600
13
13
 
14
14
 
15
- class TranscriptionApiArguments(BaseArguments):
16
- input: Path
15
+ class Arguments(RunnableArguments[None]):
16
+ AUDIO_PATH: Path
17
17
  """The audio file to transcribe."""
18
18
  output: Optional[Path] = None
19
19
  """Path to save the transcription output."""
@@ -31,7 +31,7 @@ class TranscriptionApiArguments(BaseArguments):
31
31
 
32
32
  client = OpenAI(api_key=self.api_key, base_url=self.base_url)
33
33
 
34
- audio = load_audio_segment(self.input)
34
+ audio = load_audio_segment(self.AUDIO_PATH)
35
35
 
36
36
  segments = split_audio(audio, MAX_CHUNK_DURATION)
37
37
  print(f"[i] Audio duration: {len(audio) / 1000:.1f}s; splitting into {len(segments)} segment(s)")
@@ -42,7 +42,7 @@ class TranscriptionApiArguments(BaseArguments):
42
42
  transcripts.append(transcribe_segment(seg, client, model, self.prompt))
43
43
 
44
44
  full_transcript = "\n\n".join(transcripts)
45
- output_path: Path = self.output or self.input.with_suffix(".txt")
45
+ output_path: Path = self.output or self.AUDIO_PATH.with_suffix(".txt")
46
46
  output_path.write_text(full_transcript, encoding="utf-8")
47
47
  print(f"[✓] Transcription saved to: {output_path}")
48
48
 
@@ -105,7 +105,7 @@ def transcribe_segment(segment: AudioSegment, client: OpenAI, model: str, prompt
105
105
 
106
106
 
107
107
  def main() -> None:
108
- TranscriptionApiArguments().run()
108
+ Arguments().run()
109
109
 
110
110
 
111
111
  if __name__ == "__main__":
@@ -19,8 +19,8 @@ from chatterer.tools.upstage_document_parser import (
19
19
  logger = logging.getLogger(__name__)
20
20
 
21
21
 
22
- class UpstageParserArguments(BaseArguments):
23
- input: Path
22
+ class Arguments(BaseArguments):
23
+ INPUT_PATH: Path
24
24
  """Input file to parse. Can be a PDF, image, or other supported formats."""
25
25
  output: Optional[Path] = None
26
26
  """Output file path for the parsed content. Defaults to input file with .md suffix if not provided."""
@@ -52,25 +52,25 @@ class UpstageParserArguments(BaseArguments):
52
52
  )
53
53
 
54
54
  def run(self) -> None:
55
- input = UpstageParserArguments.input.resolve()
56
- out = UpstageParserArguments.output or input.with_suffix(".md")
55
+ input = self.INPUT_PATH.resolve()
56
+ out = self.output or input.with_suffix(".md")
57
57
 
58
58
  parser = UpstageDocumentParseParser(
59
- api_key=UpstageParserArguments.api_key,
60
- base_url=UpstageParserArguments.base_url,
61
- model=UpstageParserArguments.model,
62
- split=UpstageParserArguments.split,
63
- ocr=UpstageParserArguments.ocr,
64
- output_format=UpstageParserArguments.output_format,
65
- coordinates=UpstageParserArguments.coordinates,
66
- base64_encoding=UpstageParserArguments.base64_encoding,
67
- image_description_instruction=UpstageParserArguments.image_description_instruction,
68
- image_dir=UpstageParserArguments.image_dir,
69
- chatterer=UpstageParserArguments.chatterer.value,
59
+ api_key=self.api_key,
60
+ base_url=self.base_url,
61
+ model=self.model,
62
+ split=self.split,
63
+ ocr=self.ocr,
64
+ output_format=self.output_format,
65
+ coordinates=self.coordinates,
66
+ base64_encoding=self.base64_encoding,
67
+ image_description_instruction=self.image_description_instruction,
68
+ image_dir=self.image_dir,
69
+ chatterer=self.chatterer.value,
70
70
  )
71
71
  docs = parser.parse(Blob.from_path(input)) # pyright: ignore[reportUnknownMemberType]
72
72
 
73
- if UpstageParserArguments.image_dir:
73
+ if self.image_dir:
74
74
  for path, image in parser.image_data.items():
75
75
  (path := Path(path)).parent.mkdir(parents=True, exist_ok=True)
76
76
  path.write_bytes(image)
@@ -82,7 +82,7 @@ class UpstageParserArguments(BaseArguments):
82
82
 
83
83
 
84
84
  def main() -> None:
85
- UpstageParserArguments().run()
85
+ Arguments().run()
86
86
 
87
87
 
88
88
  if __name__ == "__main__":
@@ -1,13 +1,13 @@
1
1
  from pathlib import Path
2
2
  from typing import Literal
3
3
 
4
- from spargear import ArgumentSpec, BaseArguments
4
+ from spargear import ArgumentSpec, RunnableArguments
5
5
 
6
6
  from chatterer import Chatterer, MarkdownLink, PlayWrightBot
7
7
 
8
8
 
9
- class WebpageToMarkdownArgs(BaseArguments):
10
- url: str
9
+ class Arguments(RunnableArguments[None]):
10
+ URL: str
11
11
  """The URL to crawl."""
12
12
  output: str = Path(__file__).with_suffix(".md").as_posix()
13
13
  """The output file path for the markdown file."""
@@ -21,7 +21,7 @@ class WebpageToMarkdownArgs(BaseArguments):
21
21
 
22
22
  def run(self) -> None:
23
23
  chatterer = self.chatterer.value
24
- url: str = self.url.strip()
24
+ url: str = self.URL.strip()
25
25
  output: Path = Path(self.output).resolve()
26
26
  with PlayWrightBot(chatterer=chatterer, engine=self.engine) as bot:
27
27
  md = bot.url_to_md(url)
@@ -32,15 +32,13 @@ class WebpageToMarkdownArgs(BaseArguments):
32
32
  links = MarkdownLink.from_markdown(md, referer_url=url)
33
33
  for link in links:
34
34
  if link.type == "link":
35
- print(
36
- f"- [{truncate_string(link.url)}] {truncate_string(link.inline_text)} ({truncate_string(link.inline_title)})"
37
- )
35
+ print(f"- [{truncate_string(link.url)}] {truncate_string(link.inline_text)} ({truncate_string(link.inline_title)})")
38
36
  elif link.type == "image":
39
37
  print(f"- ![{truncate_string(link.url)}] ({truncate_string(link.inline_text)})")
40
38
 
41
39
  async def arun(self) -> None:
42
40
  chatterer = self.chatterer.value
43
- url: str = self.url.strip()
41
+ url: str = self.URL.strip()
44
42
  output: Path = Path(self.output).resolve()
45
43
  async with PlayWrightBot(chatterer=chatterer, engine=self.engine) as bot:
46
44
  md = await bot.aurl_to_md(url)
@@ -51,9 +49,7 @@ class WebpageToMarkdownArgs(BaseArguments):
51
49
  links = MarkdownLink.from_markdown(md, referer_url=url)
52
50
  for link in links:
53
51
  if link.type == "link":
54
- print(
55
- f"- [{truncate_string(link.url)}] {truncate_string(link.inline_text)} ({truncate_string(link.inline_title)})"
56
- )
52
+ print(f"- [{truncate_string(link.url)}] {truncate_string(link.inline_text)} ({truncate_string(link.inline_title)})")
57
53
  elif link.type == "image":
58
54
  print(f"- ![{truncate_string(link.url)}] ({truncate_string(link.inline_text)})")
59
55
 
@@ -63,7 +59,7 @@ def truncate_string(s: str) -> str:
63
59
 
64
60
 
65
61
  def main() -> None:
66
- WebpageToMarkdownArgs().run()
62
+ Arguments().run()
67
63
 
68
64
 
69
65
  if __name__ == "__main__":
@@ -781,143 +781,143 @@ class AoTPipeline:
781
781
  # 4.6) Build or export a reasoning graph
782
782
  # ---------------------------------------------------------------------------------
783
783
 
784
- def get_reasoning_graph(self, global_id_prefix: str = "AoT"):
785
- """
786
- Constructs a Graph object (from hypothetical `neo4j_extension`)
787
- capturing the pipeline steps, including devil's advocate steps.
788
- """
789
- from neo4j_extension import Graph, Node, Relationship
790
-
791
- g = Graph()
792
- step_nodes: dict[int, Node] = {}
793
- subq_nodes: dict[str, Node] = {}
794
-
795
- # Step A: Create nodes for each pipeline step
796
- for i, record in enumerate(self.steps_history):
797
- # We'll skip nested Decomposition steps only if we want to flatten them.
798
- # But let's keep them for clarity.
799
- step_node = Node(
800
- properties=record.as_properties(), labels={record.step_name}, globalId=f"{global_id_prefix}_step_{i}"
801
- )
802
- g.add_node(step_node)
803
- step_nodes[i] = step_node
804
-
805
- # Step B: Collect sub-questions from each DECOMPOSITION or DEVILS_ADVOCATE
806
- all_sub_questions: dict[str, tuple[int, int, SubQuestionNode]] = {}
807
- for i, record in enumerate(self.steps_history):
808
- if record.sub_questions:
809
- for sq_idx, sq in enumerate(record.sub_questions):
810
- sq_id = f"{global_id_prefix}_decomp_{i}_sub_{sq_idx}"
811
- all_sub_questions[sq_id] = (i, sq_idx, sq)
812
-
813
- for sq_id, (i, sq_idx, sq) in all_sub_questions.items():
814
- n_subq = Node(
815
- properties={
816
- "question": sq.question,
817
- "answer": sq.answer or "",
818
- },
819
- labels={"SubQuestion"},
820
- globalId=sq_id,
821
- )
822
- g.add_node(n_subq)
823
- subq_nodes[sq_id] = n_subq
824
-
825
- # Step C: Add relationships. We do a simple approach:
826
- # - If StepRecord is DECOMPOSITION or DEVILS_ADVOCATE with sub_questions, link them via SPLIT_INTO.
827
- for i, record in enumerate(self.steps_history):
828
- if record.sub_questions:
829
- start_node = step_nodes[i]
830
- for sq_idx, sq in enumerate(record.sub_questions):
831
- sq_id = f"{global_id_prefix}_decomp_{i}_sub_{sq_idx}"
832
- end_node = subq_nodes[sq_id]
833
- rel = Relationship(
834
- properties={},
835
- rel_type=StepRelation.SPLIT_INTO,
836
- start_node=start_node,
837
- end_node=end_node,
838
- globalId=f"{global_id_prefix}_split_{i}_{sq_idx}",
839
- )
840
- g.add_relationship(rel)
841
- # Also add sub-question dependencies
842
- for dep in sq.depend:
843
- # The same record i -> sub-question subq
844
- if 0 <= dep < len(record.sub_questions):
845
- dep_id = f"{global_id_prefix}_decomp_{i}_sub_{dep}"
846
- if dep_id in subq_nodes:
847
- dep_node = subq_nodes[dep_id]
848
- rel_dep = Relationship(
849
- properties={},
850
- rel_type=StepRelation.DEPEND_ON,
851
- start_node=end_node,
852
- end_node=dep_node,
853
- globalId=f"{global_id_prefix}_dep_{i}_q_{sq_idx}_on_{dep}",
854
- )
855
- g.add_relationship(rel_dep)
856
-
857
- # Step D: We add PRECEDES relationships in a linear chain for the pipeline steps
858
- for i in range(len(self.steps_history) - 1):
859
- start_node = step_nodes[i]
860
- end_node = step_nodes[i + 1]
861
- rel = Relationship(
862
- properties={},
863
- rel_type=StepRelation.PRECEDES,
864
- start_node=start_node,
865
- end_node=end_node,
866
- globalId=f"{global_id_prefix}_precede_{i}_to_{i + 1}",
867
- )
868
- g.add_relationship(rel)
869
-
870
- # Step E: CRITIQUES, SELECTS, RESULT_OF can be similarly added:
871
- # We'll do a simple pass:
872
- # If step_name ends with CRITIQUE => it critiques the step before it
873
- for i, record in enumerate(self.steps_history):
874
- if "CRITIQUE" in record.step_name:
875
- # Let it point to the preceding step
876
- if i > 0:
877
- start_node = step_nodes[i]
878
- end_node = step_nodes[i - 1]
879
- rel = Relationship(
880
- properties={},
881
- rel_type=StepRelation.CRITIQUES,
882
- start_node=start_node,
883
- end_node=end_node,
884
- globalId=f"{global_id_prefix}_crit_{i}",
885
- )
886
- g.add_relationship(rel)
887
-
888
- # If there's a BEST_APPROACH_DECISION step, link it to the step it uses
889
- best_decision_idx = None
890
- used_step_idx = None
891
- for i, record in enumerate(self.steps_history):
892
- if record.step_name == StepName.BEST_APPROACH_DECISION and record.used:
893
- best_decision_idx = i
894
- # find the step with that name
895
- used_step_idx = next((j for j in step_nodes if self.steps_history[j].step_name == record.used), None)
896
- if used_step_idx is not None:
897
- rel = Relationship(
898
- properties={},
899
- rel_type=StepRelation.SELECTS,
900
- start_node=step_nodes[i],
901
- end_node=step_nodes[used_step_idx],
902
- globalId=f"{global_id_prefix}_select_{i}_use_{used_step_idx}",
903
- )
904
- g.add_relationship(rel)
905
-
906
- # And link the final answer to the best approach
907
- final_answer_idx = next(
908
- (i for i, r in enumerate(self.steps_history) if r.step_name == StepName.FINAL_ANSWER), None
909
- )
910
- if final_answer_idx is not None and best_decision_idx is not None:
911
- rel = Relationship(
912
- properties={},
913
- rel_type=StepRelation.RESULT_OF,
914
- start_node=step_nodes[final_answer_idx],
915
- end_node=step_nodes[best_decision_idx],
916
- globalId=f"{global_id_prefix}_final_{final_answer_idx}_resultof_{best_decision_idx}",
917
- )
918
- g.add_relationship(rel)
919
-
920
- return g
784
+ # def get_reasoning_graph(self, global_id_prefix: str = "AoT"):
785
+ # """
786
+ # Constructs a Graph object (from hypothetical `neo4j_extension`)
787
+ # capturing the pipeline steps, including devil's advocate steps.
788
+ # """
789
+ # from neo4j_extension import Graph, Node, Relationship
790
+
791
+ # g = Graph()
792
+ # step_nodes: dict[int, Node] = {}
793
+ # subq_nodes: dict[str, Node] = {}
794
+
795
+ # # Step A: Create nodes for each pipeline step
796
+ # for i, record in enumerate(self.steps_history):
797
+ # # We'll skip nested Decomposition steps only if we want to flatten them.
798
+ # # But let's keep them for clarity.
799
+ # step_node = Node(
800
+ # properties=record.as_properties(), labels={record.step_name}, globalId=f"{global_id_prefix}_step_{i}"
801
+ # )
802
+ # g.add_node(step_node)
803
+ # step_nodes[i] = step_node
804
+
805
+ # # Step B: Collect sub-questions from each DECOMPOSITION or DEVILS_ADVOCATE
806
+ # all_sub_questions: dict[str, tuple[int, int, SubQuestionNode]] = {}
807
+ # for i, record in enumerate(self.steps_history):
808
+ # if record.sub_questions:
809
+ # for sq_idx, sq in enumerate(record.sub_questions):
810
+ # sq_id = f"{global_id_prefix}_decomp_{i}_sub_{sq_idx}"
811
+ # all_sub_questions[sq_id] = (i, sq_idx, sq)
812
+
813
+ # for sq_id, (i, sq_idx, sq) in all_sub_questions.items():
814
+ # n_subq = Node(
815
+ # properties={
816
+ # "question": sq.question,
817
+ # "answer": sq.answer or "",
818
+ # },
819
+ # labels={"SubQuestion"},
820
+ # globalId=sq_id,
821
+ # )
822
+ # g.add_node(n_subq)
823
+ # subq_nodes[sq_id] = n_subq
824
+
825
+ # # Step C: Add relationships. We do a simple approach:
826
+ # # - If StepRecord is DECOMPOSITION or DEVILS_ADVOCATE with sub_questions, link them via SPLIT_INTO.
827
+ # for i, record in enumerate(self.steps_history):
828
+ # if record.sub_questions:
829
+ # start_node = step_nodes[i]
830
+ # for sq_idx, sq in enumerate(record.sub_questions):
831
+ # sq_id = f"{global_id_prefix}_decomp_{i}_sub_{sq_idx}"
832
+ # end_node = subq_nodes[sq_id]
833
+ # rel = Relationship(
834
+ # properties={},
835
+ # rel_type=StepRelation.SPLIT_INTO,
836
+ # start_node=start_node,
837
+ # end_node=end_node,
838
+ # globalId=f"{global_id_prefix}_split_{i}_{sq_idx}",
839
+ # )
840
+ # g.add_relationship(rel)
841
+ # # Also add sub-question dependencies
842
+ # for dep in sq.depend:
843
+ # # The same record i -> sub-question subq
844
+ # if 0 <= dep < len(record.sub_questions):
845
+ # dep_id = f"{global_id_prefix}_decomp_{i}_sub_{dep}"
846
+ # if dep_id in subq_nodes:
847
+ # dep_node = subq_nodes[dep_id]
848
+ # rel_dep = Relationship(
849
+ # properties={},
850
+ # rel_type=StepRelation.DEPEND_ON,
851
+ # start_node=end_node,
852
+ # end_node=dep_node,
853
+ # globalId=f"{global_id_prefix}_dep_{i}_q_{sq_idx}_on_{dep}",
854
+ # )
855
+ # g.add_relationship(rel_dep)
856
+
857
+ # # Step D: We add PRECEDES relationships in a linear chain for the pipeline steps
858
+ # for i in range(len(self.steps_history) - 1):
859
+ # start_node = step_nodes[i]
860
+ # end_node = step_nodes[i + 1]
861
+ # rel = Relationship(
862
+ # properties={},
863
+ # rel_type=StepRelation.PRECEDES,
864
+ # start_node=start_node,
865
+ # end_node=end_node,
866
+ # globalId=f"{global_id_prefix}_precede_{i}_to_{i + 1}",
867
+ # )
868
+ # g.add_relationship(rel)
869
+
870
+ # # Step E: CRITIQUES, SELECTS, RESULT_OF can be similarly added:
871
+ # # We'll do a simple pass:
872
+ # # If step_name ends with CRITIQUE => it critiques the step before it
873
+ # for i, record in enumerate(self.steps_history):
874
+ # if "CRITIQUE" in record.step_name:
875
+ # # Let it point to the preceding step
876
+ # if i > 0:
877
+ # start_node = step_nodes[i]
878
+ # end_node = step_nodes[i - 1]
879
+ # rel = Relationship(
880
+ # properties={},
881
+ # rel_type=StepRelation.CRITIQUES,
882
+ # start_node=start_node,
883
+ # end_node=end_node,
884
+ # globalId=f"{global_id_prefix}_crit_{i}",
885
+ # )
886
+ # g.add_relationship(rel)
887
+
888
+ # # If there's a BEST_APPROACH_DECISION step, link it to the step it uses
889
+ # best_decision_idx = None
890
+ # used_step_idx = None
891
+ # for i, record in enumerate(self.steps_history):
892
+ # if record.step_name == StepName.BEST_APPROACH_DECISION and record.used:
893
+ # best_decision_idx = i
894
+ # # find the step with that name
895
+ # used_step_idx = next((j for j in step_nodes if self.steps_history[j].step_name == record.used), None)
896
+ # if used_step_idx is not None:
897
+ # rel = Relationship(
898
+ # properties={},
899
+ # rel_type=StepRelation.SELECTS,
900
+ # start_node=step_nodes[i],
901
+ # end_node=step_nodes[used_step_idx],
902
+ # globalId=f"{global_id_prefix}_select_{i}_use_{used_step_idx}",
903
+ # )
904
+ # g.add_relationship(rel)
905
+
906
+ # # And link the final answer to the best approach
907
+ # final_answer_idx = next(
908
+ # (i for i, r in enumerate(self.steps_history) if r.step_name == StepName.FINAL_ANSWER), None
909
+ # )
910
+ # if final_answer_idx is not None and best_decision_idx is not None:
911
+ # rel = Relationship(
912
+ # properties={},
913
+ # rel_type=StepRelation.RESULT_OF,
914
+ # start_node=step_nodes[final_answer_idx],
915
+ # end_node=step_nodes[best_decision_idx],
916
+ # globalId=f"{global_id_prefix}_final_{final_answer_idx}_resultof_{best_decision_idx}",
917
+ # )
918
+ # g.add_relationship(rel)
919
+
920
+ # return g
921
921
 
922
922
 
923
923
  # ---------------------------------------------------------------------------------
@@ -944,32 +944,32 @@ class AoTStrategy(BaseStrategy):
944
944
  msgs = self.pipeline.chatterer.client._convert_input(messages).to_messages() # type: ignore
945
945
  return self.pipeline.run_pipeline(msgs)
946
946
 
947
- def get_reasoning_graph(self):
948
- """Return the AoT reasoning graph from the pipeline’s steps history."""
949
- return self.pipeline.get_reasoning_graph(global_id_prefix="AoT")
947
+ # def get_reasoning_graph(self):
948
+ # """Return the AoT reasoning graph from the pipeline’s steps history."""
949
+ # return self.pipeline.get_reasoning_graph(global_id_prefix="AoT")
950
950
 
951
951
 
952
952
  # ---------------------------------------------------------------------------------
953
953
  # Example usage (pseudo-code)
954
954
  # ---------------------------------------------------------------------------------
955
- if __name__ == "__main__":
956
- from neo4j_extension import Neo4jConnection # or your actual DB connector
957
-
958
- # You would create a Chatterer with your chosen LLM backend (OpenAI, etc.)
959
- chatterer = Chatterer.openai() # pseudo-code
960
- pipeline = AoTPipeline(chatterer=chatterer, max_depth=3)
961
- strategy = AoTStrategy(pipeline=pipeline)
962
-
963
- question = "Solve 5.9 = 5.11 - x. Also compare 9.11 and 9.9."
964
- answer = strategy.invoke(question)
965
- print("Final Answer:", answer)
966
-
967
- # Build the reasoning graph
968
- graph = strategy.get_reasoning_graph()
969
- print(f"\nGraph has {len(graph.nodes)} nodes and {len(graph.relationships)} relationships.")
970
-
971
- # Optionally store in Neo4j
972
- with Neo4jConnection() as conn:
973
- conn.clear_all()
974
- conn.upsert_graph(graph)
975
- print("Graph stored in Neo4j.")
955
+ # if __name__ == "__main__":
956
+ # from neo4j_extension import Neo4jConnection # or your actual DB connector
957
+
958
+ # # You would create a Chatterer with your chosen LLM backend (OpenAI, etc.)
959
+ # chatterer = Chatterer.openai() # pseudo-code
960
+ # pipeline = AoTPipeline(chatterer=chatterer, max_depth=3)
961
+ # strategy = AoTStrategy(pipeline=pipeline)
962
+
963
+ # question = "Solve 5.9 = 5.11 - x. Also compare 9.11 and 9.9."
964
+ # answer = strategy.invoke(question)
965
+ # print("Final Answer:", answer)
966
+
967
+ # # Build the reasoning graph
968
+ # graph = strategy.get_reasoning_graph()
969
+ # print(f"\nGraph has {len(graph.nodes)} nodes and {len(graph.relationships)} relationships.")
970
+
971
+ # # Optionally store in Neo4j
972
+ # with Neo4jConnection() as conn:
973
+ # conn.clear_all()
974
+ # conn.upsert_graph(graph)
975
+ # print("Graph stored in Neo4j.")