dialectical-framework 1.1.3__tar.gz → 1.2.1__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 (170) hide show
  1. {dialectical_framework-1.1.3 → dialectical_framework-1.2.1}/PKG-INFO +1 -1
  2. {dialectical_framework-1.1.3 → dialectical_framework-1.2.1}/pyproject.toml +1 -1
  3. {dialectical_framework-1.1.3 → dialectical_framework-1.2.1}/src/dialectical_framework/agents/conversation_facilitator.py +20 -0
  4. {dialectical_framework-1.1.3 → dialectical_framework-1.2.1}/src/dialectical_framework/agents/explorer/explorer.py +18 -8
  5. {dialectical_framework-1.1.3 → dialectical_framework-1.2.1}/src/dialectical_framework/agents/explorer/skills/explore_transformations.py +123 -40
  6. dialectical_framework-1.2.1/src/dialectical_framework/agents/explorer/skills/generate_synthesis.py +207 -0
  7. {dialectical_framework-1.1.3 → dialectical_framework-1.2.1}/src/dialectical_framework/agents/explorer/system_prompts.py +2 -0
  8. dialectical_framework-1.2.1/src/dialectical_framework/agents/explorer/tools/generate_synthesis.py +27 -0
  9. {dialectical_framework-1.1.3 → dialectical_framework-1.2.1}/src/dialectical_framework/agents/reasonable_concern.py +31 -0
  10. dialectical_framework-1.2.1/src/dialectical_framework/concerns/synthesis_generation.py +347 -0
  11. {dialectical_framework-1.1.3 → dialectical_framework-1.2.1}/src/dialectical_framework/graph/nodes/synthesis.py +3 -3
  12. dialectical_framework-1.2.1/src/dialectical_framework/utils/concurrency.py +37 -0
  13. {dialectical_framework-1.1.3 → dialectical_framework-1.2.1}/src/dialectical_framework/utils/use_brain.py +37 -9
  14. dialectical_framework-1.1.3/src/dialectical_framework/concerns/synthesis_generation.py +0 -227
  15. {dialectical_framework-1.1.3 → dialectical_framework-1.2.1}/LICENSE +0 -0
  16. {dialectical_framework-1.1.3 → dialectical_framework-1.2.1}/README.md +0 -0
  17. {dialectical_framework-1.1.3 → dialectical_framework-1.2.1}/src/dialectical_framework/__init__.py +0 -0
  18. {dialectical_framework-1.1.3 → dialectical_framework-1.2.1}/src/dialectical_framework/agents/__init__.py +0 -0
  19. {dialectical_framework-1.1.3 → dialectical_framework-1.2.1}/src/dialectical_framework/agents/agent_context.py +0 -0
  20. {dialectical_framework-1.1.3 → dialectical_framework-1.2.1}/src/dialectical_framework/agents/analyst/__init__.py +0 -0
  21. {dialectical_framework-1.1.3 → dialectical_framework-1.2.1}/src/dialectical_framework/agents/analyst/analyst.py +0 -0
  22. {dialectical_framework-1.1.3 → dialectical_framework-1.2.1}/src/dialectical_framework/agents/analyst/skills/__init__.py +0 -0
  23. {dialectical_framework-1.1.3 → dialectical_framework-1.2.1}/src/dialectical_framework/agents/analyst/skills/anchor_theses.py +0 -0
  24. {dialectical_framework-1.1.3 → dialectical_framework-1.2.1}/src/dialectical_framework/agents/analyst/skills/edit_perspective.py +0 -0
  25. {dialectical_framework-1.1.3 → dialectical_framework-1.2.1}/src/dialectical_framework/agents/analyst/skills/expand_polarities.py +0 -0
  26. {dialectical_framework-1.1.3 → dialectical_framework-1.2.1}/src/dialectical_framework/agents/analyst/skills/find_polarities.py +0 -0
  27. {dialectical_framework-1.1.3 → dialectical_framework-1.2.1}/src/dialectical_framework/agents/analyst/skills/introduce_polarity.py +0 -0
  28. {dialectical_framework-1.1.3 → dialectical_framework-1.2.1}/src/dialectical_framework/agents/analyst/skills/surface_theses.py +0 -0
  29. {dialectical_framework-1.1.3 → dialectical_framework-1.2.1}/src/dialectical_framework/agents/analyst/system_prompts.py +0 -0
  30. {dialectical_framework-1.1.3 → dialectical_framework-1.2.1}/src/dialectical_framework/agents/analyst/tools/__init__.py +0 -0
  31. {dialectical_framework-1.1.3 → dialectical_framework-1.2.1}/src/dialectical_framework/agents/analyst/tools/create_dx_input.py +0 -0
  32. {dialectical_framework-1.1.3 → dialectical_framework-1.2.1}/src/dialectical_framework/agents/analyst/tools/place_statement.py +0 -0
  33. {dialectical_framework-1.1.3 → dialectical_framework-1.2.1}/src/dialectical_framework/agents/apps.py +0 -0
  34. {dialectical_framework-1.1.3 → dialectical_framework-1.2.1}/src/dialectical_framework/agents/execution_report.py +0 -0
  35. {dialectical_framework-1.1.3 → dialectical_framework-1.2.1}/src/dialectical_framework/agents/explorer/__init__.py +0 -0
  36. {dialectical_framework-1.1.3 → dialectical_framework-1.2.1}/src/dialectical_framework/agents/explorer/skills/__init__.py +0 -0
  37. {dialectical_framework-1.1.3 → dialectical_framework-1.2.1}/src/dialectical_framework/agents/explorer/skills/build_wheels.py +0 -0
  38. {dialectical_framework-1.1.3 → dialectical_framework-1.2.1}/src/dialectical_framework/agents/explorer/tools/__init__.py +0 -0
  39. {dialectical_framework-1.1.3 → dialectical_framework-1.2.1}/src/dialectical_framework/agents/explorer/tools/create_nexus.py +0 -0
  40. {dialectical_framework-1.1.3 → dialectical_framework-1.2.1}/src/dialectical_framework/agents/explorer/tools/expand_nexus.py +0 -0
  41. {dialectical_framework-1.1.3 → dialectical_framework-1.2.1}/src/dialectical_framework/agents/explorer/tools/present_exploration.py +0 -0
  42. {dialectical_framework-1.1.3 → dialectical_framework-1.2.1}/src/dialectical_framework/agents/orchestrator/__init__.py +0 -0
  43. {dialectical_framework-1.1.3 → dialectical_framework-1.2.1}/src/dialectical_framework/agents/orchestrator/tools/__init__.py +0 -0
  44. {dialectical_framework-1.1.3 → dialectical_framework-1.2.1}/src/dialectical_framework/agents/orchestrator/tools/add_input.py +0 -0
  45. {dialectical_framework-1.1.3 → dialectical_framework-1.2.1}/src/dialectical_framework/agents/orchestrator/tools/discard.py +0 -0
  46. {dialectical_framework-1.1.3 → dialectical_framework-1.2.1}/src/dialectical_framework/agents/orchestrator/tools/get_schema.py +0 -0
  47. {dialectical_framework-1.1.3 → dialectical_framework-1.2.1}/src/dialectical_framework/agents/orchestrator/tools/inspect_node.py +0 -0
  48. {dialectical_framework-1.1.3 → dialectical_framework-1.2.1}/src/dialectical_framework/agents/orchestrator/tools/present_analysis.py +0 -0
  49. {dialectical_framework-1.1.3 → dialectical_framework-1.2.1}/src/dialectical_framework/agents/orchestrator/tools/query_graph.py +0 -0
  50. {dialectical_framework-1.1.3 → dialectical_framework-1.2.1}/src/dialectical_framework/agents/stream_events.py +0 -0
  51. {dialectical_framework-1.1.3 → dialectical_framework-1.2.1}/src/dialectical_framework/concerns/__init__.py +0 -0
  52. {dialectical_framework-1.1.3 → dialectical_framework-1.2.1}/src/dialectical_framework/concerns/ac_re_taxonomy.py +0 -0
  53. {dialectical_framework-1.1.3 → dialectical_framework-1.2.1}/src/dialectical_framework/concerns/action_extraction.py +0 -0
  54. {dialectical_framework-1.1.3 → dialectical_framework-1.2.1}/src/dialectical_framework/concerns/ai_dto/__init__.py +0 -0
  55. {dialectical_framework-1.1.3 → dialectical_framework-1.2.1}/src/dialectical_framework/concerns/ai_dto/statement_dto.py +0 -0
  56. {dialectical_framework-1.1.3 → dialectical_framework-1.2.1}/src/dialectical_framework/concerns/ai_dto/statements_deck_dto.py +0 -0
  57. {dialectical_framework-1.1.3 → dialectical_framework-1.2.1}/src/dialectical_framework/concerns/antithesis_classification.py +0 -0
  58. {dialectical_framework-1.1.3 → dialectical_framework-1.2.1}/src/dialectical_framework/concerns/antithesis_extraction.py +0 -0
  59. {dialectical_framework-1.1.3 → dialectical_framework-1.2.1}/src/dialectical_framework/concerns/aspect_classification.py +0 -0
  60. {dialectical_framework-1.1.3 → dialectical_framework-1.2.1}/src/dialectical_framework/concerns/aspect_generation.py +0 -0
  61. {dialectical_framework-1.1.3 → dialectical_framework-1.2.1}/src/dialectical_framework/concerns/causality/__init__.py +0 -0
  62. {dialectical_framework-1.1.3 → dialectical_framework-1.2.1}/src/dialectical_framework/concerns/causality/causality_estimator.py +0 -0
  63. {dialectical_framework-1.1.3 → dialectical_framework-1.2.1}/src/dialectical_framework/concerns/causality/causality_estimator_balanced.py +0 -0
  64. {dialectical_framework-1.1.3 → dialectical_framework-1.2.1}/src/dialectical_framework/concerns/causality/causality_estimator_criteria.py +0 -0
  65. {dialectical_framework-1.1.3 → dialectical_framework-1.2.1}/src/dialectical_framework/concerns/causality/causality_estimator_desirable.py +0 -0
  66. {dialectical_framework-1.1.3 → dialectical_framework-1.2.1}/src/dialectical_framework/concerns/causality/causality_estimator_feasible.py +0 -0
  67. {dialectical_framework-1.1.3 → dialectical_framework-1.2.1}/src/dialectical_framework/concerns/causality/causality_estimator_realistic.py +0 -0
  68. {dialectical_framework-1.1.3 → dialectical_framework-1.2.1}/src/dialectical_framework/concerns/causality/causality_normalizer.py +0 -0
  69. {dialectical_framework-1.1.3 → dialectical_framework-1.2.1}/src/dialectical_framework/concerns/causality/estimator_resolver.py +0 -0
  70. {dialectical_framework-1.1.3 → dialectical_framework-1.2.1}/src/dialectical_framework/concerns/causality_estimation.py +0 -0
  71. {dialectical_framework-1.1.3 → dialectical_framework-1.2.1}/src/dialectical_framework/concerns/control_statements_check.py +0 -0
  72. {dialectical_framework-1.1.3 → dialectical_framework-1.2.1}/src/dialectical_framework/concerns/create_nexus.py +0 -0
  73. {dialectical_framework-1.1.3 → dialectical_framework-1.2.1}/src/dialectical_framework/concerns/diagonal_oppositions_check.py +0 -0
  74. {dialectical_framework-1.1.3 → dialectical_framework-1.2.1}/src/dialectical_framework/concerns/display_text_edit.py +0 -0
  75. {dialectical_framework-1.1.3 → dialectical_framework-1.2.1}/src/dialectical_framework/concerns/expand_nexus.py +0 -0
  76. {dialectical_framework-1.1.3 → dialectical_framework-1.2.1}/src/dialectical_framework/concerns/perspective_combination.py +0 -0
  77. {dialectical_framework-1.1.3 → dialectical_framework-1.2.1}/src/dialectical_framework/concerns/perspective_validation.py +0 -0
  78. {dialectical_framework-1.1.3 → dialectical_framework-1.2.1}/src/dialectical_framework/concerns/positive_ac_re_apex_derivation.py +0 -0
  79. {dialectical_framework-1.1.3 → dialectical_framework-1.2.1}/src/dialectical_framework/concerns/statement_classification.py +0 -0
  80. {dialectical_framework-1.1.3 → dialectical_framework-1.2.1}/src/dialectical_framework/concerns/statement_deduplication.py +0 -0
  81. {dialectical_framework-1.1.3 → dialectical_framework-1.2.1}/src/dialectical_framework/concerns/statement_placement.py +0 -0
  82. {dialectical_framework-1.1.3 → dialectical_framework-1.2.1}/src/dialectical_framework/concerns/thesis_extraction.py +0 -0
  83. {dialectical_framework-1.1.3 → dialectical_framework-1.2.1}/src/dialectical_framework/concerns/transformation_audit.py +0 -0
  84. {dialectical_framework-1.1.3 → dialectical_framework-1.2.1}/src/dialectical_framework/concerns/transformation_generation.py +0 -0
  85. {dialectical_framework-1.1.3 → dialectical_framework-1.2.1}/src/dialectical_framework/dialectical_reasoning.py +0 -0
  86. {dialectical_framework-1.1.3 → dialectical_framework-1.2.1}/src/dialectical_framework/enums/__init__.py +0 -0
  87. {dialectical_framework-1.1.3 → dialectical_framework-1.2.1}/src/dialectical_framework/enums/causality_preset.py +0 -0
  88. {dialectical_framework-1.1.3 → dialectical_framework-1.2.1}/src/dialectical_framework/enums/di.py +0 -0
  89. {dialectical_framework-1.1.3 → dialectical_framework-1.2.1}/src/dialectical_framework/events/__init__.py +0 -0
  90. {dialectical_framework-1.1.3 → dialectical_framework-1.2.1}/src/dialectical_framework/events/graph_event.py +0 -0
  91. {dialectical_framework-1.1.3 → dialectical_framework-1.2.1}/src/dialectical_framework/events/graph_event_bus.py +0 -0
  92. {dialectical_framework-1.1.3 → dialectical_framework-1.2.1}/src/dialectical_framework/exceptions/__init__.py +0 -0
  93. {dialectical_framework-1.1.3 → dialectical_framework-1.2.1}/src/dialectical_framework/exceptions/node_errors.py +0 -0
  94. {dialectical_framework-1.1.3 → dialectical_framework-1.2.1}/src/dialectical_framework/exceptions/resolver_errors.py +0 -0
  95. {dialectical_framework-1.1.3 → dialectical_framework-1.2.1}/src/dialectical_framework/graph/__init__.py +0 -0
  96. {dialectical_framework-1.1.3 → dialectical_framework-1.2.1}/src/dialectical_framework/graph/composite_input_resolver.py +0 -0
  97. {dialectical_framework-1.1.3 → dialectical_framework-1.2.1}/src/dialectical_framework/graph/dialexity_input_resolver.py +0 -0
  98. {dialectical_framework-1.1.3 → dialectical_framework-1.2.1}/src/dialectical_framework/graph/estimation_manager.py +0 -0
  99. {dialectical_framework-1.1.3 → dialectical_framework-1.2.1}/src/dialectical_framework/graph/mixins/__init__.py +0 -0
  100. {dialectical_framework-1.1.3 → dialectical_framework-1.2.1}/src/dialectical_framework/graph/mixins/incremental_build_mixin.py +0 -0
  101. {dialectical_framework-1.1.3 → dialectical_framework-1.2.1}/src/dialectical_framework/graph/mixins/intent_mixin.py +0 -0
  102. {dialectical_framework-1.1.3 → dialectical_framework-1.2.1}/src/dialectical_framework/graph/mixins/persistable_mixin.py +0 -0
  103. {dialectical_framework-1.1.3 → dialectical_framework-1.2.1}/src/dialectical_framework/graph/nodes/__init__.py +0 -0
  104. {dialectical_framework-1.1.3 → dialectical_framework-1.2.1}/src/dialectical_framework/graph/nodes/assessable_entity.py +0 -0
  105. {dialectical_framework-1.1.3 → dialectical_framework-1.2.1}/src/dialectical_framework/graph/nodes/base_node.py +0 -0
  106. {dialectical_framework-1.1.3 → dialectical_framework-1.2.1}/src/dialectical_framework/graph/nodes/case.py +0 -0
  107. {dialectical_framework-1.1.3 → dialectical_framework-1.2.1}/src/dialectical_framework/graph/nodes/cycle.py +0 -0
  108. {dialectical_framework-1.1.3 → dialectical_framework-1.2.1}/src/dialectical_framework/graph/nodes/estimation.py +0 -0
  109. {dialectical_framework-1.1.3 → dialectical_framework-1.2.1}/src/dialectical_framework/graph/nodes/ideas.py +0 -0
  110. {dialectical_framework-1.1.3 → dialectical_framework-1.2.1}/src/dialectical_framework/graph/nodes/input.py +0 -0
  111. {dialectical_framework-1.1.3 → dialectical_framework-1.2.1}/src/dialectical_framework/graph/nodes/nexus.py +0 -0
  112. {dialectical_framework-1.1.3 → dialectical_framework-1.2.1}/src/dialectical_framework/graph/nodes/perspective.py +0 -0
  113. {dialectical_framework-1.1.3 → dialectical_framework-1.2.1}/src/dialectical_framework/graph/nodes/polarity.py +0 -0
  114. {dialectical_framework-1.1.3 → dialectical_framework-1.2.1}/src/dialectical_framework/graph/nodes/rationale.py +0 -0
  115. {dialectical_framework-1.1.3 → dialectical_framework-1.2.1}/src/dialectical_framework/graph/nodes/statement.py +0 -0
  116. {dialectical_framework-1.1.3 → dialectical_framework-1.2.1}/src/dialectical_framework/graph/nodes/transformation.py +0 -0
  117. {dialectical_framework-1.1.3 → dialectical_framework-1.2.1}/src/dialectical_framework/graph/nodes/transition.py +0 -0
  118. {dialectical_framework-1.1.3 → dialectical_framework-1.2.1}/src/dialectical_framework/graph/nodes/wheel.py +0 -0
  119. {dialectical_framework-1.1.3 → dialectical_framework-1.2.1}/src/dialectical_framework/graph/relationship_manager.py +0 -0
  120. {dialectical_framework-1.1.3 → dialectical_framework-1.2.1}/src/dialectical_framework/graph/relationships/__init__.py +0 -0
  121. {dialectical_framework-1.1.3 → dialectical_framework-1.2.1}/src/dialectical_framework/graph/relationships/action_reflection_relationship.py +0 -0
  122. {dialectical_framework-1.1.3 → dialectical_framework-1.2.1}/src/dialectical_framework/graph/relationships/belongs_to_cycle_relationship.py +0 -0
  123. {dialectical_framework-1.1.3 → dialectical_framework-1.2.1}/src/dialectical_framework/graph/relationships/belongs_to_nexus_relationship.py +0 -0
  124. {dialectical_framework-1.1.3 → dialectical_framework-1.2.1}/src/dialectical_framework/graph/relationships/changed_to_relationship.py +0 -0
  125. {dialectical_framework-1.1.3 → dialectical_framework-1.2.1}/src/dialectical_framework/graph/relationships/contradiction_of_relationship.py +0 -0
  126. {dialectical_framework-1.1.3 → dialectical_framework-1.2.1}/src/dialectical_framework/graph/relationships/critiques_relationship.py +0 -0
  127. {dialectical_framework-1.1.3 → dialectical_framework-1.2.1}/src/dialectical_framework/graph/relationships/distilled_to_relationship.py +0 -0
  128. {dialectical_framework-1.1.3 → dialectical_framework-1.2.1}/src/dialectical_framework/graph/relationships/estimates_relationship.py +0 -0
  129. {dialectical_framework-1.1.3 → dialectical_framework-1.2.1}/src/dialectical_framework/graph/relationships/explains_relationship.py +0 -0
  130. {dialectical_framework-1.1.3 → dialectical_framework-1.2.1}/src/dialectical_framework/graph/relationships/has_input_relationship.py +0 -0
  131. {dialectical_framework-1.1.3 → dialectical_framework-1.2.1}/src/dialectical_framework/graph/relationships/has_statement_relationship.py +0 -0
  132. {dialectical_framework-1.1.3 → dialectical_framework-1.2.1}/src/dialectical_framework/graph/relationships/has_wheel_relationship.py +0 -0
  133. {dialectical_framework-1.1.3 → dialectical_framework-1.2.1}/src/dialectical_framework/graph/relationships/immutable_structure.py +0 -0
  134. {dialectical_framework-1.1.3 → dialectical_framework-1.2.1}/src/dialectical_framework/graph/relationships/is_source_of_relationship.py +0 -0
  135. {dialectical_framework-1.1.3 → dialectical_framework-1.2.1}/src/dialectical_framework/graph/relationships/is_target_of_relationship.py +0 -0
  136. {dialectical_framework-1.1.3 → dialectical_framework-1.2.1}/src/dialectical_framework/graph/relationships/negative_side_of_relationship.py +0 -0
  137. {dialectical_framework-1.1.3 → dialectical_framework-1.2.1}/src/dialectical_framework/graph/relationships/opposite_direction_relationship.py +0 -0
  138. {dialectical_framework-1.1.3 → dialectical_framework-1.2.1}/src/dialectical_framework/graph/relationships/opposite_of_relationship.py +0 -0
  139. {dialectical_framework-1.1.3 → dialectical_framework-1.2.1}/src/dialectical_framework/graph/relationships/polarity_relationship.py +0 -0
  140. {dialectical_framework-1.1.3 → dialectical_framework-1.2.1}/src/dialectical_framework/graph/relationships/positive_side_of_relationship.py +0 -0
  141. {dialectical_framework-1.1.3 → dialectical_framework-1.2.1}/src/dialectical_framework/graph/relationships/provides_relationship.py +0 -0
  142. {dialectical_framework-1.1.3 → dialectical_framework-1.2.1}/src/dialectical_framework/graph/relationships/synthesis_of_relationship.py +0 -0
  143. {dialectical_framework-1.1.3 → dialectical_framework-1.2.1}/src/dialectical_framework/graph/repositories/__init__.py +0 -0
  144. {dialectical_framework-1.1.3 → dialectical_framework-1.2.1}/src/dialectical_framework/graph/repositories/case_repository.py +0 -0
  145. {dialectical_framework-1.1.3 → dialectical_framework-1.2.1}/src/dialectical_framework/graph/repositories/cycle_repository.py +0 -0
  146. {dialectical_framework-1.1.3 → dialectical_framework-1.2.1}/src/dialectical_framework/graph/repositories/input_repository.py +0 -0
  147. {dialectical_framework-1.1.3 → dialectical_framework-1.2.1}/src/dialectical_framework/graph/repositories/nexus_repository.py +0 -0
  148. {dialectical_framework-1.1.3 → dialectical_framework-1.2.1}/src/dialectical_framework/graph/repositories/node_repository.py +0 -0
  149. {dialectical_framework-1.1.3 → dialectical_framework-1.2.1}/src/dialectical_framework/graph/repositories/perspective_repository.py +0 -0
  150. {dialectical_framework-1.1.3 → dialectical_framework-1.2.1}/src/dialectical_framework/graph/repositories/polarity_repository.py +0 -0
  151. {dialectical_framework-1.1.3 → dialectical_framework-1.2.1}/src/dialectical_framework/graph/repositories/schema_repository.py +0 -0
  152. {dialectical_framework-1.1.3 → dialectical_framework-1.2.1}/src/dialectical_framework/graph/repositories/statement_repository.py +0 -0
  153. {dialectical_framework-1.1.3 → dialectical_framework-1.2.1}/src/dialectical_framework/graph/repositories/transformation_repository.py +0 -0
  154. {dialectical_framework-1.1.3 → dialectical_framework-1.2.1}/src/dialectical_framework/graph/repositories/wheel_repository.py +0 -0
  155. {dialectical_framework-1.1.3 → dialectical_framework-1.2.1}/src/dialectical_framework/graph/scope_context.py +0 -0
  156. {dialectical_framework-1.1.3 → dialectical_framework-1.2.1}/src/dialectical_framework/graph/verbatim_input_resolver.py +0 -0
  157. {dialectical_framework-1.1.3 → dialectical_framework-1.2.1}/src/dialectical_framework/graph/wheel_segment.py +0 -0
  158. {dialectical_framework-1.1.3 → dialectical_framework-1.2.1}/src/dialectical_framework/graph/wheel_segment_polar_pair.py +0 -0
  159. {dialectical_framework-1.1.3 → dialectical_framework-1.2.1}/src/dialectical_framework/protocols/__init__.py +0 -0
  160. {dialectical_framework-1.1.3 → dialectical_framework-1.2.1}/src/dialectical_framework/protocols/has_config.py +0 -0
  161. {dialectical_framework-1.1.3 → dialectical_framework-1.2.1}/src/dialectical_framework/protocols/input_resolver.py +0 -0
  162. {dialectical_framework-1.1.3 → dialectical_framework-1.2.1}/src/dialectical_framework/settings.py +0 -0
  163. {dialectical_framework-1.1.3 → dialectical_framework-1.2.1}/src/dialectical_framework/utils/__init__.py +0 -0
  164. {dialectical_framework-1.1.3 → dialectical_framework-1.2.1}/src/dialectical_framework/utils/bedrock_provider.py +0 -0
  165. {dialectical_framework-1.1.3 → dialectical_framework-1.2.1}/src/dialectical_framework/utils/dc_replace.py +0 -0
  166. {dialectical_framework-1.1.3 → dialectical_framework-1.2.1}/src/dialectical_framework/utils/decompose_probability_uniformly.py +0 -0
  167. {dialectical_framework-1.1.3 → dialectical_framework-1.2.1}/src/dialectical_framework/utils/edge_context.py +0 -0
  168. {dialectical_framework-1.1.3 → dialectical_framework-1.2.1}/src/dialectical_framework/utils/effect_logger.py +0 -0
  169. {dialectical_framework-1.1.3 → dialectical_framework-1.2.1}/src/dialectical_framework/utils/order_transitions.py +0 -0
  170. {dialectical_framework-1.1.3 → dialectical_framework-1.2.1}/src/dialectical_framework/utils/sequence_generation.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: dialectical-framework
