dialectical-framework 1.6.4__tar.gz → 1.6.5__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 (191) hide show
  1. {dialectical_framework-1.6.4 → dialectical_framework-1.6.5}/PKG-INFO +1 -1
  2. {dialectical_framework-1.6.4 → dialectical_framework-1.6.5}/pyproject.toml +1 -1
  3. {dialectical_framework-1.6.4 → dialectical_framework-1.6.5}/src/dialectical_framework/agents/analyst/system_prompts.py +11 -3
  4. {dialectical_framework-1.6.4 → dialectical_framework-1.6.5}/src/dialectical_framework/agents/apps.py +7 -3
  5. {dialectical_framework-1.6.4 → dialectical_framework-1.6.5}/src/dialectical_framework/agents/conversation_facilitator.py +3 -2
  6. {dialectical_framework-1.6.4 → dialectical_framework-1.6.5}/src/dialectical_framework/concerns/aspect_generation.py +72 -20
  7. {dialectical_framework-1.6.4 → dialectical_framework-1.6.5}/src/dialectical_framework/concerns/source_digest.py +38 -9
  8. {dialectical_framework-1.6.4 → dialectical_framework-1.6.5}/src/dialectical_framework/graph/composite_input_resolver.py +26 -0
  9. {dialectical_framework-1.6.4 → dialectical_framework-1.6.5}/src/dialectical_framework/graph/verbatim_input_resolver.py +75 -0
  10. {dialectical_framework-1.6.4 → dialectical_framework-1.6.5}/src/dialectical_framework/protocols/input_resolver.py +28 -0
  11. {dialectical_framework-1.6.4 → dialectical_framework-1.6.5}/LICENSE +0 -0
  12. {dialectical_framework-1.6.4 → dialectical_framework-1.6.5}/PATENTS +0 -0
  13. {dialectical_framework-1.6.4 → dialectical_framework-1.6.5}/README.md +0 -0
  14. {dialectical_framework-1.6.4 → dialectical_framework-1.6.5}/src/dialectical_framework/__init__.py +0 -0
  15. {dialectical_framework-1.6.4 → dialectical_framework-1.6.5}/src/dialectical_framework/agents/__init__.py +0 -0
  16. {dialectical_framework-1.6.4 → dialectical_framework-1.6.5}/src/dialectical_framework/agents/advisor/__init__.py +0 -0
  17. {dialectical_framework-1.6.4 → dialectical_framework-1.6.5}/src/dialectical_framework/agents/advisor/advisor.py +0 -0
  18. {dialectical_framework-1.6.4 → dialectical_framework-1.6.5}/src/dialectical_framework/agents/advisor/system_prompts.py +0 -0
  19. {dialectical_framework-1.6.4 → dialectical_framework-1.6.5}/src/dialectical_framework/agents/advisor/tools/__init__.py +0 -0
  20. {dialectical_framework-1.6.4 → dialectical_framework-1.6.5}/src/dialectical_framework/agents/advisor/tools/anchor.py +0 -0
  21. {dialectical_framework-1.6.4 → dialectical_framework-1.6.5}/src/dialectical_framework/agents/advisor/tools/explore.py +0 -0
  22. {dialectical_framework-1.6.4 → dialectical_framework-1.6.5}/src/dialectical_framework/agents/advisor/tools/ingest.py +0 -0
  23. {dialectical_framework-1.6.4 → dialectical_framework-1.6.5}/src/dialectical_framework/agents/advisor/tools/sync.py +0 -0
  24. {dialectical_framework-1.6.4 → dialectical_framework-1.6.5}/src/dialectical_framework/agents/agent_context.py +0 -0
  25. {dialectical_framework-1.6.4 → dialectical_framework-1.6.5}/src/dialectical_framework/agents/analyst/__init__.py +0 -0
  26. {dialectical_framework-1.6.4 → dialectical_framework-1.6.5}/src/dialectical_framework/agents/analyst/analyst.py +0 -0
  27. {dialectical_framework-1.6.4 → dialectical_framework-1.6.5}/src/dialectical_framework/agents/analyst/skills/__init__.py +0 -0
  28. {dialectical_framework-1.6.4 → dialectical_framework-1.6.5}/src/dialectical_framework/agents/analyst/skills/anchor_theses.py +0 -0
  29. {dialectical_framework-1.6.4 → dialectical_framework-1.6.5}/src/dialectical_framework/agents/analyst/skills/edit_perspective.py +0 -0
  30. {dialectical_framework-1.6.4 → dialectical_framework-1.6.5}/src/dialectical_framework/agents/analyst/skills/expand_polarities.py +0 -0
  31. {dialectical_framework-1.6.4 → dialectical_framework-1.6.5}/src/dialectical_framework/agents/analyst/skills/find_polarities.py +0 -0
  32. {dialectical_framework-1.6.4 → dialectical_framework-1.6.5}/src/dialectical_framework/agents/analyst/skills/introduce_polarity.py +0 -0
  33. {dialectical_framework-1.6.4 → dialectical_framework-1.6.5}/src/dialectical_framework/agents/analyst/skills/surface_theses.py +0 -0
  34. {dialectical_framework-1.6.4 → dialectical_framework-1.6.5}/src/dialectical_framework/agents/analyst/tools/__init__.py +0 -0
  35. {dialectical_framework-1.6.4 → dialectical_framework-1.6.5}/src/dialectical_framework/agents/analyst/tools/create_dx_input.py +0 -0
  36. {dialectical_framework-1.6.4 → dialectical_framework-1.6.5}/src/dialectical_framework/agents/analyst/tools/place_statement.py +0 -0
  37. {dialectical_framework-1.6.4 → dialectical_framework-1.6.5}/src/dialectical_framework/agents/execution_report.py +0 -0
  38. {dialectical_framework-1.6.4 → dialectical_framework-1.6.5}/src/dialectical_framework/agents/explorer/__init__.py +0 -0
  39. {dialectical_framework-1.6.4 → dialectical_framework-1.6.5}/src/dialectical_framework/agents/explorer/explorer.py +0 -0
  40. {dialectical_framework-1.6.4 → dialectical_framework-1.6.5}/src/dialectical_framework/agents/explorer/skills/__init__.py +0 -0
  41. {dialectical_framework-1.6.4 → dialectical_framework-1.6.5}/src/dialectical_framework/agents/explorer/skills/build_wheels.py +0 -0
  42. {dialectical_framework-1.6.4 → dialectical_framework-1.6.5}/src/dialectical_framework/agents/explorer/skills/explore_transformations.py +0 -0
  43. {dialectical_framework-1.6.4 → dialectical_framework-1.6.5}/src/dialectical_framework/agents/explorer/skills/generate_synthesis.py +0 -0
  44. {dialectical_framework-1.6.4 → dialectical_framework-1.6.5}/src/dialectical_framework/agents/explorer/system_prompts.py +0 -0
  45. {dialectical_framework-1.6.4 → dialectical_framework-1.6.5}/src/dialectical_framework/agents/explorer/tools/__init__.py +0 -0
  46. {dialectical_framework-1.6.4 → dialectical_framework-1.6.5}/src/dialectical_framework/agents/explorer/tools/create_nexus.py +0 -0
  47. {dialectical_framework-1.6.4 → dialectical_framework-1.6.5}/src/dialectical_framework/agents/explorer/tools/expand_nexus.py +0 -0
  48. {dialectical_framework-1.6.4 → dialectical_framework-1.6.5}/src/dialectical_framework/agents/explorer/tools/generate_synthesis.py +0 -0
  49. {dialectical_framework-1.6.4 → dialectical_framework-1.6.5}/src/dialectical_framework/agents/explorer/tools/present_exploration.py +0 -0
  50. {dialectical_framework-1.6.4 → dialectical_framework-1.6.5}/src/dialectical_framework/agents/orchestrator/__init__.py +0 -0
  51. {dialectical_framework-1.6.4 → dialectical_framework-1.6.5}/src/dialectical_framework/agents/orchestrator/tools/__init__.py +0 -0
  52. {dialectical_framework-1.6.4 → dialectical_framework-1.6.5}/src/dialectical_framework/agents/orchestrator/tools/add_input.py +0 -0
  53. {dialectical_framework-1.6.4 → dialectical_framework-1.6.5}/src/dialectical_framework/agents/orchestrator/tools/digest_input.py +0 -0
  54. {dialectical_framework-1.6.4 → dialectical_framework-1.6.5}/src/dialectical_framework/agents/orchestrator/tools/discard.py +0 -0
  55. {dialectical_framework-1.6.4 → dialectical_framework-1.6.5}/src/dialectical_framework/agents/orchestrator/tools/get_schema.py +0 -0
  56. {dialectical_framework-1.6.4 → dialectical_framework-1.6.5}/src/dialectical_framework/agents/orchestrator/tools/inspect_node.py +0 -0
  57. {dialectical_framework-1.6.4 → dialectical_framework-1.6.5}/src/dialectical_framework/agents/orchestrator/tools/present_analysis.py +0 -0
  58. {dialectical_framework-1.6.4 → dialectical_framework-1.6.5}/src/dialectical_framework/agents/orchestrator/tools/query_graph.py +0 -0
  59. {dialectical_framework-1.6.4 → dialectical_framework-1.6.5}/src/dialectical_framework/agents/orchestrator/tools/read_digest.py +0 -0
  60. {dialectical_framework-1.6.4 → dialectical_framework-1.6.5}/src/dialectical_framework/agents/orchestrator/tools/read_input.py +0 -0
  61. {dialectical_framework-1.6.4 → dialectical_framework-1.6.5}/src/dialectical_framework/agents/reasonable_concern.py +0 -0
  62. {dialectical_framework-1.6.4 → dialectical_framework-1.6.5}/src/dialectical_framework/agents/stream_events.py +0 -0
  63. {dialectical_framework-1.6.4 → dialectical_framework-1.6.5}/src/dialectical_framework/concerns/__init__.py +0 -0
  64. {dialectical_framework-1.6.4 → dialectical_framework-1.6.5}/src/dialectical_framework/concerns/ac_re_taxonomy.py +0 -0
  65. {dialectical_framework-1.6.4 → dialectical_framework-1.6.5}/src/dialectical_framework/concerns/action_extraction.py +0 -0
  66. {dialectical_framework-1.6.4 → dialectical_framework-1.6.5}/src/dialectical_framework/concerns/add_input.py +0 -0
  67. {dialectical_framework-1.6.4 → dialectical_framework-1.6.5}/src/dialectical_framework/concerns/ai_dto/__init__.py +0 -0
  68. {dialectical_framework-1.6.4 → dialectical_framework-1.6.5}/src/dialectical_framework/concerns/ai_dto/statement_dto.py +0 -0
  69. {dialectical_framework-1.6.4 → dialectical_framework-1.6.5}/src/dialectical_framework/concerns/ai_dto/statements_deck_dto.py +0 -0
  70. {dialectical_framework-1.6.4 → dialectical_framework-1.6.5}/src/dialectical_framework/concerns/antithesis_classification.py +0 -0
  71. {dialectical_framework-1.6.4 → dialectical_framework-1.6.5}/src/dialectical_framework/concerns/antithesis_extraction.py +0 -0
  72. {dialectical_framework-1.6.4 → dialectical_framework-1.6.5}/src/dialectical_framework/concerns/antithetical_thesis_detection.py +0 -0
  73. {dialectical_framework-1.6.4 → dialectical_framework-1.6.5}/src/dialectical_framework/concerns/aspect_classification.py +0 -0
  74. {dialectical_framework-1.6.4 → dialectical_framework-1.6.5}/src/dialectical_framework/concerns/causality/__init__.py +0 -0
  75. {dialectical_framework-1.6.4 → dialectical_framework-1.6.5}/src/dialectical_framework/concerns/causality/causality_estimator.py +0 -0
  76. {dialectical_framework-1.6.4 → dialectical_framework-1.6.5}/src/dialectical_framework/concerns/causality/causality_estimator_balanced.py +0 -0
  77. {dialectical_framework-1.6.4 → dialectical_framework-1.6.5}/src/dialectical_framework/concerns/causality/causality_estimator_criteria.py +0 -0
  78. {dialectical_framework-1.6.4 → dialectical_framework-1.6.5}/src/dialectical_framework/concerns/causality/causality_estimator_desirable.py +0 -0
  79. {dialectical_framework-1.6.4 → dialectical_framework-1.6.5}/src/dialectical_framework/concerns/causality/causality_estimator_feasible.py +0 -0
  80. {dialectical_framework-1.6.4 → dialectical_framework-1.6.5}/src/dialectical_framework/concerns/causality/causality_estimator_realistic.py +0 -0
  81. {dialectical_framework-1.6.4 → dialectical_framework-1.6.5}/src/dialectical_framework/concerns/causality/causality_normalizer.py +0 -0
  82. {dialectical_framework-1.6.4 → dialectical_framework-1.6.5}/src/dialectical_framework/concerns/causality/estimator_resolver.py +0 -0
  83. {dialectical_framework-1.6.4 → dialectical_framework-1.6.5}/src/dialectical_framework/concerns/causality_estimation.py +0 -0
  84. {dialectical_framework-1.6.4 → dialectical_framework-1.6.5}/src/dialectical_framework/concerns/control_statements_check.py +0 -0
  85. {dialectical_framework-1.6.4 → dialectical_framework-1.6.5}/src/dialectical_framework/concerns/create_dx_input.py +0 -0
  86. {dialectical_framework-1.6.4 → dialectical_framework-1.6.5}/src/dialectical_framework/concerns/create_nexus.py +0 -0
  87. {dialectical_framework-1.6.4 → dialectical_framework-1.6.5}/src/dialectical_framework/concerns/diagonal_oppositions_check.py +0 -0
  88. {dialectical_framework-1.6.4 → dialectical_framework-1.6.5}/src/dialectical_framework/concerns/dialectical_context.py +0 -0
  89. {dialectical_framework-1.6.4 → dialectical_framework-1.6.5}/src/dialectical_framework/concerns/discard.py +0 -0
  90. {dialectical_framework-1.6.4 → dialectical_framework-1.6.5}/src/dialectical_framework/concerns/display_text_edit.py +0 -0
  91. {dialectical_framework-1.6.4 → dialectical_framework-1.6.5}/src/dialectical_framework/concerns/expand_nexus.py +0 -0
  92. {dialectical_framework-1.6.4 → dialectical_framework-1.6.5}/src/dialectical_framework/concerns/perspective_combination.py +0 -0
  93. {dialectical_framework-1.6.4 → dialectical_framework-1.6.5}/src/dialectical_framework/concerns/perspective_validation.py +0 -0
  94. {dialectical_framework-1.6.4 → dialectical_framework-1.6.5}/src/dialectical_framework/concerns/positive_ac_re_apex_derivation.py +0 -0
  95. {dialectical_framework-1.6.4 → dialectical_framework-1.6.5}/src/dialectical_framework/concerns/scoring_scales.py +0 -0
  96. {dialectical_framework-1.6.4 → dialectical_framework-1.6.5}/src/dialectical_framework/concerns/statement_classification.py +0 -0
  97. {dialectical_framework-1.6.4 → dialectical_framework-1.6.5}/src/dialectical_framework/concerns/statement_deduplication.py +0 -0
  98. {dialectical_framework-1.6.4 → dialectical_framework-1.6.5}/src/dialectical_framework/concerns/statement_headline.py +0 -0
  99. {dialectical_framework-1.6.4 → dialectical_framework-1.6.5}/src/dialectical_framework/concerns/statement_placement.py +0 -0
  100. {dialectical_framework-1.6.4 → dialectical_framework-1.6.5}/src/dialectical_framework/concerns/synthesis_generation.py +0 -0
  101. {dialectical_framework-1.6.4 → dialectical_framework-1.6.5}/src/dialectical_framework/concerns/thesis_extraction.py +0 -0
  102. {dialectical_framework-1.6.4 → dialectical_framework-1.6.5}/src/dialectical_framework/concerns/transformation_audit.py +0 -0
  103. {dialectical_framework-1.6.4 → dialectical_framework-1.6.5}/src/dialectical_framework/concerns/transformation_generation.py +0 -0
  104. {dialectical_framework-1.6.4 → dialectical_framework-1.6.5}/src/dialectical_framework/dialectical_reasoning.py +0 -0
  105. {dialectical_framework-1.6.4 → dialectical_framework-1.6.5}/src/dialectical_framework/enums/__init__.py +0 -0
  106. {dialectical_framework-1.6.4 → dialectical_framework-1.6.5}/src/dialectical_framework/enums/causality_preset.py +0 -0
  107. {dialectical_framework-1.6.4 → dialectical_framework-1.6.5}/src/dialectical_framework/enums/di.py +0 -0
  108. {dialectical_framework-1.6.4 → dialectical_framework-1.6.5}/src/dialectical_framework/events/__init__.py +0 -0
  109. {dialectical_framework-1.6.4 → dialectical_framework-1.6.5}/src/dialectical_framework/events/graph_event.py +0 -0
  110. {dialectical_framework-1.6.4 → dialectical_framework-1.6.5}/src/dialectical_framework/events/graph_event_bus.py +0 -0
  111. {dialectical_framework-1.6.4 → dialectical_framework-1.6.5}/src/dialectical_framework/exceptions/__init__.py +0 -0
  112. {dialectical_framework-1.6.4 → dialectical_framework-1.6.5}/src/dialectical_framework/exceptions/node_errors.py +0 -0
  113. {dialectical_framework-1.6.4 → dialectical_framework-1.6.5}/src/dialectical_framework/exceptions/resolver_errors.py +0 -0
  114. {dialectical_framework-1.6.4 → dialectical_framework-1.6.5}/src/dialectical_framework/graph/__init__.py +0 -0
  115. {dialectical_framework-1.6.4 → dialectical_framework-1.6.5}/src/dialectical_framework/graph/dialexity_input_resolver.py +0 -0
  116. {dialectical_framework-1.6.4 → dialectical_framework-1.6.5}/src/dialectical_framework/graph/estimation_manager.py +0 -0
  117. {dialectical_framework-1.6.4 → dialectical_framework-1.6.5}/src/dialectical_framework/graph/mixins/__init__.py +0 -0
  118. {dialectical_framework-1.6.4 → dialectical_framework-1.6.5}/src/dialectical_framework/graph/mixins/incremental_build_mixin.py +0 -0
  119. {dialectical_framework-1.6.4 → dialectical_framework-1.6.5}/src/dialectical_framework/graph/mixins/intent_mixin.py +0 -0
  120. {dialectical_framework-1.6.4 → dialectical_framework-1.6.5}/src/dialectical_framework/graph/mixins/persistable_mixin.py +0 -0
  121. {dialectical_framework-1.6.4 → dialectical_framework-1.6.5}/src/dialectical_framework/graph/nodes/__init__.py +0 -0
  122. {dialectical_framework-1.6.4 → dialectical_framework-1.6.5}/src/dialectical_framework/graph/nodes/assessable_entity.py +0 -0
  123. {dialectical_framework-1.6.4 → dialectical_framework-1.6.5}/src/dialectical_framework/graph/nodes/base_node.py +0 -0
  124. {dialectical_framework-1.6.4 → dialectical_framework-1.6.5}/src/dialectical_framework/graph/nodes/case.py +0 -0
  125. {dialectical_framework-1.6.4 → dialectical_framework-1.6.5}/src/dialectical_framework/graph/nodes/cycle.py +0 -0
  126. {dialectical_framework-1.6.4 → dialectical_framework-1.6.5}/src/dialectical_framework/graph/nodes/estimation.py +0 -0
  127. {dialectical_framework-1.6.4 → dialectical_framework-1.6.5}/src/dialectical_framework/graph/nodes/ideas.py +0 -0
  128. {dialectical_framework-1.6.4 → dialectical_framework-1.6.5}/src/dialectical_framework/graph/nodes/input.py +0 -0
  129. {dialectical_framework-1.6.4 → dialectical_framework-1.6.5}/src/dialectical_framework/graph/nodes/nexus.py +0 -0
  130. {dialectical_framework-1.6.4 → dialectical_framework-1.6.5}/src/dialectical_framework/graph/nodes/perspective.py +0 -0
  131. {dialectical_framework-1.6.4 → dialectical_framework-1.6.5}/src/dialectical_framework/graph/nodes/polarity.py +0 -0
  132. {dialectical_framework-1.6.4 → dialectical_framework-1.6.5}/src/dialectical_framework/graph/nodes/rationale.py +0 -0
  133. {dialectical_framework-1.6.4 → dialectical_framework-1.6.5}/src/dialectical_framework/graph/nodes/statement.py +0 -0
  134. {dialectical_framework-1.6.4 → dialectical_framework-1.6.5}/src/dialectical_framework/graph/nodes/synthesis.py +0 -0
  135. {dialectical_framework-1.6.4 → dialectical_framework-1.6.5}/src/dialectical_framework/graph/nodes/transformation.py +0 -0
  136. {dialectical_framework-1.6.4 → dialectical_framework-1.6.5}/src/dialectical_framework/graph/nodes/transition.py +0 -0
  137. {dialectical_framework-1.6.4 → dialectical_framework-1.6.5}/src/dialectical_framework/graph/nodes/wheel.py +0 -0
  138. {dialectical_framework-1.6.4 → dialectical_framework-1.6.5}/src/dialectical_framework/graph/relationship_manager.py +0 -0
  139. {dialectical_framework-1.6.4 → dialectical_framework-1.6.5}/src/dialectical_framework/graph/relationships/__init__.py +0 -0
  140. {dialectical_framework-1.6.4 → dialectical_framework-1.6.5}/src/dialectical_framework/graph/relationships/action_reflection_relationship.py +0 -0
  141. {dialectical_framework-1.6.4 → dialectical_framework-1.6.5}/src/dialectical_framework/graph/relationships/belongs_to_cycle_relationship.py +0 -0
  142. {dialectical_framework-1.6.4 → dialectical_framework-1.6.5}/src/dialectical_framework/graph/relationships/belongs_to_nexus_relationship.py +0 -0
  143. {dialectical_framework-1.6.4 → dialectical_framework-1.6.5}/src/dialectical_framework/graph/relationships/changed_to_relationship.py +0 -0
  144. {dialectical_framework-1.6.4 → dialectical_framework-1.6.5}/src/dialectical_framework/graph/relationships/contradiction_of_relationship.py +0 -0
  145. {dialectical_framework-1.6.4 → dialectical_framework-1.6.5}/src/dialectical_framework/graph/relationships/critiques_relationship.py +0 -0
  146. {dialectical_framework-1.6.4 → dialectical_framework-1.6.5}/src/dialectical_framework/graph/relationships/distilled_to_relationship.py +0 -0
  147. {dialectical_framework-1.6.4 → dialectical_framework-1.6.5}/src/dialectical_framework/graph/relationships/estimates_relationship.py +0 -0
  148. {dialectical_framework-1.6.4 → dialectical_framework-1.6.5}/src/dialectical_framework/graph/relationships/explains_relationship.py +0 -0
  149. {dialectical_framework-1.6.4 → dialectical_framework-1.6.5}/src/dialectical_framework/graph/relationships/has_input_relationship.py +0 -0
  150. {dialectical_framework-1.6.4 → dialectical_framework-1.6.5}/src/dialectical_framework/graph/relationships/has_statement_relationship.py +0 -0
  151. {dialectical_framework-1.6.4 → dialectical_framework-1.6.5}/src/dialectical_framework/graph/relationships/has_wheel_relationship.py +0 -0
  152. {dialectical_framework-1.6.4 → dialectical_framework-1.6.5}/src/dialectical_framework/graph/relationships/immutable_structure.py +0 -0
  153. {dialectical_framework-1.6.4 → dialectical_framework-1.6.5}/src/dialectical_framework/graph/relationships/is_source_of_relationship.py +0 -0
  154. {dialectical_framework-1.6.4 → dialectical_framework-1.6.5}/src/dialectical_framework/graph/relationships/is_target_of_relationship.py +0 -0
  155. {dialectical_framework-1.6.4 → dialectical_framework-1.6.5}/src/dialectical_framework/graph/relationships/negative_side_of_relationship.py +0 -0
  156. {dialectical_framework-1.6.4 → dialectical_framework-1.6.5}/src/dialectical_framework/graph/relationships/opposite_direction_relationship.py +0 -0
  157. {dialectical_framework-1.6.4 → dialectical_framework-1.6.5}/src/dialectical_framework/graph/relationships/opposite_of_relationship.py +0 -0
  158. {dialectical_framework-1.6.4 → dialectical_framework-1.6.5}/src/dialectical_framework/graph/relationships/polarity_relationship.py +0 -0
  159. {dialectical_framework-1.6.4 → dialectical_framework-1.6.5}/src/dialectical_framework/graph/relationships/positive_side_of_relationship.py +0 -0
  160. {dialectical_framework-1.6.4 → dialectical_framework-1.6.5}/src/dialectical_framework/graph/relationships/provides_relationship.py +0 -0
  161. {dialectical_framework-1.6.4 → dialectical_framework-1.6.5}/src/dialectical_framework/graph/relationships/synthesis_of_relationship.py +0 -0
  162. {dialectical_framework-1.6.4 → dialectical_framework-1.6.5}/src/dialectical_framework/graph/rendering.py +0 -0
  163. {dialectical_framework-1.6.4 → dialectical_framework-1.6.5}/src/dialectical_framework/graph/repositories/__init__.py +0 -0
  164. {dialectical_framework-1.6.4 → dialectical_framework-1.6.5}/src/dialectical_framework/graph/repositories/case_repository.py +0 -0
  165. {dialectical_framework-1.6.4 → dialectical_framework-1.6.5}/src/dialectical_framework/graph/repositories/cycle_repository.py +0 -0
  166. {dialectical_framework-1.6.4 → dialectical_framework-1.6.5}/src/dialectical_framework/graph/repositories/input_repository.py +0 -0
  167. {dialectical_framework-1.6.4 → dialectical_framework-1.6.5}/src/dialectical_framework/graph/repositories/nexus_repository.py +0 -0
  168. {dialectical_framework-1.6.4 → dialectical_framework-1.6.5}/src/dialectical_framework/graph/repositories/node_repository.py +0 -0
  169. {dialectical_framework-1.6.4 → dialectical_framework-1.6.5}/src/dialectical_framework/graph/repositories/perspective_repository.py +0 -0
  170. {dialectical_framework-1.6.4 → dialectical_framework-1.6.5}/src/dialectical_framework/graph/repositories/polarity_repository.py +0 -0
  171. {dialectical_framework-1.6.4 → dialectical_framework-1.6.5}/src/dialectical_framework/graph/repositories/schema_repository.py +0 -0
  172. {dialectical_framework-1.6.4 → dialectical_framework-1.6.5}/src/dialectical_framework/graph/repositories/statement_repository.py +0 -0
  173. {dialectical_framework-1.6.4 → dialectical_framework-1.6.5}/src/dialectical_framework/graph/repositories/transformation_repository.py +0 -0
  174. {dialectical_framework-1.6.4 → dialectical_framework-1.6.5}/src/dialectical_framework/graph/repositories/wheel_repository.py +0 -0
  175. {dialectical_framework-1.6.4 → dialectical_framework-1.6.5}/src/dialectical_framework/graph/scope_context.py +0 -0
  176. {dialectical_framework-1.6.4 → dialectical_framework-1.6.5}/src/dialectical_framework/graph/wheel_segment.py +0 -0
  177. {dialectical_framework-1.6.4 → dialectical_framework-1.6.5}/src/dialectical_framework/graph/wheel_segment_polar_pair.py +0 -0
  178. {dialectical_framework-1.6.4 → dialectical_framework-1.6.5}/src/dialectical_framework/protocols/__init__.py +0 -0
  179. {dialectical_framework-1.6.4 → dialectical_framework-1.6.5}/src/dialectical_framework/protocols/has_config.py +0 -0
  180. {dialectical_framework-1.6.4 → dialectical_framework-1.6.5}/src/dialectical_framework/settings.py +0 -0
  181. {dialectical_framework-1.6.4 → dialectical_framework-1.6.5}/src/dialectical_framework/utils/__init__.py +0 -0
  182. {dialectical_framework-1.6.4 → dialectical_framework-1.6.5}/src/dialectical_framework/utils/bedrock_provider.py +0 -0
  183. {dialectical_framework-1.6.4 → dialectical_framework-1.6.5}/src/dialectical_framework/utils/concurrency.py +0 -0
  184. {dialectical_framework-1.6.4 → dialectical_framework-1.6.5}/src/dialectical_framework/utils/dc_replace.py +0 -0
  185. {dialectical_framework-1.6.4 → dialectical_framework-1.6.5}/src/dialectical_framework/utils/decompose_probability_uniformly.py +0 -0
  186. {dialectical_framework-1.6.4 → dialectical_framework-1.6.5}/src/dialectical_framework/utils/edge_context.py +0 -0
  187. {dialectical_framework-1.6.4 → dialectical_framework-1.6.5}/src/dialectical_framework/utils/effect_logger.py +0 -0
  188. {dialectical_framework-1.6.4 → dialectical_framework-1.6.5}/src/dialectical_framework/utils/input_context.py +0 -0
  189. {dialectical_framework-1.6.4 → dialectical_framework-1.6.5}/src/dialectical_framework/utils/order_transitions.py +0 -0
  190. {dialectical_framework-1.6.4 → dialectical_framework-1.6.5}/src/dialectical_framework/utils/sequence_generation.py +0 -0
  191. {dialectical_framework-1.6.4 → dialectical_framework-1.6.5}/src/dialectical_framework/utils/use_brain.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: dialectical-framework
