dialectical-framework 0.6.5__tar.gz → 0.7.0__tar.gz

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.
Files changed (93) hide show
  1. {dialectical_framework-0.6.5 → dialectical_framework-0.7.0}/PKG-INFO +3 -2
  2. {dialectical_framework-0.6.5 → dialectical_framework-0.7.0}/README.md +2 -2
  3. {dialectical_framework-0.6.5 → dialectical_framework-0.7.0}/pyproject.toml +1 -1
  4. {dialectical_framework-0.6.5 → dialectical_framework-0.7.0}/src/dialectical_framework/analyst/domain/cycle.py +1 -1
  5. {dialectical_framework-0.6.5 → dialectical_framework-0.7.0}/src/dialectical_framework/dialectical_reasoning.py +4 -2
  6. {dialectical_framework-0.6.5 → dialectical_framework-0.7.0}/src/dialectical_framework/enums/di.py +1 -1
  7. dialectical_framework-0.7.0/src/dialectical_framework/protocols/polarity_extractor.py +22 -0
  8. {dialectical_framework-0.6.5 → dialectical_framework-0.7.0}/src/dialectical_framework/synthesist/causality/causality_sequencer_balanced.py +1 -1
  9. dialectical_framework-0.7.0/src/dialectical_framework/synthesist/concepts/polarity_extractor_basic.py +264 -0
  10. {dialectical_framework-0.6.5 → dialectical_framework-0.7.0}/src/dialectical_framework/synthesist/concepts/thesis_extractor_basic.py +10 -9
  11. {dialectical_framework-0.6.5 → dialectical_framework-0.7.0}/src/dialectical_framework/synthesist/polarity/polarity_reasoner.py +1 -1
  12. {dialectical_framework-0.6.5 → dialectical_framework-0.7.0}/src/dialectical_framework/synthesist/wheel_builder.py +4 -3
  13. {dialectical_framework-0.6.5 → dialectical_framework-0.7.0}/LICENSE +0 -0
  14. {dialectical_framework-0.6.5 → dialectical_framework-0.7.0}/src/dialectical_framework/__init__.py +0 -0
  15. {dialectical_framework-0.6.5 → dialectical_framework-0.7.0}/src/dialectical_framework/ai_dto/__init__.py +0 -0
  16. {dialectical_framework-0.6.5 → dialectical_framework-0.7.0}/src/dialectical_framework/ai_dto/action_plan_dto.py +0 -0
  17. {dialectical_framework-0.6.5 → dialectical_framework-0.7.0}/src/dialectical_framework/ai_dto/causal_cycle_assessment_dto.py +0 -0
  18. {dialectical_framework-0.6.5 → dialectical_framework-0.7.0}/src/dialectical_framework/ai_dto/causal_cycle_dto.py +0 -0
  19. {dialectical_framework-0.6.5 → dialectical_framework-0.7.0}/src/dialectical_framework/ai_dto/causal_cycles_deck_dto.py +0 -0
  20. {dialectical_framework-0.6.5 → dialectical_framework-0.7.0}/src/dialectical_framework/ai_dto/constructive_convergence_transition_audit_dto.py +0 -0
  21. {dialectical_framework-0.6.5 → dialectical_framework-0.7.0}/src/dialectical_framework/ai_dto/dialectical_component_dto.py +0 -0
  22. {dialectical_framework-0.6.5 → dialectical_framework-0.7.0}/src/dialectical_framework/ai_dto/dialectical_components_deck_dto.py +0 -0
  23. {dialectical_framework-0.6.5 → dialectical_framework-0.7.0}/src/dialectical_framework/ai_dto/dto_mapper.py +0 -0
  24. {dialectical_framework-0.6.5 → dialectical_framework-0.7.0}/src/dialectical_framework/ai_dto/reciprocal_solution_dto.py +0 -0
  25. {dialectical_framework-0.6.5 → dialectical_framework-0.7.0}/src/dialectical_framework/ai_dto/transition_summary_dto.py +0 -0
  26. {dialectical_framework-0.6.5 → dialectical_framework-0.7.0}/src/dialectical_framework/analyst/__init__.py +0 -0
  27. {dialectical_framework-0.6.5 → dialectical_framework-0.7.0}/src/dialectical_framework/analyst/consultant.py +0 -0
  28. {dialectical_framework-0.6.5 → dialectical_framework-0.7.0}/src/dialectical_framework/analyst/decorator_action_reflection.py +0 -0
  29. {dialectical_framework-0.6.5 → dialectical_framework-0.7.0}/src/dialectical_framework/analyst/decorator_discrete_spiral.py +0 -0
  30. {dialectical_framework-0.6.5 → dialectical_framework-0.7.0}/src/dialectical_framework/analyst/decorator_discrete_spiral_audited.py +0 -0
  31. {dialectical_framework-0.6.5 → dialectical_framework-0.7.0}/src/dialectical_framework/analyst/domain/__init__.py +0 -0
  32. {dialectical_framework-0.6.5 → dialectical_framework-0.7.0}/src/dialectical_framework/analyst/domain/assessable_cycle.py +0 -0
  33. {dialectical_framework-0.6.5 → dialectical_framework-0.7.0}/src/dialectical_framework/analyst/domain/interpretation.py +0 -0
  34. {dialectical_framework-0.6.5 → dialectical_framework-0.7.0}/src/dialectical_framework/analyst/domain/rationale.py +0 -0
  35. {dialectical_framework-0.6.5 → dialectical_framework-0.7.0}/src/dialectical_framework/analyst/domain/spiral.py +0 -0
  36. {dialectical_framework-0.6.5 → dialectical_framework-0.7.0}/src/dialectical_framework/analyst/domain/transformation.py +0 -0
  37. {dialectical_framework-0.6.5 → dialectical_framework-0.7.0}/src/dialectical_framework/analyst/domain/transition.py +0 -0
  38. {dialectical_framework-0.6.5 → dialectical_framework-0.7.0}/src/dialectical_framework/analyst/domain/transition_cell_to_cell.py +0 -0
  39. {dialectical_framework-0.6.5 → dialectical_framework-0.7.0}/src/dialectical_framework/analyst/domain/transition_segment_to_segment.py +0 -0
  40. {dialectical_framework-0.6.5 → dialectical_framework-0.7.0}/src/dialectical_framework/analyst/strategic_consultant.py +0 -0
  41. {dialectical_framework-0.6.5 → dialectical_framework-0.7.0}/src/dialectical_framework/analyst/think_action_reflection.py +0 -0
  42. {dialectical_framework-0.6.5 → dialectical_framework-0.7.0}/src/dialectical_framework/analyst/think_constructive_convergence.py +0 -0
  43. {dialectical_framework-0.6.5 → dialectical_framework-0.7.0}/src/dialectical_framework/analyst/think_constructive_convergence_auditor.py +0 -0
  44. {dialectical_framework-0.6.5 → dialectical_framework-0.7.0}/src/dialectical_framework/analyst/wheel_builder_transition_calculator.py +0 -0
  45. {dialectical_framework-0.6.5 → dialectical_framework-0.7.0}/src/dialectical_framework/brain.py +0 -0
  46. {dialectical_framework-0.6.5 → dialectical_framework-0.7.0}/src/dialectical_framework/enums/__init__.py +0 -0
  47. {dialectical_framework-0.6.5 → dialectical_framework-0.7.0}/src/dialectical_framework/enums/causality_type.py +0 -0
  48. {dialectical_framework-0.6.5 → dialectical_framework-0.7.0}/src/dialectical_framework/enums/dialectical_reasoning_mode.py +0 -0
  49. {dialectical_framework-0.6.5 → dialectical_framework-0.7.0}/src/dialectical_framework/enums/predicate.py +0 -0
  50. {dialectical_framework-0.6.5 → dialectical_framework-0.7.0}/src/dialectical_framework/protocols/__init__.py +0 -0
  51. {dialectical_framework-0.6.5 → dialectical_framework-0.7.0}/src/dialectical_framework/protocols/assessable.py +0 -0
  52. {dialectical_framework-0.6.5 → dialectical_framework-0.7.0}/src/dialectical_framework/protocols/causality_sequencer.py +0 -0
  53. {dialectical_framework-0.6.5 → dialectical_framework-0.7.0}/src/dialectical_framework/protocols/content_fidelity_evaluator.py +0 -0
  54. {dialectical_framework-0.6.5 → dialectical_framework-0.7.0}/src/dialectical_framework/protocols/has_brain.py +0 -0
  55. {dialectical_framework-0.6.5 → dialectical_framework-0.7.0}/src/dialectical_framework/protocols/has_config.py +0 -0
  56. {dialectical_framework-0.6.5 → dialectical_framework-0.7.0}/src/dialectical_framework/protocols/ratable.py +0 -0
  57. {dialectical_framework-0.6.5 → dialectical_framework-0.7.0}/src/dialectical_framework/protocols/reloadable.py +0 -0
  58. {dialectical_framework-0.6.5 → dialectical_framework-0.7.0}/src/dialectical_framework/protocols/thesis_extractor.py +0 -0
  59. {dialectical_framework-0.6.5 → dialectical_framework-0.7.0}/src/dialectical_framework/settings.py +0 -0
  60. {dialectical_framework-0.6.5 → dialectical_framework-0.7.0}/src/dialectical_framework/synthesist/__init__.py +0 -0
  61. {dialectical_framework-0.6.5 → dialectical_framework-0.7.0}/src/dialectical_framework/synthesist/causality/__init__.py +0 -0
  62. {dialectical_framework-0.6.5 → dialectical_framework-0.7.0}/src/dialectical_framework/synthesist/causality/causality_sequencer_desirable.py +0 -0
  63. {dialectical_framework-0.6.5 → dialectical_framework-0.7.0}/src/dialectical_framework/synthesist/causality/causality_sequencer_feasible.py +0 -0
  64. {dialectical_framework-0.6.5 → dialectical_framework-0.7.0}/src/dialectical_framework/synthesist/causality/causality_sequencer_realistic.py +0 -0
  65. {dialectical_framework-0.6.5 → dialectical_framework-0.7.0}/src/dialectical_framework/synthesist/concepts/__init__.py +0 -0
  66. {dialectical_framework-0.6.5 → dialectical_framework-0.7.0}/src/dialectical_framework/synthesist/domain/__init__.py +0 -0
  67. {dialectical_framework-0.6.5 → dialectical_framework-0.7.0}/src/dialectical_framework/synthesist/domain/dialectical_analysis.py +0 -0
  68. {dialectical_framework-0.6.5 → dialectical_framework-0.7.0}/src/dialectical_framework/synthesist/domain/dialectical_component.py +0 -0
  69. {dialectical_framework-0.6.5 → dialectical_framework-0.7.0}/src/dialectical_framework/synthesist/domain/dialectical_components_deck.py +0 -0
  70. {dialectical_framework-0.6.5 → dialectical_framework-0.7.0}/src/dialectical_framework/synthesist/domain/directed_graph.py +0 -0
  71. {dialectical_framework-0.6.5 → dialectical_framework-0.7.0}/src/dialectical_framework/synthesist/domain/synthesis.py +0 -0
  72. {dialectical_framework-0.6.5 → dialectical_framework-0.7.0}/src/dialectical_framework/synthesist/domain/wheel.py +0 -0
  73. {dialectical_framework-0.6.5 → dialectical_framework-0.7.0}/src/dialectical_framework/synthesist/domain/wheel_segment.py +0 -0
  74. {dialectical_framework-0.6.5 → dialectical_framework-0.7.0}/src/dialectical_framework/synthesist/domain/wisdom_unit.py +0 -0
  75. {dialectical_framework-0.6.5 → dialectical_framework-0.7.0}/src/dialectical_framework/synthesist/polarity/__init__.py +0 -0
  76. {dialectical_framework-0.6.5 → dialectical_framework-0.7.0}/src/dialectical_framework/synthesist/polarity/reason_blind.py +0 -0
  77. {dialectical_framework-0.6.5 → dialectical_framework-0.7.0}/src/dialectical_framework/synthesist/polarity/reason_conversational.py +0 -0
  78. {dialectical_framework-0.6.5 → dialectical_framework-0.7.0}/src/dialectical_framework/synthesist/polarity/reason_fast.py +0 -0
  79. {dialectical_framework-0.6.5 → dialectical_framework-0.7.0}/src/dialectical_framework/synthesist/polarity/reason_fast_and_simple.py +0 -0
  80. {dialectical_framework-0.6.5 → dialectical_framework-0.7.0}/src/dialectical_framework/synthesist/polarity/reason_fast_polarized_conflict.py +0 -0
  81. {dialectical_framework-0.6.5 → dialectical_framework-0.7.0}/src/dialectical_framework/synthesist/reverse_engineer.py +0 -0
  82. {dialectical_framework-0.6.5 → dialectical_framework-0.7.0}/src/dialectical_framework/utils/__init__.py +0 -0
  83. {dialectical_framework-0.6.5 → dialectical_framework-0.7.0}/src/dialectical_framework/utils/dc_replace.py +0 -0
  84. {dialectical_framework-0.6.5 → dialectical_framework-0.7.0}/src/dialectical_framework/utils/decompose_probability_uniformly.py +0 -0
  85. {dialectical_framework-0.6.5 → dialectical_framework-0.7.0}/src/dialectical_framework/utils/dw_report.py +0 -0
  86. {dialectical_framework-0.6.5 → dialectical_framework-0.7.0}/src/dialectical_framework/utils/extend_tpl.py +0 -0
  87. {dialectical_framework-0.6.5 → dialectical_framework-0.7.0}/src/dialectical_framework/utils/gm.py +0 -0
  88. {dialectical_framework-0.6.5 → dialectical_framework-0.7.0}/src/dialectical_framework/utils/is_async.py +0 -0
  89. {dialectical_framework-0.6.5 → dialectical_framework-0.7.0}/src/dialectical_framework/utils/pm.py +0 -0
  90. {dialectical_framework-0.6.5 → dialectical_framework-0.7.0}/src/dialectical_framework/utils/use_brain.py +0 -0
  91. {dialectical_framework-0.6.5 → dialectical_framework-0.7.0}/src/dialectical_framework/validator/__init__.py +0 -0
  92. {dialectical_framework-0.6.5 → dialectical_framework-0.7.0}/src/dialectical_framework/validator/basic_checks.py +0 -0
  93. {dialectical_framework-0.6.5 → dialectical_framework-0.7.0}/src/dialectical_framework/validator/check.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: dialectical-framework