3
- Version: 1.1.3
3
+ Version: 1.2.1
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,perspectives,polarity-reasoning
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "dialectical-framework"
3
- version = "1.1.3"
3
+ version = "1.2.1"
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"
@@ -152,6 +152,7 @@ class ConversationFacilitator(SettingsAware):
152
152
 
153
153
  # Sync full conversation history from the response chain
154
154
  self._messages = list(response.messages)
155
+ self._strip_unsupported_input_fields()
155
156
 
156
157
  # Extract structured response
157
158
  return await self._call_with_response_model(response_model)
@@ -209,11 +210,30 @@ class ConversationFacilitator(SettingsAware):
209
210
  stream = await stream.resume(tool_outputs)
210
211
 
211
212
  self._messages = list(stream.messages)
213
+ self._strip_unsupported_input_fields()
212
214
  result = await self._call_with_response_model(response_model)
213
215
  yield ResponseComplete(result=result)
214
216
 
215
217
  # --- Internal helpers ---
216
218
 
219
+ def _strip_unsupported_input_fields(self) -> None:
220
+ """Strip output-only fields from raw_message before replaying to API.
221
+
222
+ Mirascope passes raw_message dicts back verbatim as input. If the API
223
+ added output-only fields (like 'caller' on tool_use blocks), they cause
224
+ 400 errors on the next call. This strips them.
225
+ """
226
+ for msg in self._messages:
227
+ raw = getattr(msg, "raw_message", None)
228
+ if not isinstance(raw, dict):
229
+ continue
230
+ content = raw.get("content")
231
+ if not isinstance(content, list):
232
+ continue
233
+ for block in content:
234
+ if isinstance(block, dict) and block.get("type") == "tool_use":
235
+ block.pop("caller", None)
236
+
217
237
  @staticmethod