3
- Version: 1.6.4
3
+ Version: 1.6.5
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.6.4"
3
+ version = "1.6.5"
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"
@@ -53,7 +53,7 @@ These words map to DIFFERENT tools depending on what's in scope:
53
53
 
54
54
  ## How to Work
55
55
 
56
- Act on clear intent for analysis actions (analyze, find/expand, anchor) — never ask "shall I analyze this?", just do it. Nexus creation is the one exception: confirm exploration direction first (see Exploration Setup).
56
+ Act on clear intent for analysis actions (analyze, find/expand, anchor) — never ask "shall I analyze this?", just do it. Nexus creation follows the same rule: create immediately when the perspectives to group are clear. Only pause when the request is genuinely underspecified or a similar exploration already exists (see Exploration Setup).
57
57
  Always check resonance AFTER presenting results — but never before acting.
58
58
 
59
59
  - If the user describes a situation: call `analyze` or `introduce_polarity` depending on whether a clear tension is stated
@@ -106,13 +106,21 @@ pull"), not raw numbers, unless the app preamble or the user asks for the figure
106
106
 
107
107
  ## Exploration Setup (Nexus)
108
108
 
109
+ "Nexus" is the internal name for what the user calls an **Exploration** — same
110
+ thing. Use "nexus" freely in your own reasoning and when referring to the
111
+ `create_nexus`/`expand_nexus` tools, but always say "exploration" in anything
112
+ the user reads.
113
+
109
114
  When the user wants to explore interactions between perspectives:
110
115
 
111
116
  **Before creating:**
112
117
  1. Check existing nexuses via `present_analysis`.
113
118
  2. If a nexus with similar intent exists — suggest `expand_nexus` to add perspectives to it.
114
119
  Only create separate if user deliberately wants a different scope.
115
- 3. Confirm the exploration direction with the user before creating.
120
+ 3. Create immediately when the perspectives to group are clear (named or unambiguous
121
+ from context). Only pause to ask when the request is genuinely underspecified —
122
+ no perspectives identifiable — or when Deduplication below applies. Do NOT ask the
123
+ user to state an exploration intent: infer it internally (see Intent below).
116
124
 
117
125
  **Grouping (which perspectives to combine):**
118
126
  - Prefer perspectives from different polarities (different T-A oppositions). Transformations
@@ -130,7 +138,7 @@ When the user wants to explore interactions between perspectives:
130
138
  - Derive a concise title (1-3 words) from the intent silently. No confirmation needed.
131
139
 
132
140
  **Deduplication (always communicate to the user):**
133
- - If a nexus with similar intent exists, tell the user: name the existing nexus, show its intent,
141
+ - If a nexus with similar intent exists, tell the user: name the existing exploration, show its intent,
134
142
  and ask whether to add perspectives there or create a separate exploration.