3
- Version: 0.6.5
3
+ Version: 0.7.0
4
4
  Summary: A dialectical framework for augmented intelligence. AI reasoning powered with dialectics supports humans in: system optimization (psychology, engineering, business, politics, etc.); dispute resolution (mediation, conflicts, negotiations, etc.); decision-making (dilemmas, challenging situations, win-win, etc.).
5
5
  License: MIT
6
6
  Keywords: dialectics,dialectical-reasoning,synthesis,thesis-antithesis,ai,artificial-intelligence,llm,reasoning-framework,philosophy,logic,argumentation,conflict-resolution,decision-making,critical-thinking,semantic-graph,mirascope,pydantic,wisdom-units,polarity-reasoning
@@ -35,7 +35,7 @@ Project-URL: Homepage, https://github.com/dialexity/dialectical-framework
35
35
  Project-URL: Repository, https://github.com/dialexity/dialectical-framework
36
36
  Description-Content-Type: text/markdown
37
37
 
38
- # Dialectical Framework
38
+ # Dialectical Reasoning Framework
39
39
  Turn stories, strategies, or systems into insight. Auto-generate Dialectical Wheels (DWs) from any text to reveal blind spots, surface polarities, and trace dynamic paths toward synthesis.
40
40
  DWs are semantic maps that expose tension, transformation, and coherence within a system—whether narrative, ethical, organizational, or technological.
