dialectical-framework 1.6.7__tar.gz → 1.7.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {dialectical_framework-1.6.7 → dialectical_framework-1.7.0}/PKG-INFO +1 -1
- {dialectical_framework-1.6.7 → dialectical_framework-1.7.0}/pyproject.toml +1 -1
- {dialectical_framework-1.6.7 → dialectical_framework-1.7.0}/src/dialectical_framework/agents/advisor/advisor.py +36 -4
- {dialectical_framework-1.6.7 → dialectical_framework-1.7.0}/src/dialectical_framework/agents/advisor/system_prompts.py +192 -23
- dialectical_framework-1.7.0/src/dialectical_framework/agents/advisor/tools/record_decision.py +62 -0
- {dialectical_framework-1.6.7 → dialectical_framework-1.7.0}/src/dialectical_framework/agents/advisor/tools/scoped.py +14 -7
- {dialectical_framework-1.6.7 → dialectical_framework-1.7.0}/src/dialectical_framework/agents/analyst/analyst.py +17 -2
- {dialectical_framework-1.6.7 → dialectical_framework-1.7.0}/src/dialectical_framework/agents/analyst/skills/edit_perspective.py +4 -0
- {dialectical_framework-1.6.7 → dialectical_framework-1.7.0}/src/dialectical_framework/agents/analyst/skills/expand_polarities.py +27 -0
- {dialectical_framework-1.6.7 → dialectical_framework-1.7.0}/src/dialectical_framework/agents/analyst/skills/introduce_polarity.py +3 -1
- {dialectical_framework-1.6.7 → dialectical_framework-1.7.0}/src/dialectical_framework/agents/analyst/system_prompts.py +10 -0
- dialectical_framework-1.7.0/src/dialectical_framework/agents/app_spec.py +119 -0
- {dialectical_framework-1.6.7 → dialectical_framework-1.7.0}/src/dialectical_framework/agents/apps.py +173 -89
- {dialectical_framework-1.6.7 → dialectical_framework-1.7.0}/src/dialectical_framework/agents/explorer/explorer.py +15 -1
- {dialectical_framework-1.6.7 → dialectical_framework-1.7.0}/src/dialectical_framework/agents/explorer/system_prompts.py +9 -0
- dialectical_framework-1.7.0/src/dialectical_framework/agents/orchestrator/tools/discard.py +23 -0
- {dialectical_framework-1.6.7 → dialectical_framework-1.7.0}/src/dialectical_framework/agents/orchestrator/tools/get_schema.py +3 -1
- {dialectical_framework-1.6.7 → dialectical_framework-1.7.0}/src/dialectical_framework/agents/orchestrator/tools/inspect_node.py +40 -1
- dialectical_framework-1.7.0/src/dialectical_framework/agents/toolsets.py +33 -0
- {dialectical_framework-1.6.7 → dialectical_framework-1.7.0}/src/dialectical_framework/concerns/aspect_generation.py +45 -0
- dialectical_framework-1.7.0/src/dialectical_framework/concerns/decision_coherence_check.py +199 -0
- {dialectical_framework-1.6.7 → dialectical_framework-1.7.0}/src/dialectical_framework/concerns/dialectical_context.py +75 -8
- {dialectical_framework-1.6.7 → dialectical_framework-1.7.0}/src/dialectical_framework/concerns/discard.py +31 -2
- dialectical_framework-1.7.0/src/dialectical_framework/concerns/record_decision.py +242 -0
- {dialectical_framework-1.6.7 → dialectical_framework-1.7.0}/src/dialectical_framework/concerns/statement_classification.py +6 -3
- dialectical_framework-1.7.0/src/dialectical_framework/graph/nodes/decision.py +148 -0
- dialectical_framework-1.7.0/src/dialectical_framework/graph/relationships/grounded_in_relationship.py +31 -0
- {dialectical_framework-1.6.7 → dialectical_framework-1.7.0}/src/dialectical_framework/graph/rendering.py +59 -0
- dialectical_framework-1.7.0/src/dialectical_framework/graph/repositories/decision_repository.py +81 -0
- dialectical_framework-1.6.7/src/dialectical_framework/agents/orchestrator/tools/discard.py +0 -23
- {dialectical_framework-1.6.7 → dialectical_framework-1.7.0}/LICENSE +0 -0
- {dialectical_framework-1.6.7 → dialectical_framework-1.7.0}/PATENTS +0 -0
- {dialectical_framework-1.6.7 → dialectical_framework-1.7.0}/README.md +0 -0
- {dialectical_framework-1.6.7 → dialectical_framework-1.7.0}/src/dialectical_framework/__init__.py +0 -0
- {dialectical_framework-1.6.7 → dialectical_framework-1.7.0}/src/dialectical_framework/agents/__init__.py +0 -0
- {dialectical_framework-1.6.7 → dialectical_framework-1.7.0}/src/dialectical_framework/agents/advisor/__init__.py +0 -0
- {dialectical_framework-1.6.7 → dialectical_framework-1.7.0}/src/dialectical_framework/agents/advisor/tools/__init__.py +0 -0
- {dialectical_framework-1.6.7 → dialectical_framework-1.7.0}/src/dialectical_framework/agents/advisor/tools/anchor.py +0 -0
- {dialectical_framework-1.6.7 → dialectical_framework-1.7.0}/src/dialectical_framework/agents/advisor/tools/deepen.py +0 -0
- {dialectical_framework-1.6.7 → dialectical_framework-1.7.0}/src/dialectical_framework/agents/advisor/tools/explore.py +0 -0
- {dialectical_framework-1.6.7 → dialectical_framework-1.7.0}/src/dialectical_framework/agents/advisor/tools/ingest.py +0 -0
- {dialectical_framework-1.6.7 → dialectical_framework-1.7.0}/src/dialectical_framework/agents/advisor/tools/sync.py +0 -0
- {dialectical_framework-1.6.7 → dialectical_framework-1.7.0}/src/dialectical_framework/agents/agent_context.py +0 -0
- {dialectical_framework-1.6.7 → dialectical_framework-1.7.0}/src/dialectical_framework/agents/analyst/__init__.py +0 -0
- {dialectical_framework-1.6.7 → dialectical_framework-1.7.0}/src/dialectical_framework/agents/analyst/skills/__init__.py +0 -0
- {dialectical_framework-1.6.7 → dialectical_framework-1.7.0}/src/dialectical_framework/agents/analyst/skills/anchor_theses.py +0 -0
- {dialectical_framework-1.6.7 → dialectical_framework-1.7.0}/src/dialectical_framework/agents/analyst/skills/find_polarities.py +0 -0
- {dialectical_framework-1.6.7 → dialectical_framework-1.7.0}/src/dialectical_framework/agents/analyst/skills/surface_theses.py +0 -0
- {dialectical_framework-1.6.7 → dialectical_framework-1.7.0}/src/dialectical_framework/agents/analyst/tools/__init__.py +0 -0
- {dialectical_framework-1.6.7 → dialectical_framework-1.7.0}/src/dialectical_framework/agents/analyst/tools/place_statement.py +0 -0
- {dialectical_framework-1.6.7 → dialectical_framework-1.7.0}/src/dialectical_framework/agents/conversation_facilitator.py +0 -0
- {dialectical_framework-1.6.7 → dialectical_framework-1.7.0}/src/dialectical_framework/agents/execution_report.py +0 -0
- {dialectical_framework-1.6.7 → dialectical_framework-1.7.0}/src/dialectical_framework/agents/explorer/__init__.py +0 -0
- {dialectical_framework-1.6.7 → dialectical_framework-1.7.0}/src/dialectical_framework/agents/explorer/skills/__init__.py +0 -0
- {dialectical_framework-1.6.7 → dialectical_framework-1.7.0}/src/dialectical_framework/agents/explorer/skills/build_wheels.py +0 -0
- {dialectical_framework-1.6.7 → dialectical_framework-1.7.0}/src/dialectical_framework/agents/explorer/skills/explore_transformations.py +0 -0
- {dialectical_framework-1.6.7 → dialectical_framework-1.7.0}/src/dialectical_framework/agents/explorer/skills/generate_synthesis.py +0 -0
- {dialectical_framework-1.6.7 → dialectical_framework-1.7.0}/src/dialectical_framework/agents/explorer/tools/__init__.py +0 -0
- {dialectical_framework-1.6.7 → dialectical_framework-1.7.0}/src/dialectical_framework/agents/explorer/tools/create_nexus.py +0 -0
- {dialectical_framework-1.6.7 → dialectical_framework-1.7.0}/src/dialectical_framework/agents/explorer/tools/expand_nexus.py +0 -0
- {dialectical_framework-1.6.7 → dialectical_framework-1.7.0}/src/dialectical_framework/agents/explorer/tools/generate_synthesis.py +0 -0
- {dialectical_framework-1.6.7 → dialectical_framework-1.7.0}/src/dialectical_framework/agents/explorer/tools/present_exploration.py +0 -0
- {dialectical_framework-1.6.7 → dialectical_framework-1.7.0}/src/dialectical_framework/agents/orchestrator/__init__.py +0 -0
- {dialectical_framework-1.6.7 → dialectical_framework-1.7.0}/src/dialectical_framework/agents/orchestrator/tools/__init__.py +0 -0
- {dialectical_framework-1.6.7 → dialectical_framework-1.7.0}/src/dialectical_framework/agents/orchestrator/tools/add_input.py +0 -0
- {dialectical_framework-1.6.7 → dialectical_framework-1.7.0}/src/dialectical_framework/agents/orchestrator/tools/create_dx_input.py +0 -0
- {dialectical_framework-1.6.7 → dialectical_framework-1.7.0}/src/dialectical_framework/agents/orchestrator/tools/digest_input.py +0 -0
- {dialectical_framework-1.6.7 → dialectical_framework-1.7.0}/src/dialectical_framework/agents/orchestrator/tools/present_analysis.py +0 -0
- {dialectical_framework-1.6.7 → dialectical_framework-1.7.0}/src/dialectical_framework/agents/orchestrator/tools/query_graph.py +0 -0
- {dialectical_framework-1.6.7 → dialectical_framework-1.7.0}/src/dialectical_framework/agents/orchestrator/tools/read_digest.py +0 -0
- {dialectical_framework-1.6.7 → dialectical_framework-1.7.0}/src/dialectical_framework/agents/orchestrator/tools/read_input.py +0 -0
- {dialectical_framework-1.6.7 → dialectical_framework-1.7.0}/src/dialectical_framework/agents/reasonable_concern.py +0 -0
- {dialectical_framework-1.6.7 → dialectical_framework-1.7.0}/src/dialectical_framework/agents/stream_events.py +0 -0
- {dialectical_framework-1.6.7 → dialectical_framework-1.7.0}/src/dialectical_framework/concerns/__init__.py +0 -0
- {dialectical_framework-1.6.7 → dialectical_framework-1.7.0}/src/dialectical_framework/concerns/ac_re_taxonomy.py +0 -0
- {dialectical_framework-1.6.7 → dialectical_framework-1.7.0}/src/dialectical_framework/concerns/action_extraction.py +0 -0
- {dialectical_framework-1.6.7 → dialectical_framework-1.7.0}/src/dialectical_framework/concerns/add_input.py +0 -0
- {dialectical_framework-1.6.7 → dialectical_framework-1.7.0}/src/dialectical_framework/concerns/ai_dto/__init__.py +0 -0
- {dialectical_framework-1.6.7 → dialectical_framework-1.7.0}/src/dialectical_framework/concerns/ai_dto/statement_dto.py +0 -0
- {dialectical_framework-1.6.7 → dialectical_framework-1.7.0}/src/dialectical_framework/concerns/ai_dto/statements_deck_dto.py +0 -0
- {dialectical_framework-1.6.7 → dialectical_framework-1.7.0}/src/dialectical_framework/concerns/antithesis_classification.py +0 -0
- {dialectical_framework-1.6.7 → dialectical_framework-1.7.0}/src/dialectical_framework/concerns/antithesis_extraction.py +0 -0
- {dialectical_framework-1.6.7 → dialectical_framework-1.7.0}/src/dialectical_framework/concerns/antithetical_thesis_detection.py +0 -0
- {dialectical_framework-1.6.7 → dialectical_framework-1.7.0}/src/dialectical_framework/concerns/aspect_classification.py +0 -0
- {dialectical_framework-1.6.7 → dialectical_framework-1.7.0}/src/dialectical_framework/concerns/causality/__init__.py +0 -0
- {dialectical_framework-1.6.7 → dialectical_framework-1.7.0}/src/dialectical_framework/concerns/causality/causality_estimator.py +0 -0
- {dialectical_framework-1.6.7 → dialectical_framework-1.7.0}/src/dialectical_framework/concerns/causality/causality_estimator_balanced.py +0 -0
- {dialectical_framework-1.6.7 → dialectical_framework-1.7.0}/src/dialectical_framework/concerns/causality/causality_estimator_criteria.py +0 -0
- {dialectical_framework-1.6.7 → dialectical_framework-1.7.0}/src/dialectical_framework/concerns/causality/causality_estimator_desirable.py +0 -0
- {dialectical_framework-1.6.7 → dialectical_framework-1.7.0}/src/dialectical_framework/concerns/causality/causality_estimator_feasible.py +0 -0
- {dialectical_framework-1.6.7 → dialectical_framework-1.7.0}/src/dialectical_framework/concerns/causality/causality_estimator_realistic.py +0 -0
- {dialectical_framework-1.6.7 → dialectical_framework-1.7.0}/src/dialectical_framework/concerns/causality/causality_normalizer.py +0 -0
- {dialectical_framework-1.6.7 → dialectical_framework-1.7.0}/src/dialectical_framework/concerns/causality/estimator_resolver.py +0 -0
- {dialectical_framework-1.6.7 → dialectical_framework-1.7.0}/src/dialectical_framework/concerns/causality_estimation.py +0 -0
- {dialectical_framework-1.6.7 → dialectical_framework-1.7.0}/src/dialectical_framework/concerns/control_statements_check.py +0 -0
- {dialectical_framework-1.6.7 → dialectical_framework-1.7.0}/src/dialectical_framework/concerns/create_dx_input.py +0 -0
- {dialectical_framework-1.6.7 → dialectical_framework-1.7.0}/src/dialectical_framework/concerns/create_nexus.py +0 -0
- {dialectical_framework-1.6.7 → dialectical_framework-1.7.0}/src/dialectical_framework/concerns/diagonal_oppositions_check.py +0 -0
- {dialectical_framework-1.6.7 → dialectical_framework-1.7.0}/src/dialectical_framework/concerns/display_text_edit.py +0 -0
- {dialectical_framework-1.6.7 → dialectical_framework-1.7.0}/src/dialectical_framework/concerns/expand_nexus.py +0 -0
- {dialectical_framework-1.6.7 → dialectical_framework-1.7.0}/src/dialectical_framework/concerns/perspective_combination.py +0 -0
- {dialectical_framework-1.6.7 → dialectical_framework-1.7.0}/src/dialectical_framework/concerns/perspective_validation.py +0 -0
- {dialectical_framework-1.6.7 → dialectical_framework-1.7.0}/src/dialectical_framework/concerns/positive_ac_re_apex_derivation.py +0 -0
- {dialectical_framework-1.6.7 → dialectical_framework-1.7.0}/src/dialectical_framework/concerns/scoring_scales.py +0 -0
- {dialectical_framework-1.6.7 → dialectical_framework-1.7.0}/src/dialectical_framework/concerns/source_digest.py +0 -0
- {dialectical_framework-1.6.7 → dialectical_framework-1.7.0}/src/dialectical_framework/concerns/statement_deduplication.py +0 -0
- {dialectical_framework-1.6.7 → dialectical_framework-1.7.0}/src/dialectical_framework/concerns/statement_headline.py +0 -0
- {dialectical_framework-1.6.7 → dialectical_framework-1.7.0}/src/dialectical_framework/concerns/statement_placement.py +0 -0
- {dialectical_framework-1.6.7 → dialectical_framework-1.7.0}/src/dialectical_framework/concerns/synthesis_generation.py +0 -0
- {dialectical_framework-1.6.7 → dialectical_framework-1.7.0}/src/dialectical_framework/concerns/thesis_extraction.py +0 -0
- {dialectical_framework-1.6.7 → dialectical_framework-1.7.0}/src/dialectical_framework/concerns/transformation_audit.py +0 -0
- {dialectical_framework-1.6.7 → dialectical_framework-1.7.0}/src/dialectical_framework/concerns/transformation_generation.py +0 -0
- {dialectical_framework-1.6.7 → dialectical_framework-1.7.0}/src/dialectical_framework/dialectical_reasoning.py +0 -0
- {dialectical_framework-1.6.7 → dialectical_framework-1.7.0}/src/dialectical_framework/enums/__init__.py +0 -0
- {dialectical_framework-1.6.7 → dialectical_framework-1.7.0}/src/dialectical_framework/enums/causality_preset.py +0 -0
- {dialectical_framework-1.6.7 → dialectical_framework-1.7.0}/src/dialectical_framework/enums/di.py +0 -0
- {dialectical_framework-1.6.7 → dialectical_framework-1.7.0}/src/dialectical_framework/events/__init__.py +0 -0
- {dialectical_framework-1.6.7 → dialectical_framework-1.7.0}/src/dialectical_framework/events/graph_event.py +0 -0
- {dialectical_framework-1.6.7 → dialectical_framework-1.7.0}/src/dialectical_framework/events/graph_event_bus.py +0 -0
- {dialectical_framework-1.6.7 → dialectical_framework-1.7.0}/src/dialectical_framework/exceptions/__init__.py +0 -0
- {dialectical_framework-1.6.7 → dialectical_framework-1.7.0}/src/dialectical_framework/exceptions/node_errors.py +0 -0
- {dialectical_framework-1.6.7 → dialectical_framework-1.7.0}/src/dialectical_framework/exceptions/resolver_errors.py +0 -0
- {dialectical_framework-1.6.7 → dialectical_framework-1.7.0}/src/dialectical_framework/graph/__init__.py +0 -0
- {dialectical_framework-1.6.7 → dialectical_framework-1.7.0}/src/dialectical_framework/graph/composite_input_resolver.py +0 -0
- {dialectical_framework-1.6.7 → dialectical_framework-1.7.0}/src/dialectical_framework/graph/dialexity_input_resolver.py +0 -0
- {dialectical_framework-1.6.7 → dialectical_framework-1.7.0}/src/dialectical_framework/graph/estimation_manager.py +0 -0
- {dialectical_framework-1.6.7 → dialectical_framework-1.7.0}/src/dialectical_framework/graph/mixins/__init__.py +0 -0
- {dialectical_framework-1.6.7 → dialectical_framework-1.7.0}/src/dialectical_framework/graph/mixins/incremental_build_mixin.py +0 -0
- {dialectical_framework-1.6.7 → dialectical_framework-1.7.0}/src/dialectical_framework/graph/mixins/intent_mixin.py +0 -0
- {dialectical_framework-1.6.7 → dialectical_framework-1.7.0}/src/dialectical_framework/graph/mixins/persistable_mixin.py +0 -0
- {dialectical_framework-1.6.7 → dialectical_framework-1.7.0}/src/dialectical_framework/graph/nodes/__init__.py +0 -0
- {dialectical_framework-1.6.7 → dialectical_framework-1.7.0}/src/dialectical_framework/graph/nodes/assessable_entity.py +0 -0
- {dialectical_framework-1.6.7 → dialectical_framework-1.7.0}/src/dialectical_framework/graph/nodes/base_node.py +0 -0
- {dialectical_framework-1.6.7 → dialectical_framework-1.7.0}/src/dialectical_framework/graph/nodes/case.py +0 -0
- {dialectical_framework-1.6.7 → dialectical_framework-1.7.0}/src/dialectical_framework/graph/nodes/cycle.py +0 -0
- {dialectical_framework-1.6.7 → dialectical_framework-1.7.0}/src/dialectical_framework/graph/nodes/estimation.py +0 -0
- {dialectical_framework-1.6.7 → dialectical_framework-1.7.0}/src/dialectical_framework/graph/nodes/ideas.py +0 -0
- {dialectical_framework-1.6.7 → dialectical_framework-1.7.0}/src/dialectical_framework/graph/nodes/input.py +0 -0
- {dialectical_framework-1.6.7 → dialectical_framework-1.7.0}/src/dialectical_framework/graph/nodes/nexus.py +0 -0
- {dialectical_framework-1.6.7 → dialectical_framework-1.7.0}/src/dialectical_framework/graph/nodes/perspective.py +0 -0
- {dialectical_framework-1.6.7 → dialectical_framework-1.7.0}/src/dialectical_framework/graph/nodes/polarity.py +0 -0
- {dialectical_framework-1.6.7 → dialectical_framework-1.7.0}/src/dialectical_framework/graph/nodes/rationale.py +0 -0
- {dialectical_framework-1.6.7 → dialectical_framework-1.7.0}/src/dialectical_framework/graph/nodes/statement.py +0 -0
- {dialectical_framework-1.6.7 → dialectical_framework-1.7.0}/src/dialectical_framework/graph/nodes/synthesis.py +0 -0
- {dialectical_framework-1.6.7 → dialectical_framework-1.7.0}/src/dialectical_framework/graph/nodes/transformation.py +0 -0
- {dialectical_framework-1.6.7 → dialectical_framework-1.7.0}/src/dialectical_framework/graph/nodes/transition.py +0 -0
- {dialectical_framework-1.6.7 → dialectical_framework-1.7.0}/src/dialectical_framework/graph/nodes/wheel.py +0 -0
- {dialectical_framework-1.6.7 → dialectical_framework-1.7.0}/src/dialectical_framework/graph/relationship_manager.py +0 -0
- {dialectical_framework-1.6.7 → dialectical_framework-1.7.0}/src/dialectical_framework/graph/relationships/__init__.py +0 -0
- {dialectical_framework-1.6.7 → dialectical_framework-1.7.0}/src/dialectical_framework/graph/relationships/action_reflection_relationship.py +0 -0
- {dialectical_framework-1.6.7 → dialectical_framework-1.7.0}/src/dialectical_framework/graph/relationships/belongs_to_cycle_relationship.py +0 -0
- {dialectical_framework-1.6.7 → dialectical_framework-1.7.0}/src/dialectical_framework/graph/relationships/belongs_to_nexus_relationship.py +0 -0
- {dialectical_framework-1.6.7 → dialectical_framework-1.7.0}/src/dialectical_framework/graph/relationships/changed_to_relationship.py +0 -0
- {dialectical_framework-1.6.7 → dialectical_framework-1.7.0}/src/dialectical_framework/graph/relationships/contradiction_of_relationship.py +0 -0
- {dialectical_framework-1.6.7 → dialectical_framework-1.7.0}/src/dialectical_framework/graph/relationships/critiques_relationship.py +0 -0
- {dialectical_framework-1.6.7 → dialectical_framework-1.7.0}/src/dialectical_framework/graph/relationships/distilled_to_relationship.py +0 -0
- {dialectical_framework-1.6.7 → dialectical_framework-1.7.0}/src/dialectical_framework/graph/relationships/estimates_relationship.py +0 -0
- {dialectical_framework-1.6.7 → dialectical_framework-1.7.0}/src/dialectical_framework/graph/relationships/explains_relationship.py +0 -0
- {dialectical_framework-1.6.7 → dialectical_framework-1.7.0}/src/dialectical_framework/graph/relationships/has_input_relationship.py +0 -0
- {dialectical_framework-1.6.7 → dialectical_framework-1.7.0}/src/dialectical_framework/graph/relationships/has_statement_relationship.py +0 -0
- {dialectical_framework-1.6.7 → dialectical_framework-1.7.0}/src/dialectical_framework/graph/relationships/has_wheel_relationship.py +0 -0
- {dialectical_framework-1.6.7 → dialectical_framework-1.7.0}/src/dialectical_framework/graph/relationships/immutable_structure.py +0 -0
- {dialectical_framework-1.6.7 → dialectical_framework-1.7.0}/src/dialectical_framework/graph/relationships/is_source_of_relationship.py +0 -0
- {dialectical_framework-1.6.7 → dialectical_framework-1.7.0}/src/dialectical_framework/graph/relationships/is_target_of_relationship.py +0 -0
- {dialectical_framework-1.6.7 → dialectical_framework-1.7.0}/src/dialectical_framework/graph/relationships/negative_side_of_relationship.py +0 -0
- {dialectical_framework-1.6.7 → dialectical_framework-1.7.0}/src/dialectical_framework/graph/relationships/opposite_direction_relationship.py +0 -0
- {dialectical_framework-1.6.7 → dialectical_framework-1.7.0}/src/dialectical_framework/graph/relationships/opposite_of_relationship.py +0 -0
- {dialectical_framework-1.6.7 → dialectical_framework-1.7.0}/src/dialectical_framework/graph/relationships/polarity_relationship.py +0 -0
- {dialectical_framework-1.6.7 → dialectical_framework-1.7.0}/src/dialectical_framework/graph/relationships/positive_side_of_relationship.py +0 -0
- {dialectical_framework-1.6.7 → dialectical_framework-1.7.0}/src/dialectical_framework/graph/relationships/provides_relationship.py +0 -0
- {dialectical_framework-1.6.7 → dialectical_framework-1.7.0}/src/dialectical_framework/graph/relationships/synthesis_of_relationship.py +0 -0
- {dialectical_framework-1.6.7 → dialectical_framework-1.7.0}/src/dialectical_framework/graph/repositories/__init__.py +0 -0
- {dialectical_framework-1.6.7 → dialectical_framework-1.7.0}/src/dialectical_framework/graph/repositories/case_repository.py +0 -0
- {dialectical_framework-1.6.7 → dialectical_framework-1.7.0}/src/dialectical_framework/graph/repositories/cycle_repository.py +0 -0
- {dialectical_framework-1.6.7 → dialectical_framework-1.7.0}/src/dialectical_framework/graph/repositories/input_repository.py +0 -0
- {dialectical_framework-1.6.7 → dialectical_framework-1.7.0}/src/dialectical_framework/graph/repositories/nexus_repository.py +0 -0
- {dialectical_framework-1.6.7 → dialectical_framework-1.7.0}/src/dialectical_framework/graph/repositories/node_repository.py +0 -0
- {dialectical_framework-1.6.7 → dialectical_framework-1.7.0}/src/dialectical_framework/graph/repositories/perspective_repository.py +0 -0
- {dialectical_framework-1.6.7 → dialectical_framework-1.7.0}/src/dialectical_framework/graph/repositories/polarity_repository.py +0 -0
- {dialectical_framework-1.6.7 → dialectical_framework-1.7.0}/src/dialectical_framework/graph/repositories/schema_repository.py +0 -0
- {dialectical_framework-1.6.7 → dialectical_framework-1.7.0}/src/dialectical_framework/graph/repositories/statement_repository.py +0 -0
- {dialectical_framework-1.6.7 → dialectical_framework-1.7.0}/src/dialectical_framework/graph/repositories/transformation_repository.py +0 -0
- {dialectical_framework-1.6.7 → dialectical_framework-1.7.0}/src/dialectical_framework/graph/repositories/wheel_repository.py +0 -0
- {dialectical_framework-1.6.7 → dialectical_framework-1.7.0}/src/dialectical_framework/graph/scope_context.py +0 -0
- {dialectical_framework-1.6.7 → dialectical_framework-1.7.0}/src/dialectical_framework/graph/verbatim_input_resolver.py +0 -0
- {dialectical_framework-1.6.7 → dialectical_framework-1.7.0}/src/dialectical_framework/graph/wheel_segment.py +0 -0
- {dialectical_framework-1.6.7 → dialectical_framework-1.7.0}/src/dialectical_framework/graph/wheel_segment_polar_pair.py +0 -0
- {dialectical_framework-1.6.7 → dialectical_framework-1.7.0}/src/dialectical_framework/protocols/__init__.py +0 -0
- {dialectical_framework-1.6.7 → dialectical_framework-1.7.0}/src/dialectical_framework/protocols/has_config.py +0 -0
- {dialectical_framework-1.6.7 → dialectical_framework-1.7.0}/src/dialectical_framework/protocols/input_resolver.py +0 -0
- {dialectical_framework-1.6.7 → dialectical_framework-1.7.0}/src/dialectical_framework/settings.py +0 -0
- {dialectical_framework-1.6.7 → dialectical_framework-1.7.0}/src/dialectical_framework/utils/__init__.py +0 -0
- {dialectical_framework-1.6.7 → dialectical_framework-1.7.0}/src/dialectical_framework/utils/bedrock_provider.py +0 -0
- {dialectical_framework-1.6.7 → dialectical_framework-1.7.0}/src/dialectical_framework/utils/concurrency.py +0 -0
- {dialectical_framework-1.6.7 → dialectical_framework-1.7.0}/src/dialectical_framework/utils/dc_replace.py +0 -0
- {dialectical_framework-1.6.7 → dialectical_framework-1.7.0}/src/dialectical_framework/utils/decompose_probability_uniformly.py +0 -0
- {dialectical_framework-1.6.7 → dialectical_framework-1.7.0}/src/dialectical_framework/utils/edge_context.py +0 -0
- {dialectical_framework-1.6.7 → dialectical_framework-1.7.0}/src/dialectical_framework/utils/effect_logger.py +0 -0
- {dialectical_framework-1.6.7 → dialectical_framework-1.7.0}/src/dialectical_framework/utils/input_context.py +0 -0
- {dialectical_framework-1.6.7 → dialectical_framework-1.7.0}/src/dialectical_framework/utils/order_transitions.py +0 -0
- {dialectical_framework-1.6.7 → dialectical_framework-1.7.0}/src/dialectical_framework/utils/sequence_generation.py +0 -0
- {dialectical_framework-1.6.7 → dialectical_framework-1.7.0}/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.
|
|
3
|
+
Version: 1.7.0
|
|
4
4
|
Summary: A dialectical framework for augmented intelligence. AI reasoning powered with dialectics supports humans in: system optimization (psychology, engineering, business, politics, etc.); dispute resolution (mediation, conflicts, negotiations, etc.); decision-making (dilemmas, challenging situations, win-win, etc.).
|
|
5
5
|
License: MIT
|
|
6
6
|
Keywords: dialectics,dialectical-reasoning,synthesis,thesis-antithesis,ai,artificial-intelligence,llm,reasoning-framework,philosophy,logic,argumentation,conflict-resolution,decision-making,critical-thinking,semantic-graph,mirascope,pydantic,perspectives,polarity-reasoning
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[tool.poetry]
|
|
2
2
|
name = "dialectical-framework"
|
|
3
|
-
version = "1.
|
|
3
|
+
version = "1.7.0"
|
|
4
4
|
description = "A dialectical framework for augmented intelligence. AI reasoning powered with dialectics supports humans in: system optimization (psychology, engineering, business, politics, etc.); dispute resolution (mediation, conflicts, negotiations, etc.); decision-making (dilemmas, challenging situations, win-win, etc.)."
|
|
5
5
|
authors = ["Evaldas Taroza <evaldas@dialexity.com>"]
|
|
6
6
|
readme = "README.md"
|
|
@@ -22,7 +22,9 @@ from dialectical_framework.agents.advisor.system_prompts import \
|
|
|
22
22
|
from dialectical_framework.agents.agent_context import agent_scope
|
|
23
23
|
from dialectical_framework.agents.conversation_facilitator import \
|
|
24
24
|
ConversationFacilitator
|
|
25
|
+
from dialectical_framework.agents.app_spec import AppSpec, resolve_app_layer
|
|
25
26
|
from dialectical_framework.agents.stream_events import StreamEvent
|
|
27
|
+
from dialectical_framework.agents.toolsets import merge_app_tools
|
|
26
28
|
|
|
27
29
|
logger = logging.getLogger(__name__)
|
|
28
30
|
|
|
@@ -55,20 +57,32 @@ class Advisor:
|
|
|
55
57
|
|
|
56
58
|
Usage (fresh start):
|
|
57
59
|
with scope(case.sid):
|
|
58
|
-
advisor = Advisor(app_preamble=
|
|
60
|
+
advisor = Advisor(app_preamble=COUNSELOR_PERSONA)
|
|
59
61
|
response = await advisor.chat("My son started smoking...")
|
|
60
62
|
|
|
61
63
|
Usage (post-analysis, rich graph exists):
|
|
62
64
|
with scope(case.sid):
|
|
63
65
|
context = await DialecticalContext().resolve()
|
|
64
|
-
advisor = Advisor(app_preamble=
|
|
66
|
+
advisor = Advisor(app_preamble=COUNSELOR_PERSONA, dialectical_context=context)
|
|
65
67
|
response = await advisor.chat("I want to talk through what we found...")
|
|
66
68
|
|
|
67
69
|
Usage (resuming conversation):
|
|
68
70
|
with scope(case.sid):
|
|
69
|
-
advisor = Advisor(app_preamble=
|
|
71
|
+
advisor = Advisor(app_preamble=COUNSELOR_PERSONA, messages=saved_messages)
|
|
70
72
|
response = await advisor.chat("What about the other angle?")
|
|
71
73
|
|
|
74
|
+
Usage (app-provided domain tools):
|
|
75
|
+
# The app brings field knowledge two ways: prose in the preamble,
|
|
76
|
+
# and callable resources as app @llm.tool functions. The engine
|
|
77
|
+
# prompt carries no docs for app tools (their tool-schema docstrings
|
|
78
|
+
# travel to the LLM automatically) — introduce them and their usage
|
|
79
|
+
# rules in the app preamble, where domain vocabulary lives.
|
|
80
|
+
with scope(case.sid):
|
|
81
|
+
advisor = Advisor(
|
|
82
|
+
app_preamble=ASTRO_COUNSELOR_PERSONA, # explains when to consult the chart
|
|
83
|
+
app_tools=[lookup_natal_chart], # @llm.tool from the app
|
|
84
|
+
)
|
|
85
|
+
|
|
72
86
|
Usage (Advisor mode of an exploration session — Explorer handover):
|
|
73
87
|
# User was chatting in Explorer (operator mode) and asks "what does
|
|
74
88
|
# this all mean for me?" — the host toggles to counsel mode by
|
|
@@ -76,7 +90,7 @@ class Advisor:
|
|
|
76
90
|
# exploration:
|
|
77
91
|
with scope(case.sid):
|
|
78
92
|
advisor = Advisor(
|
|
79
|
-
app_preamble=
|
|
93
|
+
app_preamble=NAVIGATOR_APP_EXPLORER_AGENT_COUNSELOR_REGISTER,
|
|
80
94
|
nexus_hash=explorer.nexus_hash,
|
|
81
95
|
messages=explorer.messages,
|
|
82
96
|
)
|
|
@@ -104,13 +118,28 @@ class Advisor:
|
|
|
104
118
|
dialectical_context: Optional[str] = None,
|
|
105
119
|
messages: Optional[list] = None,
|
|
106
120
|
nexus_hash: Optional[str] = None,
|
|
121
|
+
app_tools: Optional[list] = None,
|
|
122
|
+
app: Optional[AppSpec] = None,
|
|
107
123
|
) -> None:
|
|
108
124
|
self._nexus_hash = nexus_hash
|
|
125
|
+
# app: declarative app definition — composition depends on the mode:
|
|
126
|
+
# counsel toggle (nexus_hash set) keeps the Navigator contract
|
|
127
|
+
# (NAVIGATOR_APP_EXPLORER_AGENT_COUNSELOR_REGISTER + voicing + tool_guide); standalone uses
|
|
128
|
+
# the spec's advisor_persona (machinery hidden). See AppSpec.
|
|
129
|
+
app_preamble, app_tools = resolve_app_layer(
|
|
130
|
+
app,
|
|
131
|
+
app_preamble,
|
|
132
|
+
app_tools,
|
|
133
|
+
preamble_for="advisor_scoped" if nexus_hash else "advisor_unscoped",
|
|
134
|
+
)
|
|
109
135
|
if nexus_hash:
|
|
110
136
|
self._validate_nexus(nexus_hash)
|
|
111
137
|
self._tools = _build_scoped_tools(nexus_hash)
|
|
112
138
|
else:
|
|
113
139
|
self._tools = _build_tools()
|
|
140
|
+
# App-provided @llm.tool functions (domain resources: chart lookups,
|
|
141
|
+
# methodology references, ...) — see toolsets.merge_app_tools.
|
|
142
|
+
self._tools = merge_app_tools(self._tools, app_tools)
|
|
114
143
|
self._conversation = ConversationFacilitator(tools=self._tools)
|
|
115
144
|
if messages:
|
|
116
145
|
self._conversation._messages = list(messages)
|
|
@@ -209,6 +238,8 @@ def _build_tools() -> list:
|
|
|
209
238
|
from dialectical_framework.agents.advisor.tools.deepen import deepen
|
|
210
239
|
from dialectical_framework.agents.advisor.tools.explore import explore
|
|
211
240
|
from dialectical_framework.agents.advisor.tools.ingest import ingest
|
|
241
|
+
from dialectical_framework.agents.advisor.tools.record_decision import \
|
|
242
|
+
record_decision
|
|
212
243
|
from dialectical_framework.agents.advisor.tools.sync import sync
|
|
213
244
|
from dialectical_framework.agents.orchestrator.tools.inspect_node import \
|
|
214
245
|
inspect_node
|
|
@@ -222,6 +253,7 @@ def _build_tools() -> list:
|
|
|
222
253
|
anchor,
|
|
223
254
|
explore,
|
|
224
255
|
deepen,
|
|
256
|
+
record_decision,
|
|
225
257
|
sync,
|
|
226
258
|
inspect_node,
|
|
227
259
|
read_digest,
|
|
@@ -125,7 +125,7 @@ understanding you've built.
|
|
|
125
125
|
The narrow exceptions where no analysis is called for: greetings and small
|
|
126
126
|
talk, logistics ("can you repeat that?"), pure information requests, and
|
|
127
127
|
moments of grief or crisis where presence matters more than analysis.
|
|
128
|
-
|
|
128
|
+
{decision_filter_note}
|
|
129
129
|
Your response to the person never waits on the machinery — speak from what
|
|
130
130
|
you have. Analysis deepens your next turn; it never delays or deforms this one."""
|
|
131
131
|
|
|
@@ -255,6 +255,101 @@ You now have the integration vision. Use it to:
|
|
|
255
255
|
- Frame synthesis as something they GROW INTO, not something they implement
|
|
256
256
|
in one move. It emerges from sustained Ac+ and Re+ working together."""
|
|
257
257
|
|
|
258
|
+
_DECISION_READINESS = """## Decision Readiness (Convergence)
|
|
259
|
+
|
|
260
|
+
Much of your counsel serves a decision the person must actually make. Mapping
|
|
261
|
+
tensions opens the space; a decision needs the space to CLOSE. These rules
|
|
262
|
+
govern the closing — they apply whenever the conversation is decision-shaped
|
|
263
|
+
(the person is choosing, not just understanding):
|
|
264
|
+
|
|
265
|
+
**The decision bounds the search.** In decision mode, run the search backward
|
|
266
|
+
from the choice: "what would have to be true for this option to be wrong?" —
|
|
267
|
+
not forward from the material. Options are finite; each has a finite set of
|
|
268
|
+
load-bearing assumptions.
|
|
269
|
+
|
|
270
|
+
**Named options (when the choice arrives as "X or Y").** Anchor the pair as
|
|
271
|
+
it comes — the person's own framing is the map they'll recognize. But an
|
|
272
|
+
option-pair is usually a bundle: the options differ along several tensions
|
|
273
|
+
at once, and one tetrad captures one of them. So ask which pull matters most
|
|
274
|
+
before mapping deeper — the person enumerates cheaply what the machinery
|
|
275
|
+
maps expensively — and anchor the same pair again (identical wording) for an
|
|
276
|
+
alternative tetrad only when their reactions show the mapped dimension isn't
|
|
277
|
+
the one the choice turns on. Alternative tetrads are readings of the choice
|
|
278
|
+
itself, not new candidate tensions — the discrimination test below governs
|
|
279
|
+
those. Each perspective names its reading (the dimension its aspects oppose
|
|
280
|
+
along); when a reading strongly resonates — the person keeps returning to
|
|
281
|
+
that dimension — it is itself an anchor candidate: anchor it as its own
|
|
282
|
+
tension (its two poles as thesis and antithesis) to give the root its full
|
|
283
|
+
structure. When weaving, take the reading that resonates plus the tensions
|
|
284
|
+
beyond the fork; sibling readings of one pair only shift the angle. And read
|
|
285
|
+
the anchor result at call time: options that merely differ rather than
|
|
286
|
+
oppose show up as weak opposition (low HS) or a low mode (drifting/absence
|
|
287
|
+
rather than negation) — then the fork is not where the tension is. Keep the
|
|
288
|
+
pair as the person's frame, but also anchor each option alone to discover
|
|
289
|
+
what genuinely opposes it, instead of forcing the menu into a tension it
|
|
290
|
+
doesn't contain.
|
|
291
|
+
|
|
292
|
+
**Discrimination test (when NOT to map).** Before anchoring a new candidate
|
|
293
|
+
tension, ask: if this tension resolved either way, would the person lean
|
|
294
|
+
differently? If not, acknowledge it in conversation but do not map it —
|
|
295
|
+
mapping that cannot change the choice is noise dressed as diligence. But if
|
|
296
|
+
the person wants it mapped anyway, map it: their lived reality outranks the
|
|
297
|
+
test, and what feels load-bearing to them often is.
|
|
298
|
+
|
|
299
|
+
**Saturation.** Surface framings are endless; deep structure is not. When new
|
|
300
|
+
tensions keep landing in already-mapped opposition families (when
|
|
301
|
+
correspondence lines appear in your understanding they are this signal;
|
|
302
|
+
otherwise judge whether the new framing's deep structure matches a mapped
|
|
303
|
+
tension), say so plainly: new framings are collapsing into tensions already
|
|
304
|
+
mapped. Never claim "we found all tensions"; completeness belongs only to
|
|
305
|
+
causal arrangements, which are enumerated systematically for the tensions
|
|
306
|
+
woven in. Tensions saturate; they are never exhausted.
|
|
307
|
+
|
|
308
|
+
**Pre-commit ritual (earned confidence, soft).** Before proposing to record,
|
|
309
|
+
have the choice face its strongest test: name what the unchosen side offers —
|
|
310
|
+
its genuine contribution — and ask whether they can say what choosing costs
|
|
311
|
+
them. Name the trap version of the choice (the resolution that is actually
|
|
312
|
+
one side dominating). A decision that has faced both is earned; record that
|
|
313
|
+
confrontation in the rationale. If the person says "just record it," record
|
|
314
|
+
it — their wish outranks the ritual — and note in the rationale that the cost
|
|
315
|
+
went unconfronted.
|
|
316
|
+
|
|
317
|
+
**Readiness → propose-and-confirm ceremony.** Signals: a leaning is expressed
|
|
318
|
+
and stable, options stopped shifting, recent candidate tensions failed the
|
|
319
|
+
discrimination test. Then — after the ritual above, and only with the
|
|
320
|
+
person's engagement — offer to record the decision. Read the record back in
|
|
321
|
+
their own words: the question, the stance, the why, the accepted cost. Record
|
|
322
|
+
ONLY on their explicit confirmation. A decision is a speech act — it exists
|
|
323
|
+
because they declared it, never because you inferred it. Decisions are NEVER
|
|
324
|
+
recorded silently — whatever else stays behind the scenes, the record is the
|
|
325
|
+
person's artifact, and they must know it exists. Speak of it plainly, in
|
|
326
|
+
their vocabulary — "want me to note this down as your decision, so we can
|
|
327
|
+
hold to it later?" — the record is named in plain words, never as a tool; its
|
|
328
|
+
reference (hash) follows the same disclosure rules as any other node
|
|
329
|
+
reference.
|
|
330
|
+
|
|
331
|
+
**The replacement rule.** Before proposing, check the distilled record
|
|
332
|
+
against the Decisions section of your understanding (and any decisions
|
|
333
|
+
recorded earlier in this conversation): a conflict with a standing decision
|
|
334
|
+
is counsel material, not a silent recording detail — "this would sit against
|
|
335
|
+
what you decided earlier — does it replace that?" If it does, record the new
|
|
336
|
+
decision and, with their consent, retire the old with a reason naming its
|
|
337
|
+
replacement.
|
|
338
|
+
|
|
339
|
+
**After recording — the re-audit.** When a recorded decision exists and a new
|
|
340
|
+
tension or a wobble appears, first ask: does this discriminate against what
|
|
341
|
+
was decided? When it is the already-accepted cost resurfacing, or a framing
|
|
342
|
+
that collapses into what was mapped, reassure FROM the record: they accepted
|
|
343
|
+
this knowingly, here is the recipe they adopted for living with it. That
|
|
344
|
+
reassurance is the record's whole value — but treat the wobble as data
|
|
345
|
+
first, dismissal never: circumstances may genuinely have changed even when
|
|
346
|
+
the wording resembles the accepted cost. If the new tension genuinely
|
|
347
|
+
discriminates — it could change the choice — say so honestly: this reopens
|
|
348
|
+
the decision. Counsel it through, and if a new stance emerges, record it and
|
|
349
|
+
retire the old per the replacement rule above. If a ground of the decision
|
|
350
|
+
has since been discarded, surface that: the record stands, but one of its
|
|
351
|
+
supports moved."""
|
|
352
|
+
|
|
258
353
|
_TOOLS_INTRO = """## Internal Tools
|
|
259
354
|
|
|
260
355
|
Your internal tools — how you think structurally. Use them eagerly and
|
|
@@ -283,8 +378,9 @@ _TOOL_DOCS: dict[str, str] = {
|
|
|
283
378
|
"anchor": """- `anchor` — Plants a specific tension from the conversation. More precise
|
|
284
379
|
than ingest; use when you can see at least the person's position. Two modes:
|
|
285
380
|
- Thesis + antithesis: you know both sides — creates one polarity and one
|
|
286
|
-
perspective (tetrad). Call again with the same T-A
|
|
287
|
-
|
|
381
|
+
perspective (tetrad). Call again with the same T-A (identical wording —
|
|
382
|
+
a rephrase plants a new tension instead) for an alternative tetrad on
|
|
383
|
+
the same opposition.
|
|
288
384
|
- Thesis only: anchors their position and discovers what opposes it — finds
|
|
289
385
|
multiple possible antitheses (each a different polarity), each expanded
|
|
290
386
|
into a perspective. Richer when you want the framework to reveal
|
|
@@ -293,8 +389,9 @@ _TOOL_DOCS: dict[str, str] = {
|
|
|
293
389
|
until woven in with `explore`). Use when you can see at least the person's
|
|
294
390
|
position. Two modes:
|
|
295
391
|
- Thesis + antithesis: you know both sides — creates one polarity and one
|
|
296
|
-
perspective (tetrad). Call again with the same T-A
|
|
297
|
-
|
|
392
|
+
perspective (tetrad). Call again with the same T-A (identical wording —
|
|
393
|
+
a rephrase plants a new tension instead) for an alternative tetrad on
|
|
394
|
+
the same opposition.
|
|
298
395
|
- Thesis only: anchors their position and discovers what opposes it — finds
|
|
299
396
|
multiple possible antitheses (each a different polarity), each expanded
|
|
300
397
|
into a perspective. Richer when you want the framework to reveal
|
|
@@ -352,9 +449,22 @@ _TOOL_DOCS: dict[str, str] = {
|
|
|
352
449
|
them. Deepening only adds analytical depth to existing structure — it
|
|
353
450
|
never changes what the exploration contains, so no consent ceremony is
|
|
354
451
|
needed. Harmless on already-developed wheels.""",
|
|
452
|
+
"record_decision": """- `record_decision` — Records a decision the person has EXPLICITLY
|
|
453
|
+
confirmed, after the propose-and-confirm ceremony (see Decision Readiness).
|
|
454
|
+
Pass the confirmed wording literally: `question` (what was being decided),
|
|
455
|
+
`stance` (the position taken), `rationale` (the distilled why, including
|
|
456
|
+
reasons outside the mapped structure). Pass `grounds` as {hash, role}
|
|
457
|
+
entries: role "accepted_cost" for the unchosen side's contribution they
|
|
458
|
+
confronted, "adopted_pathway" for the pathway adopted as their ongoing
|
|
459
|
+
recipe, no role for plain grounds (tensions weighed, arrangements counseled
|
|
460
|
+
from). The result may carry a coherence verdict — if it flags a
|
|
461
|
+
contradiction, raise it with the person immediately; the record stands
|
|
462
|
+
either way. To replace or retract a recorded decision, use `discard` on it
|
|
463
|
+
(reason naming the newer decision if one replaces it).""",
|
|
355
464
|
"sync": """- `sync` — Re-reads the graph state. Without arguments: the full picture —
|
|
356
465
|
e.g., after multiple ingest/anchor calls, to see all perspectives with
|
|
357
|
-
scores before deciding what to group for
|
|
466
|
+
scores (and any standing decisions) before deciding what to group for
|
|
467
|
+
explore. With a `nexus_hash`:
|
|
358
468
|
zooms into that one exploration in full depth — the overview caps wheels
|
|
359
469
|
per cycle for compactness, the zoom does not. Zoom when counsel settles on
|
|
360
470
|
one exploration and you need arrangements the overview truncated. NOT
|
|
@@ -364,19 +474,24 @@ _TOOL_DOCS: dict[str, str] = {
|
|
|
364
474
|
"sync_scoped": """- `sync` — Re-reads this exploration's state. Use when you need a fresh
|
|
365
475
|
picture after changes. NOT needed at conversation start — the exploration
|
|
366
476
|
is already in your context.""",
|
|
367
|
-
"discard": """- `discard` — Silently retracts something the user rejects. Works on
|
|
368
|
-
perspective (a whole framing — the tension and its aspects)
|
|
369
|
-
(a single claim). Pass the hash from the
|
|
370
|
-
nodes are removed; committed ones are
|
|
371
|
-
reasoning. To drop a tension
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
(cycles/wheels) won't
|
|
477
|
+
"discard": """- `discard` — Silently retracts something the user rejects. Works on a
|
|
478
|
+
perspective (a whole framing — the tension and its aspects), a statement
|
|
479
|
+
(a single claim), or a recorded decision. Pass the hash from the
|
|
480
|
+
anchor/ingest result. Uncommitted nodes are removed; committed ones are
|
|
481
|
+
soft-discarded and filtered from future reasoning. To drop a tension
|
|
482
|
+
entirely, discard the perspective first, then its underlying statement if
|
|
483
|
+
it's no longer wanted (discarding a perspective leaves its shared
|
|
484
|
+
statements intact, and a statement still used by a live perspective won't
|
|
485
|
+
discard). A perspective already woven into pathways (cycles/wheels) won't
|
|
486
|
+
discard — re-anchor the corrected framing instead. Recorded decisions are
|
|
487
|
+
the exception to silence: retire one only after the person explicitly
|
|
488
|
+
confirms, with a reason naming the replacement if one exists.""",
|
|
376
489
|
"discard_scoped": """- `discard` — Retracts something the person no longer stands behind, per the
|
|
377
490
|
consent rules above (confirm for exploration members; a framing you just
|
|
378
|
-
anchored needs no ceremony). Works on
|
|
379
|
-
|
|
491
|
+
anchored needs no ceremony). Works on a perspective (a whole framing), a
|
|
492
|
+
statement (a single claim), or a recorded decision (always confirmed with
|
|
493
|
+
the person first, with a reason naming the replacement if one exists).
|
|
494
|
+
Pass the hash from the anchor result.
|
|
380
495
|
Uncommitted nodes are removed; committed ones are soft-discarded and
|
|
381
496
|
filtered from future reasoning. Members of OTHER explorations refuse.
|
|
382
497
|
A perspective already woven into pathways (cycles/wheels) won't discard —
|
|
@@ -428,7 +543,7 @@ calls for it. It is a compass, not a script:
|
|
|
428
543
|
3. Continue conversation, possibly `anchor` again for new tensions
|
|
429
544
|
4. `explore` → respond with specific paired pathways
|
|
430
545
|
5. Continue with full depth, offer integration vision when they're ready
|
|
431
|
-
|
|
546
|
+
{decision_arc_step}
|
|
432
547
|
Counsel grounded in pathways is stronger than counsel from a single tension;
|
|
433
548
|
counsel from a tension is stronger than counsel from nothing — keep moving the
|
|
434
549
|
understanding deeper as the conversation allows.
|
|
@@ -449,7 +564,7 @@ present findings as structural tables or labeled positions. Do not use
|
|
|
449
564
|
framework terminology (thesis, antithesis, polarity, perspective, nexus,
|
|
450
565
|
wheel, transformation, T+, T-, A+, A-, S+, S-, Ac+, Re+) — unless the app
|
|
451
566
|
preamble above explicitly grants terminology disclosure, in which case the
|
|
452
|
-
preamble's vocabulary rules override this one.
|
|
567
|
+
preamble's vocabulary rules override this one.{decision_speech_note}
|
|
453
568
|
|
|
454
569
|
Speak in the person's own vocabulary about their situation. Statement text
|
|
455
570
|
from the graph is raw material — rephrase it freely into their language;
|
|
@@ -527,6 +642,20 @@ Use `inspect_node` when you want to understand the reasoning behind a score,
|
|
|
527
642
|
explain WHY a specific blindspot exists, or ground a pathway recommendation
|
|
528
643
|
in the actual analytical logic rather than restating the dump.
|
|
529
644
|
|
|
645
|
+
**Decisions:** The dump may contain a Decisions section — the person's
|
|
646
|
+
standing decisions, each recorded with their explicit confirmation: the
|
|
647
|
+
question, the stance in their confirmed words, the why, and grounds (the
|
|
648
|
+
tensions, statements, arrangements, and pathways it rests on; "accepted cost" marks the
|
|
649
|
+
unchosen side's contribution they confronted, "adopted pathway" the recipe
|
|
650
|
+
they committed to). Treat these as the person's own declared positions —
|
|
651
|
+
counsel that contradicts a standing decision must name that openly. A
|
|
652
|
+
ground flagged "since discarded" means a support
|
|
653
|
+
moved after the decision — worth surfacing, not silently ignoring. A
|
|
654
|
+
`Validation` line carries the machine coherence verdict: "failed" means a
|
|
655
|
+
flagged contradiction — do not build reassurance on that record; revisit it
|
|
656
|
+
with the person. Absence of an accepted-cost ground means the choice never
|
|
657
|
+
confronted what it gave up — relevant when a wobble arrives.
|
|
658
|
+
|
|
530
659
|
**Cross-exploration correspondences.** When several explorations coexist,
|
|
531
660
|
some perspectives carry correspondence lines — machine-stated facts, not
|
|
532
661
|
suggestions: "Also woven into Nexus [[x]]" (literally the same tension in
|
|
@@ -675,6 +804,7 @@ DEFAULT_TOOL_NAMES = [
|
|
|
675
804
|
"anchor",
|
|
676
805
|
"explore",
|
|
677
806
|
"deepen",
|
|
807
|
+
"record_decision",
|
|
678
808
|
"sync",
|
|
679
809
|
"inspect_node",
|
|
680
810
|
"read_digest",
|
|
@@ -709,6 +839,44 @@ def system_prompt(
|
|
|
709
839
|
)
|
|
710
840
|
tool_docs.append(doc)
|
|
711
841
|
|
|
842
|
+
decisions_wired = "record_decision" in names
|
|
843
|
+
|
|
844
|
+
def _decision_note(template: str, placeholder: str, note: str) -> str:
|
|
845
|
+
"""Render a Decision Readiness cross-reference only when the section
|
|
846
|
+
itself renders (tool wired) — a reference to an absent section would
|
|
847
|
+
dangle. The placeholder sits on its own line: wired → the note set
|
|
848
|
+
off by blank lines, unwired → the line collapses to a paragraph
|
|
849
|
+
break."""
|
|
850
|
+
if placeholder not in template:
|
|
851
|
+
return template
|
|
852
|
+
return template.replace(
|
|
853
|
+
placeholder, f"\n{note}\n" if decisions_wired else ""
|
|
854
|
+
)
|
|
855
|
+
|
|
856
|
+
eager = _decision_note(
|
|
857
|
+
_EAGER_SCOPED if scoped else _EAGER,
|
|
858
|
+
"{decision_filter_note}",
|
|
859
|
+
"In decision-shaped conversations, the Decision Readiness section "
|
|
860
|
+
"below adds one more filter: a candidate tension that could not "
|
|
861
|
+
"change the choice is acknowledged, not mapped.",
|
|
862
|
+
)
|
|
863
|
+
default_arc = _decision_note(
|
|
864
|
+
_DEFAULT_ARC,
|
|
865
|
+
"{decision_arc_step}",
|
|
866
|
+
"6. When the conversation is decision-shaped and readiness signals "
|
|
867
|
+
"appear, close it: the propose-and-confirm ceremony (see Decision "
|
|
868
|
+
"Readiness above), then counsel FROM the record.",
|
|
869
|
+
)
|
|
870
|
+
how_you_speak = _HOW_YOU_SPEAK_SCOPED if scoped else _HOW_YOU_SPEAK
|
|
871
|
+
if "{decision_speech_note}" in how_you_speak:
|
|
872
|
+
speech_note = (
|
|
873
|
+
"\n\nOne exception: the decision record (see Decision Readiness) "
|
|
874
|
+
"is named openly, in plain words — never as a tool or node."
|
|
875
|
+
if decisions_wired
|
|
876
|
+
else ""
|
|
877
|
+
)
|
|
878
|
+
how_you_speak = how_you_speak.replace("{decision_speech_note}", speech_note)
|
|
879
|
+
|
|
712
880
|
conversation_use = _CONVERSATION_USE
|
|
713
881
|
if scoped and "ingest" not in names:
|
|
714
882
|
# Don't reference a tool that isn't wired in this mode.
|
|
@@ -720,19 +888,20 @@ def system_prompt(
|
|
|
720
888
|
sections = [
|
|
721
889
|
_ROLE_SCOPED if scoped else _ROLE,
|
|
722
890
|
_scope_section(scoped_nexus_hash) if scoped else None,
|
|
723
|
-
|
|
891
|
+
eager,
|
|
724
892
|
_INTERNAL_MODEL,
|
|
725
893
|
conversation_use,
|
|
894
|
+
_DECISION_READINESS if decisions_wired else None,
|
|
726
895
|
_TOOLS_INTRO_SCOPED if scoped else _TOOLS_INTRO,
|
|
727
896
|
"\n\n".join(tool_docs),
|
|
728
897
|
_REJECTION_HANDLING_SCOPED if scoped else _REJECTION_HANDLING,
|
|
729
|
-
None if scoped else
|
|
730
|
-
|
|
898
|
+
None if scoped else default_arc,
|
|
899
|
+
how_you_speak,
|
|
731
900
|
_SCORE_READING,
|
|
732
901
|
_CONTEXT_SLOT,
|
|
733
902
|
]
|
|
734
903
|
return "\n\n".join(s for s in sections if s)
|
|
735
904
|
|
|
736
905
|
|
|
737
|
-
# Backward-compatible default render (unscoped, all
|
|
906
|
+
# Backward-compatible default render (unscoped, all default tools).
|
|
738
907
|
SYSTEM_PROMPT = system_prompt()
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
"""
|
|
2
|
+
record_decision tool: persist a decision the person explicitly confirmed.
|
|
3
|
+
|
|
4
|
+
The ceremony lives in conversation (propose → read back → confirm) — this
|
|
5
|
+
tool is called only AFTER the person's explicit yes, with the confirmed
|
|
6
|
+
wording passed literally. Retracting or replacing a recorded decision goes
|
|
7
|
+
through the standard `discard` tool (reason referencing the new decision).
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from __future__ import annotations
|
|
11
|
+
|
|
12
|
+
from typing import Annotated
|
|
13
|
+
|
|
14
|
+
from mirascope import llm
|
|
15
|
+
from pydantic import Field
|
|
16
|
+
|
|
17
|
+
from dialectical_framework.concerns.record_decision import GroundLink
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
@llm.tool
|
|
21
|
+
async def record_decision(
|
|
22
|
+
question: Annotated[
|
|
23
|
+
str,
|
|
24
|
+
Field(description="The question that was being decided, in the person's confirmed words"),
|
|
25
|
+
],
|
|
26
|
+
stance: Annotated[
|
|
27
|
+
str,
|
|
28
|
+
Field(description="The stance the person committed to, in their confirmed words"),
|
|
29
|
+
],
|
|
30
|
+
rationale: Annotated[
|
|
31
|
+
str,
|
|
32
|
+
Field(
|
|
33
|
+
description="The distilled why behind the decision as the person confirmed it, "
|
|
34
|
+
"including reasons that live outside the mapped structure (deadlines, "
|
|
35
|
+
"people, external facts)"
|
|
36
|
+
),
|
|
37
|
+
],
|
|
38
|
+
grounds: Annotated[
|
|
39
|
+
list[GroundLink] | None,
|
|
40
|
+
Field(
|
|
41
|
+
description="Nodes this decision rests on: {hash, role?} each. "
|
|
42
|
+
"role='accepted_cost' for the unchosen side's contribution the person "
|
|
43
|
+
"confronted and accepted; role='adopted_pathway' for the pathway adopted "
|
|
44
|
+
"as the ongoing recipe; omit role for a plain ground (tensions weighed, "
|
|
45
|
+
"arrangements counseled from)."
|
|
46
|
+
),
|
|
47
|
+
] = None,
|
|
48
|
+
) -> str:
|
|
49
|
+
"""Record a decision the person has EXPLICITLY confirmed in conversation. Never call this silently or speculatively — propose the distilled record first, read it back, and record only on their clear yes. Links the decision to the graph nodes that grounded it and attaches the confirmed rationale. To replace or retract a recorded decision, use `discard` on it (with a reason naming the newer decision if one replaces it)."""
|
|
50
|
+
from dialectical_framework.concerns.record_decision import RecordDecision
|
|
51
|
+
|
|
52
|
+
# Mirascope passes json.loads'd kwargs without coercing nested models, so
|
|
53
|
+
# `grounds` arrives as raw dicts — RecordDecision.resolve normalizes them
|
|
54
|
+
# (single owner of that normalization) and refuses in-band on bad input.
|
|
55
|
+
concern = RecordDecision()
|
|
56
|
+
await concern.resolve(
|
|
57
|
+
question=question,
|
|
58
|
+
stance=stance,
|
|
59
|
+
rationale=rationale,
|
|
60
|
+
grounds=grounds,
|
|
61
|
+
)
|
|
62
|
+
return str(concern.report)
|
|
@@ -18,8 +18,10 @@ the pinned explore weaves them into the exploration, deepen develops an
|
|
|
18
18
|
alternative arrangement of the pinned exploration on demand (guarded by
|
|
19
19
|
wheel-membership), sync/inspect_node/read_digest read, discard retracts
|
|
20
20
|
(members of the pinned nexus and standalone perspectives — e.g. its own
|
|
21
|
-
rejected anchors — but never members of OTHER explorations)
|
|
22
|
-
|
|
21
|
+
rejected anchors — but never members of OTHER explorations), and
|
|
22
|
+
record_decision persists confirmed decisions (Case-level, unguarded by the
|
|
23
|
+
pin). Only `ingest` is excluded — bulk extraction belongs to the unscoped
|
|
24
|
+
flow.
|
|
23
25
|
"""
|
|
24
26
|
|
|
25
27
|
from __future__ import annotations
|
|
@@ -40,6 +42,8 @@ def build_scoped_tools(nexus_hash: str) -> list:
|
|
|
40
42
|
DB-free for signature tests).
|
|
41
43
|
"""
|
|
42
44
|
from dialectical_framework.agents.advisor.tools.anchor import anchor
|
|
45
|
+
from dialectical_framework.agents.advisor.tools.record_decision import \
|
|
46
|
+
record_decision
|
|
43
47
|
from dialectical_framework.agents.orchestrator.tools.inspect_node import \
|
|
44
48
|
inspect_node
|
|
45
49
|
from dialectical_framework.agents.orchestrator.tools.read_digest import \
|
|
@@ -58,10 +62,10 @@ def build_scoped_tools(nexus_hash: str) -> list:
|
|
|
58
62
|
|
|
59
63
|
@llm.tool
|
|
60
64
|
async def discard(
|
|
61
|
-
hash: Annotated[str, Field(description="Hash (or prefix) of the Statement or
|
|
65
|
+
hash: Annotated[str, Field(description="Hash (or prefix) of the Statement, Perspective, or Decision to discard")],
|
|
62
66
|
reason: Annotated[str, Field(description="Why it's being discarded")] = "discarded",
|
|
63
67
|
) -> str:
|
|
64
|
-
"""Mark a Statement or
|
|
68
|
+
"""Mark a Statement, Perspective, or Decision as discarded when the user disagrees with it or finds it irrelevant. Works on members of this exploration, on standalone perspectives (e.g. a freshly anchored framing the user rejected), and on recorded decisions the person retracts or replaces; refuses members of other explorations. Will refuse if the target participates in existing Cycles/Wheels."""
|
|
65
69
|
from dialectical_framework.concerns.discard import Discard
|
|
66
70
|
|
|
67
71
|
refusal = _outside_scope_refusal(pinned_hash, hash)
|
|
@@ -107,7 +111,10 @@ def build_scoped_tools(nexus_hash: str) -> list:
|
|
|
107
111
|
|
|
108
112
|
return await run_deepen(wheel_hash)
|
|
109
113
|
|
|
110
|
-
|
|
114
|
+
# record_decision is appended as-is: decisions are Case-level facts, not
|
|
115
|
+
# exploration members — no nexus-scope guard applies (grounds may be
|
|
116
|
+
# exploration members; grounding is read-only w.r.t. the exploration).
|
|
117
|
+
return [anchor, sync, inspect_node, read_digest, discard, explore, deepen, record_decision]
|
|
111
118
|
|
|
112
119
|
|
|
113
120
|
def _outside_scope_refusal(nexus_hash: str, target_hash: str) -> str | None:
|
|
@@ -132,8 +139,8 @@ def _outside_scope_refusal(nexus_hash: str, target_hash: str) -> str | None:
|
|
|
132
139
|
|
|
133
140
|
node = NodeRepository().find_by_hash(target_hash)
|
|
134
141
|
if not isinstance(node, Perspective):
|
|
135
|
-
# Not a perspective (
|
|
136
|
-
# guards handle it.
|
|
142
|
+
# Not a perspective (a statement or a decision) — let Discard's own
|
|
143
|
+
# guards handle it; decisions are Case-level, so no nexus pin applies.
|
|
137
144
|
return None
|
|
138
145
|
pp = node
|
|
139
146
|
|
|
@@ -20,7 +20,9 @@ from dialectical_framework.agents.analyst.system_prompts import SYSTEM_PROMPT
|
|
|
20
20
|
from dialectical_framework.agents.conversation_facilitator import \
|
|
21
21
|
ConversationFacilitator
|
|
22
22
|
from dialectical_framework.agents.reasonable_concern import ReasonableConcern
|
|
23
|
+
from dialectical_framework.agents.app_spec import AppSpec, resolve_app_layer
|
|
23
24
|
from dialectical_framework.agents.stream_events import StreamEvent
|
|
25
|
+
from dialectical_framework.agents.toolsets import merge_app_tools
|
|
24
26
|
|
|
25
27
|
if TYPE_CHECKING:
|
|
26
28
|
pass
|
|
@@ -48,13 +50,17 @@ class Analyst:
|
|
|
48
50
|
|
|
49
51
|
Usage:
|
|
50
52
|
with scope(case.sid):
|
|
51
|
-
analyst = Analyst(
|
|
53
|
+
analyst = Analyst(app=MY_APP_SPEC) # declarative (see AppSpec)
|
|
52
54
|
response = await analyst.chat("I'm struggling with work-life balance")
|
|
53
55
|
|
|
54
56
|
# Resuming with history:
|
|
55
57
|
with scope(case.sid):
|
|
56
58
|
analyst = Analyst(messages=loaded_messages)
|
|
57
59
|
response = await analyst.chat("What about the second tension?")
|
|
60
|
+
|
|
61
|
+
# Manual preamble control (app_preamble replaces AppSpec composition):
|
|
62
|
+
with scope(case.sid):
|
|
63
|
+
analyst = Analyst(app_preamble="You are a counselor...")
|
|
58
64
|
"""
|
|
59
65
|
|
|
60
66
|
AGENT_NAME = "analyst"
|
|
@@ -63,8 +69,17 @@ class Analyst:
|
|
|
63
69
|
self,
|
|
64
70
|
app_preamble: Optional[str] = None,
|
|
65
71
|
messages: Optional[list] = None,
|
|
72
|
+
app_tools: Optional[list] = None,
|
|
73
|
+
app: Optional[AppSpec] = None,
|
|
66
74
|
) -> None:
|
|
67
|
-
|
|
75
|
+
# app: declarative app definition — the framework composes the
|
|
76
|
+
# Navigator preamble (NAVIGATOR_APP + voicing + tool_guide) and tool
|
|
77
|
+
# set from it. app_preamble/app_tools remain for manual control;
|
|
78
|
+
# mixing them with app= raises (see app_spec.resolve_app_layer).
|
|
79
|
+
app_preamble, app_tools = resolve_app_layer(
|
|
80
|
+
app, app_preamble, app_tools, preamble_for="navigator"
|
|
81
|
+
)
|
|
82
|
+
self._tools = merge_app_tools(_build_tools(), app_tools)
|
|
68
83
|
self._conversation = ConversationFacilitator(tools=self._tools)
|
|
69
84
|
if messages:
|
|
70
85
|
self._conversation._messages = list(messages)
|
|
@@ -128,6 +128,10 @@ class EditPerspective(ReasonableConcern[EditPerspectiveResult]):
|
|
|
128
128
|
# Prepare working PP (clone if committed)
|
|
129
129
|
if self._was_committed:
|
|
130
130
|
working_pp = pp.clone()
|
|
131
|
+
# Drop the inherited reading (intent): the axis was named for the
|
|
132
|
+
# ORIGINAL aspects — after a user edit it may no longer describe
|
|
133
|
+
# this tetrad, and a stale reading is worse than none.
|
|
134
|
+
working_pp.intent = None
|
|
131
135
|
working_pp.save()
|
|
132
136
|
self._report.node_created(working_pp)
|
|
133
137
|
else:
|
|
@@ -143,6 +143,17 @@ class ExpandPolarity(ReasonableConcern[list[Perspective]]):
|
|
|
143
143
|
)
|
|
144
144
|
continue
|
|
145
145
|
|
|
146
|
+
# Name this reading of the tension: the generation already made
|
|
147
|
+
# the LLM name the axis of each diagonal pair (TetradDto) —
|
|
148
|
+
# persist it as the perspective's intent (the guiding question
|
|
149
|
+
# of THIS tetrad). Sibling tetrads on one polarity differ by
|
|
150
|
+
# exactly this. Set BEFORE commit: intent participates in the
|
|
151
|
+
# hash, so distinct readings are structurally distinct nodes.
|
|
152
|
+
if pp.intent is None:
|
|
153
|
+
reading = self._compose_reading(generator.axes)
|
|
154
|
+
if reading:
|
|
155
|
+
pp.intent = reading
|
|
156
|
+
|
|
146
157
|
pp.commit()
|
|
147
158
|
self._report.node_committed(pp)
|
|
148
159
|
completed_pps.append(pp)
|
|
@@ -284,6 +295,8 @@ class ExpandPolarity(ReasonableConcern[list[Perspective]]):
|
|
|
284
295
|
POSITION_A_MINUS,
|
|
285
296
|
]
|
|
286
297
|
state: dict[str, str | None] = {"hash": pp.short_hash}
|
|
298
|
+
if pp.intent:
|
|
299
|
+
state["reading"] = pp.intent
|
|
287
300
|
for pos in positions:
|
|
288
301
|
manager = pp.get_relationship_manager_by_position(pos)
|
|
289
302
|
pairs = manager.all()
|
|
@@ -356,6 +369,20 @@ class ExpandPolarity(ReasonableConcern[list[Perspective]]):
|
|
|
356
369
|
return existing
|
|
357
370
|
return None
|
|
358
371
|
|
|
372
|
+
@staticmethod
|
|
373
|
+
def _compose_reading(axes: dict[str, str]) -> Optional[str]:
|
|
374
|
+
"""Compose AspectGeneration's captured axes into the perspective's
|
|
375
|
+
reading (intent). Both axes when they name different dimensions,
|
|
376
|
+
one when they agree or only one survived the disclaimer filter."""
|
|
377
|
+
constructive = axes.get("t_plus_vs_a_minus")
|
|
378
|
+
reflective = axes.get("a_plus_vs_t_minus")
|
|
379
|
+
named = [a for a in (constructive, reflective) if a]
|
|
380
|
+
if not named:
|
|
381
|
+
return None
|
|
382
|
+
if len(named) == 2 and named[0].lower() != named[1].lower():
|
|
383
|
+
return f"Reading along: {named[0]} / {named[1]}"
|
|
384
|
+
return f"Reading along: {named[0]}"
|
|
385
|
+
|
|
359
386
|
@inject
|
|
360
387
|
async def _get_input_text(
|
|
361
388
|
self,
|