dialectical-framework 2.1.1__tar.gz → 2.1.3__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.1.1 → dialectical_framework-2.1.3}/PKG-INFO +2 -2
- {dialectical_framework-2.1.1 → dialectical_framework-2.1.3}/pyproject.toml +8 -2
- {dialectical_framework-2.1.1 → dialectical_framework-2.1.3}/src/dialectical_framework/agents/consultant/consultant.py +23 -2
- {dialectical_framework-2.1.1 → dialectical_framework-2.1.3}/src/dialectical_framework/agents/conversation_facilitator.py +15 -1
- {dialectical_framework-2.1.1 → dialectical_framework-2.1.3}/src/dialectical_framework/concerns/antithesis_extraction.py +17 -4
- dialectical_framework-2.1.3/src/dialectical_framework/concerns/attribution_check.py +148 -0
- {dialectical_framework-2.1.1 → dialectical_framework-2.1.3}/src/dialectical_framework/concerns/tetrad_candidates.py +118 -14
- {dialectical_framework-2.1.1 → dialectical_framework-2.1.3}/src/dialectical_framework/concerns/tetrad_sketch.py +8 -1
- dialectical_framework-2.1.3/src/dialectical_framework/concerns/thesis_fit_check.py +165 -0
- {dialectical_framework-2.1.1 → dialectical_framework-2.1.3}/src/dialectical_framework/concerns/view_sketch.py +97 -10
- {dialectical_framework-2.1.1 → dialectical_framework-2.1.3}/src/dialectical_framework/graph/views.py +9 -0
- {dialectical_framework-2.1.1 → dialectical_framework-2.1.3}/src/dialectical_framework/settings.py +29 -3
- {dialectical_framework-2.1.1 → dialectical_framework-2.1.3}/src/dialectical_framework/utils/bedrock_provider.py +228 -62
- {dialectical_framework-2.1.1 → dialectical_framework-2.1.3}/src/dialectical_framework/utils/format_compat.py +65 -1
- {dialectical_framework-2.1.1 → dialectical_framework-2.1.3}/src/dialectical_framework/utils/thinking_compat.py +21 -0
- {dialectical_framework-2.1.1 → dialectical_framework-2.1.3}/src/dialectical_framework/utils/use_brain.py +56 -2
- {dialectical_framework-2.1.1 → dialectical_framework-2.1.3}/LICENSE +0 -0
- {dialectical_framework-2.1.1 → dialectical_framework-2.1.3}/PATENTS +0 -0
- {dialectical_framework-2.1.1 → dialectical_framework-2.1.3}/README.md +0 -0
- {dialectical_framework-2.1.1 → dialectical_framework-2.1.3}/src/dialectical_framework/__init__.py +0 -0
- {dialectical_framework-2.1.1 → dialectical_framework-2.1.3}/src/dialectical_framework/agents/__init__.py +0 -0
- {dialectical_framework-2.1.1 → dialectical_framework-2.1.3}/src/dialectical_framework/agents/advisor/__init__.py +0 -0
- {dialectical_framework-2.1.1 → dialectical_framework-2.1.3}/src/dialectical_framework/agents/advisor/advisor.py +0 -0
- {dialectical_framework-2.1.1 → dialectical_framework-2.1.3}/src/dialectical_framework/agents/advisor/build_policy.py +0 -0
- {dialectical_framework-2.1.1 → dialectical_framework-2.1.3}/src/dialectical_framework/agents/advisor/migration.py +0 -0
- {dialectical_framework-2.1.1 → dialectical_framework-2.1.3}/src/dialectical_framework/agents/advisor/reply_hygiene.py +0 -0
- {dialectical_framework-2.1.1 → dialectical_framework-2.1.3}/src/dialectical_framework/agents/advisor/system_prompts.py +0 -0
- {dialectical_framework-2.1.1 → dialectical_framework-2.1.3}/src/dialectical_framework/agents/advisor/tools/__init__.py +0 -0
- {dialectical_framework-2.1.1 → dialectical_framework-2.1.3}/src/dialectical_framework/agents/advisor/tools/anchor.py +0 -0
- {dialectical_framework-2.1.1 → dialectical_framework-2.1.3}/src/dialectical_framework/agents/advisor/tools/deepen.py +0 -0
- {dialectical_framework-2.1.1 → dialectical_framework-2.1.3}/src/dialectical_framework/agents/advisor/tools/explore.py +0 -0
- {dialectical_framework-2.1.1 → dialectical_framework-2.1.3}/src/dialectical_framework/agents/advisor/tools/ingest.py +0 -0
- {dialectical_framework-2.1.1 → dialectical_framework-2.1.3}/src/dialectical_framework/agents/advisor/tools/note.py +0 -0
- {dialectical_framework-2.1.1 → dialectical_framework-2.1.3}/src/dialectical_framework/agents/advisor/tools/record_decision.py +0 -0
- {dialectical_framework-2.1.1 → dialectical_framework-2.1.3}/src/dialectical_framework/agents/advisor/tools/scoped.py +0 -0
- {dialectical_framework-2.1.1 → dialectical_framework-2.1.3}/src/dialectical_framework/agents/advisor/tools/sync.py +0 -0
- {dialectical_framework-2.1.1 → dialectical_framework-2.1.3}/src/dialectical_framework/agents/agent_context.py +0 -0
- {dialectical_framework-2.1.1 → dialectical_framework-2.1.3}/src/dialectical_framework/agents/analyst/__init__.py +0 -0
- {dialectical_framework-2.1.1 → dialectical_framework-2.1.3}/src/dialectical_framework/agents/analyst/analyst.py +0 -0
- {dialectical_framework-2.1.1 → dialectical_framework-2.1.3}/src/dialectical_framework/agents/analyst/skills/__init__.py +0 -0
- {dialectical_framework-2.1.1 → dialectical_framework-2.1.3}/src/dialectical_framework/agents/analyst/skills/anchor_theses.py +0 -0
- {dialectical_framework-2.1.1 → dialectical_framework-2.1.3}/src/dialectical_framework/agents/analyst/skills/edit_perspective.py +0 -0
- {dialectical_framework-2.1.1 → dialectical_framework-2.1.3}/src/dialectical_framework/agents/analyst/skills/expand_polarities.py +0 -0
- {dialectical_framework-2.1.1 → dialectical_framework-2.1.3}/src/dialectical_framework/agents/analyst/skills/find_polarities.py +0 -0
- {dialectical_framework-2.1.1 → dialectical_framework-2.1.3}/src/dialectical_framework/agents/analyst/skills/introduce_polarity.py +0 -0
- {dialectical_framework-2.1.1 → dialectical_framework-2.1.3}/src/dialectical_framework/agents/analyst/skills/sketch_tetrad.py +0 -0
- {dialectical_framework-2.1.1 → dialectical_framework-2.1.3}/src/dialectical_framework/agents/analyst/skills/surface_theses.py +0 -0
- {dialectical_framework-2.1.1 → dialectical_framework-2.1.3}/src/dialectical_framework/agents/analyst/system_prompts.py +0 -0
- {dialectical_framework-2.1.1 → dialectical_framework-2.1.3}/src/dialectical_framework/agents/analyst/tools/__init__.py +0 -0
- {dialectical_framework-2.1.1 → dialectical_framework-2.1.3}/src/dialectical_framework/agents/analyst/tools/place_statement.py +0 -0
- {dialectical_framework-2.1.1 → dialectical_framework-2.1.3}/src/dialectical_framework/agents/app_spec.py +0 -0
- {dialectical_framework-2.1.1 → dialectical_framework-2.1.3}/src/dialectical_framework/agents/apps.py +0 -0
- {dialectical_framework-2.1.1 → dialectical_framework-2.1.3}/src/dialectical_framework/agents/consultant/__init__.py +0 -0
- {dialectical_framework-2.1.1 → dialectical_framework-2.1.3}/src/dialectical_framework/agents/execution_report.py +0 -0
- {dialectical_framework-2.1.1 → dialectical_framework-2.1.3}/src/dialectical_framework/agents/explorer/__init__.py +0 -0
- {dialectical_framework-2.1.1 → dialectical_framework-2.1.3}/src/dialectical_framework/agents/explorer/explorer.py +0 -0
- {dialectical_framework-2.1.1 → dialectical_framework-2.1.3}/src/dialectical_framework/agents/explorer/skills/__init__.py +0 -0
- {dialectical_framework-2.1.1 → dialectical_framework-2.1.3}/src/dialectical_framework/agents/explorer/skills/build_wheels.py +0 -0
- {dialectical_framework-2.1.1 → dialectical_framework-2.1.3}/src/dialectical_framework/agents/explorer/skills/explore_transformations.py +0 -0
- {dialectical_framework-2.1.1 → dialectical_framework-2.1.3}/src/dialectical_framework/agents/explorer/skills/generate_synthesis.py +0 -0
- {dialectical_framework-2.1.1 → dialectical_framework-2.1.3}/src/dialectical_framework/agents/explorer/system_prompts.py +0 -0
- {dialectical_framework-2.1.1 → dialectical_framework-2.1.3}/src/dialectical_framework/agents/explorer/tools/__init__.py +0 -0
- {dialectical_framework-2.1.1 → dialectical_framework-2.1.3}/src/dialectical_framework/agents/explorer/tools/create_nexus.py +0 -0
- {dialectical_framework-2.1.1 → dialectical_framework-2.1.3}/src/dialectical_framework/agents/explorer/tools/expand_nexus.py +0 -0
- {dialectical_framework-2.1.1 → dialectical_framework-2.1.3}/src/dialectical_framework/agents/explorer/tools/generate_synthesis.py +0 -0
- {dialectical_framework-2.1.1 → dialectical_framework-2.1.3}/src/dialectical_framework/agents/explorer/tools/present_exploration.py +0 -0
- {dialectical_framework-2.1.1 → dialectical_framework-2.1.3}/src/dialectical_framework/agents/orchestrator/__init__.py +0 -0
- {dialectical_framework-2.1.1 → dialectical_framework-2.1.3}/src/dialectical_framework/agents/orchestrator/tools/__init__.py +0 -0
- {dialectical_framework-2.1.1 → dialectical_framework-2.1.3}/src/dialectical_framework/agents/orchestrator/tools/add_input.py +0 -0
- {dialectical_framework-2.1.1 → dialectical_framework-2.1.3}/src/dialectical_framework/agents/orchestrator/tools/audit_feasibility.py +0 -0
- {dialectical_framework-2.1.1 → dialectical_framework-2.1.3}/src/dialectical_framework/agents/orchestrator/tools/create_dx_input.py +0 -0
- {dialectical_framework-2.1.1 → dialectical_framework-2.1.3}/src/dialectical_framework/agents/orchestrator/tools/digest_input.py +0 -0
- {dialectical_framework-2.1.1 → dialectical_framework-2.1.3}/src/dialectical_framework/agents/orchestrator/tools/discard.py +0 -0
- {dialectical_framework-2.1.1 → dialectical_framework-2.1.3}/src/dialectical_framework/agents/orchestrator/tools/get_schema.py +0 -0
- {dialectical_framework-2.1.1 → dialectical_framework-2.1.3}/src/dialectical_framework/agents/orchestrator/tools/inspect_node.py +0 -0
- {dialectical_framework-2.1.1 → dialectical_framework-2.1.3}/src/dialectical_framework/agents/orchestrator/tools/present_analysis.py +0 -0
- {dialectical_framework-2.1.1 → dialectical_framework-2.1.3}/src/dialectical_framework/agents/orchestrator/tools/query_graph.py +0 -0
- {dialectical_framework-2.1.1 → dialectical_framework-2.1.3}/src/dialectical_framework/agents/orchestrator/tools/read_digest.py +0 -0
- {dialectical_framework-2.1.1 → dialectical_framework-2.1.3}/src/dialectical_framework/agents/orchestrator/tools/read_input.py +0 -0
- {dialectical_framework-2.1.1 → dialectical_framework-2.1.3}/src/dialectical_framework/agents/reasonable_concern.py +0 -0
- {dialectical_framework-2.1.1 → dialectical_framework-2.1.3}/src/dialectical_framework/agents/stream_events.py +0 -0
- {dialectical_framework-2.1.1 → dialectical_framework-2.1.3}/src/dialectical_framework/agents/toolsets.py +0 -0
- {dialectical_framework-2.1.1 → dialectical_framework-2.1.3}/src/dialectical_framework/agents/turn_timing.py +0 -0
- {dialectical_framework-2.1.1 → dialectical_framework-2.1.3}/src/dialectical_framework/concerns/__init__.py +0 -0
- {dialectical_framework-2.1.1 → dialectical_framework-2.1.3}/src/dialectical_framework/concerns/ac_re_taxonomy.py +0 -0
- {dialectical_framework-2.1.1 → dialectical_framework-2.1.3}/src/dialectical_framework/concerns/action_extraction.py +0 -0
- {dialectical_framework-2.1.1 → dialectical_framework-2.1.3}/src/dialectical_framework/concerns/add_input.py +0 -0
- {dialectical_framework-2.1.1 → dialectical_framework-2.1.3}/src/dialectical_framework/concerns/ai_dto/__init__.py +0 -0
- {dialectical_framework-2.1.1 → dialectical_framework-2.1.3}/src/dialectical_framework/concerns/ai_dto/statement_dto.py +0 -0
- {dialectical_framework-2.1.1 → dialectical_framework-2.1.3}/src/dialectical_framework/concerns/ai_dto/statements_deck_dto.py +0 -0
- {dialectical_framework-2.1.1 → dialectical_framework-2.1.3}/src/dialectical_framework/concerns/antithesis_classification.py +0 -0
- {dialectical_framework-2.1.1 → dialectical_framework-2.1.3}/src/dialectical_framework/concerns/antithetical_thesis_detection.py +0 -0
- {dialectical_framework-2.1.1 → dialectical_framework-2.1.3}/src/dialectical_framework/concerns/aspect_classification.py +0 -0
- {dialectical_framework-2.1.1 → dialectical_framework-2.1.3}/src/dialectical_framework/concerns/aspect_generation.py +0 -0
- {dialectical_framework-2.1.1 → dialectical_framework-2.1.3}/src/dialectical_framework/concerns/build_status.py +0 -0
- {dialectical_framework-2.1.1 → dialectical_framework-2.1.3}/src/dialectical_framework/concerns/causality/__init__.py +0 -0
- {dialectical_framework-2.1.1 → dialectical_framework-2.1.3}/src/dialectical_framework/concerns/causality/causality_estimator.py +0 -0
- {dialectical_framework-2.1.1 → dialectical_framework-2.1.3}/src/dialectical_framework/concerns/causality/causality_estimator_balanced.py +0 -0
- {dialectical_framework-2.1.1 → dialectical_framework-2.1.3}/src/dialectical_framework/concerns/causality/causality_estimator_criteria.py +0 -0
- {dialectical_framework-2.1.1 → dialectical_framework-2.1.3}/src/dialectical_framework/concerns/causality/causality_estimator_desirable.py +0 -0
- {dialectical_framework-2.1.1 → dialectical_framework-2.1.3}/src/dialectical_framework/concerns/causality/causality_estimator_feasible.py +0 -0
- {dialectical_framework-2.1.1 → dialectical_framework-2.1.3}/src/dialectical_framework/concerns/causality/causality_estimator_realistic.py +0 -0
- {dialectical_framework-2.1.1 → dialectical_framework-2.1.3}/src/dialectical_framework/concerns/causality/causality_normalizer.py +0 -0
- {dialectical_framework-2.1.1 → dialectical_framework-2.1.3}/src/dialectical_framework/concerns/causality/estimator_resolver.py +0 -0
- {dialectical_framework-2.1.1 → dialectical_framework-2.1.3}/src/dialectical_framework/concerns/causality_estimation.py +0 -0
- {dialectical_framework-2.1.1 → dialectical_framework-2.1.3}/src/dialectical_framework/concerns/control_statements_check.py +0 -0
- {dialectical_framework-2.1.1 → dialectical_framework-2.1.3}/src/dialectical_framework/concerns/create_dx_input.py +0 -0
- {dialectical_framework-2.1.1 → dialectical_framework-2.1.3}/src/dialectical_framework/concerns/create_nexus.py +0 -0
- {dialectical_framework-2.1.1 → dialectical_framework-2.1.3}/src/dialectical_framework/concerns/decision_coherence_check.py +0 -0
- {dialectical_framework-2.1.1 → dialectical_framework-2.1.3}/src/dialectical_framework/concerns/decision_confirmation_check.py +0 -0
- {dialectical_framework-2.1.1 → dialectical_framework-2.1.3}/src/dialectical_framework/concerns/diagonal_oppositions_check.py +0 -0
- {dialectical_framework-2.1.1 → dialectical_framework-2.1.3}/src/dialectical_framework/concerns/dialectical_context.py +0 -0
- {dialectical_framework-2.1.1 → dialectical_framework-2.1.3}/src/dialectical_framework/concerns/discard.py +0 -0
- {dialectical_framework-2.1.1 → dialectical_framework-2.1.3}/src/dialectical_framework/concerns/display_text_edit.py +0 -0
- {dialectical_framework-2.1.1 → dialectical_framework-2.1.3}/src/dialectical_framework/concerns/expand_nexus.py +0 -0
- {dialectical_framework-2.1.1 → dialectical_framework-2.1.3}/src/dialectical_framework/concerns/perspective_combination.py +0 -0
- {dialectical_framework-2.1.1 → dialectical_framework-2.1.3}/src/dialectical_framework/concerns/perspective_validation.py +0 -0
- {dialectical_framework-2.1.1 → dialectical_framework-2.1.3}/src/dialectical_framework/concerns/positive_ac_re_apex_derivation.py +0 -0
- {dialectical_framework-2.1.1 → dialectical_framework-2.1.3}/src/dialectical_framework/concerns/record_decision.py +0 -0
- {dialectical_framework-2.1.1 → dialectical_framework-2.1.3}/src/dialectical_framework/concerns/scoring_scales.py +0 -0
- {dialectical_framework-2.1.1 → dialectical_framework-2.1.3}/src/dialectical_framework/concerns/source_digest.py +0 -0
- {dialectical_framework-2.1.1 → dialectical_framework-2.1.3}/src/dialectical_framework/concerns/statement_classification.py +0 -0
- {dialectical_framework-2.1.1 → dialectical_framework-2.1.3}/src/dialectical_framework/concerns/statement_deduplication.py +0 -0
- {dialectical_framework-2.1.1 → dialectical_framework-2.1.3}/src/dialectical_framework/concerns/statement_headline.py +0 -0
- {dialectical_framework-2.1.1 → dialectical_framework-2.1.3}/src/dialectical_framework/concerns/statement_placement.py +0 -0
- {dialectical_framework-2.1.1 → dialectical_framework-2.1.3}/src/dialectical_framework/concerns/synthesis_generation.py +0 -0
- {dialectical_framework-2.1.1 → dialectical_framework-2.1.3}/src/dialectical_framework/concerns/tetrad_grounding.py +0 -0
- {dialectical_framework-2.1.1 → dialectical_framework-2.1.3}/src/dialectical_framework/concerns/thesis_extraction.py +0 -0
- {dialectical_framework-2.1.1 → dialectical_framework-2.1.3}/src/dialectical_framework/concerns/transformation_audit.py +0 -0
- {dialectical_framework-2.1.1 → dialectical_framework-2.1.3}/src/dialectical_framework/concerns/transformation_generation.py +0 -0
- {dialectical_framework-2.1.1 → dialectical_framework-2.1.3}/src/dialectical_framework/concerns/transformation_sketch.py +0 -0
- {dialectical_framework-2.1.1 → dialectical_framework-2.1.3}/src/dialectical_framework/dialectical_reasoning.py +0 -0
- {dialectical_framework-2.1.1 → dialectical_framework-2.1.3}/src/dialectical_framework/enums/__init__.py +0 -0
- {dialectical_framework-2.1.1 → dialectical_framework-2.1.3}/src/dialectical_framework/enums/causality_preset.py +0 -0
- {dialectical_framework-2.1.1 → dialectical_framework-2.1.3}/src/dialectical_framework/enums/di.py +0 -0
- {dialectical_framework-2.1.1 → dialectical_framework-2.1.3}/src/dialectical_framework/events/__init__.py +0 -0
- {dialectical_framework-2.1.1 → dialectical_framework-2.1.3}/src/dialectical_framework/events/graph_event.py +0 -0
- {dialectical_framework-2.1.1 → dialectical_framework-2.1.3}/src/dialectical_framework/events/graph_event_bus.py +0 -0
- {dialectical_framework-2.1.1 → dialectical_framework-2.1.3}/src/dialectical_framework/events/progress_event.py +0 -0
- {dialectical_framework-2.1.1 → dialectical_framework-2.1.3}/src/dialectical_framework/exceptions/__init__.py +0 -0
- {dialectical_framework-2.1.1 → dialectical_framework-2.1.3}/src/dialectical_framework/exceptions/node_errors.py +0 -0
- {dialectical_framework-2.1.1 → dialectical_framework-2.1.3}/src/dialectical_framework/exceptions/provider_errors.py +0 -0
- {dialectical_framework-2.1.1 → dialectical_framework-2.1.3}/src/dialectical_framework/exceptions/resolver_errors.py +0 -0
- {dialectical_framework-2.1.1 → dialectical_framework-2.1.3}/src/dialectical_framework/graph/__init__.py +0 -0
- {dialectical_framework-2.1.1 → dialectical_framework-2.1.3}/src/dialectical_framework/graph/composite_input_resolver.py +0 -0
- {dialectical_framework-2.1.1 → dialectical_framework-2.1.3}/src/dialectical_framework/graph/dialexity_input_resolver.py +0 -0
- {dialectical_framework-2.1.1 → dialectical_framework-2.1.3}/src/dialectical_framework/graph/estimation_manager.py +0 -0
- {dialectical_framework-2.1.1 → dialectical_framework-2.1.3}/src/dialectical_framework/graph/mixins/__init__.py +0 -0
- {dialectical_framework-2.1.1 → dialectical_framework-2.1.3}/src/dialectical_framework/graph/mixins/incremental_build_mixin.py +0 -0
- {dialectical_framework-2.1.1 → dialectical_framework-2.1.3}/src/dialectical_framework/graph/mixins/intent_mixin.py +0 -0
- {dialectical_framework-2.1.1 → dialectical_framework-2.1.3}/src/dialectical_framework/graph/mixins/persistable_mixin.py +0 -0
- {dialectical_framework-2.1.1 → dialectical_framework-2.1.3}/src/dialectical_framework/graph/nodes/__init__.py +0 -0
- {dialectical_framework-2.1.1 → dialectical_framework-2.1.3}/src/dialectical_framework/graph/nodes/assessable_entity.py +0 -0
- {dialectical_framework-2.1.1 → dialectical_framework-2.1.3}/src/dialectical_framework/graph/nodes/base_node.py +0 -0
- {dialectical_framework-2.1.1 → dialectical_framework-2.1.3}/src/dialectical_framework/graph/nodes/case.py +0 -0
- {dialectical_framework-2.1.1 → dialectical_framework-2.1.3}/src/dialectical_framework/graph/nodes/cycle.py +0 -0
- {dialectical_framework-2.1.1 → dialectical_framework-2.1.3}/src/dialectical_framework/graph/nodes/decision.py +0 -0
- {dialectical_framework-2.1.1 → dialectical_framework-2.1.3}/src/dialectical_framework/graph/nodes/estimation.py +0 -0
- {dialectical_framework-2.1.1 → dialectical_framework-2.1.3}/src/dialectical_framework/graph/nodes/ideas.py +0 -0
- {dialectical_framework-2.1.1 → dialectical_framework-2.1.3}/src/dialectical_framework/graph/nodes/input.py +0 -0
- {dialectical_framework-2.1.1 → dialectical_framework-2.1.3}/src/dialectical_framework/graph/nodes/nexus.py +0 -0
- {dialectical_framework-2.1.1 → dialectical_framework-2.1.3}/src/dialectical_framework/graph/nodes/note.py +0 -0
- {dialectical_framework-2.1.1 → dialectical_framework-2.1.3}/src/dialectical_framework/graph/nodes/perspective.py +0 -0
- {dialectical_framework-2.1.1 → dialectical_framework-2.1.3}/src/dialectical_framework/graph/nodes/polarity.py +0 -0
- {dialectical_framework-2.1.1 → dialectical_framework-2.1.3}/src/dialectical_framework/graph/nodes/rationale.py +0 -0
- {dialectical_framework-2.1.1 → dialectical_framework-2.1.3}/src/dialectical_framework/graph/nodes/statement.py +0 -0
- {dialectical_framework-2.1.1 → dialectical_framework-2.1.3}/src/dialectical_framework/graph/nodes/synthesis.py +0 -0
- {dialectical_framework-2.1.1 → dialectical_framework-2.1.3}/src/dialectical_framework/graph/nodes/transformation.py +0 -0
- {dialectical_framework-2.1.1 → dialectical_framework-2.1.3}/src/dialectical_framework/graph/nodes/transition.py +0 -0
- {dialectical_framework-2.1.1 → dialectical_framework-2.1.3}/src/dialectical_framework/graph/nodes/wheel.py +0 -0
- {dialectical_framework-2.1.1 → dialectical_framework-2.1.3}/src/dialectical_framework/graph/relationship_manager.py +0 -0
- {dialectical_framework-2.1.1 → dialectical_framework-2.1.3}/src/dialectical_framework/graph/relationships/__init__.py +0 -0
- {dialectical_framework-2.1.1 → dialectical_framework-2.1.3}/src/dialectical_framework/graph/relationships/action_reflection_relationship.py +0 -0
- {dialectical_framework-2.1.1 → dialectical_framework-2.1.3}/src/dialectical_framework/graph/relationships/belongs_to_cycle_relationship.py +0 -0
- {dialectical_framework-2.1.1 → dialectical_framework-2.1.3}/src/dialectical_framework/graph/relationships/belongs_to_nexus_relationship.py +0 -0
- {dialectical_framework-2.1.1 → dialectical_framework-2.1.3}/src/dialectical_framework/graph/relationships/changed_to_relationship.py +0 -0
- {dialectical_framework-2.1.1 → dialectical_framework-2.1.3}/src/dialectical_framework/graph/relationships/contradiction_of_relationship.py +0 -0
- {dialectical_framework-2.1.1 → dialectical_framework-2.1.3}/src/dialectical_framework/graph/relationships/critiques_relationship.py +0 -0
- {dialectical_framework-2.1.1 → dialectical_framework-2.1.3}/src/dialectical_framework/graph/relationships/distilled_to_relationship.py +0 -0
- {dialectical_framework-2.1.1 → dialectical_framework-2.1.3}/src/dialectical_framework/graph/relationships/estimates_relationship.py +0 -0
- {dialectical_framework-2.1.1 → dialectical_framework-2.1.3}/src/dialectical_framework/graph/relationships/explains_relationship.py +0 -0
- {dialectical_framework-2.1.1 → dialectical_framework-2.1.3}/src/dialectical_framework/graph/relationships/grounded_in_relationship.py +0 -0
- {dialectical_framework-2.1.1 → dialectical_framework-2.1.3}/src/dialectical_framework/graph/relationships/has_input_relationship.py +0 -0
- {dialectical_framework-2.1.1 → dialectical_framework-2.1.3}/src/dialectical_framework/graph/relationships/has_statement_relationship.py +0 -0
- {dialectical_framework-2.1.1 → dialectical_framework-2.1.3}/src/dialectical_framework/graph/relationships/has_wheel_relationship.py +0 -0
- {dialectical_framework-2.1.1 → dialectical_framework-2.1.3}/src/dialectical_framework/graph/relationships/immutable_structure.py +0 -0
- {dialectical_framework-2.1.1 → dialectical_framework-2.1.3}/src/dialectical_framework/graph/relationships/is_source_of_relationship.py +0 -0
- {dialectical_framework-2.1.1 → dialectical_framework-2.1.3}/src/dialectical_framework/graph/relationships/is_target_of_relationship.py +0 -0
- {dialectical_framework-2.1.1 → dialectical_framework-2.1.3}/src/dialectical_framework/graph/relationships/negative_side_of_relationship.py +0 -0
- {dialectical_framework-2.1.1 → dialectical_framework-2.1.3}/src/dialectical_framework/graph/relationships/opposite_direction_relationship.py +0 -0
- {dialectical_framework-2.1.1 → dialectical_framework-2.1.3}/src/dialectical_framework/graph/relationships/opposite_of_relationship.py +0 -0
- {dialectical_framework-2.1.1 → dialectical_framework-2.1.3}/src/dialectical_framework/graph/relationships/polarity_relationship.py +0 -0
- {dialectical_framework-2.1.1 → dialectical_framework-2.1.3}/src/dialectical_framework/graph/relationships/positive_side_of_relationship.py +0 -0
- {dialectical_framework-2.1.1 → dialectical_framework-2.1.3}/src/dialectical_framework/graph/relationships/provides_relationship.py +0 -0
- {dialectical_framework-2.1.1 → dialectical_framework-2.1.3}/src/dialectical_framework/graph/relationships/synthesis_of_relationship.py +0 -0
- {dialectical_framework-2.1.1 → dialectical_framework-2.1.3}/src/dialectical_framework/graph/rendering.py +0 -0
- {dialectical_framework-2.1.1 → dialectical_framework-2.1.3}/src/dialectical_framework/graph/repositories/__init__.py +0 -0
- {dialectical_framework-2.1.1 → dialectical_framework-2.1.3}/src/dialectical_framework/graph/repositories/case_repository.py +0 -0
- {dialectical_framework-2.1.1 → dialectical_framework-2.1.3}/src/dialectical_framework/graph/repositories/cycle_repository.py +0 -0
- {dialectical_framework-2.1.1 → dialectical_framework-2.1.3}/src/dialectical_framework/graph/repositories/decision_repository.py +0 -0
- {dialectical_framework-2.1.1 → dialectical_framework-2.1.3}/src/dialectical_framework/graph/repositories/input_repository.py +0 -0
- {dialectical_framework-2.1.1 → dialectical_framework-2.1.3}/src/dialectical_framework/graph/repositories/nexus_repository.py +0 -0
- {dialectical_framework-2.1.1 → dialectical_framework-2.1.3}/src/dialectical_framework/graph/repositories/node_repository.py +0 -0
- {dialectical_framework-2.1.1 → dialectical_framework-2.1.3}/src/dialectical_framework/graph/repositories/note_repository.py +0 -0
- {dialectical_framework-2.1.1 → dialectical_framework-2.1.3}/src/dialectical_framework/graph/repositories/perspective_repository.py +0 -0
- {dialectical_framework-2.1.1 → dialectical_framework-2.1.3}/src/dialectical_framework/graph/repositories/polarity_repository.py +0 -0
- {dialectical_framework-2.1.1 → dialectical_framework-2.1.3}/src/dialectical_framework/graph/repositories/schema_repository.py +0 -0
- {dialectical_framework-2.1.1 → dialectical_framework-2.1.3}/src/dialectical_framework/graph/repositories/statement_repository.py +0 -0
- {dialectical_framework-2.1.1 → dialectical_framework-2.1.3}/src/dialectical_framework/graph/repositories/transformation_repository.py +0 -0
- {dialectical_framework-2.1.1 → dialectical_framework-2.1.3}/src/dialectical_framework/graph/repositories/wheel_repository.py +0 -0
- {dialectical_framework-2.1.1 → dialectical_framework-2.1.3}/src/dialectical_framework/graph/scope_context.py +0 -0
- {dialectical_framework-2.1.1 → dialectical_framework-2.1.3}/src/dialectical_framework/graph/verbatim_input_resolver.py +0 -0
- {dialectical_framework-2.1.1 → dialectical_framework-2.1.3}/src/dialectical_framework/graph/wheel_segment.py +0 -0
- {dialectical_framework-2.1.1 → dialectical_framework-2.1.3}/src/dialectical_framework/graph/wheel_segment_polar_pair.py +0 -0
- {dialectical_framework-2.1.1 → dialectical_framework-2.1.3}/src/dialectical_framework/protocols/__init__.py +0 -0
- {dialectical_framework-2.1.1 → dialectical_framework-2.1.3}/src/dialectical_framework/protocols/has_config.py +0 -0
- {dialectical_framework-2.1.1 → dialectical_framework-2.1.3}/src/dialectical_framework/protocols/input_resolver.py +0 -0
- {dialectical_framework-2.1.1 → dialectical_framework-2.1.3}/src/dialectical_framework/py.typed +0 -0
- {dialectical_framework-2.1.1 → dialectical_framework-2.1.3}/src/dialectical_framework/settings_context.py +0 -0
- {dialectical_framework-2.1.1 → dialectical_framework-2.1.3}/src/dialectical_framework/utils/__init__.py +0 -0
- {dialectical_framework-2.1.1 → dialectical_framework-2.1.3}/src/dialectical_framework/utils/async_drain.py +0 -0
- {dialectical_framework-2.1.1 → dialectical_framework-2.1.3}/src/dialectical_framework/utils/call_census.py +0 -0
- {dialectical_framework-2.1.1 → dialectical_framework-2.1.3}/src/dialectical_framework/utils/chunking.py +0 -0
- {dialectical_framework-2.1.1 → dialectical_framework-2.1.3}/src/dialectical_framework/utils/concurrency.py +0 -0
- {dialectical_framework-2.1.1 → dialectical_framework-2.1.3}/src/dialectical_framework/utils/dc_replace.py +0 -0
- {dialectical_framework-2.1.1 → dialectical_framework-2.1.3}/src/dialectical_framework/utils/decompose_probability_uniformly.py +0 -0
- {dialectical_framework-2.1.1 → dialectical_framework-2.1.3}/src/dialectical_framework/utils/edge_context.py +0 -0
- {dialectical_framework-2.1.1 → dialectical_framework-2.1.3}/src/dialectical_framework/utils/effect_logger.py +0 -0
- {dialectical_framework-2.1.1 → dialectical_framework-2.1.3}/src/dialectical_framework/utils/input_context.py +0 -0
- {dialectical_framework-2.1.1 → dialectical_framework-2.1.3}/src/dialectical_framework/utils/order_transitions.py +0 -0
- {dialectical_framework-2.1.1 → dialectical_framework-2.1.3}/src/dialectical_framework/utils/progress.py +0 -0
- {dialectical_framework-2.1.1 → dialectical_framework-2.1.3}/src/dialectical_framework/utils/retry_accounting.py +0 -0
- {dialectical_framework-2.1.1 → dialectical_framework-2.1.3}/src/dialectical_framework/utils/sequence_generation.py +0 -0
- {dialectical_framework-2.1.1 → dialectical_framework-2.1.3}/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.1.
|
|
3
|
+
Version: 2.1.3
|
|
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
|
|
@@ -32,7 +32,7 @@ Requires-Dist: broadcaster (>=0.3.1,<0.4.0)
|
|
|
32
32
|
Requires-Dist: dependency-injector (>=4.48.2,<5.0.0)
|
|
33
33
|
Requires-Dist: gqlalchemy (>=1.8.0,<2.0.0)
|
|
34
34
|
Requires-Dist: langfuse (>=4.5.1,<5.0.0)
|
|
35
|
-
Requires-Dist: mirascope[anthropic,openai,ops] (>=2.
|
|
35
|
+
Requires-Dist: mirascope[anthropic,openai,ops] (>=2.8.2,<3.0.0)
|
|
36
36
|
Requires-Dist: python-dotenv (>=1.2.1,<2.0.0)
|
|
37
37
|
Requires-Dist: tabulate (>=0.9.0,<0.10.0)
|
|
38
38
|
Project-URL: Homepage, https://github.com/dialexity/dialectical-framework
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[tool.poetry]
|
|
2
2
|
name = "dialectical-framework"
|
|
3
|
-
version = "2.1.
|
|
3
|
+
version = "2.1.3"
|
|
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"
|
|
@@ -43,11 +43,17 @@ from = "src"
|
|
|
43
43
|
|
|
44
44
|
[tool.poetry.dependencies]
|
|
45
45
|
python = "^3.11"
|
|
46
|
+
# 2.8.2 is the FLOOR, not a preference: below it Mirascope's Anthropic encoder
|
|
47
|
+
# sends extended thinking as the legacy token budget, which every Claude 5 model
|
|
48
|
+
# rejects with a 400 — so a thinking structured call (the view turn, TetradSketch)
|
|
49
|
+
# cannot run on the native `anthropic/` provider at all. The framework's own
|
|
50
|
+
# `thinking_compat` covers this for `bedrock/` only. Measured 2026-10-26, story in
|
|
51
|
+
# docs/dev-notes/antithesis-selection.md.
|
|
46
52
|
mirascope = {extras = [
|
|
47
53
|
"openai",
|
|
48
54
|
"anthropic",
|
|
49
55
|
"ops"
|
|
50
|
-
], version = "^2.
|
|
56
|
+
], version = "^2.8.2"}
|
|
51
57
|
python-dotenv = "^1.2.1"
|
|
52
58
|
tabulate = "^0.9.0"
|
|
53
59
|
dependency-injector = "^4.48.2"
|
|
@@ -432,6 +432,8 @@ class Consultant:
|
|
|
432
432
|
import dataclasses
|
|
433
433
|
|
|
434
434
|
from dialectical_framework.agents.advisor.migration import person_turns
|
|
435
|
+
from dialectical_framework.concerns.attribution_check import \
|
|
436
|
+
drop_unborne_holders
|
|
435
437
|
from dialectical_framework.concerns.tetrad_candidates import (
|
|
436
438
|
clamp_attempts, ranking, select_sketch)
|
|
437
439
|
|
|
@@ -505,6 +507,20 @@ class Consultant:
|
|
|
505
507
|
for drawn_sketch in sketches:
|
|
506
508
|
if drawn_sketch.tensions:
|
|
507
509
|
drawn_sketch.tensions[0].thesis = pinned
|
|
510
|
+
# A pinned position is the host's own words for the
|
|
511
|
+
# person's own position: never someone else's.
|
|
512
|
+
drawn_sketch.tensions[0].thesis_held_by = ""
|
|
513
|
+
# The person's turns, for the judges below. Also the words a named
|
|
514
|
+
# holder is checked against.
|
|
515
|
+
words = judge_context(person_turns(history))
|
|
516
|
+
# A holder the words do not bear is dropped, in code, on every draw
|
|
517
|
+
# (`concerns/attribution_check.py`, 2026-10-27): the prompt rule
|
|
518
|
+
# halved the misquotation on pasted material and did not end it, and a
|
|
519
|
+
# runner-up served later must not carry one either. No call where no
|
|
520
|
+
# holder is named — the person's own words cost nothing here.
|
|
521
|
+
firsts_to_check = [s.tensions[0] for s in sketches if s.tensions]
|
|
522
|
+
for dropped in await drop_unborne_holders(firsts_to_check, words):
|
|
523
|
+
logger.info("A drawn holder was dropped: %s", dropped)
|
|
508
524
|
# With several draws the framework's own coherence check picks the one
|
|
509
525
|
# whose FIRST tension holds best (`concerns/tetrad_candidates.py`); a
|
|
510
526
|
# draw with no complete first tension sorts last. The card a blindspot
|
|
@@ -521,9 +537,14 @@ class Consultant:
|
|
|
521
537
|
judgeable = [i for i, t in enumerate(firsts) if t is not None and all(
|
|
522
538
|
(getattr(t, k) or "").strip() for k in ("thesis", "antithesis", "t_plus", "t_minus", "a_plus", "a_minus"))]
|
|
523
539
|
if len(judgeable) > 1:
|
|
540
|
+
# This writer CHOOSES the thesis, so a second judge reads each
|
|
541
|
+
# draw's T, A and A+ against the person's words — the claim
|
|
542
|
+
# they would sign, an opposition they have not granted
|
|
543
|
+
# (`tetrad_candidates.judge_fit`, 2026-10-20). With a pinned T
|
|
544
|
+
# the first read is the same on every draw and the second
|
|
545
|
+
# still tells them apart.
|
|
524
546
|
_best, verdicts = await select_sketch(
|
|
525
|
-
[firsts[i] for i in judgeable],
|
|
526
|
-
context=judge_context(person_turns(history)),
|
|
547
|
+
[firsts[i] for i in judgeable], context=words, fit_against=words
|
|
527
548
|
)
|
|
528
549
|
order = ranking(verdicts)
|
|
529
550
|
sketch = sketches[judgeable[order[0]]]
|
|
@@ -35,7 +35,8 @@ from dialectical_framework.utils.call_census import record_call
|
|
|
35
35
|
from dialectical_framework.utils.thinking_compat import conversational_round
|
|
36
36
|
from dialectical_framework.utils.retry_accounting import (RetryAccount,
|
|
37
37
|
retry_account)
|
|
38
|
-
from dialectical_framework.utils.use_brain import (
|
|
38
|
+
from dialectical_framework.utils.use_brain import (last_structured_reply_text,
|
|
39
|
+
prefill_token_kwargs,
|
|
39
40
|
retry_transient, use_brain)
|
|
40
41
|
|
|
41
42
|
from mirascope.llm import FinishReason, TextChunk, ThoughtChunk, ToolOutput
|
|
@@ -333,6 +334,15 @@ class ConversationFacilitator(SettingsAware):
|
|
|
333
334
|
# prefill-sensitive quantity — the one prompt caching can move — is
|
|
334
335
|
# `CallRecord.first_token_seconds` on the turn's first round, not this.
|
|
335
336
|
self.last_submit_first_delta_s: Optional[float] = None
|
|
337
|
+
# The last STRUCTURED reply of this turn as the model wrote it, before
|
|
338
|
+
# parsing — the JSON text, or the tool call's arguments as text. `None`
|
|
339
|
+
# on a turn that made no structured call (a reused written reply, a
|
|
340
|
+
# mocked one). A DTO that validates can still carry a field the model
|
|
341
|
+
# cut short and continued in the next one (the first app's handoff,
|
|
342
|
+
# 2026-10-27); nothing in a parse can see that, but a host's own check
|
|
343
|
+
# can, and then it needs the bytes to say whether the split was the
|
|
344
|
+
# writing or the wire. See `use_brain.last_structured_reply_text`.
|
|
345
|
+
self.last_structured_reply_text: Optional[str] = None
|
|
336
346
|
# Bumped once per submit, and captured by `submit_stream` so its `finally`
|
|
337
347
|
# can tell "this turn is still the current one" from "a turn the consumer
|
|
338
348
|
# walked away from, being finalised now".
|
|
@@ -522,6 +532,7 @@ class ConversationFacilitator(SettingsAware):
|
|
|
522
532
|
self.last_tool_rounds = []
|
|
523
533
|
self.last_submit_seconds = 0.0
|
|
524
534
|
self.last_submit_retries = RetryAccount()
|
|
535
|
+
self.last_structured_reply_text = None
|
|
525
536
|
# Reset here too although this path can never set it: a facilitator is
|
|
526
537
|
# reused across turns, so without this a non-streaming submit would report
|
|
527
538
|
# the previous STREAMED turn's figure as its own.
|
|
@@ -701,6 +712,7 @@ class ConversationFacilitator(SettingsAware):
|
|
|
701
712
|
self.last_submit_seconds = 0.0
|
|
702
713
|
self.last_submit_retries = RetryAccount()
|
|
703
714
|
self.last_submit_first_delta_s = None
|
|
715
|
+
self.last_structured_reply_text = None
|
|
704
716
|
|
|
705
717
|
# Re-entered per await instead of wrapped around the whole generator, and
|
|
706
718
|
# `retry_account` explains why: a contextvar set across a `yield` installs
|
|
@@ -1442,6 +1454,8 @@ class ConversationFacilitator(SettingsAware):
|
|
|
1442
1454
|
return messages
|
|
1443
1455
|
|
|
1444
1456
|
result = await _llm_call()
|
|
1457
|
+
# Same task as the call, so this is THIS call's text (see use_brain).
|
|
1458
|
+
self.last_structured_reply_text = last_structured_reply_text()
|
|
1445
1459
|
self._messages.append(
|
|
1446
1460
|
llm.messages.assistant(
|
|
1447
1461
|
self._assistant_history_text(result),
|
|
@@ -66,7 +66,17 @@ _AwaitedT = TypeVar("_AwaitedT")
|
|
|
66
66
|
# because the previous ask ("an antithesis that represents Negation of the
|
|
67
67
|
# thesis") was complied with exactly — and at the high rungs exact compliance
|
|
68
68
|
# is a caricature (measured 19/19, `tests/probe_blindspot_paths.py`).
|
|
69
|
-
|
|
69
|
+
#
|
|
70
|
+
# The second failure, the caricature's mirror (2026-10-20, the first app's
|
|
71
|
+
# handoff `docs/dev-notes/antithesis-selection.md`, "The hedge wins on coherence"): "in the thesis's own
|
|
72
|
+
# terms" was complied with exactly too, and when the thesis's own words NAME
|
|
73
|
+
# their opponent and concede it ("not only schools, but also …"), that
|
|
74
|
+
# opponent is the most literal answer — the writer drew it as A on Sonnet 5.5
|
|
75
|
+
# in every configuration, and the card handed the person their own clause as
|
|
76
|
+
# the blind spot they were not seeing. This ask was one-sided: tuned against
|
|
77
|
+
# the strawman end, it had nothing to say about the granted end. UNMEASURED
|
|
78
|
+
# as a rule; the shape is the caricature rule's, in the same place.
|
|
79
|
+
_OPPOSING_POSITION_ASK = """The antithesis is a POSITION someone holds for its own value, that functionally opposes the role the thesis plays — at this rung's mechanism, in the thesis's own terms. It is not the thesis's absence or degradation restated as a stance. Example: for "Quit my job and start my own company", Negation is "Keep the security and craft of employed work", not "Never quit, stay employed forever"; the second is a caricature nobody holds, and a caricature cannot be developed constructively — rate such a candidate's tetrad potential low rather than dressing it up. The mirror failure: an opposition the thesis's own words already name AND concede ("not only X but also Y", "X matters, but …") is seen, not missing — its constructive side is something the person has already granted, and a tetrad built on it shows them their own concession as a blind spot. Rate such a candidate's tetrad potential low as well, and oppose the claim they actually make."""
|
|
70
80
|
|
|
71
81
|
|
|
72
82
|
# --- Extraction-specific DTOs ---
|
|
@@ -94,7 +104,9 @@ class ModePointResultDto(BaseModel):
|
|
|
94
104
|
"A is a position someone holds for its own value, it can be developed so "
|
|
95
105
|
"that it also strengthens what T is for (an A+ exists), and it has its own "
|
|
96
106
|
"one-sided failure (an A-). Low: A is T's absence or degradation dressed as "
|
|
97
|
-
"a position,
|
|
107
|
+
"a position, a caricature nobody would hold — nothing to develop — or an "
|
|
108
|
+
"opposition the thesis's own words already name and concede, whose A+ the "
|
|
109
|
+
"person has granted."
|
|
98
110
|
),
|
|
99
111
|
)
|
|
100
112
|
arousal_label: str = Field(
|
|
@@ -132,7 +144,8 @@ class OptimumARankingDto(BaseModel):
|
|
|
132
144
|
"functionally opposes the role the thesis plays; it can be developed so "
|
|
133
145
|
"that it also strengthens what the thesis is for, and it has its own "
|
|
134
146
|
"one-sided failure. Worst = the thesis's absence or degradation dressed "
|
|
135
|
-
"as a stance,
|
|
147
|
+
"as a stance, a caricature nobody holds, or an opposition the thesis's own "
|
|
148
|
+
"words already concede."
|
|
136
149
|
)
|
|
137
150
|
)
|
|
138
151
|
reasoning: str = Field(
|
|
@@ -542,7 +555,7 @@ Thesis: "{thesis.prompt_text}"
|
|
|
542
555
|
Candidates:
|
|
543
556
|
{numbered}
|
|
544
557
|
|
|
545
|
-
The best antithesis is the one most likely to yield a coherent tetrad with the thesis: a POSITION someone holds for its own value that functionally opposes the role the thesis plays — it can be developed constructively so that it also strengthens what the thesis is for, and it has its own one-sided failure. Rank lower any candidate that is the thesis's absence or degradation dressed as a stance, a caricature nobody would hold, or the thesis
|
|
558
|
+
The best antithesis is the one most likely to yield a coherent tetrad with the thesis: a POSITION someone holds for its own value that functionally opposes the role the thesis plays — it can be developed constructively so that it also strengthens what the thesis is for, and it has its own one-sided failure. Rank lower any candidate that is the thesis's absence or degradation dressed as a stance, a caricature nobody would hold, the thesis merely hedged, or an opposition the thesis's own words already name and concede. Return every number exactly once."""
|
|
546
559
|
try:
|
|
547
560
|
verdict = await self._conversation.isolate().submit(
|
|
548
561
|
response_model=OptimumARankingDto, user_content=prompt
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
"""
|
|
2
|
+
AttributionCheck: do the words bear the holder a drawn pole names?
|
|
3
|
+
|
|
4
|
+
WHY A CHECK IN CODE
|
|
5
|
+
===================
|
|
6
|
+
The view turn names WHO holds each pole when it is not the person's own
|
|
7
|
+
(`ViewSketchPerspectiveDto.thesis_held_by` / `antithesis_held_by`, carried as
|
|
8
|
+
`PoleView.held_by`, since 2026-10-26), and a prompt rule says a party is
|
|
9
|
+
named only for a position its own words take (`view_sketch.HOLDER_FROM_THE_WORDS`,
|
|
10
|
+
2026-10-27). Measured on 37 pieces of pasted material, two draws each, the
|
|
11
|
+
rule HALVED the misquotation and did not end it: about one draw in twenty
|
|
12
|
+
still named a party for words the text does not bear — a speaker given the
|
|
13
|
+
inverse of their own position, two sides swapped — and a screen shows that
|
|
14
|
+
as "the Minister's position" over words the Minister never said. Prompt
|
|
15
|
+
enforcement scales with compliance (CLAUDE.md, the machinery leak), so the
|
|
16
|
+
last step is the same shape as the thesis pin: enforced by code after the
|
|
17
|
+
draw, not asked for again.
|
|
18
|
+
|
|
19
|
+
WHAT IT DOES
|
|
20
|
+
============
|
|
21
|
+
One structured call per NAMED holder, shown the person's words and one pole,
|
|
22
|
+
asked whether the text attributes that position to that party — stated in
|
|
23
|
+
their own words or reported as theirs (a role and the person in it are one
|
|
24
|
+
party). NOT attributed: the holder is dropped, so the pole reads as a built
|
|
25
|
+
opposition (A) or as unattributed (T) instead of a misquotation. Attributed:
|
|
26
|
+
kept. Never a replacement: the judge is not handed the right to put a
|
|
27
|
+
different name on the screen. Free where it matters most — on the person's
|
|
28
|
+
own words every holder is empty and no call is made.
|
|
29
|
+
|
|
30
|
+
Fail-soft the way the fit judge is: a failed judging KEEPS the holder. The
|
|
31
|
+
alternative, dropping on error, would turn "the Minister's position" into
|
|
32
|
+
"your position" on a provider hiccup, which is the 2026-10-26 defect back.
|
|
33
|
+
A missing holder is also what `held_by` means on the graph path, so a host
|
|
34
|
+
must already handle None.
|
|
35
|
+
|
|
36
|
+
THE VERDICT'S WORDING IS VERSION 2. Version 1 asked whether the text
|
|
37
|
+
"attributes this position" to the party and, applied to the 130 named
|
|
38
|
+
holders of the gate's WITH-arm draws, dropped 28 — 18 of the 27 the probe's
|
|
39
|
+
own judge had flagged, and 10 it had grounded: it read an elaboration as a
|
|
40
|
+
misquotation ("opponents of trophy hunting" refused "killing for pleasure is
|
|
41
|
+
wrong" because the text says only that the controversy is about the morality
|
|
42
|
+
of hunting for pleasure; a rhetorical question that takes a side was "no
|
|
43
|
+
stance"). A dropped true holder costs what this check exists to prevent in
|
|
44
|
+
reverse — "the Minister's position" shown as "your position" — so version 2
|
|
45
|
+
says a fair condensation of the stance the text gives them is theirs, and
|
|
46
|
+
NOT_ATTRIBUTED is no stance at all, the opposite side, or someone else's.
|
|
47
|
+
|
|
48
|
+
MEASURED (2026-10-27, `tests/e2e/rescore_attribution_check.py`): see
|
|
49
|
+
`docs/dev-notes/views.md`, "A name on words never said" — applied to the
|
|
50
|
+
saved WITH-arm draws of the holder-rule gate and then live on the 37-case
|
|
51
|
+
finding run.
|
|
52
|
+
"""
|
|
53
|
+
|
|
54
|
+
from __future__ import annotations
|
|
55
|
+
|
|
56
|
+
import asyncio
|
|
57
|
+
import logging
|
|
58
|
+
from typing import Any, Literal, Sequence
|
|
59
|
+
|
|
60
|
+
from pydantic import BaseModel, Field
|
|
61
|
+
|
|
62
|
+
from dialectical_framework.agents.conversation_facilitator import \
|
|
63
|
+
ConversationFacilitator
|
|
64
|
+
|
|
65
|
+
logger = logging.getLogger(__name__)
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
class AttributionDto(BaseModel):
|
|
69
|
+
"""One verdict; the safe value first (the mocked suite fills the first)."""
|
|
70
|
+
|
|
71
|
+
verdict: Literal["attributed", "not_attributed"] = Field(
|
|
72
|
+
description=(
|
|
73
|
+
"ATTRIBUTED: the text gives that party this position — stated in their "
|
|
74
|
+
"own words, reported as what they hold, argue, claim or did, or a fair "
|
|
75
|
+
"condensation of the stance the text gives them (an opponent of X holds "
|
|
76
|
+
"that X is wrong; a rhetorical question that takes a side takes it; a "
|
|
77
|
+
"role and the person in it, or a group and a member named as its voice, "
|
|
78
|
+
"are one party). Wording that goes a little beyond their words while "
|
|
79
|
+
"keeping their side is still theirs. NOT_ATTRIBUTED: the text gives that "
|
|
80
|
+
"party no stance on this at all, gives them the OPPOSITE side, or gives "
|
|
81
|
+
"this position to someone else; a party who is only described, who "
|
|
82
|
+
"disagrees with it, or who is merely asked about it does not hold it."
|
|
83
|
+
)
|
|
84
|
+
)
|
|
85
|
+
reasoning: str = Field(
|
|
86
|
+
description="One sentence: the words that attribute it, or that none do."
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
class AttributionCheck:
|
|
91
|
+
"""Text-only, graph-free, no report: a check the view turn runs."""
|
|
92
|
+
|
|
93
|
+
async def score_texts(
|
|
94
|
+
self, *, position: str, held_by: str, text: str
|
|
95
|
+
) -> AttributionDto:
|
|
96
|
+
prompt = f"""<text>
|
|
97
|
+
{text}
|
|
98
|
+
</text>
|
|
99
|
+
|
|
100
|
+
Does this text attribute the following position to {held_by.strip()} — stated in their own words, or reported as what they hold, argue, claim or did?
|
|
101
|
+
|
|
102
|
+
"{position.strip()}\""""
|
|
103
|
+
return await ConversationFacilitator().submit(
|
|
104
|
+
response_model=AttributionDto, user_content=prompt
|
|
105
|
+
)
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
#: The (pole, holder) attribute pairs a tension carries.
|
|
109
|
+
_POLES = (("thesis", "thesis_held_by"), ("antithesis", "antithesis_held_by"))
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
async def drop_unborne_holders(tensions: Sequence[Any], text: str) -> list[str]:
|
|
113
|
+
"""Blank every named holder the text does not bear, IN PLACE, on every
|
|
114
|
+
tension given (the winner and the spares alike — a host serving a
|
|
115
|
+
runner-up must not get the misquotation back). Returns one line per
|
|
116
|
+
holder dropped, for the log. No call where no holder is named; a failed
|
|
117
|
+
judging keeps the holder (fail-soft, see the module)."""
|
|
118
|
+
words = (text or "").strip()
|
|
119
|
+
if not words:
|
|
120
|
+
return []
|
|
121
|
+
jobs: list[tuple[Any, str, str, str]] = []
|
|
122
|
+
for tension in tensions:
|
|
123
|
+
for pole, holder_attr in _POLES:
|
|
124
|
+
holder = (getattr(tension, holder_attr, "") or "").strip()
|
|
125
|
+
position = (getattr(tension, pole, "") or "").strip()
|
|
126
|
+
if holder and position:
|
|
127
|
+
jobs.append((tension, pole, holder_attr, holder))
|
|
128
|
+
|
|
129
|
+
async def one(job: tuple[Any, str, str, str]) -> bool:
|
|
130
|
+
tension, pole, _attr, holder = job
|
|
131
|
+
try:
|
|
132
|
+
dto = await AttributionCheck().score_texts(
|
|
133
|
+
position=getattr(tension, pole), held_by=holder, text=words
|
|
134
|
+
)
|
|
135
|
+
except Exception as exc: # noqa: BLE001 — not judged, the holder stays
|
|
136
|
+
logger.warning("Checking a pole's holder failed; it stays: %s", exc)
|
|
137
|
+
return False
|
|
138
|
+
if not isinstance(dto, AttributionDto):
|
|
139
|
+
return False
|
|
140
|
+
return dto.verdict == "not_attributed"
|
|
141
|
+
|
|
142
|
+
verdicts = await asyncio.gather(*(one(j) for j in jobs))
|
|
143
|
+
dropped: list[str] = []
|
|
144
|
+
for (tension, pole, holder_attr, holder), unborne in zip(jobs, verdicts):
|
|
145
|
+
if unborne:
|
|
146
|
+
setattr(tension, holder_attr, "")
|
|
147
|
+
dropped.append(f"{pole} held_by {holder!r}: the words do not bear it")
|
|
148
|
+
return dropped
|
|
@@ -28,6 +28,21 @@ three times". What the handoff found beside it is kept: the ranking reads
|
|
|
28
28
|
dialectical validity after the pass floor (`ranking`), and the view turn's
|
|
29
29
|
judge reads the person's words (`view_sketch.judge_context`).
|
|
30
30
|
|
|
31
|
+
The second handoff (2026-10-20, `docs/dev-notes/antithesis-selection.md`, "The hedge wins on coherence")
|
|
32
|
+
found the limit of a coherence-only choice: on a multi-claim input the
|
|
33
|
+
writer drew the claim the person would sign twice in three draws and the
|
|
34
|
+
ranking kept the hedge, because a tetrad about a hedge degenerates more
|
|
35
|
+
coherently than one about a demand, and coherence was all the ranking read.
|
|
36
|
+
So the writers that CHOOSE the thesis (the view turn, the one-shot build)
|
|
37
|
+
ask for a second judge beside the control statements, `ThesisFitCheck`
|
|
38
|
+
(`concerns/thesis_fit_check.py`): is this draw's T the claim they would
|
|
39
|
+
sign, and is its A+ something their words do not already grant. It ranks
|
|
40
|
+
AFTER the pass floor as a yes/no (`SketchVerdict.fits`), so every
|
|
41
|
+
keep-any-passing figure on record stands; it is a selection term, never a
|
|
42
|
+
signal back into generation. The staged writer draws for a GIVEN pair, where
|
|
43
|
+
T and A are the same on every draw, and does not ask. Unmeasured at the time
|
|
44
|
+
of writing — the gates are named on the concern.
|
|
45
|
+
|
|
31
46
|
What this must NOT become: a loop that optimises against the judge. The judge
|
|
32
47
|
never sees the thesis or antithesis — it scores the four aspects' coherence
|
|
33
48
|
and passes a MIRROR antithesis more readily than a genuine position — so a
|
|
@@ -48,12 +63,15 @@ holding a text or an object with `.statement` (`TetradDto`'s `AspectDto`).
|
|
|
48
63
|
from __future__ import annotations
|
|
49
64
|
|
|
50
65
|
import asyncio
|
|
66
|
+
import dataclasses
|
|
51
67
|
import logging
|
|
52
68
|
from dataclasses import dataclass
|
|
53
69
|
from typing import Any, Optional, Sequence
|
|
54
70
|
|
|
55
71
|
from dialectical_framework.concerns.control_statements_check import \
|
|
56
72
|
ControlStatementsCheck
|
|
73
|
+
from dialectical_framework.concerns.thesis_fit_check import (
|
|
74
|
+
THESIS_FIT_THRESHOLD, ThesisFitCheck)
|
|
57
75
|
from dialectical_framework.graph.nodes.estimation import \
|
|
58
76
|
CONCEPTUAL_COHERENCE_THRESHOLD
|
|
59
77
|
|
|
@@ -100,6 +118,15 @@ class SketchVerdict:
|
|
|
100
118
|
handoff). `None` on the DV fields means the judge did not say — a verdict
|
|
101
119
|
built from CC alone — and such a verdict ranks BELOW any with a DV, never
|
|
102
120
|
as a zero (a zero is a claim).
|
|
121
|
+
|
|
122
|
+
`thesis_fit` is the second judge's read (`ThesisFitCheck`, 2026-10-20):
|
|
123
|
+
whether the draw stands on the claim the person would sign, against an
|
|
124
|
+
opposition they have not already granted. `None` here means the fit
|
|
125
|
+
judge did NOT run (the caller did not ask, had no words to read against,
|
|
126
|
+
or the call failed) and the draw ranks as it did before the term existed
|
|
127
|
+
— the opposite convention from DV's, on purpose: DV's `None` is a verdict
|
|
128
|
+
the judge gave without it, fit's `None` is no judging at all, and a
|
|
129
|
+
provider error on one draw must not demote it.
|
|
103
130
|
"""
|
|
104
131
|
|
|
105
132
|
t_plus_without_a_plus: float
|
|
@@ -107,6 +134,7 @@ class SketchVerdict:
|
|
|
107
134
|
reasoning: str = ""
|
|
108
135
|
t_plus_without_a_plus_dv: Optional[float] = None
|
|
109
136
|
a_plus_without_t_plus_dv: Optional[float] = None
|
|
137
|
+
thesis_fit: Optional[float] = None
|
|
110
138
|
|
|
111
139
|
@property
|
|
112
140
|
def floor(self) -> float:
|
|
@@ -130,6 +158,11 @@ class SketchVerdict:
|
|
|
130
158
|
def passes(self) -> bool:
|
|
131
159
|
return self.floor >= CONCEPTUAL_COHERENCE_THRESHOLD
|
|
132
160
|
|
|
161
|
+
@property
|
|
162
|
+
def fits(self) -> bool:
|
|
163
|
+
"""Not judged, or judged at or above `THESIS_FIT_THRESHOLD`."""
|
|
164
|
+
return self.thesis_fit is None or self.thesis_fit >= THESIS_FIT_THRESHOLD
|
|
165
|
+
|
|
133
166
|
|
|
134
167
|
def aspect_text(value: Any) -> str:
|
|
135
168
|
"""The text of an aspect field: a plain string, or an `AspectDto`'s `.statement`."""
|
|
@@ -203,17 +236,61 @@ async def judge_sketches(
|
|
|
203
236
|
return list(await asyncio.gather(*(one(c) for c in candidates)))
|
|
204
237
|
|
|
205
238
|
|
|
239
|
+
async def judge_fit(
|
|
240
|
+
candidates: Sequence[Any], context: str
|
|
241
|
+
) -> list[Optional[float]]:
|
|
242
|
+
"""`ThesisFitCheck` on every candidate, each on its own (gathered), each
|
|
243
|
+
read against the words in `context`. One call per draw beside the
|
|
244
|
+
control statements' two. Fail-soft per candidate: a failed judging is
|
|
245
|
+
`None` — NOT judged, which `SketchVerdict.fits` treats as fitting. A
|
|
246
|
+
candidate without a thesis or antithesis (a transition tetrad through
|
|
247
|
+
`as_control_tetrad`) is not judged either. A draw that names T's holder
|
|
248
|
+
(`thesis_held_by`, the view turn on pasted material) hands it on, so the
|
|
249
|
+
judge asks whose claim about the right party."""
|
|
250
|
+
|
|
251
|
+
async def one(tension: Any) -> Optional[float]:
|
|
252
|
+
thesis = aspect_text(getattr(tension, "thesis", "")).strip()
|
|
253
|
+
antithesis = aspect_text(getattr(tension, "antithesis", "")).strip()
|
|
254
|
+
if not thesis or not antithesis:
|
|
255
|
+
return None
|
|
256
|
+
try:
|
|
257
|
+
verdict = await ThesisFitCheck().score_texts(
|
|
258
|
+
thesis=thesis,
|
|
259
|
+
antithesis=antithesis,
|
|
260
|
+
a_plus=aspect_text(tension.a_plus),
|
|
261
|
+
text=context,
|
|
262
|
+
held_by=(getattr(tension, "thesis_held_by", "") or "").strip(),
|
|
263
|
+
)
|
|
264
|
+
return verdict.fit
|
|
265
|
+
except Exception as exc: # noqa: BLE001 — degrade to "not judged"
|
|
266
|
+
logger.warning("Judging a sketch's thesis fit failed; it ranks unjudged: %s", exc)
|
|
267
|
+
return None
|
|
268
|
+
|
|
269
|
+
return list(await asyncio.gather(*(one(c) for c in candidates)))
|
|
270
|
+
|
|
271
|
+
|
|
206
272
|
async def select_sketch(
|
|
207
|
-
candidates: Sequence[Any], context: str = ""
|
|
273
|
+
candidates: Sequence[Any], context: str = "", fit_against: Optional[str] = None
|
|
208
274
|
) -> tuple[int, list[Optional[SketchVerdict]]]:
|
|
209
275
|
"""Judge every candidate (gathered) and return the index of the best plus
|
|
210
276
|
all verdicts (None where judging failed).
|
|
211
277
|
|
|
212
278
|
Ranking (`ranking`): the weaker control statement first (the pass rule),
|
|
213
|
-
then
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
279
|
+
then — with `fit_against` — whether the draw stands on the person's claim
|
|
280
|
+
against an opposition they have not granted (`judge_fit`, a yes/no), then
|
|
281
|
+
the floor's value, dialectical validity, the mean coherence, then the
|
|
282
|
+
earlier draw — a deterministic tie-break, not a preference. A candidate
|
|
283
|
+
whose judging failed sorts last; if every judging failed the first draw
|
|
284
|
+
is kept, which is exactly what `attempts=1` would have done.
|
|
285
|
+
|
|
286
|
+
`fit_against` is the PERSON's words, for the writers that CHOOSE the
|
|
287
|
+
thesis (the view turn's `judge_context`, the one-shot build's material):
|
|
288
|
+
on a given pair every draw has the same T and A and the term would cost
|
|
289
|
+
N calls to say nothing. It is a separate argument from `context` because
|
|
290
|
+
the two judges read different things: the control statements take the
|
|
291
|
+
caller's context (on the build, a paraphrase the Advisor composed), the
|
|
292
|
+
fit judge must read what the person actually said. Empty or `None` skips
|
|
293
|
+
the fit judge — it is never asked blind.
|
|
217
294
|
|
|
218
295
|
ONE candidate is never judged: there is nothing to select among, so
|
|
219
296
|
`attempts=1` costs no extra call.
|
|
@@ -221,19 +298,38 @@ async def select_sketch(
|
|
|
221
298
|
if len(candidates) == 1:
|
|
222
299
|
return 0, [None]
|
|
223
300
|
|
|
224
|
-
|
|
301
|
+
words = (fit_against or "").strip()
|
|
302
|
+
if words:
|
|
303
|
+
verdicts, fits = await asyncio.gather(
|
|
304
|
+
judge_sketches(candidates, context), judge_fit(candidates, words)
|
|
305
|
+
)
|
|
306
|
+
verdicts = [
|
|
307
|
+
None if v is None else dataclasses.replace(v, thesis_fit=f)
|
|
308
|
+
for v, f in zip(verdicts, fits)
|
|
309
|
+
]
|
|
310
|
+
else:
|
|
311
|
+
verdicts = await judge_sketches(candidates, context)
|
|
225
312
|
return ranking(verdicts)[0], verdicts
|
|
226
313
|
|
|
227
314
|
|
|
228
315
|
def ranking(verdicts: Sequence[Optional[SketchVerdict]]) -> list[int]:
|
|
229
|
-
"""Candidate indices best first: the weaker control statement
|
|
316
|
+
"""Candidate indices best first: whether the weaker control statement
|
|
317
|
+
passes, then whether the draw fits the person's claim (`fits`: not
|
|
318
|
+
judged counts as fitting), then the weaker statement's value, then
|
|
230
319
|
dialectical validity, then the mean coherence, then the earlier draw;
|
|
231
320
|
unjudged last. `[0]` is the winner; the rest are the runners-up in order.
|
|
232
321
|
|
|
233
|
-
Why the
|
|
234
|
-
on
|
|
235
|
-
|
|
236
|
-
|
|
322
|
+
Why the pass rule stays FIRST: every best-of-N figure on record (CC 36/40
|
|
323
|
+
on the view turn, 30/40 on the build, 26/46 on documents) is a
|
|
324
|
+
keep-any-passing-draw figure — a term ahead of it would trade those for
|
|
325
|
+
an unmeasured one. The fit term sits INSIDE that rule, as a yes/no: among
|
|
326
|
+
draws that hold, one on the claim the person would sign against an
|
|
327
|
+
opposition they have not granted beats one on a hedge, however coherent
|
|
328
|
+
the hedge (2026-10-20, `docs/dev-notes/antithesis-selection.md`, "The hedge wins on coherence"); among
|
|
329
|
+
draws that fail, the fit decides the same way, so a card with no passing
|
|
330
|
+
draw still shows the person's claim. A yes/no rather than the score
|
|
331
|
+
because the floor's VALUE should keep deciding between two draws that
|
|
332
|
+
both fit, as it did before the term. Why DV comes next, ahead of the mean:
|
|
237
333
|
DV is the paper's acceptance term beside SP (the Advisor's quality floor
|
|
238
334
|
already reads it), and it answers the question the choice is about — of
|
|
239
335
|
two draws that both hold, which is the LIVE tension rather than the
|
|
@@ -245,11 +341,19 @@ def ranking(verdicts: Sequence[Optional[SketchVerdict]]) -> list[int]:
|
|
|
245
341
|
`explorer._causality_probability`), never as a zero.
|
|
246
342
|
"""
|
|
247
343
|
|
|
248
|
-
def key(index: int) -> tuple[int, float, float, float, int]:
|
|
344
|
+
def key(index: int) -> tuple[int, bool, bool, float, float, float, int]:
|
|
249
345
|
verdict = verdicts[index]
|
|
250
346
|
if verdict is None:
|
|
251
|
-
return (0, 0.0, 0.0, 0.0, -index)
|
|
347
|
+
return (0, False, False, 0.0, 0.0, 0.0, -index)
|
|
252
348
|
dv = verdict.dialectical_validity
|
|
253
|
-
return (
|
|
349
|
+
return (
|
|
350
|
+
1,
|
|
351
|
+
verdict.passes,
|
|
352
|
+
verdict.fits,
|
|
353
|
+
verdict.floor,
|
|
354
|
+
-1.0 if dv is None else dv,
|
|
355
|
+
verdict.mean,
|
|
356
|
+
-index,
|
|
357
|
+
)
|
|
254
358
|
|
|
255
359
|
return sorted(range(len(verdicts)), key=key, reverse=True)
|
|
@@ -159,7 +159,14 @@ class TetradSketch(ReasonableConcern[ViewSketchPerspectiveDto], SettingsAware):
|
|
|
159
159
|
self._report.summary = f"Sketch failed: {first}"
|
|
160
160
|
raise first
|
|
161
161
|
|
|
162
|
-
|
|
162
|
+
# This writer chooses the thesis (or words a given one), so the draws
|
|
163
|
+
# are ranked by the person's claim as well as by coherence
|
|
164
|
+
# (`tetrad_candidates.judge_fit`): the fit judge reads the MATERIAL,
|
|
165
|
+
# the person's own words, where the control statements keep the
|
|
166
|
+
# caller's `context`; with no material it is skipped.
|
|
167
|
+
best, verdicts = await select_sketch(
|
|
168
|
+
candidates, context or "", fit_against=material
|
|
169
|
+
)
|
|
163
170
|
tension = candidates[best]
|
|
164
171
|
self.verdict = verdicts[best]
|
|
165
172
|
self.alternatives = [
|