135
143
  - Never silently redirect to `expand_nexus` — the user must see why and agree.
136
144
  - Only skip the prompt if the user explicitly names the existing nexus themselves.
@@ -301,8 +301,9 @@ both angles and both blindspots together. A complete map of one tension.
301
301
 
302
302
  ### Exploration Phase
303
303
 
304
- **Nexus** — the exploration space where positions interact. Use: "this
305
- exploration", "the nexus", "this group of positions".
304
+ **Nexus** — the exploration space where positions interact. Internal term;
305
+ say **"exploration"** to the user, never "nexus". Use: "this exploration",
306
+ "the exploration", "this group of positions".
306
307
 
307
308
  **Wheel** — causal arrangement of positions into a cycle. Use: "wheel",
308
309
  "causal arrangement", "how these positions connect causally".
@@ -369,8 +370,11 @@ The "blindspot" label belongs ONLY to A+ and A- (the opposition's territory).
369
370
  T+ and T- are the holder's own visible territory — never label them as blindspots.
370
371
 
371
372
  - Never mention tool names, pipelines, or graph operations.
372
- - User-facing structural terms (Wheel, Nexus, Cycle, Transformation, Position,
373
+ - User-facing structural terms (Wheel, Cycle, Transformation, Position,
373
374
  Polarity) are fine to use — these are part of the UX vocabulary.
375
+ - "Nexus" is an internal name for what the user calls an **Exploration** —
376
+ they are the same thing. Always say "exploration" to the user; never surface
377
+ the word "Nexus" (some power users know it, but default to "exploration").
374
378
  - Position labels (T+, T-, A+, A-) can be introduced once the user understands
375
379
  the structure, or when they make presentation clearer. Don't hide them
376
380
  dogmatically — use them when they serve communication.
@@ -33,6 +33,7 @@ from dialectical_framework.utils.use_brain import use_brain
33
33
  from mirascope.llm import TextChunk, ThoughtChunk
34
34
 
35
35
  if TYPE_CHECKING:
36
+ from mirascope.llm import UserContent
36
37
  from mirascope.llm.calls import AsyncCall
37
38
  from mirascope.llm.responses import AsyncResponse
38
39
 
@@ -116,7 +117,7 @@ class ConversationFacilitator(SettingsAware):
116
117
  async def submit(
117
118
  self,
118
119
  response_model: type[T],
119
- user_content: str,
120
+ user_content: UserContent,
120
121
  max_tool_rounds: int = 10,
121
122
  ) -> T:
122
123
  """
@@ -162,7 +163,7 @@ class ConversationFacilitator(SettingsAware):
162
163
  async def submit_stream(
163
164
  self,
164
165
  response_model: type[T],
165
- user_content: str,
166
+ user_content: UserContent,
166
167
  max_tool_rounds: int = 10,
167
168
  ) -> AsyncGenerator[StreamEvent, None]:
168
169
  """
@@ -100,17 +100,17 @@ Your task is to generate aspects (T+, T-, A+, A-) for a thesis-antithesis pair.
100
100
 
101
101
  ## Examples
102
102
 
103
+ Each tetrad resolves into two diagonal contradiction pairs. A pair shares one
104
+ **axis** — a single dimension on which its two aspects sit at opposite ends, so
105
+ they cannot both hold at once. Name the axis first, then place each pole on it.
106
+
103
107
  T = Love, A = Indifference:
104
- - T+ = Bonding (love that also makes room for the other's autonomy)
105
- - T- = Enmeshment (love overdeveloped, smothering autonomy)
106
- - A+ = Autonomy (independence that also deepens the bond)
107
- - A- = Alienation (independence overdeveloped, severing the bond)
108
+ - Axis "closeness": T+ = Bonding (love that makes room for autonomy) ⟷ A- = Alienation (independence severing the bond)
109
+ - Axis "self-standing": A+ = Autonomy (independence that deepens the bond) ⟷ T- = Enmeshment (love smothering autonomy)
108
110
 
109
111
  T = Courage, A = Fear:
110
- - T+ = Trust
111
- - T- = Foolhardiness
112
- - A+ = Prudence
113
- - A- = Paranoia
112
+ - Axis "risk-taking": T+ = Trust ⟷ A- = Paranoia
113
+ - Axis "caution": A+ = Prudence ⟷ T- = Foolhardiness
114
114
 
115
115
  Generate aspect statements that fit the semantic structure."""
116
116
 
@@ -139,19 +139,56 @@ class AspectDto(BaseModel):
139
139
 
140
140
 
141
141
  class ContradictionPairDto(BaseModel):
142
- """Two aspects that form a contradiction pair."""
142
+ """Two aspects that form a diagonal contradiction pair.
143
+
144
+ The pair is defined by its `axis`: a single dimension on which the two
145
+ aspects sit at opposite ends, so they cannot both hold at once. The model
146
+ must name the axis first, which forces a genuine opposition rather than two
147
+ independently-plausible aspects.
148
+ """
143
149
 
150
+ axis: str = Field(
151
+ description=(
152
+ "The single dimension on which the two aspects are opposite ends "
153
+ "(e.g. 'closeness', 'risk-taking'). If no such shared dimension "
154
+ "exists, the pair is not a genuine contradiction — say so here."
155
+ )
156
+ )
144
157
  positive_aspect: AspectDto = Field(description="The positive aspect (T+ or A+)")
145
158
  negative_aspect: AspectDto = Field(description="The negative aspect (A- or T-)")
146
159
 
147
160
 
148
161
  class TetradDto(BaseModel):
149
- """Full tetrad of four aspects."""
162
+ """Full tetrad: four aspects grouped by the two axes they oppose along.
150
163
 
151
- t_plus: AspectDto = Field(description="T+ - constructive thesis")
152
- t_minus: AspectDto = Field(description="T- - exaggerated thesis")
153
- a_plus: AspectDto = Field(description="A+ - constructive antithesis")
154
- a_minus: AspectDto = Field(description="A- - exaggerated antithesis")
164
+ The four aspects stay as top-level fields (deep nesting made the model drop
165
+ a whole diagonal branch), but each diagonal pair leads with an explicit
166
+ `axis` field — the shared dimension its two poles sit at opposite ends of.
167
+ Naming the axis forces genuine diagonal contradiction into the output
168
+ instead of leaving it to a trailing constraint.
169
+ """
170
+
171
+ # --- Pair 1: T+ vs A- (thesis-constructive axis) ---
172
+ t_plus_vs_a_minus_axis: str = Field(
173
+ description=(
174
+ "The single dimension on which T+ and A- are opposite ends "
175
+ "(e.g. 'closeness'). If T and A share no such dimension, the pair "
176
+ "is not a genuine contradiction — say so here."
177
+ )
178
+ )
179
+ t_plus: AspectDto = Field(description="T+ - constructive thesis (positive end of the axis)")
180
+ a_minus: AspectDto = Field(description="A- - exaggerated antithesis (negative end of the axis)")
181
+
182
+ # --- Pair 2: A+ vs T- (antithesis-constructive axis) ---
183
+ a_plus_vs_t_minus_axis: str = Field(
184
+ description=(
185
+ "The single dimension on which A+ and T- are opposite ends. If A "
186
+ "and T share no such dimension, say so here rather than inventing "
187
+ "two unrelated aspects."
188
+ )
189
+ )
190
+ a_plus: AspectDto = Field(description="A+ - constructive antithesis (positive end of the axis)")
191
+ t_minus: AspectDto = Field(description="T- - exaggerated thesis (negative end of the axis)")
155
192
 
156
193
 
157
194
  # --- Result ---
@@ -178,6 +215,17 @@ class AspectGeneration(ReasonableConcern[list[AspectResult]], SettingsAware):
178
215
 
179
216
  Generates aspects with HS calculated against taxonomy apex and K values.
180
217
  Contradiction pairs are generated together to ensure coherence.
218
+
219
+ Design decision — diagonal contradiction is enforced by the PROMPT here, not
220
+ by a post-hoc scorer. `TetradDto` makes each diagonal pair name the `axis`
221
+ it opposes along, which forces genuine contradiction into the output. We do
222
+ NOT run `DiagonalOppositionsCheck` on this path: generation is latency- and
223
+ cost-sensitive (it fans out across polarities × aspects), and the scorer
224
+ would add 2 LLM calls per perspective. `DiagonalOppositionsCheck` exists as
225
+ a safety net only where the prompt is bypassed — user edits in
226
+ `edit_perspective._validate_tetrad_coherence`. If a T/A pair is genuinely
227
+ not an opposition, that is caught upstream at polarity formation
228
+ (AntitheticalThesisDetection / the HS gate), not here.
181
229
  """
182
230
 
183
231
  def __init__(self) -> None:
@@ -325,9 +373,9 @@ class AspectGeneration(ReasonableConcern[list[AspectResult]], SettingsAware):
325
373
  results = []
326
374
  position_to_dto = {
327
375
  POSITION_T_PLUS: result.t_plus,
328
- POSITION_T_MINUS: result.t_minus,
329
- POSITION_A_PLUS: result.a_plus,
330
376
  POSITION_A_MINUS: result.a_minus,
377
+ POSITION_A_PLUS: result.a_plus,
378
+ POSITION_T_MINUS: result.t_minus,
331
379
  }
332
380
 
333
381
  for pos in positions:
@@ -535,13 +583,17 @@ Taxonomy apex concepts for reference:
535
583
  - A+ apex: {a_plus_apex}
536
584
  - A- apex: {a_minus_apex}
537
585
  {existing_section}{avoid_section}
586
+ Build the tetrad as its two diagonal contradiction pairs (T+ vs A-, A+ vs T-).
587
+ For each pair:
588
+ 1. First name the **axis** — the single dimension on which the two aspects are opposite ends, so they cannot both hold at once.
589
+ 2. Then place each aspect at an opposite end of that axis.
590
+ If T and A do not admit a genuine shared axis of opposition, say so in the pair's `axis` field rather than inventing two unrelated aspects.
591
+
538
592
  Generate each aspect (1-{max_words} words) with:
539
593
 
540
594
  {HS_SCALE}
541
595
 
542
- {COMPLEMENTARITY_SCALE}
543
-
544
- Ensure T+ contradicts A-, and A+ contradicts T-."""
596
+ {COMPLEMENTARITY_SCALE}"""
545
597
 
546
598
  def _contradiction_pair_prompt(
547
599
  self,
@@ -590,7 +642,7 @@ Generate each aspect (1-{max_words} words) with:
590
642
  {COMPLEMENTARITY_SCALE}
591
643
 
592
644
  The positive_aspect is {positive_pos}, the negative_aspect is {negative_pos}.
593
- They must contradict each other - they cannot both be true/good simultaneously."""
645
+ First name the **axis** — the single dimension on which they are opposite ends — then place each at an opposite end of it. They cannot both hold at once. If no such shared axis exists, say so in the `axis` field rather than inventing two unrelated aspects."""
594
646
 
595
647
  def _single_aspect_prompt(self, position: str, existing_context: str) -> str:
596
648
  """Build prompt for single aspect generation."""
@@ -19,12 +19,15 @@ Programmatic usage:
19
19
 
20
20
  from __future__ import annotations
21
21
 
22
- from typing import Union
22
+ from typing import TYPE_CHECKING, Union
23
23
 
24
24
  from dependency_injector.wiring import Provide, inject
25
25
  from gqlalchemy import Memgraph, Neo4j
26
26
  from pydantic import BaseModel, Field
27
27
 
28
+ if TYPE_CHECKING:
29
+ from mirascope.llm import UserContent
30
+
28
31
  from dialectical_framework.agents.conversation_facilitator import \
29
32
  ConversationFacilitator
30
33
  from dialectical_framework.agents.reasonable_concern import ReasonableConcern
@@ -96,11 +99,20 @@ class SourceDigest(ReasonableConcern[Input], SettingsAware):
96
99
  if not input_node:
97
100
  raise ValueError(f"Input not found: {input_hash}")
98
101
 
99
- resolved_content = await input_resolver.resolve(input_node)
102
+ resolved_content = await input_resolver.resolve_native(input_node)
100
103
  if not resolved_content:
101
104
  raise ValueError(f"Input {input_hash} has no resolvable content")
102
105
 
103
- if len(resolved_content) <= DIGEST_THRESHOLD and not input_node.digest:
106
+ is_text = isinstance(resolved_content, str)
107
+
108
+ # Only text can be used verbatim as its own digest. Native media
109
+ # (image/PDF parts) must always go through the model's vision pass —
110
+ # there is no meaningful "raw content" to store as a digest.
111
+ if (
112
+ is_text
113
+ and len(resolved_content) <= DIGEST_THRESHOLD
114
+ and not input_node.digest
115
+ ):
104
116
  new_digest = resolved_content
105
117
  self._report.summary = f"Content compact enough to use as digest for input {input_node.short_hash}"
106
118
  else:
@@ -122,7 +134,7 @@ class SourceDigest(ReasonableConcern[Input], SettingsAware):
122
134
 
123
135
  async def _generate_digest(
124
136
  self,
125
- content: str,
137
+ content: UserContent,
126
138
  existing_digest: str | None = None,
127
139
  context: str = "",
128
140
  ) -> str:
@@ -140,17 +152,23 @@ class SourceDigest(ReasonableConcern[Input], SettingsAware):
140
152
 
141
153
  def _build_prompt(
142
154
  self,
143
- content: str,
155
+ content: UserContent,
144
156
  existing_digest: str | None,
145
157
  context: str,
146
- ) -> str:
158
+ ) -> UserContent:
159
+ # Text sources are interpolated inline; native media (image/PDF parts)
160
+ # are appended as a separate content part with a text placeholder in the
161
+ # <source> slot so the model knows where the attached source belongs.
162
+ is_text = isinstance(content, str)
163
+ source_text = content if is_text else "(see the attached source below)"
164
+
147
165
  sections = []
148
166
 
149
167
  if existing_digest:
150
168
  sections.append(f"<existing_digest>\n{existing_digest}\n</existing_digest>")
151
169
  if context:
152
170
  sections.append(f"<context>\n{context}\n</context>")
153
- sections.append(f"<source>\n{content}\n</source>")
171
+ sections.append(f"<source>\n{source_text}\n</source>")
154
172
  sections.append(
155
173
  "Refine the existing digest incorporating the context provided. "
156
174
  "Sharpen focus, add relevant details, remove irrelevant parts."
@@ -158,9 +176,20 @@ class SourceDigest(ReasonableConcern[Input], SettingsAware):
158
176
  else:
159
177
  if context:
160
178
  sections.append(f"<context>\n{context}\n</context>")
161
- sections.append(f"<source>\n{content}\n</source>")
179
+ sections.append(f"<source>\n{source_text}\n</source>")
162
180
  sections.append(
163
181
  "Generate an initial analytical digest of this source."
164
182
  )
165
183
 
166
- return "\n\n".join(sections)
184
+ prompt_text = "\n\n".join(sections)
185
+
186
+ if is_text:
187
+ return prompt_text
188
+
189
+ # Multimodal: text instructions followed by the native source part(s).
190
+ parts: list = [prompt_text]
191
+ if isinstance(content, (list, tuple)):
192
+ parts.extend(content)
193
+ else:
194
+ parts.append(content)
195
+ return parts
@@ -17,6 +17,8 @@ from dialectical_framework.graph.verbatim_input_resolver import VerbatimInputRes
17
17
  from dialectical_framework.protocols.input_resolver import InputResolver
18
18
 
19
19
  if TYPE_CHECKING:
20
+ from mirascope.llm import UserContent
21
+
20
22
  from dialectical_framework.graph.nodes.case import Case
21
23
  from dialectical_framework.graph.nodes.input import Input
22
24
 
@@ -88,6 +90,30 @@ class CompositeInputResolver(InputResolver):
88
90
  # VerbatimInputResolver handles plain text and data: URIs
89
91
  return await self._verbatim.resolve(input_node)
90
92
 
93
+ async def resolve_native(self, input_node: Input) -> UserContent:
94
+ """
95
+ Resolve an Input to native model content, preserving image/PDF modality.
96
+
97
+ Delegates data: and plain-text content to the VerbatimInputResolver's
98
+ native path (which emits Image/Document parts for supported media). dx://
99
+ references resolve to graph-node text, so they stay text.
100
+
101
+ Args:
102
+ input_node: Input node with content to resolve.
103
+
104
+ Returns:
105
+ `UserContent` — a media part for supported binary data: URIs,
106
+ otherwise the resolved text `str`.
107
+ """
108
+ content = input_node.content
109
+ if not content:
110
+ return ""
111
+
112
+ if content.startswith("dx://"):
113
+ return await self._dialexity.resolve(content)
114
+
115
+ return await self._verbatim.resolve_native(input_node)
116
+
91
117
  async def resolve_all(self, source: Union[Case, list[Input]]) -> str:
92
118
  """
93
119
  Resolve multiple inputs to combined text content.
@@ -14,9 +14,18 @@ from urllib.parse import unquote
14
14
  from dialectical_framework.protocols.input_resolver import InputResolver
15
15
 
16
16
  if TYPE_CHECKING:
17
+ from mirascope.llm import UserContent
18
+
17
19
  from dialectical_framework.graph.nodes.case import Case
18
20
  from dialectical_framework.graph.nodes.input import Input
19
21
 
22
+ # Mime types Mirascope accepts as native image/document parts. Anything else
23
+ # (text, unknown binary) falls back to text resolution.
24
+ _IMAGE_MIME_TYPES = frozenset(
25
+ {"image/png", "image/jpeg", "image/webp", "image/gif", "image/heic", "image/heif"}
26
+ )
27
+ _DOCUMENT_MIME_TYPES = frozenset({"application/pdf"})
28
+
20
29
 
21
30
  class VerbatimInputResolver(InputResolver):
22
31
  """
@@ -68,6 +77,31 @@ class VerbatimInputResolver(InputResolver):
68
77
  # Otherwise, treat as plain text
69
78
  return content
70
79
 
80
+ async def resolve_native(self, input_node: Input) -> UserContent:
81
+ """
82
+ Resolve content to native model content, preserving image/PDF modality.
83
+
84
+ For a `data:` URI whose mime type is a supported image or PDF format,
85
+ returns a Mirascope `Image` / `Document` part carrying the base64 bytes,
86
+ so the model reads the source natively instead of a transcription. All
87
+ other content (text, `data:text/*`, non-base64, unsupported binary)
88
+ falls back to `resolve()` text.
89
+
90
+ Args:
91
+ input_node: Input node with content (plain text or data: URI).
92
+
93
+ Returns:
94
+ A multimodal part (`Image`/`Document`) for supported binary media,
95
+ otherwise the resolved text `str`.
96
+ """
97
+ content = input_node.content
98
+ if content and content.startswith("data:"):
99
+ part = self._decode_data_uri_native(content)
100
+ if part is not None:
101
+ return part
102
+
103
+ return await self.resolve(input_node)
104
+
71
105
  async def resolve_all(self, source: Union[Case, list[Input]]) -> str:
72
106
  """
73
107
  Resolve multiple inputs to combined text content.
@@ -106,6 +140,47 @@ class VerbatimInputResolver(InputResolver):
106
140
 
107
141
  return "\n\n".join(parts)
108
142
 
143
+ @staticmethod
144
+ def _decode_data_uri_native(uri: str) -> UserContent | None:
145
+ """
146
+ Build a native image/document part from a data: URI, if it is one.
147
+
148
+ Returns an `Image`/`Document` for a base64 data: URI whose mime type is a
149
+ supported image or PDF format. Returns None for anything that should be
150
+ handled as text (text mime types, non-base64 payloads, unsupported types),
151
+ letting the caller fall back to text resolution.
152
+ """
153
+ # data:<mime>[;base64],<data>
154
+ content_part = uri[5:]
155
+ if "," not in content_part:
156
+ return None
157
+
158
+ metadata, data = content_part.split(",", 1)
159
+ if ";base64" not in metadata.lower():
160
+ # Text data: URIs (or non-base64 binary) are handled as text.
161
+ return None
162
+
163
+ mime_type = metadata.split(";", 1)[0].strip().lower()
164
+
165
+ from mirascope.llm import (Base64DocumentSource, Base64ImageSource,
166
+ Document, Image)
167
+
168
+ if mime_type in _IMAGE_MIME_TYPES:
169
+ return Image(
170
+ source=Base64ImageSource(
171
+ type="base64_image_source", data=data, mime_type=mime_type
172
+ )
173
+ )
174
+
175
+ if mime_type in _DOCUMENT_MIME_TYPES:
176
+ return Document(
177
+ source=Base64DocumentSource(
178
+ type="base64_document_source", data=data, media_type=mime_type
179
+ )
180
+ )
181
+
182
+ return None
183
+
109
184
  @staticmethod
110
185
  def _decode_data_uri(uri: str) -> str:
111
186
  """Decode a data: URI to text."""
@@ -8,6 +8,8 @@ from abc import ABC, abstractmethod
8
8
  from typing import TYPE_CHECKING
9
9
 
10
10
  if TYPE_CHECKING:
11
+ from mirascope.llm import UserContent
12
+
11
13
  from dialectical_framework.graph.nodes.case import Case
12
14
  from dialectical_framework.graph.nodes.input import Input
13
15
 
@@ -51,6 +53,32 @@ class InputResolver(ABC):
51
53
  """
52
54
  ...
53
55
 
56
+ async def resolve_native(self, input_node: Input) -> UserContent:
57
+ """
58
+ Resolve a single Input to native model content, preserving modality.
59
+
60
+ Unlike `resolve()`, which always flattens to text, this returns Mirascope
61
+ `UserContent` — either a plain `str` for text sources, or multimodal parts
62
+ (`Image`, `Document`, or a list mixing text and such parts) for images/PDFs.
63
+ Callers that pass content straight to the model (e.g. the `SourceDigest`
64
+ concern's vision pass) use this to let the model read the source natively
65
+ rather than a lossy transcription.
66
+
67
+ The default implementation delegates to `resolve()`, so text-only resolvers
68
+ need not override it. Resolvers that back image/PDF sources should override
69
+ this to emit the corresponding `Image`/`Document` parts.
70
+
71
+ Args:
72
+ input_node: Input node with content to resolve.
73
+
74
+ Returns:
75
+ `UserContent` — a `str`, a single multimodal part, or a list of parts.
76
+
77
+ Raises:
78
+ ValueError: If content format is unsupported (implementation-dependent).
79
+ """
80
+ return await self.resolve(input_node)
81
+
54
82
  @abstractmethod
55
83
  async def resolve_all(self, source: Case | list[Input]) -> str:
56
84
  """