dialectical-framework 2.0.0__tar.gz → 2.0.2__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-2.0.0 → dialectical_framework-2.0.2}/PKG-INFO +1 -1
- {dialectical_framework-2.0.0 → dialectical_framework-2.0.2}/pyproject.toml +1 -1
- {dialectical_framework-2.0.0 → dialectical_framework-2.0.2}/src/dialectical_framework/agents/consultant/consultant.py +47 -11
- {dialectical_framework-2.0.0 → dialectical_framework-2.0.2}/src/dialectical_framework/agents/conversation_facilitator.py +6 -0
- {dialectical_framework-2.0.0 → dialectical_framework-2.0.2}/src/dialectical_framework/concerns/control_statements_check.py +189 -23
- {dialectical_framework-2.0.0 → dialectical_framework-2.0.2}/src/dialectical_framework/concerns/tetrad_candidates.py +79 -34
- {dialectical_framework-2.0.0 → dialectical_framework-2.0.2}/src/dialectical_framework/concerns/view_sketch.py +38 -8
- {dialectical_framework-2.0.0 → dialectical_framework-2.0.2}/src/dialectical_framework/graph/views.py +6 -0
- {dialectical_framework-2.0.0 → dialectical_framework-2.0.2}/src/dialectical_framework/utils/bedrock_provider.py +80 -5
- {dialectical_framework-2.0.0 → dialectical_framework-2.0.2}/LICENSE +0 -0
- {dialectical_framework-2.0.0 → dialectical_framework-2.0.2}/PATENTS +0 -0
- {dialectical_framework-2.0.0 → dialectical_framework-2.0.2}/README.md +0 -0
- {dialectical_framework-2.0.0 → dialectical_framework-2.0.2}/src/dialectical_framework/__init__.py +0 -0
- {dialectical_framework-2.0.0 → dialectical_framework-2.0.2}/src/dialectical_framework/agents/__init__.py +0 -0
- {dialectical_framework-2.0.0 → dialectical_framework-2.0.2}/src/dialectical_framework/agents/advisor/__init__.py +0 -0
- {dialectical_framework-2.0.0 → dialectical_framework-2.0.2}/src/dialectical_framework/agents/advisor/advisor.py +0 -0
- {dialectical_framework-2.0.0 → dialectical_framework-2.0.2}/src/dialectical_framework/agents/advisor/build_policy.py +0 -0
- {dialectical_framework-2.0.0 → dialectical_framework-2.0.2}/src/dialectical_framework/agents/advisor/migration.py +0 -0
- {dialectical_framework-2.0.0 → dialectical_framework-2.0.2}/src/dialectical_framework/agents/advisor/reply_hygiene.py +0 -0
- {dialectical_framework-2.0.0 → dialectical_framework-2.0.2}/src/dialectical_framework/agents/advisor/system_prompts.py +0 -0
- {dialectical_framework-2.0.0 → dialectical_framework-2.0.2}/src/dialectical_framework/agents/advisor/tools/__init__.py +0 -0
- {dialectical_framework-2.0.0 → dialectical_framework-2.0.2}/src/dialectical_framework/agents/advisor/tools/anchor.py +0 -0
- {dialectical_framework-2.0.0 → dialectical_framework-2.0.2}/src/dialectical_framework/agents/advisor/tools/deepen.py +0 -0
- {dialectical_framework-2.0.0 → dialectical_framework-2.0.2}/src/dialectical_framework/agents/advisor/tools/explore.py +0 -0
- {dialectical_framework-2.0.0 → dialectical_framework-2.0.2}/src/dialectical_framework/agents/advisor/tools/ingest.py +0 -0
- {dialectical_framework-2.0.0 → dialectical_framework-2.0.2}/src/dialectical_framework/agents/advisor/tools/note.py +0 -0
- {dialectical_framework-2.0.0 → dialectical_framework-2.0.2}/src/dialectical_framework/agents/advisor/tools/record_decision.py +0 -0
- {dialectical_framework-2.0.0 → dialectical_framework-2.0.2}/src/dialectical_framework/agents/advisor/tools/scoped.py +0 -0
- {dialectical_framework-2.0.0 → dialectical_framework-2.0.2}/src/dialectical_framework/agents/advisor/tools/sync.py +0 -0
- {dialectical_framework-2.0.0 → dialectical_framework-2.0.2}/src/dialectical_framework/agents/agent_context.py +0 -0
- {dialectical_framework-2.0.0 → dialectical_framework-2.0.2}/src/dialectical_framework/agents/analyst/__init__.py +0 -0
- {dialectical_framework-2.0.0 → dialectical_framework-2.0.2}/src/dialectical_framework/agents/analyst/analyst.py +0 -0
- {dialectical_framework-2.0.0 → dialectical_framework-2.0.2}/src/dialectical_framework/agents/analyst/skills/__init__.py +0 -0
- {dialectical_framework-2.0.0 → dialectical_framework-2.0.2}/src/dialectical_framework/agents/analyst/skills/anchor_theses.py +0 -0
- {dialectical_framework-2.0.0 → dialectical_framework-2.0.2}/src/dialectical_framework/agents/analyst/skills/edit_perspective.py +0 -0
- {dialectical_framework-2.0.0 → dialectical_framework-2.0.2}/src/dialectical_framework/agents/analyst/skills/expand_polarities.py +0 -0
- {dialectical_framework-2.0.0 → dialectical_framework-2.0.2}/src/dialectical_framework/agents/analyst/skills/find_polarities.py +0 -0
- {dialectical_framework-2.0.0 → dialectical_framework-2.0.2}/src/dialectical_framework/agents/analyst/skills/introduce_polarity.py +0 -0
- {dialectical_framework-2.0.0 → dialectical_framework-2.0.2}/src/dialectical_framework/agents/analyst/skills/sketch_tetrad.py +0 -0
- {dialectical_framework-2.0.0 → dialectical_framework-2.0.2}/src/dialectical_framework/agents/analyst/skills/surface_theses.py +0 -0
- {dialectical_framework-2.0.0 → dialectical_framework-2.0.2}/src/dialectical_framework/agents/analyst/system_prompts.py +0 -0
- {dialectical_framework-2.0.0 → dialectical_framework-2.0.2}/src/dialectical_framework/agents/analyst/tools/__init__.py +0 -0
- {dialectical_framework-2.0.0 → dialectical_framework-2.0.2}/src/dialectical_framework/agents/analyst/tools/place_statement.py +0 -0
- {dialectical_framework-2.0.0 → dialectical_framework-2.0.2}/src/dialectical_framework/agents/app_spec.py +0 -0
- {dialectical_framework-2.0.0 → dialectical_framework-2.0.2}/src/dialectical_framework/agents/apps.py +0 -0
- {dialectical_framework-2.0.0 → dialectical_framework-2.0.2}/src/dialectical_framework/agents/consultant/__init__.py +0 -0
- {dialectical_framework-2.0.0 → dialectical_framework-2.0.2}/src/dialectical_framework/agents/execution_report.py +0 -0
- {dialectical_framework-2.0.0 → dialectical_framework-2.0.2}/src/dialectical_framework/agents/explorer/__init__.py +0 -0
- {dialectical_framework-2.0.0 → dialectical_framework-2.0.2}/src/dialectical_framework/agents/explorer/explorer.py +0 -0
- {dialectical_framework-2.0.0 → dialectical_framework-2.0.2}/src/dialectical_framework/agents/explorer/skills/__init__.py +0 -0
- {dialectical_framework-2.0.0 → dialectical_framework-2.0.2}/src/dialectical_framework/agents/explorer/skills/build_wheels.py +0 -0
- {dialectical_framework-2.0.0 → dialectical_framework-2.0.2}/src/dialectical_framework/agents/explorer/skills/explore_transformations.py +0 -0
- {dialectical_framework-2.0.0 → dialectical_framework-2.0.2}/src/dialectical_framework/agents/explorer/skills/generate_synthesis.py +0 -0
- {dialectical_framework-2.0.0 → dialectical_framework-2.0.2}/src/dialectical_framework/agents/explorer/system_prompts.py +0 -0
- {dialectical_framework-2.0.0 → dialectical_framework-2.0.2}/src/dialectical_framework/agents/explorer/tools/__init__.py +0 -0
- {dialectical_framework-2.0.0 → dialectical_framework-2.0.2}/src/dialectical_framework/agents/explorer/tools/create_nexus.py +0 -0
- {dialectical_framework-2.0.0 → dialectical_framework-2.0.2}/src/dialectical_framework/agents/explorer/tools/expand_nexus.py +0 -0
- {dialectical_framework-2.0.0 → dialectical_framework-2.0.2}/src/dialectical_framework/agents/explorer/tools/generate_synthesis.py +0 -0
- {dialectical_framework-2.0.0 → dialectical_framework-2.0.2}/src/dialectical_framework/agents/explorer/tools/present_exploration.py +0 -0
- {dialectical_framework-2.0.0 → dialectical_framework-2.0.2}/src/dialectical_framework/agents/orchestrator/__init__.py +0 -0
- {dialectical_framework-2.0.0 → dialectical_framework-2.0.2}/src/dialectical_framework/agents/orchestrator/tools/__init__.py +0 -0
- {dialectical_framework-2.0.0 → dialectical_framework-2.0.2}/src/dialectical_framework/agents/orchestrator/tools/add_input.py +0 -0
- {dialectical_framework-2.0.0 → dialectical_framework-2.0.2}/src/dialectical_framework/agents/orchestrator/tools/audit_feasibility.py +0 -0
- {dialectical_framework-2.0.0 → dialectical_framework-2.0.2}/src/dialectical_framework/agents/orchestrator/tools/create_dx_input.py +0 -0
- {dialectical_framework-2.0.0 → dialectical_framework-2.0.2}/src/dialectical_framework/agents/orchestrator/tools/digest_input.py +0 -0
- {dialectical_framework-2.0.0 → dialectical_framework-2.0.2}/src/dialectical_framework/agents/orchestrator/tools/discard.py +0 -0
- {dialectical_framework-2.0.0 → dialectical_framework-2.0.2}/src/dialectical_framework/agents/orchestrator/tools/get_schema.py +0 -0
- {dialectical_framework-2.0.0 → dialectical_framework-2.0.2}/src/dialectical_framework/agents/orchestrator/tools/inspect_node.py +0 -0
- {dialectical_framework-2.0.0 → dialectical_framework-2.0.2}/src/dialectical_framework/agents/orchestrator/tools/present_analysis.py +0 -0
- {dialectical_framework-2.0.0 → dialectical_framework-2.0.2}/src/dialectical_framework/agents/orchestrator/tools/query_graph.py +0 -0
- {dialectical_framework-2.0.0 → dialectical_framework-2.0.2}/src/dialectical_framework/agents/orchestrator/tools/read_digest.py +0 -0
- {dialectical_framework-2.0.0 → dialectical_framework-2.0.2}/src/dialectical_framework/agents/orchestrator/tools/read_input.py +0 -0
- {dialectical_framework-2.0.0 → dialectical_framework-2.0.2}/src/dialectical_framework/agents/reasonable_concern.py +0 -0
- {dialectical_framework-2.0.0 → dialectical_framework-2.0.2}/src/dialectical_framework/agents/stream_events.py +0 -0
- {dialectical_framework-2.0.0 → dialectical_framework-2.0.2}/src/dialectical_framework/agents/toolsets.py +0 -0
- {dialectical_framework-2.0.0 → dialectical_framework-2.0.2}/src/dialectical_framework/agents/turn_timing.py +0 -0
- {dialectical_framework-2.0.0 → dialectical_framework-2.0.2}/src/dialectical_framework/concerns/__init__.py +0 -0
- {dialectical_framework-2.0.0 → dialectical_framework-2.0.2}/src/dialectical_framework/concerns/ac_re_taxonomy.py +0 -0
- {dialectical_framework-2.0.0 → dialectical_framework-2.0.2}/src/dialectical_framework/concerns/action_extraction.py +0 -0
- {dialectical_framework-2.0.0 → dialectical_framework-2.0.2}/src/dialectical_framework/concerns/add_input.py +0 -0
- {dialectical_framework-2.0.0 → dialectical_framework-2.0.2}/src/dialectical_framework/concerns/ai_dto/__init__.py +0 -0
- {dialectical_framework-2.0.0 → dialectical_framework-2.0.2}/src/dialectical_framework/concerns/ai_dto/statement_dto.py +0 -0
- {dialectical_framework-2.0.0 → dialectical_framework-2.0.2}/src/dialectical_framework/concerns/ai_dto/statements_deck_dto.py +0 -0
- {dialectical_framework-2.0.0 → dialectical_framework-2.0.2}/src/dialectical_framework/concerns/antithesis_classification.py +0 -0
- {dialectical_framework-2.0.0 → dialectical_framework-2.0.2}/src/dialectical_framework/concerns/antithesis_extraction.py +0 -0
- {dialectical_framework-2.0.0 → dialectical_framework-2.0.2}/src/dialectical_framework/concerns/antithetical_thesis_detection.py +0 -0
- {dialectical_framework-2.0.0 → dialectical_framework-2.0.2}/src/dialectical_framework/concerns/aspect_classification.py +0 -0
- {dialectical_framework-2.0.0 → dialectical_framework-2.0.2}/src/dialectical_framework/concerns/aspect_generation.py +0 -0
- {dialectical_framework-2.0.0 → dialectical_framework-2.0.2}/src/dialectical_framework/concerns/build_status.py +0 -0
- {dialectical_framework-2.0.0 → dialectical_framework-2.0.2}/src/dialectical_framework/concerns/causality/__init__.py +0 -0
- {dialectical_framework-2.0.0 → dialectical_framework-2.0.2}/src/dialectical_framework/concerns/causality/causality_estimator.py +0 -0
- {dialectical_framework-2.0.0 → dialectical_framework-2.0.2}/src/dialectical_framework/concerns/causality/causality_estimator_balanced.py +0 -0
- {dialectical_framework-2.0.0 → dialectical_framework-2.0.2}/src/dialectical_framework/concerns/causality/causality_estimator_criteria.py +0 -0
- {dialectical_framework-2.0.0 → dialectical_framework-2.0.2}/src/dialectical_framework/concerns/causality/causality_estimator_desirable.py +0 -0
- {dialectical_framework-2.0.0 → dialectical_framework-2.0.2}/src/dialectical_framework/concerns/causality/causality_estimator_feasible.py +0 -0
- {dialectical_framework-2.0.0 → dialectical_framework-2.0.2}/src/dialectical_framework/concerns/causality/causality_estimator_realistic.py +0 -0
- {dialectical_framework-2.0.0 → dialectical_framework-2.0.2}/src/dialectical_framework/concerns/causality/causality_normalizer.py +0 -0
- {dialectical_framework-2.0.0 → dialectical_framework-2.0.2}/src/dialectical_framework/concerns/causality/estimator_resolver.py +0 -0
- {dialectical_framework-2.0.0 → dialectical_framework-2.0.2}/src/dialectical_framework/concerns/causality_estimation.py +0 -0
- {dialectical_framework-2.0.0 → dialectical_framework-2.0.2}/src/dialectical_framework/concerns/create_dx_input.py +0 -0
- {dialectical_framework-2.0.0 → dialectical_framework-2.0.2}/src/dialectical_framework/concerns/create_nexus.py +0 -0
- {dialectical_framework-2.0.0 → dialectical_framework-2.0.2}/src/dialectical_framework/concerns/decision_coherence_check.py +0 -0
- {dialectical_framework-2.0.0 → dialectical_framework-2.0.2}/src/dialectical_framework/concerns/decision_confirmation_check.py +0 -0
- {dialectical_framework-2.0.0 → dialectical_framework-2.0.2}/src/dialectical_framework/concerns/diagonal_oppositions_check.py +0 -0
- {dialectical_framework-2.0.0 → dialectical_framework-2.0.2}/src/dialectical_framework/concerns/dialectical_context.py +0 -0
- {dialectical_framework-2.0.0 → dialectical_framework-2.0.2}/src/dialectical_framework/concerns/discard.py +0 -0
- {dialectical_framework-2.0.0 → dialectical_framework-2.0.2}/src/dialectical_framework/concerns/display_text_edit.py +0 -0
- {dialectical_framework-2.0.0 → dialectical_framework-2.0.2}/src/dialectical_framework/concerns/expand_nexus.py +0 -0
- {dialectical_framework-2.0.0 → dialectical_framework-2.0.2}/src/dialectical_framework/concerns/perspective_combination.py +0 -0
- {dialectical_framework-2.0.0 → dialectical_framework-2.0.2}/src/dialectical_framework/concerns/perspective_validation.py +0 -0
- {dialectical_framework-2.0.0 → dialectical_framework-2.0.2}/src/dialectical_framework/concerns/positive_ac_re_apex_derivation.py +0 -0
- {dialectical_framework-2.0.0 → dialectical_framework-2.0.2}/src/dialectical_framework/concerns/record_decision.py +0 -0
- {dialectical_framework-2.0.0 → dialectical_framework-2.0.2}/src/dialectical_framework/concerns/scoring_scales.py +0 -0
- {dialectical_framework-2.0.0 → dialectical_framework-2.0.2}/src/dialectical_framework/concerns/source_digest.py +0 -0
- {dialectical_framework-2.0.0 → dialectical_framework-2.0.2}/src/dialectical_framework/concerns/statement_classification.py +0 -0
- {dialectical_framework-2.0.0 → dialectical_framework-2.0.2}/src/dialectical_framework/concerns/statement_deduplication.py +0 -0
- {dialectical_framework-2.0.0 → dialectical_framework-2.0.2}/src/dialectical_framework/concerns/statement_headline.py +0 -0
- {dialectical_framework-2.0.0 → dialectical_framework-2.0.2}/src/dialectical_framework/concerns/statement_placement.py +0 -0
- {dialectical_framework-2.0.0 → dialectical_framework-2.0.2}/src/dialectical_framework/concerns/synthesis_generation.py +0 -0
- {dialectical_framework-2.0.0 → dialectical_framework-2.0.2}/src/dialectical_framework/concerns/tetrad_grounding.py +0 -0
- {dialectical_framework-2.0.0 → dialectical_framework-2.0.2}/src/dialectical_framework/concerns/tetrad_sketch.py +0 -0
- {dialectical_framework-2.0.0 → dialectical_framework-2.0.2}/src/dialectical_framework/concerns/thesis_extraction.py +0 -0
- {dialectical_framework-2.0.0 → dialectical_framework-2.0.2}/src/dialectical_framework/concerns/transformation_audit.py +0 -0
- {dialectical_framework-2.0.0 → dialectical_framework-2.0.2}/src/dialectical_framework/concerns/transformation_generation.py +0 -0
- {dialectical_framework-2.0.0 → dialectical_framework-2.0.2}/src/dialectical_framework/dialectical_reasoning.py +0 -0
- {dialectical_framework-2.0.0 → dialectical_framework-2.0.2}/src/dialectical_framework/enums/__init__.py +0 -0
- {dialectical_framework-2.0.0 → dialectical_framework-2.0.2}/src/dialectical_framework/enums/causality_preset.py +0 -0
- {dialectical_framework-2.0.0 → dialectical_framework-2.0.2}/src/dialectical_framework/enums/di.py +0 -0
- {dialectical_framework-2.0.0 → dialectical_framework-2.0.2}/src/dialectical_framework/events/__init__.py +0 -0
- {dialectical_framework-2.0.0 → dialectical_framework-2.0.2}/src/dialectical_framework/events/graph_event.py +0 -0
- {dialectical_framework-2.0.0 → dialectical_framework-2.0.2}/src/dialectical_framework/events/graph_event_bus.py +0 -0
- {dialectical_framework-2.0.0 → dialectical_framework-2.0.2}/src/dialectical_framework/events/progress_event.py +0 -0
- {dialectical_framework-2.0.0 → dialectical_framework-2.0.2}/src/dialectical_framework/exceptions/__init__.py +0 -0
- {dialectical_framework-2.0.0 → dialectical_framework-2.0.2}/src/dialectical_framework/exceptions/node_errors.py +0 -0
- {dialectical_framework-2.0.0 → dialectical_framework-2.0.2}/src/dialectical_framework/exceptions/resolver_errors.py +0 -0
- {dialectical_framework-2.0.0 → dialectical_framework-2.0.2}/src/dialectical_framework/graph/__init__.py +0 -0
- {dialectical_framework-2.0.0 → dialectical_framework-2.0.2}/src/dialectical_framework/graph/composite_input_resolver.py +0 -0
- {dialectical_framework-2.0.0 → dialectical_framework-2.0.2}/src/dialectical_framework/graph/dialexity_input_resolver.py +0 -0
- {dialectical_framework-2.0.0 → dialectical_framework-2.0.2}/src/dialectical_framework/graph/estimation_manager.py +0 -0
- {dialectical_framework-2.0.0 → dialectical_framework-2.0.2}/src/dialectical_framework/graph/mixins/__init__.py +0 -0
- {dialectical_framework-2.0.0 → dialectical_framework-2.0.2}/src/dialectical_framework/graph/mixins/incremental_build_mixin.py +0 -0
- {dialectical_framework-2.0.0 → dialectical_framework-2.0.2}/src/dialectical_framework/graph/mixins/intent_mixin.py +0 -0
- {dialectical_framework-2.0.0 → dialectical_framework-2.0.2}/src/dialectical_framework/graph/mixins/persistable_mixin.py +0 -0
- {dialectical_framework-2.0.0 → dialectical_framework-2.0.2}/src/dialectical_framework/graph/nodes/__init__.py +0 -0
- {dialectical_framework-2.0.0 → dialectical_framework-2.0.2}/src/dialectical_framework/graph/nodes/assessable_entity.py +0 -0
- {dialectical_framework-2.0.0 → dialectical_framework-2.0.2}/src/dialectical_framework/graph/nodes/base_node.py +0 -0
- {dialectical_framework-2.0.0 → dialectical_framework-2.0.2}/src/dialectical_framework/graph/nodes/case.py +0 -0
- {dialectical_framework-2.0.0 → dialectical_framework-2.0.2}/src/dialectical_framework/graph/nodes/cycle.py +0 -0
- {dialectical_framework-2.0.0 → dialectical_framework-2.0.2}/src/dialectical_framework/graph/nodes/decision.py +0 -0
- {dialectical_framework-2.0.0 → dialectical_framework-2.0.2}/src/dialectical_framework/graph/nodes/estimation.py +0 -0
- {dialectical_framework-2.0.0 → dialectical_framework-2.0.2}/src/dialectical_framework/graph/nodes/ideas.py +0 -0
- {dialectical_framework-2.0.0 → dialectical_framework-2.0.2}/src/dialectical_framework/graph/nodes/input.py +0 -0
- {dialectical_framework-2.0.0 → dialectical_framework-2.0.2}/src/dialectical_framework/graph/nodes/nexus.py +0 -0
- {dialectical_framework-2.0.0 → dialectical_framework-2.0.2}/src/dialectical_framework/graph/nodes/note.py +0 -0
- {dialectical_framework-2.0.0 → dialectical_framework-2.0.2}/src/dialectical_framework/graph/nodes/perspective.py +0 -0
- {dialectical_framework-2.0.0 → dialectical_framework-2.0.2}/src/dialectical_framework/graph/nodes/polarity.py +0 -0
- {dialectical_framework-2.0.0 → dialectical_framework-2.0.2}/src/dialectical_framework/graph/nodes/rationale.py +0 -0
- {dialectical_framework-2.0.0 → dialectical_framework-2.0.2}/src/dialectical_framework/graph/nodes/statement.py +0 -0
- {dialectical_framework-2.0.0 → dialectical_framework-2.0.2}/src/dialectical_framework/graph/nodes/synthesis.py +0 -0
- {dialectical_framework-2.0.0 → dialectical_framework-2.0.2}/src/dialectical_framework/graph/nodes/transformation.py +0 -0
- {dialectical_framework-2.0.0 → dialectical_framework-2.0.2}/src/dialectical_framework/graph/nodes/transition.py +0 -0
- {dialectical_framework-2.0.0 → dialectical_framework-2.0.2}/src/dialectical_framework/graph/nodes/wheel.py +0 -0
- {dialectical_framework-2.0.0 → dialectical_framework-2.0.2}/src/dialectical_framework/graph/relationship_manager.py +0 -0
- {dialectical_framework-2.0.0 → dialectical_framework-2.0.2}/src/dialectical_framework/graph/relationships/__init__.py +0 -0
- {dialectical_framework-2.0.0 → dialectical_framework-2.0.2}/src/dialectical_framework/graph/relationships/action_reflection_relationship.py +0 -0
- {dialectical_framework-2.0.0 → dialectical_framework-2.0.2}/src/dialectical_framework/graph/relationships/belongs_to_cycle_relationship.py +0 -0
- {dialectical_framework-2.0.0 → dialectical_framework-2.0.2}/src/dialectical_framework/graph/relationships/belongs_to_nexus_relationship.py +0 -0
- {dialectical_framework-2.0.0 → dialectical_framework-2.0.2}/src/dialectical_framework/graph/relationships/changed_to_relationship.py +0 -0
- {dialectical_framework-2.0.0 → dialectical_framework-2.0.2}/src/dialectical_framework/graph/relationships/contradiction_of_relationship.py +0 -0
- {dialectical_framework-2.0.0 → dialectical_framework-2.0.2}/src/dialectical_framework/graph/relationships/critiques_relationship.py +0 -0
- {dialectical_framework-2.0.0 → dialectical_framework-2.0.2}/src/dialectical_framework/graph/relationships/distilled_to_relationship.py +0 -0
- {dialectical_framework-2.0.0 → dialectical_framework-2.0.2}/src/dialectical_framework/graph/relationships/estimates_relationship.py +0 -0
- {dialectical_framework-2.0.0 → dialectical_framework-2.0.2}/src/dialectical_framework/graph/relationships/explains_relationship.py +0 -0
- {dialectical_framework-2.0.0 → dialectical_framework-2.0.2}/src/dialectical_framework/graph/relationships/grounded_in_relationship.py +0 -0
- {dialectical_framework-2.0.0 → dialectical_framework-2.0.2}/src/dialectical_framework/graph/relationships/has_input_relationship.py +0 -0
- {dialectical_framework-2.0.0 → dialectical_framework-2.0.2}/src/dialectical_framework/graph/relationships/has_statement_relationship.py +0 -0
- {dialectical_framework-2.0.0 → dialectical_framework-2.0.2}/src/dialectical_framework/graph/relationships/has_wheel_relationship.py +0 -0
- {dialectical_framework-2.0.0 → dialectical_framework-2.0.2}/src/dialectical_framework/graph/relationships/immutable_structure.py +0 -0
- {dialectical_framework-2.0.0 → dialectical_framework-2.0.2}/src/dialectical_framework/graph/relationships/is_source_of_relationship.py +0 -0
- {dialectical_framework-2.0.0 → dialectical_framework-2.0.2}/src/dialectical_framework/graph/relationships/is_target_of_relationship.py +0 -0
- {dialectical_framework-2.0.0 → dialectical_framework-2.0.2}/src/dialectical_framework/graph/relationships/negative_side_of_relationship.py +0 -0
- {dialectical_framework-2.0.0 → dialectical_framework-2.0.2}/src/dialectical_framework/graph/relationships/opposite_direction_relationship.py +0 -0
- {dialectical_framework-2.0.0 → dialectical_framework-2.0.2}/src/dialectical_framework/graph/relationships/opposite_of_relationship.py +0 -0
- {dialectical_framework-2.0.0 → dialectical_framework-2.0.2}/src/dialectical_framework/graph/relationships/polarity_relationship.py +0 -0
- {dialectical_framework-2.0.0 → dialectical_framework-2.0.2}/src/dialectical_framework/graph/relationships/positive_side_of_relationship.py +0 -0
- {dialectical_framework-2.0.0 → dialectical_framework-2.0.2}/src/dialectical_framework/graph/relationships/provides_relationship.py +0 -0
- {dialectical_framework-2.0.0 → dialectical_framework-2.0.2}/src/dialectical_framework/graph/relationships/synthesis_of_relationship.py +0 -0
- {dialectical_framework-2.0.0 → dialectical_framework-2.0.2}/src/dialectical_framework/graph/rendering.py +0 -0
- {dialectical_framework-2.0.0 → dialectical_framework-2.0.2}/src/dialectical_framework/graph/repositories/__init__.py +0 -0
- {dialectical_framework-2.0.0 → dialectical_framework-2.0.2}/src/dialectical_framework/graph/repositories/case_repository.py +0 -0
- {dialectical_framework-2.0.0 → dialectical_framework-2.0.2}/src/dialectical_framework/graph/repositories/cycle_repository.py +0 -0
- {dialectical_framework-2.0.0 → dialectical_framework-2.0.2}/src/dialectical_framework/graph/repositories/decision_repository.py +0 -0
- {dialectical_framework-2.0.0 → dialectical_framework-2.0.2}/src/dialectical_framework/graph/repositories/input_repository.py +0 -0
- {dialectical_framework-2.0.0 → dialectical_framework-2.0.2}/src/dialectical_framework/graph/repositories/nexus_repository.py +0 -0
- {dialectical_framework-2.0.0 → dialectical_framework-2.0.2}/src/dialectical_framework/graph/repositories/node_repository.py +0 -0
- {dialectical_framework-2.0.0 → dialectical_framework-2.0.2}/src/dialectical_framework/graph/repositories/note_repository.py +0 -0
- {dialectical_framework-2.0.0 → dialectical_framework-2.0.2}/src/dialectical_framework/graph/repositories/perspective_repository.py +0 -0
- {dialectical_framework-2.0.0 → dialectical_framework-2.0.2}/src/dialectical_framework/graph/repositories/polarity_repository.py +0 -0
- {dialectical_framework-2.0.0 → dialectical_framework-2.0.2}/src/dialectical_framework/graph/repositories/schema_repository.py +0 -0
- {dialectical_framework-2.0.0 → dialectical_framework-2.0.2}/src/dialectical_framework/graph/repositories/statement_repository.py +0 -0
- {dialectical_framework-2.0.0 → dialectical_framework-2.0.2}/src/dialectical_framework/graph/repositories/transformation_repository.py +0 -0
- {dialectical_framework-2.0.0 → dialectical_framework-2.0.2}/src/dialectical_framework/graph/repositories/wheel_repository.py +0 -0
- {dialectical_framework-2.0.0 → dialectical_framework-2.0.2}/src/dialectical_framework/graph/scope_context.py +0 -0
- {dialectical_framework-2.0.0 → dialectical_framework-2.0.2}/src/dialectical_framework/graph/verbatim_input_resolver.py +0 -0
- {dialectical_framework-2.0.0 → dialectical_framework-2.0.2}/src/dialectical_framework/graph/wheel_segment.py +0 -0
- {dialectical_framework-2.0.0 → dialectical_framework-2.0.2}/src/dialectical_framework/graph/wheel_segment_polar_pair.py +0 -0
- {dialectical_framework-2.0.0 → dialectical_framework-2.0.2}/src/dialectical_framework/protocols/__init__.py +0 -0
- {dialectical_framework-2.0.0 → dialectical_framework-2.0.2}/src/dialectical_framework/protocols/has_config.py +0 -0
- {dialectical_framework-2.0.0 → dialectical_framework-2.0.2}/src/dialectical_framework/protocols/input_resolver.py +0 -0
- {dialectical_framework-2.0.0 → dialectical_framework-2.0.2}/src/dialectical_framework/py.typed +0 -0
- {dialectical_framework-2.0.0 → dialectical_framework-2.0.2}/src/dialectical_framework/settings.py +0 -0
- {dialectical_framework-2.0.0 → dialectical_framework-2.0.2}/src/dialectical_framework/utils/__init__.py +0 -0
- {dialectical_framework-2.0.0 → dialectical_framework-2.0.2}/src/dialectical_framework/utils/async_drain.py +0 -0
- {dialectical_framework-2.0.0 → dialectical_framework-2.0.2}/src/dialectical_framework/utils/call_census.py +0 -0
- {dialectical_framework-2.0.0 → dialectical_framework-2.0.2}/src/dialectical_framework/utils/chunking.py +0 -0
- {dialectical_framework-2.0.0 → dialectical_framework-2.0.2}/src/dialectical_framework/utils/concurrency.py +0 -0
- {dialectical_framework-2.0.0 → dialectical_framework-2.0.2}/src/dialectical_framework/utils/dc_replace.py +0 -0
- {dialectical_framework-2.0.0 → dialectical_framework-2.0.2}/src/dialectical_framework/utils/decompose_probability_uniformly.py +0 -0
- {dialectical_framework-2.0.0 → dialectical_framework-2.0.2}/src/dialectical_framework/utils/edge_context.py +0 -0
- {dialectical_framework-2.0.0 → dialectical_framework-2.0.2}/src/dialectical_framework/utils/effect_logger.py +0 -0
- {dialectical_framework-2.0.0 → dialectical_framework-2.0.2}/src/dialectical_framework/utils/input_context.py +0 -0
- {dialectical_framework-2.0.0 → dialectical_framework-2.0.2}/src/dialectical_framework/utils/order_transitions.py +0 -0
- {dialectical_framework-2.0.0 → dialectical_framework-2.0.2}/src/dialectical_framework/utils/progress.py +0 -0
- {dialectical_framework-2.0.0 → dialectical_framework-2.0.2}/src/dialectical_framework/utils/retry_accounting.py +0 -0
- {dialectical_framework-2.0.0 → dialectical_framework-2.0.2}/src/dialectical_framework/utils/sequence_generation.py +0 -0
- {dialectical_framework-2.0.0 → dialectical_framework-2.0.2}/src/dialectical_framework/utils/thinking_compat.py +0 -0
- {dialectical_framework-2.0.0 → dialectical_framework-2.0.2}/src/dialectical_framework/utils/use_brain.py +0 -0
- {dialectical_framework-2.0.0 → dialectical_framework-2.0.2}/src/dialectical_framework/utils/utterance.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: dialectical-framework
|
|
3
|
-
Version: 2.0.
|
|
3
|
+
Version: 2.0.2
|
|
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 = "2.0.
|
|
3
|
+
version = "2.0.2"
|
|
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"
|
|
@@ -51,7 +51,7 @@ from dialectical_framework.agents.conversation_facilitator import (
|
|
|
51
51
|
FROM_SETTINGS, ConversationFacilitator)
|
|
52
52
|
from dialectical_framework.agents.stream_events import StreamEvent
|
|
53
53
|
from dialectical_framework.agents.turn_timing import TurnTiming
|
|
54
|
-
from dialectical_framework.graph.views import ExplorationView
|
|
54
|
+
from dialectical_framework.graph.views import ExplorationView, PerspectiveView
|
|
55
55
|
|
|
56
56
|
#: Tool verbs → mental acts. These are REWRITES, not drops: dropping every
|
|
57
57
|
#: paragraph that mentions a tool name would also delete the discrimination test
|
|
@@ -324,7 +324,12 @@ class Consultant:
|
|
|
324
324
|
if messages:
|
|
325
325
|
self._conversation.load_messages(messages)
|
|
326
326
|
parts = [p for p in (preamble, method_prompt(include_decision)) if p]
|
|
327
|
-
|
|
327
|
+
# Kept because the view turn's system prompt is this text plus the
|
|
328
|
+
# static half of its request (`exploration_view`): appending keeps the
|
|
329
|
+
# head a PREFIX of it, so the chat turns' cached prefix is readable by
|
|
330
|
+
# the sketch turns instead of each paying for its own.
|
|
331
|
+
self._system_prompt = "\n\n".join(parts)
|
|
332
|
+
self._conversation.set_system_prompt(self._system_prompt)
|
|
328
333
|
|
|
329
334
|
async def chat(self, user_message: str) -> str:
|
|
330
335
|
result = await self._conversation.submit(ChatResponse, user_message)
|
|
@@ -362,7 +367,8 @@ class Consultant:
|
|
|
362
367
|
view shows what has been ESTABLISHED, and where `focus` asks for
|
|
363
368
|
structure not yet worked out ("a perspective for the thesis you
|
|
364
369
|
named") the turn builds it by the method first
|
|
365
|
-
(`concerns/view_sketch.py
|
|
370
|
+
(`concerns/view_sketch.py`: the static instructions ride on the system
|
|
371
|
+
prompt, the focus is the turn). What it built is
|
|
366
372
|
kept in `messages` as the consultant's own words, so the next turn can
|
|
367
373
|
be asked about a corner it just drew.
|
|
368
374
|
|
|
@@ -380,16 +386,30 @@ class Consultant:
|
|
|
380
386
|
"""
|
|
381
387
|
from dialectical_framework.concerns.view_sketch import (
|
|
382
388
|
ViewSketchDto, exploration_view_from_sketch, history_ask,
|
|
383
|
-
history_text,
|
|
389
|
+
history_text, perspective_from_sketch, view_sketch_focus,
|
|
390
|
+
view_sketch_instructions)
|
|
384
391
|
|
|
385
392
|
import asyncio
|
|
393
|
+
import dataclasses
|
|
386
394
|
|
|
387
395
|
from dialectical_framework.concerns.tetrad_candidates import (
|
|
388
|
-
clamp_attempts, select_sketch)
|
|
396
|
+
clamp_attempts, ranking, select_sketch)
|
|
389
397
|
|
|
390
398
|
level = self._conversation._thinking_kwargs().get("thinking")
|
|
391
399
|
history = self._conversation._messages
|
|
392
|
-
request
|
|
400
|
+
# The request travels in two places, not one. Its static half
|
|
401
|
+
# (definitions, rules, the build procedure) goes on the SYSTEM prompt,
|
|
402
|
+
# after this head's own, because that is where the provider's cache
|
|
403
|
+
# breakpoint sits: as part of the user message it followed the history
|
|
404
|
+
# and was re-prefilled at full rate on every draw (~1.2k tokens x N a
|
|
405
|
+
# card, measured 2026-10-06). Only the focus — the person's own ask —
|
|
406
|
+
# is the turn. Same words, and the history record is untouched
|
|
407
|
+
# (`history_ask`, which is why the long request was never kept anyway).
|
|
408
|
+
system = (
|
|
409
|
+
f"{self._system_prompt}\n\n"
|
|
410
|
+
f"{view_sketch_instructions(self._conversation.settings.component_length)}"
|
|
411
|
+
)
|
|
412
|
+
ask = view_sketch_focus(focus)
|
|
393
413
|
|
|
394
414
|
async def draw() -> ViewSketchDto:
|
|
395
415
|
# Each draw works on a COPY of the history: the turns run in
|
|
@@ -397,9 +417,20 @@ class Consultant:
|
|
|
397
417
|
# read. What the view leaves behind is appended once, below.
|
|
398
418
|
sketch_turn = ConversationFacilitator(format_mode="json", thinking=level)
|
|
399
419
|
sketch_turn._messages = list(history)
|
|
400
|
-
|
|
420
|
+
sketch_turn.set_system_prompt(system)
|
|
421
|
+
return await sketch_turn.submit(ViewSketchDto, ask)
|
|
401
422
|
|
|
402
423
|
n = clamp_attempts(attempts)
|
|
424
|
+
# Fully parallel, deliberately. On a COLD cache all N draws write the
|
|
425
|
+
# ~12k system head instead of one writing and the rest reading (~$0.06
|
|
426
|
+
# a card at list prices, 2026-10-06), and the fix would be to await the
|
|
427
|
+
# first draw before firing the rest — but these turns do not stream, so
|
|
428
|
+
# nothing here can tell a cold cache from a warm one, and the stagger
|
|
429
|
+
# would spend a whole draw's latency (~6 s) on EVERY card to save money
|
|
430
|
+
# on the rare one. The 1h breakpoint TTL
|
|
431
|
+
# (`utils/bedrock_provider.py::_CACHE_TTL`) attacks the same cost
|
|
432
|
+
# without the latency, which is the trade taken. Revisit only with a
|
|
433
|
+
# measured cold-start rate.
|
|
403
434
|
drawn = await asyncio.gather(*(draw() for _ in range(n)), return_exceptions=True)
|
|
404
435
|
sketches = [d for d in drawn if not isinstance(d, BaseException)]
|
|
405
436
|
if not sketches:
|
|
@@ -407,19 +438,24 @@ class Consultant:
|
|
|
407
438
|
# With several draws the framework's own coherence check picks the one
|
|
408
439
|
# whose FIRST tension holds best (`concerns/tetrad_candidates.py`); a
|
|
409
440
|
# draw with no complete first tension sorts last. The card a blindspot
|
|
410
|
-
# screen shows is that first tension.
|
|
441
|
+
# screen shows is that first tension. The judged draws that lost come
|
|
442
|
+
# back too, best first, as `runners_up` — a host's "again"/"another"
|
|
443
|
+
# can serve one without a new call (the module's stated intent).
|
|
444
|
+
runners_up: list[PerspectiveView] = []
|
|
411
445
|
if len(sketches) > 1:
|
|
412
446
|
firsts = [s.tensions[0] if s.tensions else None for s in sketches]
|
|
413
447
|
judgeable = [i for i, t in enumerate(firsts) if t is not None and all(
|
|
414
448
|
(getattr(t, k) or "").strip() for k in ("thesis", "antithesis", "t_plus", "t_minus", "a_plus", "a_minus"))]
|
|
415
449
|
if len(judgeable) > 1:
|
|
416
|
-
|
|
417
|
-
|
|
450
|
+
_best, verdicts = await select_sketch([firsts[i] for i in judgeable])
|
|
451
|
+
order = ranking(verdicts)
|
|
452
|
+
sketch = sketches[judgeable[order[0]]]
|
|
453
|
+
runners_up = [perspective_from_sketch(firsts[judgeable[i]]) for i in order[1:]]
|
|
418
454
|
else:
|
|
419
455
|
sketch = sketches[judgeable[0]] if judgeable else sketches[0]
|
|
420
456
|
else:
|
|
421
457
|
sketch = sketches[0]
|
|
422
|
-
view = exploration_view_from_sketch(sketch)
|
|
458
|
+
view = dataclasses.replace(exploration_view_from_sketch(sketch), runners_up=runners_up)
|
|
423
459
|
# What the turn leaves behind is NOT what it sent: the long request and
|
|
424
460
|
# the DTO come off, and the person's ask plus the drawing in words go
|
|
425
461
|
# on. Kept as a request→prose pair, the long request taught the next
|
|
@@ -429,6 +429,12 @@ class ConversationFacilitator(SettingsAware):
|
|
|
429
429
|
str((p.get("text") if isinstance(p, dict) else getattr(p, "text", "")) or "")
|
|
430
430
|
for p in parts
|
|
431
431
|
)
|
|
432
|
+
elif isinstance(content, dict) and content.get("type") == "text":
|
|
433
|
+
# A SYSTEM message's content is ONE text part, not a list, and the
|
|
434
|
+
# documented recipe (`dataclasses.asdict`) renders it as a dict —
|
|
435
|
+
# the shape a host's saved history carries for the prompt at
|
|
436
|
+
# position 0 (found by the first app's backend, 2026-10-05).
|
|
437
|
+
text = str(content.get("text") or "")
|
|
432
438
|
elif isinstance(content, str) or content is None:
|
|
433
439
|
text = content or ""
|
|
434
440
|
else:
|
|
@@ -32,7 +32,7 @@ from __future__ import annotations
|
|
|
32
32
|
|
|
33
33
|
import asyncio
|
|
34
34
|
from dataclasses import dataclass
|
|
35
|
-
from typing import TYPE_CHECKING
|
|
35
|
+
from typing import TYPE_CHECKING, Optional, Sequence
|
|
36
36
|
|
|
37
37
|
from pydantic import BaseModel, Field
|
|
38
38
|
|
|
@@ -97,6 +97,107 @@ class CoherenceEvaluationDto(BaseModel):
|
|
|
97
97
|
)
|
|
98
98
|
|
|
99
99
|
|
|
100
|
+
class TetradCoherenceDto(BaseModel):
|
|
101
|
+
"""One tetrad's two control statements, from the JOINT call.
|
|
102
|
+
|
|
103
|
+
No DV field, deliberately. DV is an annotation that only `resolve`
|
|
104
|
+
persists (`DialecticalValidityEstimation`) and nothing reads it when
|
|
105
|
+
choosing among drafts — `SketchVerdict` has no DV term — so asking for it
|
|
106
|
+
here would buy output tokens and nothing else. A draft that goes on to be
|
|
107
|
+
persisted is scored by `resolve` afterwards, DV and all.
|
|
108
|
+
"""
|
|
109
|
+
|
|
110
|
+
tetrad: int = Field(
|
|
111
|
+
description=(
|
|
112
|
+
"The number of the tetrad this verdict scores, exactly as labelled "
|
|
113
|
+
"in the request."
|
|
114
|
+
)
|
|
115
|
+
)
|
|
116
|
+
t_plus_without_a_plus_yields_t_minus: float = Field(
|
|
117
|
+
ge=0.0,
|
|
118
|
+
le=1.0,
|
|
119
|
+
description="Conceptual Coherence (0.0-1.0) of that tetrad's statement (a).",
|
|
120
|
+
)
|
|
121
|
+
a_plus_without_t_plus_yields_a_minus: float = Field(
|
|
122
|
+
ge=0.0,
|
|
123
|
+
le=1.0,
|
|
124
|
+
description="Conceptual Coherence (0.0-1.0) of that tetrad's statement (b).",
|
|
125
|
+
)
|
|
126
|
+
reasoning: str = Field(
|
|
127
|
+
description=(
|
|
128
|
+
"One sentence on the weaker of that tetrad's two statements. A note, "
|
|
129
|
+
"not an essay."
|
|
130
|
+
)
|
|
131
|
+
)
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
class JointCoherenceEvaluationDto(BaseModel):
|
|
135
|
+
"""Every tetrad of one request, scored in one call."""
|
|
136
|
+
|
|
137
|
+
verdicts: list[TetradCoherenceDto] = Field(
|
|
138
|
+
description=(
|
|
139
|
+
"One entry per tetrad in the request, in the request's order. Score "
|
|
140
|
+
"every tetrad; omit none."
|
|
141
|
+
)
|
|
142
|
+
)
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
@dataclass(frozen=True)
|
|
146
|
+
class TetradTexts:
|
|
147
|
+
"""The four aspect texts of one tetrad — all the control statements need.
|
|
148
|
+
|
|
149
|
+
What `score_texts_many` takes, so a caller holding several drafts as text
|
|
150
|
+
cannot mix up the order of four positional strings.
|
|
151
|
+
"""
|
|
152
|
+
|
|
153
|
+
t_plus: str
|
|
154
|
+
t_minus: str
|
|
155
|
+
a_plus: str
|
|
156
|
+
a_minus: str
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
def control_statements(
|
|
160
|
+
*, t_plus: str, t_minus: str, a_plus: str, a_minus: str
|
|
161
|
+
) -> tuple[str, str]:
|
|
162
|
+
"""The paper's two control statements, built from four texts.
|
|
163
|
+
|
|
164
|
+
One builder for all three askers (`resolve` off a committed Perspective,
|
|
165
|
+
`score_texts` and `score_texts_many` off bare texts) so the quoting cannot
|
|
166
|
+
drift: a verdict taken before persistence and one taken after must be the
|
|
167
|
+
same question asked of the same words.
|
|
168
|
+
"""
|
|
169
|
+
return (
|
|
170
|
+
f'"{t_plus}" without "{a_plus}" yields "{t_minus}"',
|
|
171
|
+
f'"{a_plus}" without "{t_plus}" yields "{a_minus}"',
|
|
172
|
+
)
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
# --- Shared prompt text ---
|
|
176
|
+
#
|
|
177
|
+
# Written once because two prompts ask for it: one statement at a time
|
|
178
|
+
# (`_evaluate_control_statement`, for `resolve` and `score_texts`) and all of
|
|
179
|
+
# several tetrads at once (`score_texts_many`). Re-typed inline the scales
|
|
180
|
+
# would drift, and a verdict from one prompt is ranked against a verdict from
|
|
181
|
+
# the other (`concerns/tetrad_candidates.py`).
|
|
182
|
+
|
|
183
|
+
_PATTERN = """The pattern "[Positive] without [Balancing factor] yields [Negative]" tests whether
|
|
184
|
+
the absence of a balancing positive aspect naturally leads to the negative/shadow aspect."""
|
|
185
|
+
|
|
186
|
+
_CC_SCALE = """Conceptual Coherence (CC) — is the statement logically meaningful?
|
|
187
|
+
- 0.9-1.0: Highly coherent, clear logical/causal relationship
|
|
188
|
+
- 0.7-0.9: Coherent, reasonable logical connection
|
|
189
|
+
- 0.5-0.7: Somewhat coherent, plausible but weak
|
|
190
|
+
- 0.3-0.5: Weak coherence, tenuous connection
|
|
191
|
+
- 0.0-0.3: Not coherent, no clear logical relationship"""
|
|
192
|
+
|
|
193
|
+
_DV_SCALE = """Dialectical Validity (DV) — is the dialectical relationship natural, judged
|
|
194
|
+
independently of coherence? A statement can be perfectly coherent yet dialectically
|
|
195
|
+
distorted (e.g. one pole arbitrarily privileged, outcomes enforced by coercion or
|
|
196
|
+
ideology rather than natural system dynamics). 1.0 = natural, balanced, generative;
|
|
197
|
+
0.0 = forced, artificial, distorted. Ignore factual correctness; evaluate only the
|
|
198
|
+
quality of the dialectical relationship as a generative principle."""
|
|
199
|
+
|
|
200
|
+
|
|
100
201
|
# --- Result ---
|
|
101
202
|
|
|
102
203
|
|
|
@@ -182,8 +283,12 @@ class ControlStatementsCheck(ReasonableConcern[ControlStatementsCheckResult]):
|
|
|
182
283
|
a_minus = perspective.get_component(POSITION_A_MINUS)
|
|
183
284
|
|
|
184
285
|
# Build control statements
|
|
185
|
-
stmt_1 =
|
|
186
|
-
|
|
286
|
+
stmt_1, stmt_2 = control_statements(
|
|
287
|
+
t_plus=t_plus.prompt_text,
|
|
288
|
+
t_minus=t_minus.prompt_text,
|
|
289
|
+
a_plus=a_plus.prompt_text,
|
|
290
|
+
a_minus=a_minus.prompt_text,
|
|
291
|
+
)
|
|
187
292
|
|
|
188
293
|
# Evaluate both statements in parallel using isolated conversations
|
|
189
294
|
result_1, result_2 = await asyncio.gather(
|
|
@@ -267,18 +372,90 @@ class ControlStatementsCheck(ReasonableConcern[ControlStatementsCheckResult]):
|
|
|
267
372
|
"""The two control statements on bare texts — no Perspective, no graph.
|
|
268
373
|
|
|
269
374
|
What `resolve` does after reading the four aspects off a committed
|
|
270
|
-
node,
|
|
271
|
-
|
|
272
|
-
the same
|
|
273
|
-
|
|
375
|
+
node, for a caller holding ONE tetrad as text and wanting DV with it.
|
|
376
|
+
Built by the same statement builder as `resolve`, so a verdict here is
|
|
377
|
+
the same question as the one asked after persistence.
|
|
378
|
+
|
|
379
|
+
Not what best-of-N selection uses: a field of drafts goes through
|
|
380
|
+
`score_texts_many`, which asks once for all of them and leaves DV out
|
|
381
|
+
(nothing in selection reads it). Two statements, two calls, both with
|
|
382
|
+
reasoning, is the shape that made judging a third of a card's cost.
|
|
274
383
|
"""
|
|
275
|
-
stmt_1 =
|
|
276
|
-
|
|
384
|
+
stmt_1, stmt_2 = control_statements(
|
|
385
|
+
t_plus=t_plus, t_minus=t_minus, a_plus=a_plus, a_minus=a_minus
|
|
386
|
+
)
|
|
277
387
|
return await asyncio.gather(
|
|
278
388
|
self._evaluate_control_statement(stmt_1, text),
|
|
279
389
|
self._evaluate_control_statement(stmt_2, text),
|
|
280
390
|
)
|
|
281
391
|
|
|
392
|
+
async def score_texts_many(
|
|
393
|
+
self,
|
|
394
|
+
tetrads: Sequence[TetradTexts],
|
|
395
|
+
*,
|
|
396
|
+
text: str = "",
|
|
397
|
+
) -> list[Optional[TetradCoherenceDto]]:
|
|
398
|
+
"""Several tetrads' control statements, all in ONE call.
|
|
399
|
+
|
|
400
|
+
What `score_texts` asks for one draft at a time, asked for a whole
|
|
401
|
+
best-of-N field at once. Measured reason (2026-10-06, the first app's
|
|
402
|
+
real entry points through `utils/call_census.py`): per-statement calls
|
|
403
|
+
made judging 6 of the 11 provider calls behind one card and ~3.3k of
|
|
404
|
+
its ~5.1k output tokens — about a third of the card's cost — because
|
|
405
|
+
each call spent ~550 output tokens of reasoning on one sentence. One
|
|
406
|
+
call with a short note per tetrad replaces six.
|
|
407
|
+
|
|
408
|
+
The question is unchanged and so is its discipline: the judge sees
|
|
409
|
+
only each tetrad's four aspects (never the thesis or antithesis, see
|
|
410
|
+
`concerns/tetrad_candidates.py`), the statements are built by the same
|
|
411
|
+
builder `resolve` uses, and the prompt forbids comparing the drafts —
|
|
412
|
+
selection stays among independent draws. CC only; `TetradCoherenceDto`
|
|
413
|
+
says why DV is not asked for here.
|
|
414
|
+
|
|
415
|
+
Fail-soft per tetrad: a verdict the model omits, or labels with a
|
|
416
|
+
number that is out of range or already filled, comes back `None` for
|
|
417
|
+
that tetrad rather than shifting every later one onto the wrong draft.
|
|
418
|
+
"""
|
|
419
|
+
if not tetrads:
|
|
420
|
+
return []
|
|
421
|
+
|
|
422
|
+
blocks = []
|
|
423
|
+
for n, tetrad in enumerate(tetrads, 1):
|
|
424
|
+
stmt_a, stmt_b = control_statements(
|
|
425
|
+
t_plus=tetrad.t_plus,
|
|
426
|
+
t_minus=tetrad.t_minus,
|
|
427
|
+
a_plus=tetrad.a_plus,
|
|
428
|
+
a_minus=tetrad.a_minus,
|
|
429
|
+
)
|
|
430
|
+
blocks.append(f"Tetrad {n}:\n(a) {stmt_a}\n(b) {stmt_b}")
|
|
431
|
+
|
|
432
|
+
context_section = f"<context>\n{text}\n</context>\n\n" if text else ""
|
|
433
|
+
joined = "\n\n".join(blocks)
|
|
434
|
+
prompt = f"""{context_section}Rate the control statements of {len(tetrads)} tetrads. Each tetrad has two statements, (a) and (b).
|
|
435
|
+
|
|
436
|
+
{joined}
|
|
437
|
+
|
|
438
|
+
{_PATTERN}
|
|
439
|
+
|
|
440
|
+
Rate every statement on one scale:
|
|
441
|
+
|
|
442
|
+
{_CC_SCALE}
|
|
443
|
+
|
|
444
|
+
Score each tetrad on its own, against the pattern above and nothing else. These are independent drafts: do not compare them with each other, do not rank them, do not pick a best, and do not let one tetrad's wording move another's score. The order they appear in carries no information. Return one verdict per tetrad, labelled with that tetrad's number."""
|
|
445
|
+
|
|
446
|
+
conversation = ConversationFacilitator()
|
|
447
|
+
result = await conversation.submit(
|
|
448
|
+
response_model=JointCoherenceEvaluationDto,
|
|
449
|
+
user_content=prompt,
|
|
450
|
+
)
|
|
451
|
+
|
|
452
|
+
placed: list[Optional[TetradCoherenceDto]] = [None] * len(tetrads)
|
|
453
|
+
for verdict in result.verdicts:
|
|
454
|
+
index = verdict.tetrad - 1
|
|
455
|
+
if 0 <= index < len(placed) and placed[index] is None:
|
|
456
|
+
placed[index] = verdict
|
|
457
|
+
return placed
|
|
458
|
+
|
|
282
459
|
async def _evaluate_control_statement(
|
|
283
460
|
self,
|
|
284
461
|
statement: str,
|
|
@@ -291,22 +468,11 @@ class ControlStatementsCheck(ReasonableConcern[ControlStatementsCheckResult]):
|
|
|
291
468
|
|
|
292
469
|
{statement}
|
|
293
470
|
|
|
294
|
-
|
|
295
|
-
the absence of a balancing positive aspect naturally leads to the negative/shadow aspect.
|
|
471
|
+
{_PATTERN}
|
|
296
472
|
|
|
297
|
-
1.
|
|
298
|
-
- 0.9-1.0: Highly coherent, clear logical/causal relationship
|
|
299
|
-
- 0.7-0.9: Coherent, reasonable logical connection
|
|
300
|
-
- 0.5-0.7: Somewhat coherent, plausible but weak
|
|
301
|
-
- 0.3-0.5: Weak coherence, tenuous connection
|
|
302
|
-
- 0.0-0.3: Not coherent, no clear logical relationship
|
|
473
|
+
1. {_CC_SCALE}
|
|
303
474
|
|
|
304
|
-
2.
|
|
305
|
-
independently of coherence? A statement can be perfectly coherent yet dialectically
|
|
306
|
-
distorted (e.g. one pole arbitrarily privileged, outcomes enforced by coercion or
|
|
307
|
-
ideology rather than natural system dynamics). 1.0 = natural, balanced, generative;
|
|
308
|
-
0.0 = forced, artificial, distorted. Ignore factual correctness; evaluate only the
|
|
309
|
-
quality of the dialectical relationship as a generative principle."""
|
|
475
|
+
2. {_DV_SCALE}"""
|
|
310
476
|
|
|
311
477
|
conversation = ConversationFacilitator()
|
|
312
478
|
return await conversation.submit(
|
|
@@ -31,13 +31,12 @@ holding a text or an object with `.statement` (`TetradDto`'s `AspectDto`).
|
|
|
31
31
|
|
|
32
32
|
from __future__ import annotations
|
|
33
33
|
|
|
34
|
-
import asyncio
|
|
35
34
|
import logging
|
|
36
35
|
from dataclasses import dataclass
|
|
37
36
|
from typing import Any, Optional, Sequence
|
|
38
37
|
|
|
39
|
-
from dialectical_framework.concerns.control_statements_check import
|
|
40
|
-
ControlStatementsCheck
|
|
38
|
+
from dialectical_framework.concerns.control_statements_check import (
|
|
39
|
+
ControlStatementsCheck, TetradTexts)
|
|
41
40
|
from dialectical_framework.graph.nodes.estimation import \
|
|
42
41
|
CONCEPTUAL_COHERENCE_THRESHOLD
|
|
43
42
|
|
|
@@ -106,49 +105,96 @@ def is_complete(candidate: Any) -> bool:
|
|
|
106
105
|
)
|
|
107
106
|
|
|
108
107
|
|
|
109
|
-
async def
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
108
|
+
async def judge_sketches(
|
|
109
|
+
candidates: Sequence[Any], context: str = ""
|
|
110
|
+
) -> list[Optional[SketchVerdict]]:
|
|
111
|
+
"""The validator's two control statements for EVERY candidate, in ONE call.
|
|
112
|
+
|
|
113
|
+
Statement texts are built by the same builder `ControlStatementsCheck.resolve`
|
|
114
|
+
uses, so a verdict here and one taken after persistence are the same
|
|
115
|
+
question asked of the same words. The judge still sees only the four
|
|
116
|
+
aspects of each draft — never the thesis or the antithesis — and the
|
|
117
|
+
prompt forbids comparing the drafts with each other, so selection stays
|
|
118
|
+
"among independent draws" (this module's docstring) and this call cannot
|
|
119
|
+
be turned into regeneration.
|
|
120
|
+
|
|
121
|
+
Why one call: judging used to be a call per control statement per
|
|
122
|
+
candidate — 6 of the 11 provider calls behind one best-of-3 card, and
|
|
123
|
+
~3.3k of its ~5.1k output tokens, about a third of its cost (measured
|
|
124
|
+
2026-10-06 on the first app's real entry points with
|
|
125
|
+
`utils/call_census.py`). One call with a short note per tetrad replaces
|
|
126
|
+
six long ones.
|
|
127
|
+
|
|
128
|
+
Fail-soft twice over. A verdict the model omits is `None` for that
|
|
129
|
+
candidate, which sorts last; a failed call leaves EVERY candidate
|
|
130
|
+
unjudged, which keeps the first draw — exactly what `attempts=1` would
|
|
131
|
+
have done. The second shape is new with the joint call and is the cost of
|
|
132
|
+
it: per-statement calls could lose one candidate's verdict and keep the
|
|
133
|
+
rest.
|
|
134
|
+
|
|
135
|
+
OPEN, because this was a cost change and not a quality one. Judge
|
|
136
|
+
stability was 92% on re-judging the same text when each statement had its
|
|
137
|
+
own call (`docs/dev-notes/antithesis-selection.md`, "Best-of-N"); the
|
|
138
|
+
joint call's stability, and its position bias (a candidate winning for
|
|
139
|
+
being listed first), are unmeasured — measure both with the same probe,
|
|
140
|
+
and the card quality with the app's paired eval, before reading any
|
|
141
|
+
quality claim into this. If stability falls below ~85%, the rung down is
|
|
142
|
+
one call per candidate with both statements in one DTO, which is still
|
|
143
|
+
half the output of the per-statement shape.
|
|
115
144
|
"""
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
145
|
+
try:
|
|
146
|
+
verdicts = await ControlStatementsCheck().score_texts_many(
|
|
147
|
+
[
|
|
148
|
+
TetradTexts(
|
|
149
|
+
t_plus=aspect_text(c.t_plus),
|
|
150
|
+
t_minus=aspect_text(c.t_minus),
|
|
151
|
+
a_plus=aspect_text(c.a_plus),
|
|
152
|
+
a_minus=aspect_text(c.a_minus),
|
|
153
|
+
)
|
|
154
|
+
for c in candidates
|
|
155
|
+
],
|
|
156
|
+
text=context,
|
|
157
|
+
)
|
|
158
|
+
except Exception as exc: # noqa: BLE001 — degrade to "all unjudged"
|
|
159
|
+
logger.warning("Judging the sketches failed; the first draw is kept: %s", exc)
|
|
160
|
+
return [None] * len(candidates)
|
|
161
|
+
return [
|
|
162
|
+
None
|
|
163
|
+
if v is None
|
|
164
|
+
else SketchVerdict(
|
|
165
|
+
t_plus_without_a_plus=v.t_plus_without_a_plus_yields_t_minus,
|
|
166
|
+
a_plus_without_t_plus=v.a_plus_without_t_plus_yields_a_minus,
|
|
167
|
+
reasoning=v.reasoning,
|
|
168
|
+
)
|
|
169
|
+
for v in verdicts
|
|
170
|
+
]
|
|
128
171
|
|
|
129
172
|
|
|
130
173
|
async def select_sketch(
|
|
131
174
|
candidates: Sequence[Any], context: str = ""
|
|
132
175
|
) -> tuple[int, list[Optional[SketchVerdict]]]:
|
|
133
|
-
"""Judge every candidate (
|
|
134
|
-
all verdicts (None where
|
|
176
|
+
"""Judge every candidate (one call) and return the index of the best plus
|
|
177
|
+
all verdicts (None where a verdict did not come back).
|
|
135
178
|
|
|
136
179
|
Ranking: the weaker control statement first (the pass rule), then the
|
|
137
180
|
mean, then the earlier draw — a deterministic tie-break, not a preference.
|
|
138
|
-
|
|
139
|
-
|
|
181
|
+
An unjudged candidate sorts last; if no verdict came back at all the first
|
|
182
|
+
draw is kept, which is exactly what `attempts=1` would have done.
|
|
183
|
+
|
|
184
|
+
ONE candidate is never judged: there is nothing to select among, so
|
|
185
|
+
`attempts=1` costs no extra call.
|
|
140
186
|
"""
|
|
141
187
|
if len(candidates) == 1:
|
|
142
188
|
return 0, [None]
|
|
143
189
|
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
except Exception as exc: # noqa: BLE001 — degrade to "unjudged"
|
|
148
|
-
logger.warning("Judging a sketch failed; it sorts last: %s", exc)
|
|
149
|
-
return None
|
|
190
|
+
verdicts = await judge_sketches(candidates, context)
|
|
191
|
+
return ranking(verdicts)[0], verdicts
|
|
192
|
+
|
|
150
193
|
|
|
151
|
-
|
|
194
|
+
def ranking(verdicts: Sequence[Optional[SketchVerdict]]) -> list[int]:
|
|
195
|
+
"""Candidate indices best first, by `select_sketch`'s rule: the weaker
|
|
196
|
+
control statement, then the mean, then the earlier draw; unjudged last.
|
|
197
|
+
`[0]` is the winner; the rest are the runners-up in order."""
|
|
152
198
|
|
|
153
199
|
def key(index: int) -> tuple[int, float, float, int]:
|
|
154
200
|
verdict = verdicts[index]
|
|
@@ -157,5 +203,4 @@ async def select_sketch(
|
|
|
157
203
|
mean = (verdict.t_plus_without_a_plus + verdict.a_plus_without_t_plus) / 2
|
|
158
204
|
return (1, verdict.floor, mean, -index)
|
|
159
205
|
|
|
160
|
-
|
|
161
|
-
return best, verdicts
|
|
206
|
+
return sorted(range(len(verdicts)), key=key, reverse=True)
|
|
@@ -186,8 +186,22 @@ For each pair:
|
|
|
186
186
|
3. Re-read both aspects against step 1 for two distinct failures. (a) Wrong parent: if one is really the OTHER parent developed, rewrite it from its own parent. (b) {PLUS_RESTATEMENT_CHECK}"""
|
|
187
187
|
|
|
188
188
|
|
|
189
|
-
def
|
|
190
|
-
"""The
|
|
189
|
+
def view_sketch_focus(focus: Optional[str]) -> str:
|
|
190
|
+
"""The one per-call line of the request: what this draw is to show.
|
|
191
|
+
|
|
192
|
+
Split from the instructions so the Consultant can send this alone as the
|
|
193
|
+
turn's user message and carry the static half on the system prompt, where
|
|
194
|
+
the provider's cache breakpoint already sits (`view_sketch_instructions`).
|
|
195
|
+
"""
|
|
196
|
+
return (
|
|
197
|
+
f"What to show: {focus}"
|
|
198
|
+
if focus and focus.strip()
|
|
199
|
+
else "What to show: the tensions this conversation has worked out so far."
|
|
200
|
+
)
|
|
201
|
+
|
|
202
|
+
|
|
203
|
+
def view_sketch_instructions(max_words: int) -> str:
|
|
204
|
+
"""The STATIC half of the view turn's request — everything but the focus.
|
|
191
205
|
|
|
192
206
|
Two instructions in one, because the ask is one or the other and the model
|
|
193
207
|
knows which: render what is ESTABLISHED (a tension already named and taken
|
|
@@ -203,15 +217,20 @@ def view_sketch_prompt(focus: Optional[str], max_words: int) -> str:
|
|
|
203
217
|
(docs/dev-notes/antithesis-selection.md, 2026-10-02). The thesis is asked
|
|
204
218
|
for as a STANCE (`THESIS_IS_A_STANCE`): on 20 questions the turn echoed the
|
|
205
219
|
question as T in 6 before the ask was here (the alsotrue app repo, docs/lab-notes-entry.md).
|
|
220
|
+
|
|
221
|
+
Static because every word of it is fixed per deployment (`max_words` comes
|
|
222
|
+
from settings), which is what lets the Consultant append it to its system
|
|
223
|
+
prompt: as a user message it sat after the history and was re-prefilled at
|
|
224
|
+
full rate on every draw — ~1.2k tokens x N per card, measured 2026-10-06.
|
|
225
|
+
On the system prompt it rides inside the cached prefix. This is a cache
|
|
226
|
+
placement, not a content change; the one thing it does move is WHERE the
|
|
227
|
+
focus line sits, from just under the opening paragraph to the end of the
|
|
228
|
+
turn (`tests/e2e/probe_view_turn_ask.py` is the instrument if that is ever
|
|
229
|
+
suspected of mattering).
|
|
206
230
|
"""
|
|
207
|
-
focus_line = (
|
|
208
|
-
f"What to show: {focus}\n\n"
|
|
209
|
-
if focus and focus.strip()
|
|
210
|
-
else "What to show: the tensions this conversation has worked out so far.\n\n"
|
|
211
|
-
)
|
|
212
231
|
return f"""Draw the structure of this conversation as dialectical tetrads. Answer with ONE JSON object in the requested schema and nothing else — no prose. Earlier drawings in this conversation are recorded in prose; that is their record, not the format of this answer.
|
|
213
232
|
|
|
214
|
-
{
|
|
233
|
+
{ASPECT_DEFINITIONS}
|
|
215
234
|
|
|
216
235
|
Rules for what goes in the view:
|
|
217
236
|
- Show what this conversation has ESTABLISHED: a tension you named and the person took up is drawn as you both have it, in their terms. Do not re-derive what is already on the table.
|
|
@@ -228,6 +247,17 @@ Rules for what goes in the view:
|
|
|
228
247
|
Write every position in the person's own terms, {max_words} words or fewer each."""
|
|
229
248
|
|
|
230
249
|
|
|
250
|
+
def view_sketch_prompt(focus: Optional[str], max_words: int) -> str:
|
|
251
|
+
"""The whole request as ONE user message: the instructions and the focus.
|
|
252
|
+
|
|
253
|
+
The shape for a caller with no system prompt of its own to append to — the
|
|
254
|
+
probes and the harness. The Consultant sends the two halves apart
|
|
255
|
+
(`view_sketch_instructions` on the system prompt, `view_sketch_focus` as
|
|
256
|
+
the turn), which is the same words in a different place.
|
|
257
|
+
"""
|
|
258
|
+
return f"{view_sketch_instructions(max_words)}\n\n{view_sketch_focus(focus)}"
|
|
259
|
+
|
|
260
|
+
|
|
231
261
|
# --- The view shaping (pure) ----------------------------------------------
|
|
232
262
|
|
|
233
263
|
|
{dialectical_framework-2.0.0 → dialectical_framework-2.0.2}/src/dialectical_framework/graph/views.py
RENAMED
|
@@ -339,12 +339,18 @@ class ExplorationView:
|
|
|
339
339
|
perspectives: list[PerspectiveView] = field(default_factory=list)
|
|
340
340
|
#: The Nexus this is a view of; None when it is the whole case.
|
|
341
341
|
nexus_hash: Optional[str] = None
|
|
342
|
+
#: The best-of-N draws that lost, best first — the first tension of each
|
|
343
|
+
#: (`concerns/tetrad_candidates.py`: "returned, not thrown away"). A
|
|
344
|
+
#: host's "again"/"another" can show one without a new reasoning call.
|
|
345
|
+
#: Empty for a view read off the graph, or drawn once.
|
|
346
|
+
runners_up: list[PerspectiveView] = field(default_factory=list)
|
|
342
347
|
|
|
343
348
|
def without_terminology(self) -> ExplorationView:
|
|
344
349
|
return dataclasses.replace(
|
|
345
350
|
self,
|
|
346
351
|
perspectives=[p.without_terminology() for p in self.perspectives],
|
|
347
352
|
nexus_hash=None,
|
|
353
|
+
runners_up=[p.without_terminology() for p in self.runners_up],
|
|
348
354
|
)
|
|
349
355
|
|
|
350
356
|
def to_dict(self) -> dict[str, Any]:
|