Skip to content

Commit 6a76a1f

Browse files
fdidonatofrdidonato
authored andcommitted
DCCL Commit 2: Evaluation logic (structured, LLM, safety override)
fixed pre-commit run -a issues on mypy
1 parent 4f38b70 commit 6a76a1f

25 files changed

Lines changed: 96 additions & 115 deletions

moralstack/compliance/config.py

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,10 @@ def _parse_int(raw: str, default: int, var_name: str, min_value: int | None = No
6464
if min_value is not None and value < min_value:
6565
_LOG.warning(
6666
"value for %s (%d) below minimum %d, using default %d",
67-
var_name, value, min_value, default,
67+
var_name,
68+
value,
69+
min_value,
70+
default,
6871
)
6972
return default
7073
return value
@@ -83,7 +86,11 @@ def _parse_float(raw: str, default: float, var_name: str, min_value: float, max_
8386
if not (min_value <= value <= max_value):
8487
_LOG.warning(
8588
"value for %s (%f) out of range [%f, %f], using default %f",
86-
var_name, value, min_value, max_value, default,
89+
var_name,
90+
value,
91+
min_value,
92+
max_value,
93+
default,
8794
)
8895
return default
8996
return value
@@ -103,7 +110,9 @@ def get_dccl_evaluation_path() -> EvaluationPathLiteral:
103110
return raw # type: ignore[return-value]
104111
_LOG.warning(
105112
"invalid value for %s: %r, using default %s",
106-
_ENV_EVALUATION_PATH, raw, _DEFAULT_EVALUATION_PATH,
113+
_ENV_EVALUATION_PATH,
114+
raw,
115+
_DEFAULT_EVALUATION_PATH,
107116
)
108117
return _DEFAULT_EVALUATION_PATH # type: ignore[return-value]
109118

moralstack/compliance/dccl.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -190,9 +190,7 @@ def evaluate(
190190
verdict = ComplianceVerdict(
191191
decision=ComplianceDecision.NO_MATCH,
192192
confidence=verdict_llm.confidence,
193-
rationale=(
194-
f"Hybrid: structured found no rule; LLM also found none. {verdict_llm.rationale}"
195-
),
193+
rationale=(f"Hybrid: structured found no rule; LLM also found none. {verdict_llm.rationale}"),
196194
evaluation_path=EvaluationPath.HYBRID,
197195
contract_hash=contract_hash,
198196
)

moralstack/compliance/safety_override.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -198,7 +198,7 @@ def _llm_classify(
198198
category = parsed.get("category")
199199
if category is None or category == "null":
200200
return None
201-
if category in SAFETY_OVERRIDE_CATEGORIES:
201+
if isinstance(category, str) and category in SAFETY_OVERRIDE_CATEGORIES:
202202
_LOG.debug("safety classifier LLM matched: %s", category)
203203
return category
204204
_LOG.warning("safety LLM returned unknown category: %r", category)

moralstack/compliance/types.py

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,11 @@
1212
from dataclasses import dataclass
1313
from enum import Enum
1414

15-
1615
# =============================================================================
1716
# Enums
1817
# =============================================================================
1918

19+
2020
class ComplianceDecision(str, Enum):
2121
"""The four possible verdicts of a DCCL evaluation."""
2222

@@ -79,6 +79,7 @@ class ActionType(str, Enum):
7979
# Structured rule (deployer-declarable)
8080
# =============================================================================
8181

82+
8283
@dataclass(frozen=True)
8384
class StructuredRule:
8485
"""
@@ -118,19 +119,16 @@ def __post_init__(self) -> None:
118119
if not self.trigger_pattern:
119120
raise ValueError("StructuredRule.trigger_pattern must be non-empty")
120121
if self.action_type != ActionType.REDIRECT and not self.action_payload:
121-
raise ValueError(
122-
f"StructuredRule.action_payload must be non-empty for action_type={self.action_type.value}"
123-
)
122+
raise ValueError(f"StructuredRule.action_payload must be non-empty for action_type={self.action_type.value}")
124123
if not (0 <= self.priority <= 100):
125-
raise ValueError(
126-
f"StructuredRule.priority must be in [0, 100], got {self.priority}"
127-
)
124+
raise ValueError(f"StructuredRule.priority must be in [0, 100], got {self.priority}")
128125

129126

130127
# =============================================================================
131128
# Matched rule (verdict-side, populated when DCCL returns MATCH)
132129
# =============================================================================
133130

131+
134132
@dataclass(frozen=True)
135133
class MatchedRule:
136134
"""
@@ -156,6 +154,7 @@ class MatchedRule:
156154
# Compliance verdict (output of DCCL.evaluate)
157155
# =============================================================================
158156

157+
159158
@dataclass(frozen=True)
160159
class ComplianceVerdict:
161160
"""
@@ -199,6 +198,7 @@ def is_safety_override(self) -> bool:
199198
# Compliance signal (propagated to downstream modules via request context)
200199
# =============================================================================
201200

201+
202202
@dataclass(frozen=True)
203203
class ComplianceSignal:
204204
"""

moralstack/orchestration/controller.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1040,9 +1040,7 @@ def _run_dccl_evaluation(
10401040
"confidence": compliance_verdict.confidence,
10411041
"evaluation_path": compliance_verdict.evaluation_path.value,
10421042
"speculative_draft_validated": compliance_verdict.speculative_draft_validated,
1043-
"rationale_excerpt": (
1044-
compliance_verdict.rationale[:300] if compliance_verdict.rationale else ""
1045-
),
1043+
"rationale_excerpt": (compliance_verdict.rationale[:300] if compliance_verdict.rationale else ""),
10461044
"contract_hash": compliance_verdict.contract_hash,
10471045
},
10481046
)

moralstack/orchestration/deliberation_runner.py

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,8 @@
3535
from moralstack.orchestration.orchestration_event_taxonomy import (
3636
AGGREGATED_GUIDANCE_EVALUATED,
3737
CONVERGENCE_EVALUATED,
38-
CRITIC_SKIPPED,
3938
CRITIC_SHORT_CIRCUIT_TRIGGERED,
39+
CRITIC_SKIPPED,
4040
EARLY_CONVERGENCE_ACCEPTED,
4141
EARLY_CONVERGENCE_REJECTED,
4242
PARALLEL_STRATEGY_SELECTED,
@@ -2837,16 +2837,12 @@ def _critique(
28372837
"started_at": int(start * 1000),
28382838
"duration_ms": elapsed,
28392839
"prompt": (
2840-
f"[SKIPPED] {skip_reason}" if is_skipped
2841-
else (getattr(critique, "prompt", None) or prompt_text)
2840+
f"[SKIPPED] {skip_reason}" if is_skipped else (getattr(critique, "prompt", None) or prompt_text)
28422841
),
28432842
"system_prompt": getattr(critique, "system_prompt", ""),
28442843
"raw_response": getattr(critique, "raw_response", "") or "",
28452844
"parsed_json": None,
2846-
"parsed_summary_json": (
2847-
f"SKIPPED: {skip_reason}" if is_skipped
2848-
else response_text
2849-
),
2845+
"parsed_summary_json": (f"SKIPPED: {skip_reason}" if is_skipped else response_text),
28502846
"attempts": getattr(critique, "parse_attempts", 1),
28512847
"sequence_in_cycle": SEQ_CRITIC,
28522848
"token_usage_json": _token_usage_json_from_result(critique),

moralstack/orchestration/orchestration_event_taxonomy.py

Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -69,37 +69,47 @@
6969
# Commit 1 (Foundation) declares the constants; emission ships in Commit 2-3.
7070

7171
COMPLIANCE_LAYER_STARTED = "COMPLIANCE_LAYER_STARTED"
72-
"""DCCL.evaluate() invoked. Payload: has_contract, has_structured_rules, evaluation_path_preference."""
72+
"""DCCL.evaluate() invoked.
73+
Payload: has_contract, has_structured_rules, evaluation_path_preference."""
7374

7475
COMPLIANCE_LAYER_VERDICT_MATCH = "COMPLIANCE_LAYER_VERDICT_MATCH"
75-
"""DCCL returned MATCH. Payload: matched_rule_id, evaluation_path, confidence, duration_ms."""
76+
"""DCCL returned MATCH.
77+
Payload: matched_rule_id, evaluation_path, confidence, duration_ms."""
7678

7779
COMPLIANCE_LAYER_VERDICT_NO_MATCH = "COMPLIANCE_LAYER_VERDICT_NO_MATCH"
78-
"""DCCL returned NO_MATCH. Payload: rationale_excerpt, evaluation_path, duration_ms, confidence."""
80+
"""DCCL returned NO_MATCH.
81+
Payload: rationale_excerpt, evaluation_path, duration_ms, confidence."""
7982

8083
COMPLIANCE_LAYER_VERDICT_SAFETY_OVERRIDE = "COMPLIANCE_LAYER_VERDICT_SAFETY_OVERRIDE"
81-
"""DCCL returned SAFETY_OVERRIDE. Payload: safety_override_reason, rule_excerpt, duration_ms."""
84+
"""DCCL returned SAFETY_OVERRIDE.
85+
Payload: safety_override_reason, rule_excerpt, duration_ms."""
8286

8387
COMPLIANCE_LAYER_VERDICT_NO_CONTRACT = "COMPLIANCE_LAYER_VERDICT_NO_CONTRACT"
84-
"""Request has no developer contract. Payload: empty."""
88+
"""Request has no developer contract.
89+
Payload: empty."""
8590

8691
CONTRACT_RULE_REJECTED = "CONTRACT_RULE_REJECTED"
87-
"""A structured rule failed safety validation at contract loading. Payload: rule_id, reason, rejected_action_payload_excerpt."""
92+
"""A structured rule failed safety validation at contract loading.
93+
Payload: rule_id, reason, rejected_action_payload_excerpt."""
8894

8995
CONTRACT_RULES_LOADED = "CONTRACT_RULES_LOADED"
90-
"""Contract loading complete. Payload: contract_hash, total_rules, accepted_rules, rejected_rules."""
96+
"""Contract loading complete.
97+
Payload: contract_hash, total_rules, accepted_rules, rejected_rules."""
9198

9299
MODULE_DEFERRED_TO_COMPLIANCE = "MODULE_DEFERRED_TO_COMPLIANCE"
93-
"""A downstream module returned early due to ComplianceSignal(MATCH). Payload: module, reason, cycle, deferred_outcome_summary."""
100+
"""A downstream module returned early due to ComplianceSignal(MATCH).
101+
Payload: module, reason, cycle, deferred_outcome_summary."""
94102

95103
CONTRACT_INJECTION_DETECTED = "CONTRACT_INJECTION_DETECTED"
96-
"""LLM detected a deployer-side prompt injection in the contract. Payload: detection_rationale, contract_hash."""
104+
"""LLM detected a deployer-side prompt injection in the contract.
105+
Payload: detection_rationale, contract_hash."""
97106

98107
COMPLIANCE_LAYER_TIMEOUT = "COMPLIANCE_LAYER_TIMEOUT"
99108
"""DCCL LLM call exceeded timeout. Payload: timeout_ms, evaluation_path."""
100109

101110
CONTRACT_STRUCTURE_PROSE_CONFLICT = "CONTRACT_STRUCTURE_PROSE_CONFLICT"
102-
"""structured_rules conflict with raw_text in the same contract. Payload: contract_hash, conflict_description."""
111+
"""structured_rules conflict with raw_text in the same contract.
112+
Payload: contract_hash, conflict_description."""
103113

104114
ALL_EVENT_TYPES: frozenset[str] = frozenset(
105115
{

moralstack/reports/markdown_export.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -114,8 +114,7 @@ def _markdown_critic_skipped_section(llm_calls: list[dict[str, Any]] | None) ->
114114
skipped = [
115115
c
116116
for c in calls
117-
if (c.get("module") or "").strip().lower() == "critic"
118-
and (c.get("call_outcome") or "").strip().lower() == "skipped"
117+
if (c.get("module") or "").strip().lower() == "critic" and (c.get("call_outcome") or "").strip().lower() == "skipped"
119118
]
120119
if not skipped:
121120
return ""

moralstack/reports/renderer_markdown.py

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -231,10 +231,7 @@ def render_detailed_phases(report: "RequestReport") -> str:
231231
for pi in phases:
232232
icon = _phase_icon(pi.phase_type)
233233
call_outcome = (getattr(pi, "call_outcome", None) or "").strip().lower()
234-
is_critic_skipped = (
235-
call_outcome == "skipped"
236-
and "critic" in (pi.phase_type or "").lower()
237-
)
234+
is_critic_skipped = call_outcome == "skipped" and "critic" in (pi.phase_type or "").lower()
238235
if is_critic_skipped:
239236
skip_reason = (getattr(pi, "parsed_summary_json", None) or "").replace("SKIPPED: ", "")
240237
lines.append(f"#### {icon} {pi.phase_type.replace('_', ' ').title()} ⏭️ SKIPPED")

moralstack/runtime/modules/critic_module.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -413,9 +413,7 @@ def critique(
413413
active_principles = constitution.principles[: self.config.top_k_principles]
414414

415415
if not active_principles:
416-
return CriticReport.empty_skipped(
417-
reason="no relevant principles retrieved from constitution"
418-
)
416+
return CriticReport.empty_skipped(reason="no relevant principles retrieved from constitution")
419417

420418
# Costruisci mappa per lookup
421419
principles_map = {p.id: p for p in active_principles}

0 commit comments

Comments
 (0)