218
238
  def _log_tool_calls(tool_calls: list) -> None:
219
239
  """Log tool invocations to the effect logger if configured."""
@@ -9,6 +9,7 @@ Also contains ExplorationPipeline — the headless pipeline for programmatic use
9
9
 
10
10
  from __future__ import annotations
11
11
 
12
+ import asyncio
12
13
  from typing import TYPE_CHECKING, Annotated, AsyncGenerator, Optional
13
14
 
14
15
  from mirascope import llm
@@ -121,6 +122,8 @@ def _build_tools() -> list:
121
122
  explore_transformations
122
123
  from dialectical_framework.agents.explorer.tools.expand_nexus import \
123
124
  expand_nexus
125
+ from dialectical_framework.agents.explorer.tools.generate_synthesis import \
126
+ generate_synthesis
124
127
  from dialectical_framework.agents.explorer.tools.present_exploration import \
125
128
  present_exploration
126
129
  from dialectical_framework.agents.orchestrator.tools.get_schema import \
@@ -133,6 +136,7 @@ def _build_tools() -> list:
133
136
  return [
134
137
  build_wheels,
135
138
  explore_transformations,
139
+ generate_synthesis,
136
140
  expand_nexus,
137
141
  present_exploration,
138
142
  inspect_node,
@@ -227,21 +231,27 @@ class ExplorationPipeline(ReasonableConcern[ExplorationResult]):
227
231
 
228
232
  transformation_count = 0
229
233
 
230
- for wheel_hash in wheel_hashes:
234
+ async def _explore_wheel(wheel_hash: str) -> tuple[int, Optional[StepError]]:
231
235
  try:
232
236
  explore_tr = ExploreTransformations(wheel_hash=wheel_hash)
233
237
  tr_result = await explore_tr.resolve()
234
238
  reports.append(explore_tr.report)
235
- transformation_count += len(tr_result.new)
239
+ return len(tr_result.new), None
236
240
  except Exception as e:
237
- errors.append(
238
- StepError(
239
- step="explore_transformations",
240
- message=str(e),
241
- hash=wheel_hash,
242
- )
241
+ return 0, StepError(
242
+ step="explore_transformations",
243
+ message=str(e),
244
+ hash=wheel_hash,
243
245
  )
244
246
 
247
+ wheel_results = await asyncio.gather(*[
248
+ _explore_wheel(wh) for wh in wheel_hashes
249
+ ])
250
+ for count, error in wheel_results:
251
+ transformation_count += count
252
+ if error:
253
+ errors.append(error)
254
+
245
255
  self._report.ok = True
246
256
  self._report.summary = (
247
257
  f"Exploration complete: {len(cycle_hashes)} cycles, "
@@ -22,8 +22,10 @@ Usage:
22
22
 
23
23
  from __future__ import annotations
24
24
 
25
+ import asyncio
26
+ import logging
25
27
  from dataclasses import dataclass, field
26
- from typing import Annotated, Optional, TYPE_CHECKING
28
+ from typing import Annotated, Any, Optional, TYPE_CHECKING
27
29
 
28
30
  from dependency_injector.wiring import Provide, inject
29
31
  from mirascope import llm
@@ -116,27 +118,47 @@ class ExploreTransformations(ReasonableConcern[ExploreTransformationsResult]):
116
118
  # 3. Get input text from scope
117
119
  input_text = await self._get_input_text()
118
120
 
119
- # 4. Process each edge pair — both edges get Transformations
121
+ # 4. Process edge pairs in parallel — both edges get Transformations
122
+ pair_results = await asyncio.gather(
123
+ *[
124
+ self._process_edge_pair(wheel, nexus, edge_a, edge_b, input_text)
125
+ for edge_a, edge_b in edge_pairs
126
+ ],
127
+ return_exceptions=True,
128
+ )
129
+
120
130
  all_existing: list[Transformation] = []
121
131
  all_new: list[Transformation] = []
122
132
  last_apexes: Optional[ApexDerivationResultDto] = None
123
133
 
124
- for edge_a, edge_b in edge_pairs:
125
- existing, new, apexes = await self._process_edge_pair(
126
- wheel, nexus, edge_a, edge_b, input_text
127
- )
134
+ for result in pair_results:
135
+ if isinstance(result, Exception):
136
+ logging.getLogger(__name__).warning("Edge pair failed: %s", result)
137
+ continue
138
+ existing, new, apexes = result
128
139
  all_existing.extend(existing)
129
140
  all_new.extend(new)
130
141
  if apexes:
131
142
  last_apexes = apexes
132
143
 
133
- # 5. Audit new transformations for feasibility
144
+ # 5. Audit new transformations in parallel
134
145
  if all_new:
135
146
  from dialectical_framework.concerns.transformation_audit import TransformationAudit
136
- for tr in all_new:
147
+
148
+ async def _audit_one(tr: Transformation) -> TransformationAudit:
137
149
  auditor = TransformationAudit()
138
150
  await auditor.resolve(tr, input_text)
139
- self._report = self._report.merge(auditor.report)
151
+ return auditor
152
+
153
+ audit_results = await asyncio.gather(
154
+ *[_audit_one(tr) for tr in all_new],
155
+ return_exceptions=True,
156
+ )
157
+ for result in audit_results:
158
+ if isinstance(result, Exception):
159
+ logging.getLogger(__name__).warning("Audit failed: %s", result)
160
+ continue
161
+ self._report = self._report.merge(result.report)
140
162
 
141
163
  # Summary
142
164
  self._report.artifacts["wheel_hash"] = wheel.short_hash
@@ -166,8 +188,8 @@ class ExploreTransformations(ReasonableConcern[ExploreTransformationsResult]):
166
188
  """
167
189
  Process a diametrically opposite edge pair.
168
190
 
169
- Phase 1: Extract Ac+ candidates for both edges independently.
170
- Phase 2: Generate tetrads for each edge, passing the opposite Ac+ for Re-side.
191
+ Phase 1: Extract Ac+ candidates for both edges in parallel.
192
+ Phase 2: Generate tetrads for all candidates in parallel.
171
193
 
172
194
  Returns:
173
195
  Tuple of (existing, new, apexes)
@@ -176,11 +198,12 @@ class ExploreTransformations(ReasonableConcern[ExploreTransformationsResult]):
176
198
 
177
199
  tr_repo = TransformationRepository()
178
200
  all_existing: list[Transformation] = []
179
- all_new: list[Transformation] = []
180
201
  last_apexes: Optional[ApexDerivationResultDto] = None
181
202
 
182
- # Phase 1: Extract Ac+ for both edges (check existing first)
203
+ # Phase 1: Extract Ac+ for both edges in parallel (check existing first)
204
+ phase1_tasks: list[tuple[Transition, asyncio.Task]] = []
183
205
  edge_data: dict[str, _EdgeProcessingData] = {}
206
+
184
207
  for edge in (edge_a, edge_b):
185
208
  assert edge.hash is not None
186
209
  existing = tr_repo.find_by_edge(edge=edge)
@@ -198,43 +221,50 @@ class ExploreTransformations(ReasonableConcern[ExploreTransformationsResult]):
198
221
  edge_data[edge.hash] = _EdgeProcessingData(skip=True)
199
222
  continue
200
223
 
201
- apex_service = ApexDerivation()
202
- apexes = await apex_service.resolve(edge, input_text)
203
- self._report = self._report.merge(apex_service.report)
204
- last_apexes = apexes
205
-
206
- extractor = ActionExtraction()
207
- ac_candidates = await extractor.resolve(
208
- edge, input_text,
209
- not_like_these=wheel.transformations,
210
- )
211
- self._report = self._report.merge(extractor.report)
212
-
213
224
  edge_data[edge.hash] = _EdgeProcessingData(
214
- ac_candidates=ac_candidates or [],
215
- apexes=apexes,
216
225
  source_segment=source_segment,
217
226
  target_segment=target_segment,
218
227
  )
228
+ phase1_tasks.append((edge, asyncio.ensure_future(
229
+ self._phase1_for_edge(edge, wheel, input_text)
230
+ )))
231
+
232
+ # Await Phase 1 tasks in parallel
233
+ if phase1_tasks:
234
+ results = await asyncio.gather(
235
+ *[t for _, t in phase1_tasks],
236
+ return_exceptions=True,
237
+ )
238
+ for (edge, _), result in zip(phase1_tasks, results):
239
+ assert edge.hash is not None
240
+ if isinstance(result, Exception):
241
+ logging.getLogger(__name__).warning(
242
+ "Phase 1 failed for edge %s: %s", edge.short_hash, result
243
+ )
244
+ edge_data[edge.hash] = _EdgeProcessingData(skip=True)
245
+ continue
246
+ apexes, ac_candidates, report = result
247
+ self._report = self._report.merge(report)
248
+ if apexes:
249
+ last_apexes = apexes
250
+ data = edge_data[edge.hash]
251
+ data.apexes = apexes
252
+ data.ac_candidates = ac_candidates or []
253
+
254
+ # Phase 2: Generate tetrads in parallel
255
+ generation_tasks: list[tuple[Transition, _EdgeProcessingData, ActionCandidateResultDto, asyncio.Task]] = []
219
256
 
220
- # Phase 2: Generate tetrads using opposite Ac+ for Re-side
221
257
  for edge, opposite_edge in [(edge_a, edge_b), (edge_b, edge_a)]:
222
258
  assert edge.hash is not None
223
259
  assert opposite_edge.hash is not None
224
260
  data = edge_data.get(edge.hash)
225
261
  if not data or data.existing or data.skip:
226
262
  continue
227
-
228
263
  if not data.ac_candidates:
229
264
  continue
230
-
231
- source_segment = data.source_segment
232
- target_segment = data.target_segment
233
- apexes = data.apexes
234
- if not source_segment or not target_segment or not apexes:
265
+ if not data.source_segment or not data.target_segment or not data.apexes:
235
266
  continue
236
267
 
237
- # Get opposite edge's Ac+ candidates for Re-side context
238
268
  opp_data = edge_data.get(opposite_edge.hash)
239
269
  opp_ac_candidates = opp_data.ac_candidates if opp_data else []
240
270
 
@@ -245,19 +275,72 @@ class ExploreTransformations(ReasonableConcern[ExploreTransformationsResult]):
245
275
  if not opposite_ac:
246
276
  continue
247
277
 
248
- generator = TransformationGeneration()
249
- tetrad = await generator.resolve(
250
- edge, ac_plus, opposite_ac, apexes, input_text
278
+ task = asyncio.ensure_future(
279
+ self._generate_tetrad(edge, ac_plus, opposite_ac, data.apexes, input_text)
251
280
  )
252
- self._report = self._report.merge(generator.report)
281
+ generation_tasks.append((edge, data, ac_plus, task))
253
282
 
283
+ # Await all generation tasks and create graph nodes sequentially
284
+ all_new: list[Transformation] = []
285
+ if generation_tasks:
286
+ tetrad_results = await asyncio.gather(
287
+ *[t for _, _, _, t in generation_tasks],
288
+ return_exceptions=True,
289
+ )
290
+ for (edge, data, _, _), result in zip(generation_tasks, tetrad_results):
291
+ if isinstance(result, Exception):
292
+ logging.getLogger(__name__).warning(
293
+ "Tetrad generation failed for edge %s: %s", edge.short_hash, result
294
+ )
295
+ continue
296
+ tetrad, report = result
297
+ self._report = self._report.merge(report)
298
+ assert data.source_segment is not None
299
+ assert data.target_segment is not None
254
300
  transformation = self._create_transformation(
255
- nexus, edge, source_segment, target_segment, tetrad,
301
+ nexus, edge, data.source_segment, data.target_segment, tetrad,
256
302
  )
257
303
  all_new.append(transformation)
258
304
 
259
305
  return all_existing, all_new, last_apexes
260
306
 
307
+ async def _phase1_for_edge(
308
+ self,
309
+ edge: Transition,
310
+ wheel: Wheel,
311
+ input_text: str,
312
+ ) -> tuple[Optional[ApexDerivationResultDto], list[ActionCandidateResultDto], Any]:
313
+ """Run ApexDerivation + ActionExtraction for a single edge. Returns (apexes, candidates, merged_report)."""
314
+ from dialectical_framework.agents.execution_report import ExecutionReport
315
+
316
+ merged_report = ExecutionReport(tool=self.__class__.__name__)
317
+
318
+ apex_service = ApexDerivation()
319
+ apexes = await apex_service.resolve(edge, input_text)
320
+ merged_report = merged_report.merge(apex_service.report)
321
+
322
+ extractor = ActionExtraction()
323
+ ac_candidates = await extractor.resolve(
324
+ edge, input_text,
325
+ not_like_these=wheel.transformations,
326
+ )
327
+ merged_report = merged_report.merge(extractor.report)
328
+
329
+ return apexes, ac_candidates or [], merged_report
330
+
331
+ async def _generate_tetrad(
332
+ self,
333
+ edge: Transition,
334
+ ac_plus: ActionCandidateResultDto,
335
+ opposite_ac: ActionCandidateResultDto,
336
+ apexes: ApexDerivationResultDto,
337
+ input_text: str,
338
+ ) -> tuple[TransformationTetradDto, Any]:
339
+ """Run TransformationGeneration for one candidate. Returns (tetrad, report)."""
340
+ generator = TransformationGeneration()
341
+ tetrad = await generator.resolve(edge, ac_plus, opposite_ac, apexes, input_text)
342
+ return tetrad, generator.report
343
+
261
344
  @staticmethod
262
345
  def _find_matching_category(
263
346
  candidates: list[ActionCandidateResultDto],
@@ -0,0 +1,207 @@
1
+ """
2
+ GenerateSynthesis: Skill for generating S+/S- synthesis from a Wheel.
3
+
4
+ Orchestrates: resolve wheel → verify transformations → gather lower-layer
5
+ context → call SynthesisGeneration concern → create Synthesis node → commit.
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ from dataclasses import dataclass
11
+ from itertools import combinations
12
+ from typing import Optional, TYPE_CHECKING
13
+
14
+ from dependency_injector.wiring import Provide, inject
15
+
16
+ from dialectical_framework.agents.reasonable_concern import ReasonableConcern
17
+ from dialectical_framework.concerns.synthesis_generation import (
18
+ SynthesisGeneration,
19
+ SynthesisResult,
20
+ )
21
+ from dialectical_framework.enums.di import DI
22
+ from dialectical_framework.graph.nodes.statement import Statement
23
+ from dialectical_framework.graph.nodes.synthesis import (
24
+ POSITION_S_MINUS,
25
+ POSITION_S_PLUS,
26
+ Synthesis,
27
+ )
28
+ from dialectical_framework.graph.relationships.polarity_relationship import (
29
+ SMinusRelationship,
30
+ SPlusRelationship,
31
+ )
32
+ from dialectical_framework.graph.relationships.synthesis_of_relationship import (
33
+ SynthesisOfRelationship,
34
+ )
35
+
36
+ if TYPE_CHECKING:
37
+ from dialectical_framework.graph.nodes.nexus import Nexus
38
+ from dialectical_framework.graph.nodes.perspective import Perspective
39
+ from dialectical_framework.graph.nodes.wheel import Wheel
40
+ from dialectical_framework.protocols.input_resolver import InputResolver
41
+
42
+
43
+ @dataclass
44
+ class GenerateSynthesisResult:
45
+ """Result from GenerateSynthesis skill."""
46
+
47
+ synthesis: Synthesis
48
+ is_new: bool = True
49
+
50
+
51
+ class GenerateSynthesis(ReasonableConcern[GenerateSynthesisResult]):
52
+ """
53
+ Skill for generating S+/S- synthesis from a Wheel's Transformations.
54
+
55
+ Idempotent: returns existing Synthesis if one already exists for the Wheel.
56
+ """
57
+
58
+ def __init__(self, wheel_hash: str) -> None:
59
+ self.wheel_hash = wheel_hash
60
+
61
+ async def resolve(self) -> GenerateSynthesisResult:
62
+ """
63
+ Generate synthesis for a Wheel.
64
+
65
+ Returns:
66
+ GenerateSynthesisResult with the Synthesis node
67
+
68
+ Raises:
69
+ ValueError: If wheel not found or has no Transformations
70
+ """
71
+ wheel = self._resolve_wheel()
72
+
73
+ # Check transformations exist
74
+ transformations = wheel.transformations
75
+ if not transformations:
76
+ raise ValueError(
77
+ f"Wheel {wheel.short_hash} has no Transformations — "
78
+ f"run explore_transformations first"
79
+ )
80
+
81
+ # Idempotent: return existing synthesis
82
+ existing = self._find_existing_synthesis(wheel)
83
+ if existing:
84
+ self._report.summary = (
85
+ f"Synthesis already exists for Wheel {wheel.short_hash}"
86
+ )
87
+ self._report.artifacts["wheel"] = wheel.short_hash
88
+ self._report.artifacts["existing"] = True
89
+ return GenerateSynthesisResult(synthesis=existing, is_new=False)
90
+
91
+ # Gather context
92
+ input_text = await self._get_input_text()
93
+ lower_layer_context = self._build_lower_layer_context(wheel)
94
+
95
+ # Call the concern
96
+ concern = SynthesisGeneration()
97
+ result = await concern.resolve(
98
+ wheel=wheel,
99
+ input_text=input_text,
100
+ lower_layer_context=lower_layer_context,
101
+ )
102
+ self._report = self._report.merge(concern.report)
103
+
104
+ if result is None:
105
+ raise ValueError(
106
+ f"Synthesis generation failed for Wheel {wheel.short_hash}"
107
+ )
108
+
109
+ # Create Synthesis node and wire up
110
+ synthesis = Synthesis()
111
+ synthesis.save()
112
+
113
+ synthesis.s_plus.connect(
114
+ result.s_plus_statement,
115
+ relationship=SPlusRelationship(
116
+ alias=POSITION_S_PLUS, heuristic_similarity=None
117
+ ),
118
+ )
119
+ synthesis.s_minus.connect(
120
+ result.s_minus_statement,
121
+ relationship=SMinusRelationship(
122
+ alias=POSITION_S_MINUS, heuristic_similarity=None
123
+ ),
124
+ )
125
+ synthesis.target.connect(wheel, relationship=SynthesisOfRelationship())
126
+ synthesis.commit()
127
+
128
+ # Report
129
+ self._report.node_created(synthesis)
130
+ self._report.artifacts["wheel"] = wheel.short_hash
131
+ self._report.artifacts["s_plus"] = result.s_plus_statement.text
132
+ self._report.artifacts["s_minus"] = result.s_minus_statement.text
133
+ self._report.summary = (
134
+ f"Generated synthesis for Wheel {wheel.short_hash}: "
135
+ f"S+ = \"{result.s_plus_statement.text}\", "
136
+ f"S- = \"{result.s_minus_statement.text}\""
137
+ )
138
+
139
+ return GenerateSynthesisResult(synthesis=synthesis)
140
+
141
+ def _resolve_wheel(self) -> Wheel:
142
+ """Resolve Wheel from hash or prefix."""
143
+ from dialectical_framework.graph.nodes.wheel import Wheel
144
+ from dialectical_framework.graph.repositories.node_repository import (
145
+ NodeRepository,
146
+ )
147
+
148
+ repo = NodeRepository()
149
+ node = repo.find_by_hash(self.wheel_hash, node_type=Wheel)
150
+ if node is None:
151
+ raise ValueError(f"Wheel not found: {self.wheel_hash}")
152
+ return node
153
+
154
+ @staticmethod
155
+ def _find_existing_synthesis(wheel: Wheel) -> Optional[Synthesis]:
156
+ """Check if the wheel already has a synthesis."""
157
+ existing = list(wheel.synthesis.all())
158
+ if existing:
159
+ return existing[0][0]
160
+ return None
161
+
162
+ @inject
163
+ async def _get_input_text(
164
+ self,
165
+ input_resolver: InputResolver = Provide[DI.input_resolver],
166
+ ) -> str:
167
+ """Get concatenated text from all inputs in scope."""
168
+ from dialectical_framework.graph.repositories.input_repository import (
169
+ InputRepository,
170
+ )
171
+
172
+ repo = InputRepository()
173
+ inputs = repo.get_all()
174
+
175
+ if not inputs:
176
+ return ""
177
+
178
+ texts = []
179
+ for input_node in inputs:
180
+ resolved = await input_resolver.resolve(input_node)
181
+ texts.append(resolved)
182
+
183
+ return "\n\n---\n\n".join(texts)
184
+
185
+ def _build_lower_layer_context(self, wheel: Wheel) -> str:
186
+ """Find synthesis from sub-wheels (lower PP layers) sharing perspectives."""
187
+ from dialectical_framework.graph.repositories.wheel_repository import (
188
+ WheelRepository,
189
+ )
190
+
191
+ pps = wheel._perspectives
192
+ if len(pps) < 2:
193
+ return ""
194
+
195
+ wheel_repo = WheelRepository()
196
+ parts: list[str] = []
197
+
198
+ # Look one layer down (N-1 perspectives)
199
+ for combo in combinations(pps, len(pps) - 1):
200
+ sub_wheels = wheel_repo.find_by_layer(list(combo))
201
+ for sub_wheel in sub_wheels:
202
+ for synth, _ in sub_wheel.synthesis.all():
203
+ parts.append(
204
+ f"Sub-wheel [{sub_wheel.short_hash}]: {synth:positions:0}"
205
+ )
206
+
207
+ return "\n".join(parts)
@@ -25,6 +25,7 @@ On first message (or when resuming):
25
25
  - If wheels don't exist yet, call `build_wheels` to create them.
26
26
  - If build_wheels yields no wheels (e.g., only one position), explain that exploration needs at least two positions interacting — suggest the user adds more via the analysis thread.
27
27
  - If transformations don't exist yet, call `explore_transformations` for each wheel.
28
+ - After transformations exist, call `generate_synthesis` to derive S+/S- for each wheel.
28
29
 
29
30
  When presenting transformations:
30
31
  - Focus on the Ac+ (constructive action: T- -> A+) and Re+ (constructive reflection: A- -> T+).
@@ -48,6 +49,7 @@ When the user wants to go deeper on a specific transformation:
48
49
 
49
50
  - `build_wheels` -- Generate causal structures (Cycles + Wheels) from this Nexus. Use nexus_hash: "{nexus_hash}".
50
51
  - `explore_transformations` -- Generate Action-Reflection transformations for a Wheel.
52
+ - `generate_synthesis` -- Generate S+/S- synthesis for a Wheel. Requires transformations first.
51
53
  - `present_exploration` -- Show current state of this Nexus: perspectives, wheels, transformations.
52
54
  - `inspect_node` -- Deep-dive any node by hash.
53
55
  - `query_graph` -- Raw Cypher for custom queries. Call `get_schema` first.
@@ -0,0 +1,27 @@
1
+ """
2
+ generate_synthesis tool: thin LLM-facing wrapper around the GenerateSynthesis skill.
3
+ """
4
+
5
+ from __future__ import annotations
6
+
7
+ from typing import Annotated
8
+
9
+ from mirascope import llm
10
+ from pydantic import Field
11
+
12
+ from dialectical_framework.agents.explorer.skills.generate_synthesis import (
13
+ GenerateSynthesis,
14
+ )
15
+
16
+
17
+ @llm.tool
18
+ async def generate_synthesis(
19
+ wheel_hash: Annotated[
20
+ str,
21
+ Field(description="Hash of the Wheel to generate synthesis for"),
22
+ ],
23
+ ) -> str:
24
+ """Generate S+/S- synthesis for a Wheel — the emergent properties arising from circular causality. S+ represents complementary harmony (1+1>2), S- represents reinforcing uniformity (1+1<2). The wheel must have transformations computed first."""
25
+ skill = GenerateSynthesis(wheel_hash=wheel_hash)
26
+ await skill.resolve()
27
+ return str(skill.report)
@@ -10,15 +10,40 @@ All three levels inherit from ReasonableConcern — same interface (resolve + re
10
10
 
11
11
  from __future__ import annotations
12
12
 
13
+ import asyncio
14
+ import inspect
13
15
  from abc import ABC, abstractmethod
16
+ from functools import wraps
14
17
  from typing import TYPE_CHECKING, Any, Generic, TypeVar
15
18
 
19
+ from langfuse import get_client, observe
20
+
16
21
  if TYPE_CHECKING:
17
22
  from dialectical_framework.agents.execution_report import ExecutionReport
18
23
 
19
24
  R_co = TypeVar("R_co", covariant=True)
20
25
 
21
26
 
27
+ def _conditional_observe(name: str, fn: Any) -> Any:
28
+ """Wrap fn with @observe only when an active Langfuse trace exists."""
29
+ observed = observe(name=name)(fn)
30
+
31
+ if asyncio.iscoroutinefunction(fn):
32
+ @wraps(fn)
33
+ async def wrapper(*args: Any, **kwargs: Any) -> Any:
34
+ if get_client().get_current_trace_id():
35
+ return await observed(*args, **kwargs)
36
+ return await fn(*args, **kwargs)
37
+ else:
38
+ @wraps(fn)
39
+ def wrapper(*args: Any, **kwargs: Any) -> Any:
40
+ if get_client().get_current_trace_id():
41
+ return observed(*args, **kwargs)
42
+ return fn(*args, **kwargs)
43
+
44
+ return wrapper
45
+
46
+
22
47
  class ReasonableConcern(ABC, Generic[R_co]):
23
48
  """
24
49
  Base class for concerns, skills, and tools.
@@ -34,6 +59,12 @@ class ReasonableConcern(ABC, Generic[R_co]):
34
59
 
35
60
  _report: ExecutionReport
36
61
 
62
+ def __init_subclass__(cls, **kwargs: Any) -> None:
63
+ super().__init_subclass__(**kwargs)
64
+ if "resolve" in cls.__dict__:
65
+ original = cls.__dict__["resolve"]
66
+ cls.resolve = _conditional_observe(cls.__name__, original) # type: ignore[assignment]
67
+
37
68
  def __getattr__(self, name: str) -> Any:
38
69
  if name == "_report":
39
70
  from dialectical_framework.agents.execution_report import ExecutionReport