dialectical-framework 0.4.4__py3-none-any.whl
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/__init__.py +43 -0
- dialectical_framework/ai_dto/__init__.py +0 -0
- dialectical_framework/ai_dto/causal_cycle_assessment_dto.py +16 -0
- dialectical_framework/ai_dto/causal_cycle_dto.py +16 -0
- dialectical_framework/ai_dto/causal_cycles_deck_dto.py +11 -0
- dialectical_framework/ai_dto/dialectical_component_dto.py +16 -0
- dialectical_framework/ai_dto/dialectical_components_deck_dto.py +12 -0
- dialectical_framework/ai_dto/dto_mapper.py +103 -0
- dialectical_framework/ai_dto/reciprocal_solution_dto.py +24 -0
- dialectical_framework/analyst/__init__.py +1 -0
- dialectical_framework/analyst/audit/__init__.py +0 -0
- dialectical_framework/analyst/consultant.py +30 -0
- dialectical_framework/analyst/decorator_action_reflection.py +28 -0
- dialectical_framework/analyst/decorator_discrete_spiral.py +30 -0
- dialectical_framework/analyst/domain/__init__.py +0 -0
- dialectical_framework/analyst/domain/assessable_cycle.py +128 -0
- dialectical_framework/analyst/domain/cycle.py +133 -0
- dialectical_framework/analyst/domain/interpretation.py +16 -0
- dialectical_framework/analyst/domain/rationale.py +116 -0
- dialectical_framework/analyst/domain/spiral.py +47 -0
- dialectical_framework/analyst/domain/transformation.py +24 -0
- dialectical_framework/analyst/domain/transition.py +160 -0
- dialectical_framework/analyst/domain/transition_cell_to_cell.py +29 -0
- dialectical_framework/analyst/domain/transition_segment_to_segment.py +16 -0
- dialectical_framework/analyst/strategic_consultant.py +32 -0
- dialectical_framework/analyst/think_action_reflection.py +217 -0
- dialectical_framework/analyst/think_constructive_convergence.py +87 -0
- dialectical_framework/analyst/wheel_builder_transition_calculator.py +157 -0
- dialectical_framework/brain.py +81 -0
- dialectical_framework/dialectical_analysis.py +14 -0
- dialectical_framework/dialectical_component.py +111 -0
- dialectical_framework/dialectical_components_deck.py +48 -0
- dialectical_framework/dialectical_reasoning.py +105 -0
- dialectical_framework/directed_graph.py +419 -0
- dialectical_framework/enums/__init__.py +0 -0
- dialectical_framework/enums/causality_type.py +10 -0
- dialectical_framework/enums/di.py +11 -0
- dialectical_framework/enums/dialectical_reasoning_mode.py +9 -0
- dialectical_framework/enums/predicate.py +9 -0
- dialectical_framework/protocols/__init__.py +0 -0
- dialectical_framework/protocols/assessable.py +141 -0
- dialectical_framework/protocols/causality_sequencer.py +111 -0
- dialectical_framework/protocols/content_fidelity_evaluator.py +16 -0
- dialectical_framework/protocols/has_brain.py +18 -0
- dialectical_framework/protocols/has_config.py +18 -0
- dialectical_framework/protocols/ratable.py +79 -0
- dialectical_framework/protocols/reloadable.py +7 -0
- dialectical_framework/protocols/thesis_extractor.py +15 -0
- dialectical_framework/settings.py +77 -0
- dialectical_framework/synthesis.py +7 -0
- dialectical_framework/synthesist/__init__.py +1 -0
- dialectical_framework/synthesist/causality/__init__.py +0 -0
- dialectical_framework/synthesist/causality/causality_sequencer_balanced.py +398 -0
- dialectical_framework/synthesist/causality/causality_sequencer_desirable.py +55 -0
- dialectical_framework/synthesist/causality/causality_sequencer_feasible.py +55 -0
- dialectical_framework/synthesist/causality/causality_sequencer_realistic.py +56 -0
- dialectical_framework/synthesist/concepts/__init__.py +0 -0
- dialectical_framework/synthesist/concepts/thesis_extractor_basic.py +135 -0
- dialectical_framework/synthesist/polarity/__init__.py +0 -0
- dialectical_framework/synthesist/polarity/polarity_reasoner.py +700 -0
- dialectical_framework/synthesist/polarity/reason_blind.py +62 -0
- dialectical_framework/synthesist/polarity/reason_conversational.py +91 -0
- dialectical_framework/synthesist/polarity/reason_fast.py +177 -0
- dialectical_framework/synthesist/polarity/reason_fast_and_simple.py +52 -0
- dialectical_framework/synthesist/polarity/reason_fast_polarized_conflict.py +55 -0
- dialectical_framework/synthesist/reverse_engineer.py +401 -0
- dialectical_framework/synthesist/wheel_builder.py +337 -0
- dialectical_framework/utils/__init__.py +1 -0
- dialectical_framework/utils/dc_replace.py +42 -0
- dialectical_framework/utils/decompose_probability_uniformly.py +15 -0
- dialectical_framework/utils/extend_tpl.py +12 -0
- dialectical_framework/utils/gm.py +12 -0
- dialectical_framework/utils/is_async.py +13 -0
- dialectical_framework/utils/use_brain.py +80 -0
- dialectical_framework/validator/__init__.py +1 -0
- dialectical_framework/validator/basic_checks.py +75 -0
- dialectical_framework/validator/check.py +12 -0
- dialectical_framework/wheel.py +395 -0
- dialectical_framework/wheel_segment.py +203 -0
- dialectical_framework/wisdom_unit.py +185 -0
- dialectical_framework-0.4.4.dist-info/LICENSE +21 -0
- dialectical_framework-0.4.4.dist-info/METADATA +123 -0
- dialectical_framework-0.4.4.dist-info/RECORD +84 -0
- dialectical_framework-0.4.4.dist-info/WHEEL +4 -0
@@ -0,0 +1,111 @@
|
|
1
|
+
from abc import abstractmethod
|
2
|
+
from itertools import permutations
|
3
|
+
from typing import Union
|
4
|
+
|
5
|
+
from dialectical_framework.analyst.domain.cycle import Cycle
|
6
|
+
from dialectical_framework.dialectical_component import DialecticalComponent
|
7
|
+
from dialectical_framework.protocols.reloadable import Reloadable
|
8
|
+
from dialectical_framework.wisdom_unit import WisdomUnit
|
9
|
+
|
10
|
+
|
11
|
+
class CausalitySequencer(Reloadable):
|
12
|
+
@abstractmethod
|
13
|
+
async def arrange(
|
14
|
+
self, thoughts: Union[list[str], list[WisdomUnit], list[DialecticalComponent]]
|
15
|
+
) -> list[Cycle]:
|
16
|
+
"""
|
17
|
+
Arranges items in multiple sequences and arranges them as cycles.
|
18
|
+
IMPORTANT: we don't do single sequence estimation isolated, because they all depend on each other.
|
19
|
+
Isolated estimation would lead to some sort of unnormalized probabilities, which is not good.
|
20
|
+
"""
|
21
|
+
...
|
22
|
+
|
23
|
+
|
24
|
+
def generate_permutation_sequences(
|
25
|
+
dialectical_components: list[DialecticalComponent],
|
26
|
+
) -> list[list[DialecticalComponent]]:
|
27
|
+
if len(dialectical_components) < 2:
|
28
|
+
return []
|
29
|
+
|
30
|
+
first, rest = dialectical_components[0], dialectical_components[1:]
|
31
|
+
sequences = list([first, *p] for p in permutations(rest))
|
32
|
+
return sequences
|
33
|
+
|
34
|
+
|
35
|
+
def generate_compatible_sequences(
|
36
|
+
ordered_wisdom_units: list[WisdomUnit],
|
37
|
+
) -> list[list[DialecticalComponent]]:
|
38
|
+
"""
|
39
|
+
Generate all circular, diagonally symmetric arrangements for T/A pairs.
|
40
|
+
|
41
|
+
Each wisdom unit consists of a thesis (`T`) and its antithesis (`A`). This function arranges them
|
42
|
+
around a circle (of length 2n, where n is the number of units) such that:
|
43
|
+
|
44
|
+
1. **Circular Symmetry**: For each pair, if `T_i` is at position `p`, then `A_i` is at position `(p + n) % (2n)`.
|
45
|
+
2. **Order Preservation**: The order of all `T`s matches their input order and is strictly increasing
|
46
|
+
in placement (i.e., `T1` before `T2`, etc.).
|
47
|
+
3. **Start Condition**: The sequence always starts with the first thesis (`T1`) at position 0.
|
48
|
+
|
49
|
+
Parameters:
|
50
|
+
ordered_wisdom_units (list): List of wisdom units, each having `.t.alias` (thesis) and `.a.alias` (antithesis).
|
51
|
+
|
52
|
+
Returns:
|
53
|
+
list of list: Each inner list is a possible arrangement; positions 0..n-1 represent the 'top row'
|
54
|
+
(or first semi-circle), and positions n..2n-1 represent the 'bottom row' (or mirrored second semi-circle),
|
55
|
+
such that the diagonal relationship and thesis order constraints are always met.
|
56
|
+
|
57
|
+
Example:
|
58
|
+
For input units T1/A1, T2/A2, T3/A3, T4/A4, a valid output can be:
|
59
|
+
[T1, T2, A4, T3, A1, A2, T4, A3]
|
60
|
+
Which means:
|
61
|
+
Top: T1 -> T2 -> A4 -> T3
|
62
|
+
Bottom: A1 -> A2 -> T4 -> A3 (mirrored on the circle)
|
63
|
+
"""
|
64
|
+
|
65
|
+
n = len(ordered_wisdom_units)
|
66
|
+
ts = [u.t for u in ordered_wisdom_units]
|
67
|
+
as_ = [u.a for u in ordered_wisdom_units]
|
68
|
+
size = 2 * n
|
69
|
+
|
70
|
+
results = []
|
71
|
+
|
72
|
+
# Step 1: set T1 at 0, its diagonal A1 at n
|
73
|
+
def backtrack(t_positions, next_t_idx):
|
74
|
+
if next_t_idx == n:
|
75
|
+
# Fill arrangement based on t_positions
|
76
|
+
arrangement = [None] * size
|
77
|
+
occupied = set()
|
78
|
+
for t_idx, pos in enumerate(t_positions):
|
79
|
+
arrangement[pos] = ts[t_idx]
|
80
|
+
diag = (pos + n) % size
|
81
|
+
arrangement[diag] = as_[t_idx]
|
82
|
+
occupied.add(pos)
|
83
|
+
occupied.add(diag)
|
84
|
+
results.append(arrangement)
|
85
|
+
return
|
86
|
+
|
87
|
+
# Next ti to place: always in order, always > previous ti's position
|
88
|
+
# Skip positions already assigned (to ensure symmetry and distinctness)
|
89
|
+
prev_pos = t_positions[-1]
|
90
|
+
for pos in range(prev_pos + 1, size):
|
91
|
+
diag = (pos + n) % size
|
92
|
+
|
93
|
+
# Check if pos or diag are used by previous Ts/A's
|
94
|
+
collision = False
|
95
|
+
for prev_t_pos in t_positions:
|
96
|
+
if pos == prev_t_pos or diag == prev_t_pos:
|
97
|
+
collision = True
|
98
|
+
break
|
99
|
+
prev_diag = (prev_t_pos + n) % size
|
100
|
+
if pos == prev_diag or diag == prev_diag:
|
101
|
+
collision = True
|
102
|
+
break
|
103
|
+
if collision:
|
104
|
+
continue
|
105
|
+
|
106
|
+
# Place next T at pos, corresponding A at diag
|
107
|
+
backtrack(t_positions + [pos], next_t_idx + 1)
|
108
|
+
|
109
|
+
# T1 fixed at position 0
|
110
|
+
backtrack([0], 1)
|
111
|
+
return results
|
@@ -0,0 +1,16 @@
|
|
1
|
+
from abc import abstractmethod
|
2
|
+
from typing import Union
|
3
|
+
|
4
|
+
|
5
|
+
from dialectical_framework.dialectical_component import DialecticalComponent
|
6
|
+
from dialectical_framework.protocols.reloadable import Reloadable
|
7
|
+
from dialectical_framework.wheel import Wheel
|
8
|
+
from dialectical_framework.wheel_segment import WheelSegment
|
9
|
+
|
10
|
+
|
11
|
+
class ContentFidelityEvaluator(Reloadable):
|
12
|
+
@abstractmethod
|
13
|
+
async def evaluate(self, *, target: Union[
|
14
|
+
list[str | DialecticalComponent | WheelSegment | Wheel],
|
15
|
+
str, DialecticalComponent, WheelSegment, Wheel
|
16
|
+
]) -> DialecticalComponent: ...
|
@@ -0,0 +1,18 @@
|
|
1
|
+
from typing import Protocol, runtime_checkable
|
2
|
+
|
3
|
+
from dependency_injector.wiring import Provide, inject
|
4
|
+
|
5
|
+
from dialectical_framework.brain import Brain
|
6
|
+
from dialectical_framework.enums.di import DI
|
7
|
+
|
8
|
+
|
9
|
+
@inject
|
10
|
+
def di_brain(brain: Brain = Provide[DI.brain]) -> Brain:
|
11
|
+
return brain
|
12
|
+
|
13
|
+
|
14
|
+
@runtime_checkable
|
15
|
+
class HasBrain(Protocol):
|
16
|
+
@property
|
17
|
+
def brain(self) -> Brain:
|
18
|
+
return di_brain()
|
@@ -0,0 +1,18 @@
|
|
1
|
+
from typing import Protocol, runtime_checkable
|
2
|
+
|
3
|
+
from dependency_injector.wiring import Provide, inject
|
4
|
+
|
5
|
+
from dialectical_framework.enums.di import DI
|
6
|
+
from dialectical_framework.settings import Settings
|
7
|
+
|
8
|
+
|
9
|
+
@inject
|
10
|
+
def di_settings(settings: Settings = Provide[DI.settings]) -> Settings:
|
11
|
+
return settings
|
12
|
+
|
13
|
+
|
14
|
+
@runtime_checkable
|
15
|
+
class SettingsAware(Protocol):
|
16
|
+
@property
|
17
|
+
def settings(self) -> Settings:
|
18
|
+
return di_settings()
|
@@ -0,0 +1,79 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
|
3
|
+
from abc import ABC
|
4
|
+
from typing import TYPE_CHECKING, final, List
|
5
|
+
|
6
|
+
from pydantic import ConfigDict, Field
|
7
|
+
|
8
|
+
from dialectical_framework.protocols.assessable import Assessable
|
9
|
+
from dialectical_framework.utils.gm import gm_with_zeros_and_nones_handled
|
10
|
+
|
11
|
+
if TYPE_CHECKING: # Conditionally import Rationale for type checking only
|
12
|
+
pass
|
13
|
+
|
14
|
+
class Ratable(Assessable, ABC):
|
15
|
+
model_config = ConfigDict(
|
16
|
+
arbitrary_types_allowed=True,
|
17
|
+
)
|
18
|
+
|
19
|
+
rating: float | None = Field(
|
20
|
+
default=None, ge=0.0, le=1.0,
|
21
|
+
description="Importance/quality rating."
|
22
|
+
)
|
23
|
+
|
24
|
+
confidence: float | None = Field(
|
25
|
+
default=None, ge=0.0, le=1.0,
|
26
|
+
description="Credibility/reputation/confidence of the expert making probability assessments. Used for weighing probabilities (applied during aggregation)")
|
27
|
+
|
28
|
+
def rating_or_default(self) -> float:
|
29
|
+
"""
|
30
|
+
The default rating is 1.0 when None.
|
31
|
+
It's a convenient thing, this way we can estimate higher level CFs and propagate them up and down.
|
32
|
+
"""
|
33
|
+
return self.rating if self.rating is not None else 1.0
|
34
|
+
|
35
|
+
def confidence_or_default(self) -> float:
|
36
|
+
"""
|
37
|
+
The default confidence is 0.5 when None. This is a rather technical thing, we are never 100% sure, so 0.5 is ok.
|
38
|
+
"""
|
39
|
+
return self.confidence if self.confidence is not None else 0.5
|
40
|
+
|
41
|
+
def _hard_veto_on_own_zero(self) -> bool:
|
42
|
+
return True # default for DC and Transition
|
43
|
+
|
44
|
+
def calculate_contextual_fidelity(self, *, mutate: bool = True) -> float:
|
45
|
+
"""
|
46
|
+
Leaves combine:
|
47
|
+
- own intrinsic CF × own rating (if present; 0 may be a hard veto by policy),
|
48
|
+
- rated rationale CFs (weighted in the base helper),
|
49
|
+
- any subclass sub-elements (e.g., Rationale's wheels), unrated here.
|
50
|
+
Neutral fallback = 1.0. Parent rating never reweights children.
|
51
|
+
"""
|
52
|
+
own_cf = self.contextual_fidelity
|
53
|
+
own_rating = self.rating_or_default()
|
54
|
+
|
55
|
+
# 1) Hard veto on intrinsic CF == 0, independent of rating (policy-controlled)
|
56
|
+
if own_cf is not None and own_cf == 0.0 and self._hard_veto_on_own_zero():
|
57
|
+
# Do NOT overwrite the manual CF field; return veto as the effective value
|
58
|
+
return 0.0
|
59
|
+
|
60
|
+
# 2) Collect child contributions (already filtered/weighted by helpers)
|
61
|
+
parts: List[float] = []
|
62
|
+
parts.extend(v for v in (self._calculate_contextual_fidelity_for_rationales(mutate=mutate) or [])
|
63
|
+
if v is not None and v > 0.0)
|
64
|
+
parts.extend(
|
65
|
+
v for v in (self._calculate_contextual_fidelity_for_sub_elements_excl_rationales(mutate=mutate) or [])
|
66
|
+
if v is not None and v > 0.0)
|
67
|
+
|
68
|
+
# 3) Include own positive contribution if present
|
69
|
+
if own_cf is not None and own_cf > 0.0 and own_rating > 0.0:
|
70
|
+
parts.append(own_cf * own_rating)
|
71
|
+
|
72
|
+
# 4) Aggregate or neutral fallback
|
73
|
+
fidelity = gm_with_zeros_and_nones_handled(parts) if parts else 1.0
|
74
|
+
|
75
|
+
# 5) Cache only if there was no manual CF provided (don't clobber human input)
|
76
|
+
if mutate and own_cf is None:
|
77
|
+
self.contextual_fidelity = fidelity
|
78
|
+
|
79
|
+
return fidelity
|
@@ -0,0 +1,15 @@
|
|
1
|
+
from abc import abstractmethod
|
2
|
+
|
3
|
+
|
4
|
+
from dialectical_framework.dialectical_component import DialecticalComponent
|
5
|
+
from dialectical_framework.dialectical_components_deck import \
|
6
|
+
DialecticalComponentsDeck
|
7
|
+
from dialectical_framework.protocols.reloadable import Reloadable
|
8
|
+
|
9
|
+
|
10
|
+
class ThesisExtractor(Reloadable):
|
11
|
+
@abstractmethod
|
12
|
+
async def extract_multiple_theses( self, *, count: int = 2) -> DialecticalComponentsDeck: ...
|
13
|
+
|
14
|
+
@abstractmethod
|
15
|
+
async def extract_single_thesis(self) -> DialecticalComponent: ...
|
@@ -0,0 +1,77 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
|
3
|
+
import os
|
4
|
+
from typing import Optional, Self
|
5
|
+
|
6
|
+
from dotenv import load_dotenv
|
7
|
+
from pydantic import BaseModel, ConfigDict, Field
|
8
|
+
|
9
|
+
from dialectical_framework.enums.causality_type import CausalityType
|
10
|
+
|
11
|
+
|
12
|
+
class Settings(BaseModel):
|
13
|
+
model_config = ConfigDict(
|
14
|
+
arbitrary_types_allowed=True,
|
15
|
+
)
|
16
|
+
|
17
|
+
ai_model: str = Field(..., description="AI model alias/deployment to use.")
|
18
|
+
ai_provider: Optional[str] = Field(default=None, description="AI model provider to use.")
|
19
|
+
component_length: int = Field(default=7, description="Approximate length in words of the dialectical component.")
|
20
|
+
causality_type: CausalityType = Field(default=CausalityType.BALANCED, description="Type of causality in the wheel.")
|
21
|
+
|
22
|
+
@classmethod
|
23
|
+
def from_partial(cls, partial_settings: Optional[Settings] = None) -> Self:
|
24
|
+
"""
|
25
|
+
Create GenerationSettings by merging partial settings with environment defaults.
|
26
|
+
Missing fields in partial_settings are filled from Settings.from_env().
|
27
|
+
"""
|
28
|
+
if partial_settings is None:
|
29
|
+
return cls.from_env()
|
30
|
+
|
31
|
+
# Get full defaults from environment
|
32
|
+
env_defaults = cls.from_env()
|
33
|
+
|
34
|
+
# Convert partial_settings to dict, excluding None values
|
35
|
+
partial_dict = partial_settings.model_dump(exclude_none=True) if partial_settings else {}
|
36
|
+
|
37
|
+
# Convert env_defaults to dict
|
38
|
+
env_dict = env_defaults.model_dump()
|
39
|
+
|
40
|
+
# Merge: partial_settings override env_defaults
|
41
|
+
merged_dict = {**env_dict, **partial_dict}
|
42
|
+
|
43
|
+
# Create new instance from merged data
|
44
|
+
return cls(**merged_dict)
|
45
|
+
|
46
|
+
@classmethod
|
47
|
+
def from_env(cls) -> Self:
|
48
|
+
"""
|
49
|
+
Static method to set up and return a Config instance.
|
50
|
+
It uses environment variables or hardcoded defaults for configuration.
|
51
|
+
"""
|
52
|
+
load_dotenv()
|
53
|
+
|
54
|
+
model = os.getenv("DIALEXITY_DEFAULT_MODEL", None)
|
55
|
+
provider = os.getenv("DIALEXITY_DEFAULT_MODEL_PROVIDER", None)
|
56
|
+
missing = []
|
57
|
+
if not model:
|
58
|
+
missing.append("DIALEXITY_DEFAULT_MODEL")
|
59
|
+
if not provider:
|
60
|
+
if "/" not in model:
|
61
|
+
missing.append("DIALEXITY_DEFAULT_MODEL_PROVIDER")
|
62
|
+
else:
|
63
|
+
# We will give litellm a chance to derive the provider from the model
|
64
|
+
pass
|
65
|
+
if missing:
|
66
|
+
raise ValueError(
|
67
|
+
f"Missing required environment variables: {', '.join(missing)}"
|
68
|
+
)
|
69
|
+
|
70
|
+
return cls(
|
71
|
+
ai_model=model,
|
72
|
+
ai_provider=provider,
|
73
|
+
component_length=int(os.getenv("DIALEXITY_DEFAULT_COMPONENT_LENGTH", 7)),
|
74
|
+
causality_type=CausalityType(
|
75
|
+
os.getenv("DIALEXITY_DEFAULT_CAUSALITY_TYPE", CausalityType.BALANCED.value)
|
76
|
+
),
|
77
|
+
)
|
@@ -0,0 +1 @@
|
|
1
|
+
|
File without changes
|