41
41
 
@@ -132,3 +132,4 @@ Working beta product [Argument Inspector](https://dialexity.com/start). Useful f
132
132
 
133
133
  ### "Spiral" Lila game
134
134
  In [this blog post](https://dialexity.com/blog/spiral-lila-with-character-traits/) we explain how the ancient Lila (Leela) game has been elevated to a new level.
135
+
@@ -1,4 +1,4 @@
1
- # Dialectical Framework
1
+ # Dialectical Reasoning Framework
2
2
  Turn stories, strategies, or systems into insight. Auto-generate Dialectical Wheels (DWs) from any text to reveal blind spots, surface polarities, and trace dynamic paths toward synthesis.
3
3
  DWs are semantic maps that expose tension, transformation, and coherence within a system—whether narrative, ethical, organizational, or technological.
4
4
 
@@ -94,4 +94,4 @@ Working beta product [Argument Inspector](https://dialexity.com/start). Useful f
94
94
  [The Atlas of Feelings](https://dialexity.com/blog/atlas-of-feelings-character-qualities/) is the Plutchik's wheel converted into the „vortex“ model, whereby the most gentle emotions are inside of the wheel, whereas the rudest are outside. As everything is interconnected with dialectical rules, we can understand human nature better.
95
95
 
96
96
  ### "Spiral" Lila game
97
- In [this blog post](https://dialexity.com/blog/spiral-lila-with-character-traits/) we explain how the ancient Lila (Leela) game has been elevated to a new level.
97
+ In [this blog post](https://dialexity.com/blog/spiral-lila-with-character-traits/) we explain how the ancient Lila (Leela) game has been elevated to a new level.
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "dialectical-framework"
3
- version = "0.6.5"
3
+ version = "0.7.0"
4
4
  description = "A dialectical framework for augmented intelligence. AI reasoning powered with dialectics supports humans in: system optimization (psychology, engineering, business, politics, etc.); dispute resolution (mediation, conflicts, negotiations, etc.); decision-making (dilemmas, challenging situations, win-win, etc.)."
5
5
  authors = ["Evaldas Taroza <evaldas@dialexity.com>"]
6
6
  readme = "README.md"
@@ -37,7 +37,7 @@ class Cycle(AssessableCycle[TransitionCellToCell]):
37
37
  def __init__(
38
38
  self,
39
39
  dialectical_components: list[DialecticalComponent],
40
- causality_type: CausalityType = CausalityType.REALISTIC,
40
+ causality_type: CausalityType = CausalityType.BALANCED,
41
41
  default_transition_probability: float | None = None,
42
42
  **data,
43
43
  ):
@@ -7,6 +7,7 @@ from dialectical_framework.brain import Brain
7
7
  from dialectical_framework.enums.causality_type import CausalityType
8
8
  from dialectical_framework.protocols.causality_sequencer import \
9
9
  CausalitySequencer
10
+ from dialectical_framework.protocols.polarity_extractor import PolarityExtractor
10
11
  from dialectical_framework.protocols.thesis_extractor import ThesisExtractor
11
12
  from dialectical_framework.settings import Settings
12
13
  from dialectical_framework.synthesist.causality.causality_sequencer_balanced import \
@@ -17,6 +18,7 @@ from dialectical_framework.synthesist.causality.causality_sequencer_feasible imp
17
18
  CausalitySequencerFeasible
18
19
  from dialectical_framework.synthesist.causality.causality_sequencer_realistic import \
19
20
  CausalitySequencerRealistic
21
+ from dialectical_framework.synthesist.concepts.polarity_extractor_basic import PolarityExtractorBasic
20
22
  from dialectical_framework.synthesist.concepts.thesis_extractor_basic import \
21
23
  ThesisExtractorBasic
22
24
  from dialectical_framework.synthesist.polarity.polarity_reasoner import \
@@ -76,8 +78,8 @@ class DialecticalReasoning(containers.DeclarativeContainer):
76
78
  settings=settings,
77
79
  )
78
80
 
79
- thesis_extractor: providers.Factory[ThesisExtractor] = providers.Factory(
80
- ThesisExtractorBasic,
81
+ polarity_extractor: providers.Factory[PolarityExtractor] = providers.Factory(
82
+ PolarityExtractorBasic,
81
83
  )
82
84
 
83
85
  wheel_builder: providers.Factory[WheelBuilder] = providers.Factory(WheelBuilder)
@@ -8,4 +8,4 @@ class DI(str, Enum):
8
8
  brain = "brain"
9
9
  polarity_reasoner = "polarity_reasoner"
10
10
  causality_sequencer = "causality_sequencer"
11
- thesis_extractor = "thesis_extractor"
11
+ polarity_extractor = "polarity_extractor"
@@ -0,0 +1,22 @@
1
+ from abc import abstractmethod
2
+
3
+ from dialectical_framework.protocols.thesis_extractor import ThesisExtractor
4
+ from dialectical_framework.synthesist.domain.dialectical_component import DialecticalComponent
5
+ from dialectical_framework.synthesist.domain.dialectical_components_deck import DialecticalComponentsDeck
6
+
7
+
8
+ class PolarityExtractor(ThesisExtractor):
9
+ @abstractmethod
10
+ async def extract_polarities( self, *, given: list[tuple[str | None, str | None]]) -> list[tuple[DialecticalComponent, DialecticalComponent]]:
11
+ """
12
+ Given tuple of (None, None) a single polarity will be extracted.
13
+ Given tuple of (None, some_thesis) a theses will be extracted.
14
+ Given more tuples, all the given theses/antitheses will be taken into account.
15
+ """
16
+ ...
17
+
18
+ @abstractmethod
19
+ async def extract_multiple_antitheses( self, *, theses: list[str], not_like_these: list[str] | None = None) -> DialecticalComponentsDeck: ...
20
+
21
+ @abstractmethod
22
+ async def extract_single_antithesis(self, *, thesis: str, not_like_these: list[str] | None = None) -> DialecticalComponent: ...
@@ -216,7 +216,7 @@ class CausalitySequencerBalanced(CausalitySequencer, HasBrain, SettingsAware):
216
216
  CausalCycleDto(
217
217
  aliases=[ordered_wisdom_units[0].t.alias, ordered_wisdom_units[0].a.alias],
218
218
  probability=1.0,
219
- reasoning_explanation="Single unit self-loop cycle",
219
+ reasoning_explanation="Single unit cycle",
220
220
  argumentation="Default unit cycle"
221
221
  )
222
222
  ]
@@ -0,0 +1,264 @@
1
+ from mirascope import Messages, prompt_template
2
+ from mirascope.integrations.langfuse import with_langfuse
3
+
4
+ from dialectical_framework.ai_dto.dialectical_component_dto import \
5
+ DialecticalComponentDto
6
+ from dialectical_framework.ai_dto.dialectical_components_deck_dto import \
7
+ DialecticalComponentsDeckDto
8
+ from dialectical_framework.ai_dto.dto_mapper import (map_from_dto,
9
+ map_list_from_dto)
10
+ from dialectical_framework.protocols.polarity_extractor import PolarityExtractor
11
+ from dialectical_framework.synthesist.concepts.thesis_extractor_basic import ThesisExtractorBasic
12
+ from dialectical_framework.synthesist.domain.dialectical_component import DialecticalComponent
13
+ from dialectical_framework.synthesist.domain.dialectical_components_deck import \
14
+ DialecticalComponentsDeck
15
+ from dialectical_framework.synthesist.domain.wheel_segment import ALIAS_T
16
+ from dialectical_framework.synthesist.domain.wisdom_unit import ALIAS_A
17
+ from dialectical_framework.utils.use_brain import use_brain
18
+
19
+
20
+ class PolarityExtractorBasic(ThesisExtractorBasic, PolarityExtractor):
21
+
22
+ @prompt_template(
23
+ """
24
+ MESSAGES:
25
+ {thesis_extraction}
26
+
27
+ ASSISTANT:
28
+ T = {thesis}
29
+
30
+ USER:
31
+ A dialectical opposition presents the conceptual or functional antithesis of the original statement that creates direct opposition, while potentially still allowing their mutual coexistence. For instance, Love vs. Hate or Indifference; Science vs. Superstition, Faith/Belief; Human-caused Global Warming vs. Natural Cycles.
32
+
33
+ Generate a dialectical opposition (A) of the thesis "{thesis}" (T). Be detailed enough to show deep understanding, yet concise enough to maintain clarity.
34
+
35
+ Output the dialectical component A within {component_length} word(s), the shorter, the better. Compose the explanation how it was derived in the passive voice. Don't mention any special denotations such as "T" or "A" in the explanation.
36
+
37
+ {rule_out}
38
+ """
39
+ )
40
+ def prompt_single_antithesis(self, *, thesis: str, not_like_these: list[str] | None = None) -> Messages.Type:
41
+ rule_out = ""
42
+
43
+ if not_like_these:
44
+ rule_out = "**Rules**\nIMPORTANT: The output must be different than these already known theses:\n" + "\n".join(not_like_these)
45
+
46
+ return {
47
+ "computed_fields": {
48
+ 'thesis_extraction': self.prompt_single_thesis(),
49
+ "thesis": thesis,
50
+ "rule_out": rule_out,
51
+ "component_length": self.settings.component_length,
52
+ },
53
+ }
54
+
55
+ @prompt_template(
56
+ """
57
+ MESSAGES:
58
+ {theses_extraction}
59
+
60
+ ASSISTANT:
61
+ {theses}
62
+
63
+ USER:
64
+ A dialectical opposition presents the conceptual or functional antithesis of the original statement that creates direct opposition, while potentially still allowing their mutual coexistence. For instance, Love vs. Hate or Indifference; Science vs. Superstition, Faith/Belief; Human-caused Global Warming vs. Natural Cycles.
65
+
66
+ For each thesis, generate a dialectical opposition (A). Be detailed enough to show deep understanding, yet concise enough to maintain clarity.
67
+
68
+ **Output Format:**
69
+ A1 = [antithesis of T1 in 1-{component_length} words]
70
+ Explanation: [The explanation how it was derived in the passive voice]
71
+
72
+ A2 = [antithesis of T2 in 1-{component_length} words]
73
+ Explanation: [The explanation how it was derived in the passive voice]
74
+
75
+ ...
76
+
77
+ Ax = [antithesis of Tx in 1-{component_length} words]
78
+ Explanation: [The explanation how it was derived in the passive voice]
79
+
80
+ **Rules**
81
+ Make sure to output {count} antitheses, i.e. one for each thesis, no more no less.
82
+ {rule_out}
83
+ """
84
+ )
85
+ def prompt_multiple_antitheses(self, *, theses: list[str], not_like_these: list[str] | None = None) -> Messages.Type:
86
+ rule_out = ""
87
+
88
+ if not_like_these:
89
+ rule_out = "IMPORTANT: The output antitheses must be different than these already known theses/antitheses:\n" + "\n".join(
90
+ not_like_these)
91
+
92
+ theses_str = "\n".join(f"T{i + 1} = {thesis}" for i, thesis in enumerate(theses))
93
+
94
+ return {
95
+ "computed_fields": {
96
+ "theses_extraction": self.prompt_multiple_theses(count=len(theses)),
97
+ "theses": theses_str,
98
+ "count": len(theses),
99
+ "rule_out": rule_out,
100
+ "component_length": self.settings.component_length,
101
+ },
102
+ }
103
+
104
+ async def extract_polarities(self, *, given: list[tuple[str | None, str | None]]) \
105
+ -> list[tuple[DialecticalComponent, DialecticalComponent]]:
106
+ count = len(given)
107
+ if count > 4 or count < 1:
108
+ raise ValueError(
109
+ f"Incorrect number of polarities requested. Max 4 are supported."
110
+ )
111
+
112
+ # Collect all provided statements to avoid duplicates
113
+ not_like_these = []
114
+ for thesis, antithesis in given:
115
+ if thesis:
116
+ not_like_these.append(thesis)
117
+ if antithesis:
118
+ not_like_these.append(antithesis)
119
+
120
+ theses_to_find = []
121
+ theses_indices = []
122
+
123
+ # For every tuple that has both None, we want to first find theses
124
+ empty_count = 0
125
+ for i, (thesis, antithesis) in enumerate(given):
126
+ if thesis is None and antithesis is None:
127
+ empty_count += 1
128
+ theses_indices.append(i)
129
+
130
+ # Extract missing theses for empty positions
131
+ if empty_count == 1:
132
+ t = await self.extract_single_thesis(not_like_these=not_like_these)
133
+ not_like_these.append(t.statement)
134
+ theses_to_find = [t]
135
+ elif empty_count > 1:
136
+ ts = await self.extract_multiple_theses(count=empty_count, not_like_these=not_like_these)
137
+ not_like_these.extend(t.statement for t in ts.dialectical_components)
138
+ theses_to_find = ts.dialectical_components
139
+
140
+ # Initialize result list with placeholders
141
+ result: list[tuple[DialecticalComponent | None, DialecticalComponent | None]] = [(None, None) for _ in range(len(given))]
142
+
143
+ # Determine the index to use: 0 if only one tuple, otherwise 1-based index
144
+ def get_friendly_index(i: int) -> int:
145
+ return 0 if count == 1 else i + 1
146
+
147
+ # Helper to get the correct alias
148
+ def get_alias(base_alias: str, i: int) -> str:
149
+ return base_alias if count == 1 else f"{base_alias}{i + 1}"
150
+
151
+ # Fill in known statements and place found theses in correct positions
152
+ thesis_counter = 0
153
+ for i, (thesis, antithesis) in enumerate(given):
154
+ friendly_idx = get_friendly_index(i)
155
+ if thesis is not None and antithesis is not None:
156
+ # Both provided - just create components with correct aliases
157
+ t = DialecticalComponent(alias=get_alias(ALIAS_T, i), statement=thesis)
158
+ t.set_human_friendly_index(friendly_idx)
159
+ a = DialecticalComponent(alias=get_alias(ALIAS_A, i), statement=antithesis)
160
+ a.set_human_friendly_index(friendly_idx)
161
+ result[i] = (t, a)
162
+ elif thesis is not None:
163
+ # Thesis provided, need to find antithesis
164
+ t = DialecticalComponent(alias=get_alias(ALIAS_T, i), statement=thesis)
165
+ t.set_human_friendly_index(friendly_idx)
166
+ result[i] = (t, None)
167
+ elif antithesis is not None:
168
+ # Antithesis provided, need to find its opposite (which goes in thesis position)
169
+ a = DialecticalComponent(alias=get_alias(ALIAS_A, i), statement=antithesis)
170
+ a.set_human_friendly_index(friendly_idx)
171
+ result[i] = (None, a)
172
+ elif i in theses_indices:
173
+ # Both empty - use found thesis
174
+ t = theses_to_find[thesis_counter]
175
+ t.alias = get_alias(ALIAS_T, i)
176
+ t.set_human_friendly_index(friendly_idx)
177
+ result[i] = (t, None)
178
+ thesis_counter += 1
179
+
180
+ # Collect all statements that need opposites
181
+ statements_needing_opposites = []
182
+ indices_needing_opposites = []
183
+ is_thesis_position = [] # Track which position the opposite should go to
184
+
185
+ for i, (t, a) in enumerate(result):
186
+ if t is None:
187
+ # Need to find opposite for the provided antithesis
188
+ statements_needing_opposites.append(a.statement)
189
+ indices_needing_opposites.append(i)
190
+ is_thesis_position.append(True) # Opposite goes to thesis position
191
+ elif a is None:
192
+ # Need to find opposite for the provided thesis
193
+ statements_needing_opposites.append(t.statement)
194
+ indices_needing_opposites.append(i)
195
+ is_thesis_position.append(False) # Opposite goes to antithesis position
196
+
197
+ # Extract all opposites in one batch
198
+ if len(statements_needing_opposites) == 1:
199
+ opposite = await self.extract_single_antithesis(
200
+ thesis=statements_needing_opposites[0],
201
+ not_like_these=not_like_these
202
+ )
203
+ opposites = [opposite]
204
+ elif len(statements_needing_opposites) > 1:
205
+ deck = await self.extract_multiple_antitheses(
206
+ theses=statements_needing_opposites,
207
+ not_like_these=not_like_these
208
+ )
209
+ opposites = deck.dialectical_components
210
+ else:
211
+ opposites = []
212
+
213
+ # Place the opposites in the correct positions with correct aliases
214
+ for idx, opposite, is_t_pos in zip(indices_needing_opposites, opposites, is_thesis_position):
215
+ friendly_idx = get_friendly_index(idx)
216
+ if is_t_pos:
217
+ # Opposite goes to thesis position
218
+ opposite.alias = get_alias(ALIAS_T, idx)
219
+ opposite.set_human_friendly_index(friendly_idx)
220
+ result[idx] = (opposite, result[idx][1])
221
+ else:
222
+ # Opposite goes to antithesis position
223
+ opposite.alias = get_alias(ALIAS_A, idx)
224
+ opposite.set_human_friendly_index(friendly_idx)
225
+ result[idx] = (result[idx][0], opposite)
226
+
227
+ return result
228
+
229
+ async def extract_multiple_antitheses(self, *, theses: list[str], not_like_these: list[str] | None = None) \
230
+ -> DialecticalComponentsDeck:
231
+ count = len(theses)
232
+
233
+ @with_langfuse()
234
+ @use_brain(brain=self.brain, response_model=DialecticalComponentsDeckDto)
235
+ async def _find_antitheses():
236
+ return self.prompt_multiple_antitheses(theses=theses, not_like_these=not_like_these)
237
+
238
+ deck_dto = await _find_antitheses()
239
+ components = map_list_from_dto(deck_dto.dialectical_components, DialecticalComponent)
240
+ # It may happen that AI will return theses as well, so let's be prepared to filter out only the antitheses
241
+ antitheses = []
242
+ for dc in components:
243
+ if dc.alias.startswith(ALIAS_A):
244
+ antitheses.append(dc)
245
+
246
+ if len(antitheses) < count:
247
+ raise ValueError(f"AI returned {len(antitheses)} antitheses but {count} were requested.")
248
+
249
+ # Take only the requested count if AI returned more
250
+ deck = DialecticalComponentsDeck(dialectical_components=antitheses[:count])
251
+ if count == 1 and len(deck.dialectical_components) == 1:
252
+ dc: DialecticalComponent = deck.dialectical_components[0]
253
+ dc.set_human_friendly_index(0)
254
+ return deck
255
+
256
+ async def extract_single_antithesis(self, *, thesis: str, not_like_these: list[str] | None = None) \
257
+ -> DialecticalComponent:
258
+ @with_langfuse()
259
+ @use_brain(brain=self.brain, response_model=DialecticalComponentDto)
260
+ async def _find_antithesis():
261
+ return self.prompt_single_antithesis(thesis=thesis, not_like_these=not_like_these)
262
+
263
+ dc_dto = await _find_antithesis()
264
+ return map_from_dto(dc_dto, DialecticalComponent)
@@ -106,7 +106,7 @@ class ThesisExtractorBasic(ThesisExtractor, HasBrain, SettingsAware):
106
106
  rule_out = ""
107
107
 
108
108
  if not_like_these:
109
- rule_out = "IMPORTANT: The output concepts must have different than these already known theses:\n" + "\n".join(
109
+ rule_out = "IMPORTANT: The output concepts must be different than these already known theses:\n" + "\n".join(
110
110
  not_like_these)
111
111
 
112
112
  return {
@@ -129,14 +129,15 @@ class ThesisExtractorBasic(ThesisExtractor, HasBrain, SettingsAware):
129
129
  async def _find_theses():
130
130
  return self.prompt_multiple_theses(count=count, not_like_these=not_like_these)
131
131
 
132
- if count <= 4:
133
- deck_dto = await _find_theses()
134
- deck = DialecticalComponentsDeck(dialectical_components=map_list_from_dto(deck_dto.dialectical_components, DialecticalComponent))
135
- if count == 1 and len(deck.dialectical_components) == 1:
136
- dc: DialecticalComponent = deck.dialectical_components[0]
137
- dc.set_human_friendly_index(0)
138
- else:
139
- raise ValueError(f"More than 4 theses are not supported yet.")
132
+ deck_dto = await _find_theses()
133
+ components = map_list_from_dto(deck_dto.dialectical_components, DialecticalComponent)
134
+ if len(components) < count:
135
+ raise ValueError(f"AI returned {len(components)} components but {count} were requested.")
136
+ # Take only the requested count if AI returned more
137
+ deck = DialecticalComponentsDeck(dialectical_components=components[:count])
138
+ if count == 1 and len(deck.dialectical_components) == 1:
139
+ dc: DialecticalComponent = deck.dialectical_components[0]
140
+ dc.set_human_friendly_index(0)
140
141
  return deck
141
142
 
142
143
  async def extract_single_thesis(self, *, not_like_these: list[str] | None = None) -> DialecticalComponent:
@@ -108,7 +108,7 @@ class PolarityReasoner(HasBrain, Reloadable):
108
108
  """
109
109
  A dialectical opposition presents the conceptual or functional antithesis of the original statement that creates direct opposition, while potentially still allowing their mutual coexistence. For instance, Love vs. Hate or Indifference; Science vs. Superstition, Faith/Belief; Human-caused Global Warming vs. Natural Cycles.
110
110
 
111
- Generate a dialectical opposition (A) of the thesis "{thesis}" (T). Be detailed enough to show deep understanding, yet concise enough to maintain clarity. Generalize all of them using up to 6 words.
111
+ Generate a dialectical opposition (A) of the thesis "{thesis}" (T). Be detailed enough to show deep understanding, yet concise enough to maintain clarity.
112
112
 
113
113
  Output the dialectical component A within {component_length} word(s), the shorter, the better. Compose the explanation how it was derived in the passive voice. Don't mention any special denotations such as "T" or "A" in the explanation.
114
114
  """
@@ -7,6 +7,7 @@ from dependency_injector.wiring import Provide
7
7
  from dialectical_framework.ai_dto.dto_mapper import (map_from_dto,
8
8
  map_list_from_dto)
9
9
  from dialectical_framework.analyst.domain.cycle import Cycle
10
+ from dialectical_framework.protocols.polarity_extractor import PolarityExtractor
10
11
  from dialectical_framework.synthesist.domain.dialectical_component import DialecticalComponent
11
12
  from dialectical_framework.synthesist.domain.dialectical_components_deck import \
12
13
  DialecticalComponentsDeck
@@ -26,7 +27,7 @@ from dialectical_framework.synthesist.domain.wisdom_unit import WisdomUnit
26
27
  class WheelBuilder(SettingsAware):
27
28
  def __init__(
28
29
  self,
29
- thesis_extractor: ThesisExtractor = Provide[DI.thesis_extractor],
30
+ polarity_extractor: PolarityExtractor = Provide[DI.polarity_extractor],
30
31
  causality_sequencer: CausalitySequencer = Provide[DI.causality_sequencer],
31
32
  polarity_reasoner: PolarityReasoner = Provide[DI.polarity_reasoner],
32
33
  *,
@@ -38,7 +39,7 @@ class WheelBuilder(SettingsAware):
38
39
 
39
40
  # TODO: reloading singletons isn't very good design here, because we're guessing the parameters...
40
41
 
41
- self.__extractor = thesis_extractor
42
+ self.__extractor = polarity_extractor
42
43
  self.__extractor.reload(text=text)
43
44
 
44
45
  self.__sequencer = causality_sequencer
@@ -56,7 +57,7 @@ class WheelBuilder(SettingsAware):
56
57
  return self.__text
57
58
 
58
59
  @property
59
- def extractor(self) -> ThesisExtractor:
60
+ def extractor(self) -> PolarityExtractor:
60
61
  return self.__extractor
61
62
 
62
63
  @property