dialectical-framework 2.0.6__tar.gz → 2.0.8__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.6 → dialectical_framework-2.0.8}/PKG-INFO +1 -1
- {dialectical_framework-2.0.6 → dialectical_framework-2.0.8}/pyproject.toml +1 -1
- {dialectical_framework-2.0.6 → dialectical_framework-2.0.8}/src/dialectical_framework/agents/advisor/advisor.py +59 -27
- {dialectical_framework-2.0.6 → dialectical_framework-2.0.8}/src/dialectical_framework/agents/consultant/consultant.py +39 -3
- {dialectical_framework-2.0.6 → dialectical_framework-2.0.8}/src/dialectical_framework/agents/conversation_facilitator.py +32 -1
- dialectical_framework-2.0.8/src/dialectical_framework/exceptions/provider_errors.py +42 -0
- {dialectical_framework-2.0.6 → dialectical_framework-2.0.8}/src/dialectical_framework/graph/views.py +32 -0
- {dialectical_framework-2.0.6 → dialectical_framework-2.0.8}/src/dialectical_framework/utils/bedrock_provider.py +157 -34
- dialectical_framework-2.0.8/src/dialectical_framework/utils/format_compat.py +176 -0
- dialectical_framework-2.0.8/src/dialectical_framework/utils/thinking_compat.py +401 -0
- {dialectical_framework-2.0.6 → dialectical_framework-2.0.8}/src/dialectical_framework/utils/use_brain.py +17 -0
- dialectical_framework-2.0.6/src/dialectical_framework/utils/thinking_compat.py +0 -203
- {dialectical_framework-2.0.6 → dialectical_framework-2.0.8}/LICENSE +0 -0
- {dialectical_framework-2.0.6 → dialectical_framework-2.0.8}/PATENTS +0 -0
- {dialectical_framework-2.0.6 → dialectical_framework-2.0.8}/README.md +0 -0
- {dialectical_framework-2.0.6 → dialectical_framework-2.0.8}/src/dialectical_framework/__init__.py +0 -0
- {dialectical_framework-2.0.6 → dialectical_framework-2.0.8}/src/dialectical_framework/agents/__init__.py +0 -0
- {dialectical_framework-2.0.6 → dialectical_framework-2.0.8}/src/dialectical_framework/agents/advisor/__init__.py +0 -0
- {dialectical_framework-2.0.6 → dialectical_framework-2.0.8}/src/dialectical_framework/agents/advisor/build_policy.py +0 -0
- {dialectical_framework-2.0.6 → dialectical_framework-2.0.8}/src/dialectical_framework/agents/advisor/migration.py +0 -0
- {dialectical_framework-2.0.6 → dialectical_framework-2.0.8}/src/dialectical_framework/agents/advisor/reply_hygiene.py +0 -0
- {dialectical_framework-2.0.6 → dialectical_framework-2.0.8}/src/dialectical_framework/agents/advisor/system_prompts.py +0 -0
- {dialectical_framework-2.0.6 → dialectical_framework-2.0.8}/src/dialectical_framework/agents/advisor/tools/__init__.py +0 -0
- {dialectical_framework-2.0.6 → dialectical_framework-2.0.8}/src/dialectical_framework/agents/advisor/tools/anchor.py +0 -0
- {dialectical_framework-2.0.6 → dialectical_framework-2.0.8}/src/dialectical_framework/agents/advisor/tools/deepen.py +0 -0
- {dialectical_framework-2.0.6 → dialectical_framework-2.0.8}/src/dialectical_framework/agents/advisor/tools/explore.py +0 -0
- {dialectical_framework-2.0.6 → dialectical_framework-2.0.8}/src/dialectical_framework/agents/advisor/tools/ingest.py +0 -0
- {dialectical_framework-2.0.6 → dialectical_framework-2.0.8}/src/dialectical_framework/agents/advisor/tools/note.py +0 -0
- {dialectical_framework-2.0.6 → dialectical_framework-2.0.8}/src/dialectical_framework/agents/advisor/tools/record_decision.py +0 -0
- {dialectical_framework-2.0.6 → dialectical_framework-2.0.8}/src/dialectical_framework/agents/advisor/tools/scoped.py +0 -0
- {dialectical_framework-2.0.6 → dialectical_framework-2.0.8}/src/dialectical_framework/agents/advisor/tools/sync.py +0 -0
- {dialectical_framework-2.0.6 → dialectical_framework-2.0.8}/src/dialectical_framework/agents/agent_context.py +0 -0
- {dialectical_framework-2.0.6 → dialectical_framework-2.0.8}/src/dialectical_framework/agents/analyst/__init__.py +0 -0
- {dialectical_framework-2.0.6 → dialectical_framework-2.0.8}/src/dialectical_framework/agents/analyst/analyst.py +0 -0
- {dialectical_framework-2.0.6 → dialectical_framework-2.0.8}/src/dialectical_framework/agents/analyst/skills/__init__.py +0 -0
- {dialectical_framework-2.0.6 → dialectical_framework-2.0.8}/src/dialectical_framework/agents/analyst/skills/anchor_theses.py +0 -0
- {dialectical_framework-2.0.6 → dialectical_framework-2.0.8}/src/dialectical_framework/agents/analyst/skills/edit_perspective.py +0 -0
- {dialectical_framework-2.0.6 → dialectical_framework-2.0.8}/src/dialectical_framework/agents/analyst/skills/expand_polarities.py +0 -0
- {dialectical_framework-2.0.6 → dialectical_framework-2.0.8}/src/dialectical_framework/agents/analyst/skills/find_polarities.py +0 -0
- {dialectical_framework-2.0.6 → dialectical_framework-2.0.8}/src/dialectical_framework/agents/analyst/skills/introduce_polarity.py +0 -0
- {dialectical_framework-2.0.6 → dialectical_framework-2.0.8}/src/dialectical_framework/agents/analyst/skills/sketch_tetrad.py +0 -0
- {dialectical_framework-2.0.6 → dialectical_framework-2.0.8}/src/dialectical_framework/agents/analyst/skills/surface_theses.py +0 -0
- {dialectical_framework-2.0.6 → dialectical_framework-2.0.8}/src/dialectical_framework/agents/analyst/system_prompts.py +0 -0
- {dialectical_framework-2.0.6 → dialectical_framework-2.0.8}/src/dialectical_framework/agents/analyst/tools/__init__.py +0 -0
- {dialectical_framework-2.0.6 → dialectical_framework-2.0.8}/src/dialectical_framework/agents/analyst/tools/place_statement.py +0 -0
- {dialectical_framework-2.0.6 → dialectical_framework-2.0.8}/src/dialectical_framework/agents/app_spec.py +0 -0
- {dialectical_framework-2.0.6 → dialectical_framework-2.0.8}/src/dialectical_framework/agents/apps.py +0 -0
- {dialectical_framework-2.0.6 → dialectical_framework-2.0.8}/src/dialectical_framework/agents/consultant/__init__.py +0 -0
- {dialectical_framework-2.0.6 → dialectical_framework-2.0.8}/src/dialectical_framework/agents/execution_report.py +0 -0
- {dialectical_framework-2.0.6 → dialectical_framework-2.0.8}/src/dialectical_framework/agents/explorer/__init__.py +0 -0
- {dialectical_framework-2.0.6 → dialectical_framework-2.0.8}/src/dialectical_framework/agents/explorer/explorer.py +0 -0
- {dialectical_framework-2.0.6 → dialectical_framework-2.0.8}/src/dialectical_framework/agents/explorer/skills/__init__.py +0 -0
- {dialectical_framework-2.0.6 → dialectical_framework-2.0.8}/src/dialectical_framework/agents/explorer/skills/build_wheels.py +0 -0
- {dialectical_framework-2.0.6 → dialectical_framework-2.0.8}/src/dialectical_framework/agents/explorer/skills/explore_transformations.py +0 -0
- {dialectical_framework-2.0.6 → dialectical_framework-2.0.8}/src/dialectical_framework/agents/explorer/skills/generate_synthesis.py +0 -0
- {dialectical_framework-2.0.6 → dialectical_framework-2.0.8}/src/dialectical_framework/agents/explorer/system_prompts.py +0 -0
- {dialectical_framework-2.0.6 → dialectical_framework-2.0.8}/src/dialectical_framework/agents/explorer/tools/__init__.py +0 -0
- {dialectical_framework-2.0.6 → dialectical_framework-2.0.8}/src/dialectical_framework/agents/explorer/tools/create_nexus.py +0 -0
- {dialectical_framework-2.0.6 → dialectical_framework-2.0.8}/src/dialectical_framework/agents/explorer/tools/expand_nexus.py +0 -0
- {dialectical_framework-2.0.6 → dialectical_framework-2.0.8}/src/dialectical_framework/agents/explorer/tools/generate_synthesis.py +0 -0
- {dialectical_framework-2.0.6 → dialectical_framework-2.0.8}/src/dialectical_framework/agents/explorer/tools/present_exploration.py +0 -0
- {dialectical_framework-2.0.6 → dialectical_framework-2.0.8}/src/dialectical_framework/agents/orchestrator/__init__.py +0 -0
- {dialectical_framework-2.0.6 → dialectical_framework-2.0.8}/src/dialectical_framework/agents/orchestrator/tools/__init__.py +0 -0
- {dialectical_framework-2.0.6 → dialectical_framework-2.0.8}/src/dialectical_framework/agents/orchestrator/tools/add_input.py +0 -0
- {dialectical_framework-2.0.6 → dialectical_framework-2.0.8}/src/dialectical_framework/agents/orchestrator/tools/audit_feasibility.py +0 -0
- {dialectical_framework-2.0.6 → dialectical_framework-2.0.8}/src/dialectical_framework/agents/orchestrator/tools/create_dx_input.py +0 -0
- {dialectical_framework-2.0.6 → dialectical_framework-2.0.8}/src/dialectical_framework/agents/orchestrator/tools/digest_input.py +0 -0
- {dialectical_framework-2.0.6 → dialectical_framework-2.0.8}/src/dialectical_framework/agents/orchestrator/tools/discard.py +0 -0
- {dialectical_framework-2.0.6 → dialectical_framework-2.0.8}/src/dialectical_framework/agents/orchestrator/tools/get_schema.py +0 -0
- {dialectical_framework-2.0.6 → dialectical_framework-2.0.8}/src/dialectical_framework/agents/orchestrator/tools/inspect_node.py +0 -0
- {dialectical_framework-2.0.6 → dialectical_framework-2.0.8}/src/dialectical_framework/agents/orchestrator/tools/present_analysis.py +0 -0
- {dialectical_framework-2.0.6 → dialectical_framework-2.0.8}/src/dialectical_framework/agents/orchestrator/tools/query_graph.py +0 -0
- {dialectical_framework-2.0.6 → dialectical_framework-2.0.8}/src/dialectical_framework/agents/orchestrator/tools/read_digest.py +0 -0
- {dialectical_framework-2.0.6 → dialectical_framework-2.0.8}/src/dialectical_framework/agents/orchestrator/tools/read_input.py +0 -0
- {dialectical_framework-2.0.6 → dialectical_framework-2.0.8}/src/dialectical_framework/agents/reasonable_concern.py +0 -0
- {dialectical_framework-2.0.6 → dialectical_framework-2.0.8}/src/dialectical_framework/agents/stream_events.py +0 -0
- {dialectical_framework-2.0.6 → dialectical_framework-2.0.8}/src/dialectical_framework/agents/toolsets.py +0 -0
- {dialectical_framework-2.0.6 → dialectical_framework-2.0.8}/src/dialectical_framework/agents/turn_timing.py +0 -0
- {dialectical_framework-2.0.6 → dialectical_framework-2.0.8}/src/dialectical_framework/concerns/__init__.py +0 -0
- {dialectical_framework-2.0.6 → dialectical_framework-2.0.8}/src/dialectical_framework/concerns/ac_re_taxonomy.py +0 -0
- {dialectical_framework-2.0.6 → dialectical_framework-2.0.8}/src/dialectical_framework/concerns/action_extraction.py +0 -0
- {dialectical_framework-2.0.6 → dialectical_framework-2.0.8}/src/dialectical_framework/concerns/add_input.py +0 -0
- {dialectical_framework-2.0.6 → dialectical_framework-2.0.8}/src/dialectical_framework/concerns/ai_dto/__init__.py +0 -0
- {dialectical_framework-2.0.6 → dialectical_framework-2.0.8}/src/dialectical_framework/concerns/ai_dto/statement_dto.py +0 -0
- {dialectical_framework-2.0.6 → dialectical_framework-2.0.8}/src/dialectical_framework/concerns/ai_dto/statements_deck_dto.py +0 -0
- {dialectical_framework-2.0.6 → dialectical_framework-2.0.8}/src/dialectical_framework/concerns/antithesis_classification.py +0 -0
- {dialectical_framework-2.0.6 → dialectical_framework-2.0.8}/src/dialectical_framework/concerns/antithesis_extraction.py +0 -0
- {dialectical_framework-2.0.6 → dialectical_framework-2.0.8}/src/dialectical_framework/concerns/antithetical_thesis_detection.py +0 -0
- {dialectical_framework-2.0.6 → dialectical_framework-2.0.8}/src/dialectical_framework/concerns/aspect_classification.py +0 -0
- {dialectical_framework-2.0.6 → dialectical_framework-2.0.8}/src/dialectical_framework/concerns/aspect_generation.py +0 -0
- {dialectical_framework-2.0.6 → dialectical_framework-2.0.8}/src/dialectical_framework/concerns/build_status.py +0 -0
- {dialectical_framework-2.0.6 → dialectical_framework-2.0.8}/src/dialectical_framework/concerns/causality/__init__.py +0 -0
- {dialectical_framework-2.0.6 → dialectical_framework-2.0.8}/src/dialectical_framework/concerns/causality/causality_estimator.py +0 -0
- {dialectical_framework-2.0.6 → dialectical_framework-2.0.8}/src/dialectical_framework/concerns/causality/causality_estimator_balanced.py +0 -0
- {dialectical_framework-2.0.6 → dialectical_framework-2.0.8}/src/dialectical_framework/concerns/causality/causality_estimator_criteria.py +0 -0
- {dialectical_framework-2.0.6 → dialectical_framework-2.0.8}/src/dialectical_framework/concerns/causality/causality_estimator_desirable.py +0 -0
- {dialectical_framework-2.0.6 → dialectical_framework-2.0.8}/src/dialectical_framework/concerns/causality/causality_estimator_feasible.py +0 -0
- {dialectical_framework-2.0.6 → dialectical_framework-2.0.8}/src/dialectical_framework/concerns/causality/causality_estimator_realistic.py +0 -0
- {dialectical_framework-2.0.6 → dialectical_framework-2.0.8}/src/dialectical_framework/concerns/causality/causality_normalizer.py +0 -0
- {dialectical_framework-2.0.6 → dialectical_framework-2.0.8}/src/dialectical_framework/concerns/causality/estimator_resolver.py +0 -0
- {dialectical_framework-2.0.6 → dialectical_framework-2.0.8}/src/dialectical_framework/concerns/causality_estimation.py +0 -0
- {dialectical_framework-2.0.6 → dialectical_framework-2.0.8}/src/dialectical_framework/concerns/control_statements_check.py +0 -0
- {dialectical_framework-2.0.6 → dialectical_framework-2.0.8}/src/dialectical_framework/concerns/create_dx_input.py +0 -0
- {dialectical_framework-2.0.6 → dialectical_framework-2.0.8}/src/dialectical_framework/concerns/create_nexus.py +0 -0
- {dialectical_framework-2.0.6 → dialectical_framework-2.0.8}/src/dialectical_framework/concerns/decision_coherence_check.py +0 -0
- {dialectical_framework-2.0.6 → dialectical_framework-2.0.8}/src/dialectical_framework/concerns/decision_confirmation_check.py +0 -0
- {dialectical_framework-2.0.6 → dialectical_framework-2.0.8}/src/dialectical_framework/concerns/diagonal_oppositions_check.py +0 -0
- {dialectical_framework-2.0.6 → dialectical_framework-2.0.8}/src/dialectical_framework/concerns/dialectical_context.py +0 -0
- {dialectical_framework-2.0.6 → dialectical_framework-2.0.8}/src/dialectical_framework/concerns/discard.py +0 -0
- {dialectical_framework-2.0.6 → dialectical_framework-2.0.8}/src/dialectical_framework/concerns/display_text_edit.py +0 -0
- {dialectical_framework-2.0.6 → dialectical_framework-2.0.8}/src/dialectical_framework/concerns/expand_nexus.py +0 -0
- {dialectical_framework-2.0.6 → dialectical_framework-2.0.8}/src/dialectical_framework/concerns/perspective_combination.py +0 -0
- {dialectical_framework-2.0.6 → dialectical_framework-2.0.8}/src/dialectical_framework/concerns/perspective_validation.py +0 -0
- {dialectical_framework-2.0.6 → dialectical_framework-2.0.8}/src/dialectical_framework/concerns/positive_ac_re_apex_derivation.py +0 -0
- {dialectical_framework-2.0.6 → dialectical_framework-2.0.8}/src/dialectical_framework/concerns/record_decision.py +0 -0
- {dialectical_framework-2.0.6 → dialectical_framework-2.0.8}/src/dialectical_framework/concerns/scoring_scales.py +0 -0
- {dialectical_framework-2.0.6 → dialectical_framework-2.0.8}/src/dialectical_framework/concerns/source_digest.py +0 -0
- {dialectical_framework-2.0.6 → dialectical_framework-2.0.8}/src/dialectical_framework/concerns/statement_classification.py +0 -0
- {dialectical_framework-2.0.6 → dialectical_framework-2.0.8}/src/dialectical_framework/concerns/statement_deduplication.py +0 -0
- {dialectical_framework-2.0.6 → dialectical_framework-2.0.8}/src/dialectical_framework/concerns/statement_headline.py +0 -0
- {dialectical_framework-2.0.6 → dialectical_framework-2.0.8}/src/dialectical_framework/concerns/statement_placement.py +0 -0
- {dialectical_framework-2.0.6 → dialectical_framework-2.0.8}/src/dialectical_framework/concerns/synthesis_generation.py +0 -0
- {dialectical_framework-2.0.6 → dialectical_framework-2.0.8}/src/dialectical_framework/concerns/tetrad_candidates.py +0 -0
- {dialectical_framework-2.0.6 → dialectical_framework-2.0.8}/src/dialectical_framework/concerns/tetrad_grounding.py +0 -0
- {dialectical_framework-2.0.6 → dialectical_framework-2.0.8}/src/dialectical_framework/concerns/tetrad_sketch.py +0 -0
- {dialectical_framework-2.0.6 → dialectical_framework-2.0.8}/src/dialectical_framework/concerns/thesis_extraction.py +0 -0
- {dialectical_framework-2.0.6 → dialectical_framework-2.0.8}/src/dialectical_framework/concerns/transformation_audit.py +0 -0
- {dialectical_framework-2.0.6 → dialectical_framework-2.0.8}/src/dialectical_framework/concerns/transformation_generation.py +0 -0
- {dialectical_framework-2.0.6 → dialectical_framework-2.0.8}/src/dialectical_framework/concerns/transformation_sketch.py +0 -0
- {dialectical_framework-2.0.6 → dialectical_framework-2.0.8}/src/dialectical_framework/concerns/view_sketch.py +0 -0
- {dialectical_framework-2.0.6 → dialectical_framework-2.0.8}/src/dialectical_framework/dialectical_reasoning.py +0 -0
- {dialectical_framework-2.0.6 → dialectical_framework-2.0.8}/src/dialectical_framework/enums/__init__.py +0 -0
- {dialectical_framework-2.0.6 → dialectical_framework-2.0.8}/src/dialectical_framework/enums/causality_preset.py +0 -0
- {dialectical_framework-2.0.6 → dialectical_framework-2.0.8}/src/dialectical_framework/enums/di.py +0 -0
- {dialectical_framework-2.0.6 → dialectical_framework-2.0.8}/src/dialectical_framework/events/__init__.py +0 -0
- {dialectical_framework-2.0.6 → dialectical_framework-2.0.8}/src/dialectical_framework/events/graph_event.py +0 -0
- {dialectical_framework-2.0.6 → dialectical_framework-2.0.8}/src/dialectical_framework/events/graph_event_bus.py +0 -0
- {dialectical_framework-2.0.6 → dialectical_framework-2.0.8}/src/dialectical_framework/events/progress_event.py +0 -0
- {dialectical_framework-2.0.6 → dialectical_framework-2.0.8}/src/dialectical_framework/exceptions/__init__.py +0 -0
- {dialectical_framework-2.0.6 → dialectical_framework-2.0.8}/src/dialectical_framework/exceptions/node_errors.py +0 -0
- {dialectical_framework-2.0.6 → dialectical_framework-2.0.8}/src/dialectical_framework/exceptions/resolver_errors.py +0 -0
- {dialectical_framework-2.0.6 → dialectical_framework-2.0.8}/src/dialectical_framework/graph/__init__.py +0 -0
- {dialectical_framework-2.0.6 → dialectical_framework-2.0.8}/src/dialectical_framework/graph/composite_input_resolver.py +0 -0
- {dialectical_framework-2.0.6 → dialectical_framework-2.0.8}/src/dialectical_framework/graph/dialexity_input_resolver.py +0 -0
- {dialectical_framework-2.0.6 → dialectical_framework-2.0.8}/src/dialectical_framework/graph/estimation_manager.py +0 -0
- {dialectical_framework-2.0.6 → dialectical_framework-2.0.8}/src/dialectical_framework/graph/mixins/__init__.py +0 -0
- {dialectical_framework-2.0.6 → dialectical_framework-2.0.8}/src/dialectical_framework/graph/mixins/incremental_build_mixin.py +0 -0
- {dialectical_framework-2.0.6 → dialectical_framework-2.0.8}/src/dialectical_framework/graph/mixins/intent_mixin.py +0 -0
- {dialectical_framework-2.0.6 → dialectical_framework-2.0.8}/src/dialectical_framework/graph/mixins/persistable_mixin.py +0 -0
- {dialectical_framework-2.0.6 → dialectical_framework-2.0.8}/src/dialectical_framework/graph/nodes/__init__.py +0 -0
- {dialectical_framework-2.0.6 → dialectical_framework-2.0.8}/src/dialectical_framework/graph/nodes/assessable_entity.py +0 -0
- {dialectical_framework-2.0.6 → dialectical_framework-2.0.8}/src/dialectical_framework/graph/nodes/base_node.py +0 -0
- {dialectical_framework-2.0.6 → dialectical_framework-2.0.8}/src/dialectical_framework/graph/nodes/case.py +0 -0
- {dialectical_framework-2.0.6 → dialectical_framework-2.0.8}/src/dialectical_framework/graph/nodes/cycle.py +0 -0
- {dialectical_framework-2.0.6 → dialectical_framework-2.0.8}/src/dialectical_framework/graph/nodes/decision.py +0 -0
- {dialectical_framework-2.0.6 → dialectical_framework-2.0.8}/src/dialectical_framework/graph/nodes/estimation.py +0 -0
- {dialectical_framework-2.0.6 → dialectical_framework-2.0.8}/src/dialectical_framework/graph/nodes/ideas.py +0 -0
- {dialectical_framework-2.0.6 → dialectical_framework-2.0.8}/src/dialectical_framework/graph/nodes/input.py +0 -0
- {dialectical_framework-2.0.6 → dialectical_framework-2.0.8}/src/dialectical_framework/graph/nodes/nexus.py +0 -0
- {dialectical_framework-2.0.6 → dialectical_framework-2.0.8}/src/dialectical_framework/graph/nodes/note.py +0 -0
- {dialectical_framework-2.0.6 → dialectical_framework-2.0.8}/src/dialectical_framework/graph/nodes/perspective.py +0 -0
- {dialectical_framework-2.0.6 → dialectical_framework-2.0.8}/src/dialectical_framework/graph/nodes/polarity.py +0 -0
- {dialectical_framework-2.0.6 → dialectical_framework-2.0.8}/src/dialectical_framework/graph/nodes/rationale.py +0 -0
- {dialectical_framework-2.0.6 → dialectical_framework-2.0.8}/src/dialectical_framework/graph/nodes/statement.py +0 -0
- {dialectical_framework-2.0.6 → dialectical_framework-2.0.8}/src/dialectical_framework/graph/nodes/synthesis.py +0 -0
- {dialectical_framework-2.0.6 → dialectical_framework-2.0.8}/src/dialectical_framework/graph/nodes/transformation.py +0 -0
- {dialectical_framework-2.0.6 → dialectical_framework-2.0.8}/src/dialectical_framework/graph/nodes/transition.py +0 -0
- {dialectical_framework-2.0.6 → dialectical_framework-2.0.8}/src/dialectical_framework/graph/nodes/wheel.py +0 -0
- {dialectical_framework-2.0.6 → dialectical_framework-2.0.8}/src/dialectical_framework/graph/relationship_manager.py +0 -0
- {dialectical_framework-2.0.6 → dialectical_framework-2.0.8}/src/dialectical_framework/graph/relationships/__init__.py +0 -0
- {dialectical_framework-2.0.6 → dialectical_framework-2.0.8}/src/dialectical_framework/graph/relationships/action_reflection_relationship.py +0 -0
- {dialectical_framework-2.0.6 → dialectical_framework-2.0.8}/src/dialectical_framework/graph/relationships/belongs_to_cycle_relationship.py +0 -0
- {dialectical_framework-2.0.6 → dialectical_framework-2.0.8}/src/dialectical_framework/graph/relationships/belongs_to_nexus_relationship.py +0 -0
- {dialectical_framework-2.0.6 → dialectical_framework-2.0.8}/src/dialectical_framework/graph/relationships/changed_to_relationship.py +0 -0
- {dialectical_framework-2.0.6 → dialectical_framework-2.0.8}/src/dialectical_framework/graph/relationships/contradiction_of_relationship.py +0 -0
- {dialectical_framework-2.0.6 → dialectical_framework-2.0.8}/src/dialectical_framework/graph/relationships/critiques_relationship.py +0 -0
- {dialectical_framework-2.0.6 → dialectical_framework-2.0.8}/src/dialectical_framework/graph/relationships/distilled_to_relationship.py +0 -0
- {dialectical_framework-2.0.6 → dialectical_framework-2.0.8}/src/dialectical_framework/graph/relationships/estimates_relationship.py +0 -0
- {dialectical_framework-2.0.6 → dialectical_framework-2.0.8}/src/dialectical_framework/graph/relationships/explains_relationship.py +0 -0
- {dialectical_framework-2.0.6 → dialectical_framework-2.0.8}/src/dialectical_framework/graph/relationships/grounded_in_relationship.py +0 -0
- {dialectical_framework-2.0.6 → dialectical_framework-2.0.8}/src/dialectical_framework/graph/relationships/has_input_relationship.py +0 -0
- {dialectical_framework-2.0.6 → dialectical_framework-2.0.8}/src/dialectical_framework/graph/relationships/has_statement_relationship.py +0 -0
- {dialectical_framework-2.0.6 → dialectical_framework-2.0.8}/src/dialectical_framework/graph/relationships/has_wheel_relationship.py +0 -0
- {dialectical_framework-2.0.6 → dialectical_framework-2.0.8}/src/dialectical_framework/graph/relationships/immutable_structure.py +0 -0
- {dialectical_framework-2.0.6 → dialectical_framework-2.0.8}/src/dialectical_framework/graph/relationships/is_source_of_relationship.py +0 -0
- {dialectical_framework-2.0.6 → dialectical_framework-2.0.8}/src/dialectical_framework/graph/relationships/is_target_of_relationship.py +0 -0
- {dialectical_framework-2.0.6 → dialectical_framework-2.0.8}/src/dialectical_framework/graph/relationships/negative_side_of_relationship.py +0 -0
- {dialectical_framework-2.0.6 → dialectical_framework-2.0.8}/src/dialectical_framework/graph/relationships/opposite_direction_relationship.py +0 -0
- {dialectical_framework-2.0.6 → dialectical_framework-2.0.8}/src/dialectical_framework/graph/relationships/opposite_of_relationship.py +0 -0
- {dialectical_framework-2.0.6 → dialectical_framework-2.0.8}/src/dialectical_framework/graph/relationships/polarity_relationship.py +0 -0
- {dialectical_framework-2.0.6 → dialectical_framework-2.0.8}/src/dialectical_framework/graph/relationships/positive_side_of_relationship.py +0 -0
- {dialectical_framework-2.0.6 → dialectical_framework-2.0.8}/src/dialectical_framework/graph/relationships/provides_relationship.py +0 -0
- {dialectical_framework-2.0.6 → dialectical_framework-2.0.8}/src/dialectical_framework/graph/relationships/synthesis_of_relationship.py +0 -0
- {dialectical_framework-2.0.6 → dialectical_framework-2.0.8}/src/dialectical_framework/graph/rendering.py +0 -0
- {dialectical_framework-2.0.6 → dialectical_framework-2.0.8}/src/dialectical_framework/graph/repositories/__init__.py +0 -0
- {dialectical_framework-2.0.6 → dialectical_framework-2.0.8}/src/dialectical_framework/graph/repositories/case_repository.py +0 -0
- {dialectical_framework-2.0.6 → dialectical_framework-2.0.8}/src/dialectical_framework/graph/repositories/cycle_repository.py +0 -0
- {dialectical_framework-2.0.6 → dialectical_framework-2.0.8}/src/dialectical_framework/graph/repositories/decision_repository.py +0 -0
- {dialectical_framework-2.0.6 → dialectical_framework-2.0.8}/src/dialectical_framework/graph/repositories/input_repository.py +0 -0
- {dialectical_framework-2.0.6 → dialectical_framework-2.0.8}/src/dialectical_framework/graph/repositories/nexus_repository.py +0 -0
- {dialectical_framework-2.0.6 → dialectical_framework-2.0.8}/src/dialectical_framework/graph/repositories/node_repository.py +0 -0
- {dialectical_framework-2.0.6 → dialectical_framework-2.0.8}/src/dialectical_framework/graph/repositories/note_repository.py +0 -0
- {dialectical_framework-2.0.6 → dialectical_framework-2.0.8}/src/dialectical_framework/graph/repositories/perspective_repository.py +0 -0
- {dialectical_framework-2.0.6 → dialectical_framework-2.0.8}/src/dialectical_framework/graph/repositories/polarity_repository.py +0 -0
- {dialectical_framework-2.0.6 → dialectical_framework-2.0.8}/src/dialectical_framework/graph/repositories/schema_repository.py +0 -0
- {dialectical_framework-2.0.6 → dialectical_framework-2.0.8}/src/dialectical_framework/graph/repositories/statement_repository.py +0 -0
- {dialectical_framework-2.0.6 → dialectical_framework-2.0.8}/src/dialectical_framework/graph/repositories/transformation_repository.py +0 -0
- {dialectical_framework-2.0.6 → dialectical_framework-2.0.8}/src/dialectical_framework/graph/repositories/wheel_repository.py +0 -0
- {dialectical_framework-2.0.6 → dialectical_framework-2.0.8}/src/dialectical_framework/graph/scope_context.py +0 -0
- {dialectical_framework-2.0.6 → dialectical_framework-2.0.8}/src/dialectical_framework/graph/verbatim_input_resolver.py +0 -0
- {dialectical_framework-2.0.6 → dialectical_framework-2.0.8}/src/dialectical_framework/graph/wheel_segment.py +0 -0
- {dialectical_framework-2.0.6 → dialectical_framework-2.0.8}/src/dialectical_framework/graph/wheel_segment_polar_pair.py +0 -0
- {dialectical_framework-2.0.6 → dialectical_framework-2.0.8}/src/dialectical_framework/protocols/__init__.py +0 -0
- {dialectical_framework-2.0.6 → dialectical_framework-2.0.8}/src/dialectical_framework/protocols/has_config.py +0 -0
- {dialectical_framework-2.0.6 → dialectical_framework-2.0.8}/src/dialectical_framework/protocols/input_resolver.py +0 -0
- {dialectical_framework-2.0.6 → dialectical_framework-2.0.8}/src/dialectical_framework/py.typed +0 -0
- {dialectical_framework-2.0.6 → dialectical_framework-2.0.8}/src/dialectical_framework/settings.py +0 -0
- {dialectical_framework-2.0.6 → dialectical_framework-2.0.8}/src/dialectical_framework/settings_context.py +0 -0
- {dialectical_framework-2.0.6 → dialectical_framework-2.0.8}/src/dialectical_framework/utils/__init__.py +0 -0
- {dialectical_framework-2.0.6 → dialectical_framework-2.0.8}/src/dialectical_framework/utils/async_drain.py +0 -0
- {dialectical_framework-2.0.6 → dialectical_framework-2.0.8}/src/dialectical_framework/utils/call_census.py +0 -0
- {dialectical_framework-2.0.6 → dialectical_framework-2.0.8}/src/dialectical_framework/utils/chunking.py +0 -0
- {dialectical_framework-2.0.6 → dialectical_framework-2.0.8}/src/dialectical_framework/utils/concurrency.py +0 -0
- {dialectical_framework-2.0.6 → dialectical_framework-2.0.8}/src/dialectical_framework/utils/dc_replace.py +0 -0
- {dialectical_framework-2.0.6 → dialectical_framework-2.0.8}/src/dialectical_framework/utils/decompose_probability_uniformly.py +0 -0
- {dialectical_framework-2.0.6 → dialectical_framework-2.0.8}/src/dialectical_framework/utils/edge_context.py +0 -0
- {dialectical_framework-2.0.6 → dialectical_framework-2.0.8}/src/dialectical_framework/utils/effect_logger.py +0 -0
- {dialectical_framework-2.0.6 → dialectical_framework-2.0.8}/src/dialectical_framework/utils/input_context.py +0 -0
- {dialectical_framework-2.0.6 → dialectical_framework-2.0.8}/src/dialectical_framework/utils/order_transitions.py +0 -0
- {dialectical_framework-2.0.6 → dialectical_framework-2.0.8}/src/dialectical_framework/utils/progress.py +0 -0
- {dialectical_framework-2.0.6 → dialectical_framework-2.0.8}/src/dialectical_framework/utils/retry_accounting.py +0 -0
- {dialectical_framework-2.0.6 → dialectical_framework-2.0.8}/src/dialectical_framework/utils/sequence_generation.py +0 -0
- {dialectical_framework-2.0.6 → dialectical_framework-2.0.8}/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.8
|
|
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.8"
|
|
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"
|
|
@@ -42,6 +42,7 @@ from dialectical_framework.agents.stream_events import (ResponseComplete,
|
|
|
42
42
|
StreamEvent, TextDelta,
|
|
43
43
|
ToolResult, ToolStart)
|
|
44
44
|
from dialectical_framework.agents.toolsets import merge_app_tools
|
|
45
|
+
from dialectical_framework.exceptions.provider_errors import ModelRefusal
|
|
45
46
|
from dialectical_framework.agents.turn_timing import (ClosingOutcome,
|
|
46
47
|
DeferralOutcome,
|
|
47
48
|
TurnTiming)
|
|
@@ -850,7 +851,13 @@ class Advisor(SettingsAware):
|
|
|
850
851
|
# Around the provider round ONLY — the closing seam and the
|
|
851
852
|
# off-turn scheduling below run outside it on purpose.
|
|
852
853
|
with speaking(user_message):
|
|
853
|
-
|
|
854
|
+
try:
|
|
855
|
+
result = await self._conversation.submit(
|
|
856
|
+
ChatResponse, user_message
|
|
857
|
+
)
|
|
858
|
+
except ModelRefusal:
|
|
859
|
+
await self._settle_refused_turn(user_message)
|
|
860
|
+
raise
|
|
854
861
|
reply_path_s = (
|
|
855
862
|
deferred_wait_s
|
|
856
863
|
+ context_render_s
|
|
@@ -966,33 +973,37 @@ class Advisor(SettingsAware):
|
|
|
966
973
|
# Entered here, before the stream is opened, because the tool runs
|
|
967
974
|
# inside the generator while it is being iterated.
|
|
968
975
|
with speaking(user_message):
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
if hygiene is not None:
|
|
985
|
-
if isinstance(event, TextDelta):
|
|
986
|
-
clean = hygiene.feed(event.text)
|
|
987
|
-
if clean:
|
|
988
|
-
yield TextDelta(text=clean)
|
|
976
|
+
try:
|
|
977
|
+
async with aclosing(
|
|
978
|
+
self._conversation.submit_stream(ChatResponse, user_message)
|
|
979
|
+
) as rounds:
|
|
980
|
+
async for event in rounds:
|
|
981
|
+
if isinstance(event, ResponseComplete):
|
|
982
|
+
final_event = event
|
|
983
|
+
reply = event.message
|
|
984
|
+
# `continue`, so `submit_stream` is asked for one more
|
|
985
|
+
# event and runs to its own end instead of being closed
|
|
986
|
+
# while suspended at its yield. The seconds read below are
|
|
987
|
+
# safe either way — it stamps them as this event passes
|
|
988
|
+
# through it, not on exit — but running out is still the
|
|
989
|
+
# cleaner exit: the provider's connection is let go of by
|
|
990
|
+
# exhaustion rather than by a close unwinding a live frame.
|
|
989
991
|
continue
|
|
990
|
-
if
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
992
|
+
if hygiene is not None:
|
|
993
|
+
if isinstance(event, TextDelta):
|
|
994
|
+
clean = hygiene.feed(event.text)
|
|
995
|
+
if clean:
|
|
996
|
+
yield TextDelta(text=clean)
|
|
997
|
+
continue
|
|
998
|
+
if isinstance(event, (ToolStart, ToolResult)):
|
|
999
|
+
tail = hygiene.flush()
|
|
1000
|
+
if tail:
|
|
1001
|
+
yield TextDelta(text=tail)
|
|
1002
|
+
hygiene = HashCitationFilter()
|
|
1003
|
+
yield event
|
|
1004
|
+
except ModelRefusal:
|
|
1005
|
+
await self._settle_refused_turn(user_message)
|
|
1006
|
+
raise
|
|
996
1007
|
if hygiene is not None:
|
|
997
1008
|
tail = hygiene.flush()
|
|
998
1009
|
if tail:
|
|
@@ -2829,6 +2840,27 @@ class Advisor(SettingsAware):
|
|
|
2829
2840
|
logger.exception("Accepted-cost ground resolution failed (fail-soft)")
|
|
2830
2841
|
return None
|
|
2831
2842
|
|
|
2843
|
+
async def _settle_refused_turn(self, user_message: str) -> None:
|
|
2844
|
+
"""The closing work a refused turn still owes, before the refusal goes on.
|
|
2845
|
+
|
|
2846
|
+
A refusal can arrive AFTER tool rounds: the resume round is the one
|
|
2847
|
+
declined, so `record_decision` may already have written a Decision and
|
|
2848
|
+
`note` may already have kept a Note. The facilitator drops the turn
|
|
2849
|
+
from history, but the graph keeps what the tools wrote — and without
|
|
2850
|
+
this the recorded decision never gets its pathway weave (the seam's
|
|
2851
|
+
MODEL_RECORDED branch is what schedules it) and the noted tensions wait
|
|
2852
|
+
for the next turn to be offered. Only the work the tools already did is
|
|
2853
|
+
followed up: the classifier is not asked about a turn that was refused,
|
|
2854
|
+
so no decision is written here that the model did not write.
|
|
2855
|
+
Fail-soft: the refusal is what the host must see.
|
|
2856
|
+
"""
|
|
2857
|
+
try:
|
|
2858
|
+
if self._recorded_decision_this_turn():
|
|
2859
|
+
await self._repair_unrecorded_decision(user_message, "")
|
|
2860
|
+
self._schedule_noted_tensions()
|
|
2861
|
+
except Exception:
|
|
2862
|
+
logger.exception("Closing work after a refused turn failed (fail-soft)")
|
|
2863
|
+
|
|
2832
2864
|
def _recorded_decision_this_turn(self) -> bool:
|
|
2833
2865
|
"""Did `record_decision` already run, successfully, on this turn?
|
|
2834
2866
|
|
|
@@ -36,6 +36,7 @@ dropped from BOTH the product and the baseline.
|
|
|
36
36
|
|
|
37
37
|
from __future__ import annotations
|
|
38
38
|
|
|
39
|
+
import logging
|
|
39
40
|
import re
|
|
40
41
|
from contextlib import aclosing
|
|
41
42
|
from typing import Any, AsyncGenerator, Optional
|
|
@@ -51,7 +52,14 @@ from dialectical_framework.agents.conversation_facilitator import (
|
|
|
51
52
|
FROM_SETTINGS, ConversationFacilitator)
|
|
52
53
|
from dialectical_framework.agents.stream_events import StreamEvent
|
|
53
54
|
from dialectical_framework.agents.turn_timing import TurnTiming
|
|
54
|
-
from dialectical_framework.
|
|
55
|
+
from dialectical_framework.exceptions.provider_errors import ModelRefusal
|
|
56
|
+
from dialectical_framework.graph.views import (
|
|
57
|
+
ExplorationView,
|
|
58
|
+
FailedDrawView,
|
|
59
|
+
PerspectiveView,
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
logger = logging.getLogger(__name__)
|
|
55
63
|
|
|
56
64
|
#: Tool verbs → mental acts. These are REWRITES, not drops: dropping every
|
|
57
65
|
#: paragraph that mentions a tool name would also delete the discrimination test
|
|
@@ -135,6 +143,16 @@ _TOOL_TOKENS = (
|
|
|
135
143
|
)
|
|
136
144
|
|
|
137
145
|
|
|
146
|
+
def _failed_draw(error: BaseException) -> FailedDrawView:
|
|
147
|
+
"""A failed draw's record: the exception's type, the provider's refusal
|
|
148
|
+
category when it is a `ModelRefusal` that carries one, and its text."""
|
|
149
|
+
return FailedDrawView(
|
|
150
|
+
kind=type(error).__name__,
|
|
151
|
+
category=getattr(error, "category", None) if isinstance(error, ModelRefusal) else None,
|
|
152
|
+
message=str(error) or None,
|
|
153
|
+
)
|
|
154
|
+
|
|
155
|
+
|
|
138
156
|
def _apply_rewrites(section: str) -> tuple[str, list[str]]:
|
|
139
157
|
"""Apply the rewrite table whitespace-insensitively.
|
|
140
158
|
|
|
@@ -425,8 +443,22 @@ class Consultant:
|
|
|
425
443
|
# measured cold-start rate.
|
|
426
444
|
drawn = await asyncio.gather(*(draw() for _ in range(n)), return_exceptions=True)
|
|
427
445
|
sketches = [d for d in drawn if not isinstance(d, BaseException)]
|
|
446
|
+
failures = [d for d in drawn if isinstance(d, BaseException)]
|
|
447
|
+
# A failed draw is a finding, not noise: it costs the card a candidate
|
|
448
|
+
# (or its whole comparison, when two fail) and it costs time before it
|
|
449
|
+
# fails. Said once per failure, by type — the host that counted 8 lost
|
|
450
|
+
# draws in 240 on Opus 5.5 had no cause on record because this branch
|
|
451
|
+
# was silent — and carried on the view as `failed_draws`.
|
|
452
|
+
failed_draws = [_failed_draw(e) for e in failures]
|
|
453
|
+
for failed in failed_draws:
|
|
454
|
+
logger.warning(
|
|
455
|
+
"A view-turn draw failed (%d of %d drawn): %s%s: %s",
|
|
456
|
+
len(sketches), n, failed.kind,
|
|
457
|
+
f" ({failed.category})" if failed.category else "",
|
|
458
|
+
failed.message,
|
|
459
|
+
)
|
|
428
460
|
if not sketches:
|
|
429
|
-
raise
|
|
461
|
+
raise failures[0]
|
|
430
462
|
# With several draws the framework's own coherence check picks the one
|
|
431
463
|
# whose FIRST tension holds best (`concerns/tetrad_candidates.py`); a
|
|
432
464
|
# draw with no complete first tension sorts last. The card a blindspot
|
|
@@ -447,7 +479,11 @@ class Consultant:
|
|
|
447
479
|
sketch = sketches[judgeable[0]] if judgeable else sketches[0]
|
|
448
480
|
else:
|
|
449
481
|
sketch = sketches[0]
|
|
450
|
-
view = dataclasses.replace(
|
|
482
|
+
view = dataclasses.replace(
|
|
483
|
+
exploration_view_from_sketch(sketch),
|
|
484
|
+
runners_up=runners_up,
|
|
485
|
+
failed_draws=failed_draws,
|
|
486
|
+
)
|
|
451
487
|
# What the turn leaves behind is NOT what it sent: the long request and
|
|
452
488
|
# the DTO come off, and the person's ask plus the drawing in words go
|
|
453
489
|
# on. Kept as a request→prose pair, the long request taught the next
|
|
@@ -29,6 +29,7 @@ from dialectical_framework.agents.stream_events import (
|
|
|
29
29
|
ToolStart,
|
|
30
30
|
)
|
|
31
31
|
from dialectical_framework.agents.turn_timing import ToolRound
|
|
32
|
+
from dialectical_framework.exceptions.provider_errors import ModelRefusal
|
|
32
33
|
from dialectical_framework.protocols.has_config import SettingsAware
|
|
33
34
|
from dialectical_framework.utils.call_census import record_call
|
|
34
35
|
from dialectical_framework.utils.thinking_compat import conversational_round
|
|
@@ -37,7 +38,7 @@ from dialectical_framework.utils.retry_accounting import (RetryAccount,
|
|
|
37
38
|
from dialectical_framework.utils.use_brain import (prefill_token_kwargs,
|
|
38
39
|
retry_transient, use_brain)
|
|
39
40
|
|
|
40
|
-
from mirascope.llm import TextChunk, ThoughtChunk, ToolOutput
|
|
41
|
+
from mirascope.llm import FinishReason, TextChunk, ThoughtChunk, ToolOutput
|
|
41
42
|
|
|
42
43
|
if TYPE_CHECKING:
|
|
43
44
|
from mirascope.llm import UserContent
|
|
@@ -513,6 +514,7 @@ class ConversationFacilitator(SettingsAware):
|
|
|
513
514
|
Returns:
|
|
514
515
|
Structured response matching response_model
|
|
515
516
|
"""
|
|
517
|
+
history_before = len(self._messages)
|
|
516
518
|
self._messages.append(llm.messages.user(user_content))
|
|
517
519
|
self.last_tool_calls = []
|
|
518
520
|
self.last_tool_results = []
|
|
@@ -567,6 +569,10 @@ class ConversationFacilitator(SettingsAware):
|
|
|
567
569
|
# Mirascope's own request, so nothing in `use_brain` marks it).
|
|
568
570
|
with conversational_round():
|
|
569
571
|
response = await response.resume(tool_outputs)
|
|
572
|
+
# Mirascope's own request, below `use_brain`: a provider that
|
|
573
|
+
# does not raise on a refusal itself only says so here.
|
|
574
|
+
if getattr(response, "finish_reason", None) == FinishReason.REFUSAL:
|
|
575
|
+
raise ModelRefusal(getattr(response, "model_id", "unknown"))
|
|
570
576
|
|
|
571
577
|
# Sync full conversation history from the response chain
|
|
572
578
|
self._messages = list(response.messages)
|
|
@@ -580,6 +586,9 @@ class ConversationFacilitator(SettingsAware):
|
|
|
580
586
|
|
|
581
587
|
# Extract structured response
|
|
582
588
|
return await self._call_with_response_model(response_model)
|
|
589
|
+
except ModelRefusal:
|
|
590
|
+
self._forget_refused_turn(history_before)
|
|
591
|
+
raise
|
|
583
592
|
finally:
|
|
584
593
|
if epoch == self._turn_epoch:
|
|
585
594
|
self.last_submit_seconds = time.monotonic() - started
|
|
@@ -631,6 +640,7 @@ class ConversationFacilitator(SettingsAware):
|
|
|
631
640
|
#: with a later reading, or every ordinary turn would silently absorb
|
|
632
641
|
#: however long the consumer took to come back for the last event.
|
|
633
642
|
recorded = False
|
|
643
|
+
history_before = len(self._messages)
|
|
634
644
|
# Held in a name so it can be closed; see the docstring.
|
|
635
645
|
rounds = self._stream_turn(
|
|
636
646
|
response_model,
|
|
@@ -644,6 +654,9 @@ class ConversationFacilitator(SettingsAware):
|
|
|
644
654
|
self.last_submit_seconds = time.monotonic() - started
|
|
645
655
|
recorded = True
|
|
646
656
|
yield event
|
|
657
|
+
except ModelRefusal:
|
|
658
|
+
self._forget_refused_turn(history_before)
|
|
659
|
+
raise
|
|
647
660
|
finally:
|
|
648
661
|
# Before the close, which can raise: the figure is the point.
|
|
649
662
|
if not recorded and epoch == self._turn_epoch:
|
|
@@ -802,6 +815,13 @@ class ConversationFacilitator(SettingsAware):
|
|
|
802
815
|
first_chunk_at=first_chunk_at,
|
|
803
816
|
yielded_s=yielded_s,
|
|
804
817
|
)
|
|
818
|
+
if getattr(stream, "finish_reason", None) == FinishReason.REFUSAL:
|
|
819
|
+
# The awaited path raises at the provider; a stream only says so
|
|
820
|
+
# in its last chunk, after any text has been yielded. The host
|
|
821
|
+
# has shown a partial reply and should replace it — the
|
|
822
|
+
# exception is the signal. No `stop_details` on this path.
|
|
823
|
+
raise ModelRefusal(getattr(stream, "model_id", "unknown"))
|
|
824
|
+
|
|
805
825
|
if self.last_submit_first_delta_s is None and first_delta_at is not None:
|
|
806
826
|
# From the TURN's start, not this round's: the person has been
|
|
807
827
|
# waiting since they hit send, through the context re-render's
|
|
@@ -900,6 +920,17 @@ class ConversationFacilitator(SettingsAware):
|
|
|
900
920
|
|
|
901
921
|
# --- Internal helpers ---
|
|
902
922
|
|
|
923
|
+
def _forget_refused_turn(self, history_before: int) -> None:
|
|
924
|
+
"""Drop the refused turn from history, user message included.
|
|
925
|
+
|
|
926
|
+
A refused exchange left in history is the context the next request is
|
|
927
|
+
refused in again; the provider's guidance on a refusal is to change or
|
|
928
|
+
drop the turn that triggered it. The turn did not happen, so history
|
|
929
|
+
returns to where it stood before it — the host still has the person's
|
|
930
|
+
words and can offer them back for rephrasing.
|
|
931
|
+
"""
|
|
932
|
+
del self._messages[history_before:]
|
|
933
|
+
|
|
903
934
|
#: Answer written into the synthetic tool_result when the tool loop is cut
|
|
904
935
|
#: short. Addressed to the model, because the model reads it: it must
|
|
905
936
|
#: understand that the tool did not run and that it should wrap up with
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Provider verdicts that are not transport faults.
|
|
3
|
+
|
|
4
|
+
`ModelRefusal` is raised when the model stops with ``stop_reason: "refusal"``
|
|
5
|
+
(the provider's policy classifiers intervened). It is deliberately its own
|
|
6
|
+
type: before it existed a refusal reached the framework as an empty or cut-off
|
|
7
|
+
reply, so a structured call re-asked it as a ParseError (ten times) and a
|
|
8
|
+
conversational turn fell through to a second, structured call that was refused
|
|
9
|
+
again. A refusal is the provider's answer to THIS request, so nothing retries
|
|
10
|
+
it; the caller decides — a concern fails soft as on any error, a host shows the
|
|
11
|
+
person something and lets them rephrase.
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
from __future__ import annotations
|
|
15
|
+
|
|
16
|
+
from typing import Optional
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class ModelRefusal(RuntimeError):
|
|
20
|
+
"""The model declined the request (``stop_reason: "refusal"``).
|
|
21
|
+
|
|
22
|
+
``category`` and ``explanation`` are the provider's ``stop_details`` when it
|
|
23
|
+
sends them (``"cyber"``, ``"bio"``, or None for an unnamed category); a
|
|
24
|
+
streamed round does not carry them and leaves both None. The explanation
|
|
25
|
+
text is not guaranteed stable by the provider — show it, never branch on it.
|
|
26
|
+
"""
|
|
27
|
+
|
|
28
|
+
def __init__(
|
|
29
|
+
self,
|
|
30
|
+
model: str,
|
|
31
|
+
*,
|
|
32
|
+
category: Optional[str] = None,
|
|
33
|
+
explanation: Optional[str] = None,
|
|
34
|
+
) -> None:
|
|
35
|
+
self.model = model
|
|
36
|
+
self.category = category
|
|
37
|
+
self.explanation = explanation
|
|
38
|
+
detail = f" ({category})" if category else ""
|
|
39
|
+
super().__init__(
|
|
40
|
+
f"Model {model} refused the request{detail}"
|
|
41
|
+
+ (f": {explanation}" if explanation else "")
|
|
42
|
+
)
|
{dialectical_framework-2.0.6 → dialectical_framework-2.0.8}/src/dialectical_framework/graph/views.py
RENAMED
|
@@ -326,6 +326,32 @@ class WheelView:
|
|
|
326
326
|
return dataclasses.asdict(self)
|
|
327
327
|
|
|
328
328
|
|
|
329
|
+
@dataclass(frozen=True)
|
|
330
|
+
class FailedDrawView:
|
|
331
|
+
"""One best-of-N draw that produced nothing — what failed, by type.
|
|
332
|
+
|
|
333
|
+
A drawn view is best-of-N (`Consultant.exploration_view(attempts=)`), and a
|
|
334
|
+
draw that raises used to vanish: the gather dropped it, nothing logged it,
|
|
335
|
+
and the census never saw a call it could not record. A host measured the
|
|
336
|
+
gap as 8 of 240 draws on Opus 5.5 with no cause on record. `kind` is the
|
|
337
|
+
exception's class name (`ModelRefusal`, `ParseError`, a provider error);
|
|
338
|
+
`category` is the provider's refusal category when the kind is a refusal
|
|
339
|
+
and it sent one; `message` is the exception's text, for a log, never for
|
|
340
|
+
a screen — `without_terminology()` drops it, since a parse error names DTO
|
|
341
|
+
fields.
|
|
342
|
+
"""
|
|
343
|
+
|
|
344
|
+
kind: str
|
|
345
|
+
category: Optional[str] = None
|
|
346
|
+
message: Optional[str] = None
|
|
347
|
+
|
|
348
|
+
def without_terminology(self) -> FailedDrawView:
|
|
349
|
+
return dataclasses.replace(self, message=None)
|
|
350
|
+
|
|
351
|
+
def to_dict(self) -> dict[str, Any]:
|
|
352
|
+
return dataclasses.asdict(self)
|
|
353
|
+
|
|
354
|
+
|
|
329
355
|
@dataclass(frozen=True)
|
|
330
356
|
class ExplorationView:
|
|
331
357
|
"""The perspectives seen together — an exploration's (a Nexus's) members,
|
|
@@ -344,6 +370,11 @@ class ExplorationView:
|
|
|
344
370
|
#: host's "again"/"another" can show one without a new reasoning call.
|
|
345
371
|
#: Empty for a view read off the graph, or drawn once.
|
|
346
372
|
runners_up: list[PerspectiveView] = field(default_factory=list)
|
|
373
|
+
#: The best-of-N draws that FAILED, in no particular order — so a host can
|
|
374
|
+
#: count them and tell a card chosen among three from one that was the only
|
|
375
|
+
#: draw left. Empty for a view read off the graph. A view with every draw
|
|
376
|
+
#: failed is never returned: the method raises the first failure instead.
|
|
377
|
+
failed_draws: list[FailedDrawView] = field(default_factory=list)
|
|
347
378
|
|
|
348
379
|
def without_terminology(self) -> ExplorationView:
|
|
349
380
|
return dataclasses.replace(
|
|
@@ -351,6 +382,7 @@ class ExplorationView:
|
|
|
351
382
|
perspectives=[p.without_terminology() for p in self.perspectives],
|
|
352
383
|
nexus_hash=None,
|
|
353
384
|
runners_up=[p.without_terminology() for p in self.runners_up],
|
|
385
|
+
failed_draws=[f.without_terminology() for f in self.failed_draws],
|
|
354
386
|
)
|
|
355
387
|
|
|
356
388
|
def to_dict(self) -> dict[str, Any]:
|
|
@@ -16,10 +16,17 @@ from mirascope.llm.responses import AsyncResponse, AsyncStreamResponse, Response
|
|
|
16
16
|
from typing_extensions import Unpack
|
|
17
17
|
|
|
18
18
|
from dialectical_framework.enums.di import DI
|
|
19
|
+
from dialectical_framework.exceptions.provider_errors import ModelRefusal
|
|
19
20
|
from dialectical_framework.settings import Settings
|
|
21
|
+
from dialectical_framework.utils.format_compat import (
|
|
22
|
+
learn_format_mode_from_error,
|
|
23
|
+
structured_output_format,
|
|
24
|
+
with_format_compat,
|
|
25
|
+
)
|
|
20
26
|
from dialectical_framework.utils.thinking_compat import (
|
|
21
27
|
learn_thinking_shape_from_error,
|
|
22
28
|
with_thinking_compat,
|
|
29
|
+
without_earlier_turn_thinking,
|
|
23
30
|
)
|
|
24
31
|
|
|
25
32
|
if TYPE_CHECKING:
|
|
@@ -288,6 +295,62 @@ def _bedrock_model_name(model_id: str) -> str:
|
|
|
288
295
|
return model_id.removeprefix("bedrock/").removeprefix("anthropic/")
|
|
289
296
|
|
|
290
297
|
|
|
298
|
+
class _LearningStreamManager:
|
|
299
|
+
"""`messages.stream(...)` that learns the thinking shape from a 400, once.
|
|
300
|
+
|
|
301
|
+
The request is made when the manager is ENTERED (Mirascope's decoder does
|
|
302
|
+
`async with` on the first chunk), before anything has been yielded, so a
|
|
303
|
+
rejected shape can be re-sent without replaying a token — and the streamed
|
|
304
|
+
conversational round is exactly where an unset level goes out as "off",
|
|
305
|
+
so without this a wrong name heuristic would fail every streamed turn until
|
|
306
|
+
some awaited call taught the process. Thinking only: a learned FORMAT mode
|
|
307
|
+
would change the response's parser, which the caller already holds.
|
|
308
|
+
"""
|
|
309
|
+
|
|
310
|
+
def __init__(
|
|
311
|
+
self, client: Any, kwargs: dict[str, Any], params: Mapping[str, Any]
|
|
312
|
+
) -> None:
|
|
313
|
+
self._client = client
|
|
314
|
+
self._kwargs = kwargs
|
|
315
|
+
self._params = params
|
|
316
|
+
self._manager: Any = None
|
|
317
|
+
|
|
318
|
+
def _open(self) -> Any:
|
|
319
|
+
return self._client.messages.stream(
|
|
320
|
+
**with_thinking_compat(self._kwargs["model"], self._kwargs, self._params)
|
|
321
|
+
)
|
|
322
|
+
|
|
323
|
+
async def __aenter__(self) -> Any:
|
|
324
|
+
self._manager = self._open()
|
|
325
|
+
try:
|
|
326
|
+
return await self._manager.__aenter__()
|
|
327
|
+
except Exception as e:
|
|
328
|
+
if not learn_thinking_shape_from_error(self._kwargs["model"], e):
|
|
329
|
+
raise
|
|
330
|
+
self._manager = self._open()
|
|
331
|
+
return await self._manager.__aenter__()
|
|
332
|
+
|
|
333
|
+
async def __aexit__(self, *exc: Any) -> Any:
|
|
334
|
+
return await self._manager.__aexit__(*exc)
|
|
335
|
+
|
|
336
|
+
|
|
337
|
+
def raise_on_refusal(response: Any, model_name: str) -> None:
|
|
338
|
+
"""Raise `ModelRefusal` when the provider stopped on a refusal.
|
|
339
|
+
|
|
340
|
+
Raised at the provider, below `use_brain`, so no retry ladder ever re-asks
|
|
341
|
+
it (an unknown exception re-raises there) and the tool loop's resumes —
|
|
342
|
+
Mirascope's own requests, which reach this provider too — are covered.
|
|
343
|
+
"""
|
|
344
|
+
if getattr(response, "stop_reason", None) != "refusal":
|
|
345
|
+
return
|
|
346
|
+
details = getattr(response, "stop_details", None)
|
|
347
|
+
raise ModelRefusal(
|
|
348
|
+
model_name,
|
|
349
|
+
category=getattr(details, "category", None),
|
|
350
|
+
explanation=getattr(details, "explanation", None),
|
|
351
|
+
)
|
|
352
|
+
|
|
353
|
+
|
|
291
354
|
class BedrockAnthropicProvider(AnthropicProvider):
|
|
292
355
|
"""Mirascope v2 provider that routes through AnthropicBedrock client (async-native).
|
|
293
356
|
|
|
@@ -305,6 +368,65 @@ class BedrockAnthropicProvider(AnthropicProvider):
|
|
|
305
368
|
self.async_client = AsyncAnthropicBedrock(timeout=timeout)
|
|
306
369
|
self._beta_provider = None
|
|
307
370
|
|
|
371
|
+
@staticmethod
|
|
372
|
+
def _encode(
|
|
373
|
+
model_id: str,
|
|
374
|
+
messages: Sequence[Message],
|
|
375
|
+
toolkit: AsyncToolkit | Toolkit,
|
|
376
|
+
format: FormatSpec[FormattableT] | None,
|
|
377
|
+
params: Mapping[str, Any],
|
|
378
|
+
) -> tuple[Any, Any, dict[str, Any]]:
|
|
379
|
+
"""Encode a request in the formatting mode this model can take.
|
|
380
|
+
|
|
381
|
+
Forced tool use is re-asked as JSON for a model that refuses it (Claude
|
|
382
|
+
5.5); see format_compat. Encoding happens here, BEFORE the request is
|
|
383
|
+
built, because the mode decides the request's shape — tools and
|
|
384
|
+
tool_choice for one, system instructions for the other — and the
|
|
385
|
+
decoded response's parser follows the resolved format. A JSON-mode
|
|
386
|
+
call to such a model also carries its schema as a structured output
|
|
387
|
+
(`output_config.format`, see `structured_output_format`). Earlier turns'
|
|
388
|
+
thinking blocks are dropped here too, for a model that binds them to
|
|
389
|
+
their prefix (see `without_earlier_turn_thinking`).
|
|
390
|
+
|
|
391
|
+
A call moved OFF forced tool use is also sent with thinking off unless
|
|
392
|
+
it asked for thinking: forced tool choice is what kept every structured
|
|
393
|
+
call from thinking by default (0 thinking tokens with no parameter,
|
|
394
|
+
2026-09-24), and JSON mode with nothing sent is the provider's default,
|
|
395
|
+
thinking on. Without this every concern on a 5.5 model would think —
|
|
396
|
+
a cost, latency and behaviour change no figure was taken under.
|
|
397
|
+
`with_thinking_compat` turns "disabled" into the model's own off.
|
|
398
|
+
"""
|
|
399
|
+
model_name = _bedrock_model_name(model_id)
|
|
400
|
+
compatible = with_format_compat(model_name, format)
|
|
401
|
+
input_messages, resolved_format, kwargs = _utils.encode_request(
|
|
402
|
+
model_id=model_id,
|
|
403
|
+
messages=messages,
|
|
404
|
+
tools=toolkit,
|
|
405
|
+
format=compatible,
|
|
406
|
+
params=params,
|
|
407
|
+
)
|
|
408
|
+
kwargs["model"] = model_name
|
|
409
|
+
# A structured call is never a round of the tool loop: no tools, and in
|
|
410
|
+
# JSON mode extra system text, so even THIS turn's blocks are bound to a
|
|
411
|
+
# prefix the request no longer has. Drop them all there.
|
|
412
|
+
kwargs["messages"] = without_earlier_turn_thinking(
|
|
413
|
+
model_name, kwargs["messages"], every_turn=format is not None
|
|
414
|
+
)
|
|
415
|
+
if compatible is not format and "thinking" not in kwargs:
|
|
416
|
+
kwargs["thinking"] = {"type": "disabled"}
|
|
417
|
+
# A JSON-mode call to a model whose structured calls go in JSON mode
|
|
418
|
+
# is constrained to its schema (`output_config.format`): JSON asked for
|
|
419
|
+
# in prose came back with trailing commas often enough to cost a whole
|
|
420
|
+
# re-ask per 2.5 cards on Sonnet 5.5 (see format_compat). The thinking
|
|
421
|
+
# layer merges its `effort` into the same `output_config` later.
|
|
422
|
+
constraint = structured_output_format(model_name, compatible)
|
|
423
|
+
if constraint is not None:
|
|
424
|
+
output_config = dict(kwargs.get("output_config") or {})
|
|
425
|
+
output_config["format"] = constraint
|
|
426
|
+
kwargs["output_config"] = output_config
|
|
427
|
+
_fix_cache_breakpoints(kwargs)
|
|
428
|
+
return input_messages, resolved_format, kwargs
|
|
429
|
+
|
|
308
430
|
async def _create_async(
|
|
309
431
|
self, kwargs: dict[str, Any], params: Mapping[str, Any]
|
|
310
432
|
) -> AnthropicMessage:
|
|
@@ -353,18 +475,23 @@ class BedrockAnthropicProvider(AnthropicProvider):
|
|
|
353
475
|
**params: Unpack[Params],
|
|
354
476
|
) -> AsyncResponse | AsyncResponse[FormattableT]:
|
|
355
477
|
"""Always use standard path — bedrock doesn't support beta structured outputs."""
|
|
356
|
-
input_messages, resolved_format, kwargs =
|
|
357
|
-
model_id
|
|
358
|
-
messages=messages,
|
|
359
|
-
tools=toolkit,
|
|
360
|
-
format=format,
|
|
361
|
-
params=params,
|
|
362
|
-
)
|
|
363
|
-
kwargs["model"] = _bedrock_model_name(model_id)
|
|
364
|
-
_fix_cache_breakpoints(kwargs)
|
|
365
|
-
anthropic_response = cast(
|
|
366
|
-
AnthropicMessage, await self._create_async(kwargs, params)
|
|
478
|
+
input_messages, resolved_format, kwargs = self._encode(
|
|
479
|
+
model_id, messages, toolkit, format, params
|
|
367
480
|
)
|
|
481
|
+
try:
|
|
482
|
+
anthropic_response = cast(
|
|
483
|
+
AnthropicMessage, await self._create_async(kwargs, params)
|
|
484
|
+
)
|
|
485
|
+
except Exception as e:
|
|
486
|
+
if not learn_format_mode_from_error(kwargs["model"], format, e):
|
|
487
|
+
raise
|
|
488
|
+
input_messages, resolved_format, kwargs = self._encode(
|
|
489
|
+
model_id, messages, toolkit, format, params
|
|
490
|
+
)
|
|
491
|
+
anthropic_response = cast(
|
|
492
|
+
AnthropicMessage, await self._create_async(kwargs, params)
|
|
493
|
+
)
|
|
494
|
+
raise_on_refusal(anthropic_response, kwargs["model"])
|
|
368
495
|
include_thoughts = _utils.get_include_thoughts(params)
|
|
369
496
|
assistant_message, finish_reason, usage = _utils.decode_response(
|
|
370
497
|
anthropic_response, model_id, include_thoughts=include_thoughts
|
|
@@ -393,21 +520,10 @@ class BedrockAnthropicProvider(AnthropicProvider):
|
|
|
393
520
|
**params: Unpack[Params],
|
|
394
521
|
) -> AsyncStreamResponse | AsyncStreamResponse[FormattableT]:
|
|
395
522
|
"""Stream responses from Bedrock Anthropic."""
|
|
396
|
-
input_messages, resolved_format, kwargs =
|
|
397
|
-
model_id
|
|
398
|
-
messages=messages,
|
|
399
|
-
tools=toolkit,
|
|
400
|
-
format=format,
|
|
401
|
-
params=params,
|
|
523
|
+
input_messages, resolved_format, kwargs = self._encode(
|
|
524
|
+
model_id, messages, toolkit, format, params
|
|
402
525
|
)
|
|
403
|
-
|
|
404
|
-
_fix_cache_breakpoints(kwargs)
|
|
405
|
-
# Streaming cannot learn from a 400: the error surfaces when the caller
|
|
406
|
-
# consumes the iterator, by which point retrying would replay tokens
|
|
407
|
-
# already yielded. The name heuristic is applied, and a genuine mismatch
|
|
408
|
-
# raises to the caller.
|
|
409
|
-
kwargs = with_thinking_compat(kwargs["model"], kwargs, params)
|
|
410
|
-
anthropic_stream = self.async_client.messages.stream(**kwargs)
|
|
526
|
+
anthropic_stream = _LearningStreamManager(self.async_client, kwargs, params)
|
|
411
527
|
include_thoughts = _utils.get_include_thoughts(params)
|
|
412
528
|
chunk_iterator = _utils.decode_async_stream(
|
|
413
529
|
anthropic_stream, include_thoughts=include_thoughts
|
|
@@ -433,16 +549,23 @@ class BedrockAnthropicProvider(AnthropicProvider):
|
|
|
433
549
|
**params: Unpack[Params],
|
|
434
550
|
) -> Response | Response[FormattableT]:
|
|
435
551
|
"""Always use standard path — bedrock doesn't support beta structured outputs."""
|
|
436
|
-
input_messages, resolved_format, kwargs =
|
|
437
|
-
model_id
|
|
438
|
-
messages=messages,
|
|
439
|
-
tools=toolkit,
|
|
440
|
-
format=format,
|
|
441
|
-
params=params,
|
|
552
|
+
input_messages, resolved_format, kwargs = self._encode(
|
|
553
|
+
model_id, messages, toolkit, format, params
|
|
442
554
|
)
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
555
|
+
try:
|
|
556
|
+
anthropic_response = cast(
|
|
557
|
+
AnthropicMessage, self._create_sync(kwargs, params)
|
|
558
|
+
)
|
|
559
|
+
except Exception as e:
|
|
560
|
+
if not learn_format_mode_from_error(kwargs["model"], format, e):
|
|
561
|
+
raise
|
|
562
|
+
input_messages, resolved_format, kwargs = self._encode(
|
|
563
|
+
model_id, messages, toolkit, format, params
|
|
564
|
+
)
|
|
565
|
+
anthropic_response = cast(
|
|
566
|
+
AnthropicMessage, self._create_sync(kwargs, params)
|
|
567
|
+
)
|
|
568
|
+
raise_on_refusal(anthropic_response, kwargs["model"])
|
|
446
569
|
include_thoughts = _utils.get_include_thoughts(params)
|
|
447
570
|
assistant_message, finish_reason, usage = _utils.decode_response(
|
|
448
571
|
anthropic_response, model_id, include_thoughts=include_thoughts
|