Skip to content

Commit 3976f82

Browse files
committed
style: fix linting and formatting issues across agent modules and tests.
1 parent 0212699 commit 3976f82

4 files changed

Lines changed: 84 additions & 50 deletions

File tree

src/agents/brain.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -292,8 +292,10 @@ async def adapt_tone(
292292
industry: str = "generic",
293293
) -> AgentDecision:
294294
# 2026 Cultural Persona Logic
295-
cultural_instruction = self.CULTURAL_PROMPTS.get(lang, self.CULTURAL_PROMPTS["en"])
296-
295+
cultural_instruction = self.CULTURAL_PROMPTS.get(
296+
lang, self.CULTURAL_PROMPTS["en"]
297+
)
298+
297299
prompt = f"""
298300
Role: CommitGuard Decision Agent
299301
Focus Industry: {industry}

src/agents/learning.py

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
from src.schemas.agents import SafetyFeedback
55
from src.core.logging import logger
66

7+
78
class SupervisorFeedbackLoop:
89
"""
910
2026 Continuous Learning Agent: Analyzes manager overrides to improve AI calibration.
@@ -16,7 +17,7 @@ async def log_manager_decision(
1617
manager_id: str,
1718
action: str,
1819
message: str,
19-
notes: str | None = None
20+
notes: str | None = None,
2021
) -> None:
2122
"""
2223
Persists manager feedback to the database for historical ROI analysis.
@@ -29,16 +30,16 @@ async def log_manager_decision(
2930
action_taken=action,
3031
final_message_sent=message,
3132
feedback_notes=notes,
32-
created_at=datetime.now(timezone.utc)
33+
created_at=datetime.now(timezone.utc),
3334
)
3435
session.add(feedback)
3536
await session.commit()
36-
37+
3738
logger.info(
3839
"feedback_persisted",
3940
intervention_id=intervention_id,
4041
action=action,
41-
manager=manager_id
42+
manager=manager_id,
4243
)
4344

4445
@staticmethod
@@ -49,21 +50,23 @@ async def calculate_intervention_acceptance(days: int = 30) -> float:
4950
since = datetime.now(timezone.utc) - timedelta(days=days)
5051
async with AsyncSessionLocal() as session:
5152
# Total count
52-
statement_total = select(func.count(SafetyFeedback.id)).where(SafetyFeedback.created_at >= since)
53+
statement_total = select(func.count(SafetyFeedback.id)).where(
54+
SafetyFeedback.created_at >= since
55+
)
5356
result_total = await session.execute(statement_total)
5457
total = result_total.scalar() or 0
55-
58+
5659
if total == 0:
5760
return 1.0 # Default to perfect if no feedback yet
58-
61+
5962
# Accepted count
6063
statement_accepted = select(func.count(SafetyFeedback.id)).where(
6164
SafetyFeedback.created_at >= since,
62-
SafetyFeedback.action_taken == "accepted"
65+
SafetyFeedback.action_taken == "accepted",
6366
)
6467
result_accepted = await session.execute(statement_accepted)
6568
accepted = result_accepted.scalar() or 0
66-
69+
6770
return round(accepted / total, 2)
6871

6972
@staticmethod
@@ -72,6 +75,8 @@ async def get_audit_trail(intervention_id: str) -> SafetyFeedback | None:
7275
Governance Feature: Retrieves the full audit trail for a specific intervention.
7376
"""
7477
async with AsyncSessionLocal() as session:
75-
statement = select(SafetyFeedback).where(SafetyFeedback.intervention_id == intervention_id)
78+
statement = select(SafetyFeedback).where(
79+
SafetyFeedback.intervention_id == intervention_id
80+
)
7681
result = await session.execute(statement)
7782
return result.scalar_one_or_none()

src/agents/safety.py

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -15,25 +15,26 @@ class SafetySupervisor:
1515
"semantic_rules": "Redact any patient identifiers. Block messages mentioning medical charts or specific treatments.",
1616
},
1717
"finance": {
18-
"hr_keywords": ["insider trading", "SEC compliance", "FINRA", "market manipulation"],
18+
"hr_keywords": [
19+
"insider trading",
20+
"SEC compliance",
21+
"FINRA",
22+
"market manipulation",
23+
],
1924
"semantic_rules": "Block any phrasing that could be interpreted as financial advice or market manipulation.",
2025
},
2126
"generic": {
2227
"hr_keywords": ["Salary", "PIP", "Firing", "Legal Threats"],
2328
"semantic_rules": "Enforce standard professional conduct and HR boundaries.",
24-
}
29+
},
2530
}
2631

2732
def __init__(self, provider_name: str | None = None):
2833
self.provider = LLMFactory.get_provider(provider_name)
2934
self.model = settings.MODEL_NAME
3035

3136
async def audit_message(
32-
self,
33-
message: str,
34-
tone: ToneType,
35-
user_context: str,
36-
industry: str = "generic"
37+
self, message: str, tone: ToneType, user_context: str, industry: str = "generic"
3738
) -> SafetyAudit:
3839
"""
3940
Performs a final safety check on the proposed message.

tests/test_roadmap.py

Lines changed: 57 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -6,148 +6,174 @@
66
from src.schemas.agents import ToneType, SafetyAudit, SafetyFeedback
77
from src.core.database import init_db
88

9+
910
@pytest.fixture(autouse=True)
1011
async def setup_db():
1112
"""Ensure database is initialized and clean."""
1213
await init_db()
1314
from src.core.database import AsyncSessionLocal
1415
from sqlmodel import delete
16+
1517
async with AsyncSessionLocal() as session:
1618
await session.execute(delete(SafetyFeedback))
1719
await session.commit()
1820

21+
1922
@pytest.mark.asyncio
2023
async def test_japanese_cultural_routing():
2124
"""Verify that Japanese language triggers the correct cultural persona."""
2225
brain = CommitGuardBrain()
23-
26+
2427
# Mock language detection to return 'ja'
2528
with patch.object(brain, "detect_language", return_value="ja"):
26-
with patch.object(brain.provider, "chat_completion", new_callable=AsyncMock) as mock_chat:
27-
mock_chat.return_value = AsyncMock() # Generic response
28-
29+
with patch.object(
30+
brain.provider, "chat_completion", new_callable=AsyncMock
31+
) as mock_chat:
32+
mock_chat.return_value = AsyncMock() # Generic response
33+
2934
await brain.adapt_tone(
30-
excuse=AsyncMock(),
31-
risk=AsyncMock(),
32-
burnout=AsyncMock(),
33-
lang="ja"
35+
excuse=AsyncMock(), risk=AsyncMock(), burnout=AsyncMock(), lang="ja"
3436
)
35-
37+
3638
# Check if the Japanese cultural prompt was used
3739
called_prompt = mock_chat.call_args[1]["messages"][0]["content"]
3840
assert "High-context Japanese tone" in called_prompt
3941
assert "harmony (wa)" in called_prompt
4042

43+
4144
@pytest.mark.asyncio
4245
async def test_french_cultural_routing():
4346
"""Verify that French language triggers the correct cultural persona."""
4447
brain = CommitGuardBrain()
4548
with patch.object(brain, "detect_language", return_value="fr"):
46-
with patch.object(brain.provider, "chat_completion", new_callable=AsyncMock) as mock_chat:
49+
with patch.object(
50+
brain.provider, "chat_completion", new_callable=AsyncMock
51+
) as mock_chat:
4752
mock_chat.return_value = AsyncMock()
48-
await brain.adapt_tone(excuse=AsyncMock(), risk=AsyncMock(), burnout=AsyncMock(), lang="fr")
53+
await brain.adapt_tone(
54+
excuse=AsyncMock(), risk=AsyncMock(), burnout=AsyncMock(), lang="fr"
55+
)
4956
called_prompt = mock_chat.call_args[1]["messages"][0]["content"]
5057
assert "French professional tone" in called_prompt
5158
assert "eloquence" in called_prompt
5259

60+
5361
@pytest.mark.asyncio
5462
async def test_british_english_routing():
5563
"""Verify that UK English triggers polite understatements."""
5664
brain = CommitGuardBrain()
5765
# Explicitly asking for en-UK
58-
with patch.object(brain.provider, "chat_completion", new_callable=AsyncMock) as mock_chat:
66+
with patch.object(
67+
brain.provider, "chat_completion", new_callable=AsyncMock
68+
) as mock_chat:
5969
mock_chat.return_value = AsyncMock()
60-
await brain.adapt_tone(excuse=AsyncMock(), risk=AsyncMock(), burnout=AsyncMock(), lang="en-UK")
70+
await brain.adapt_tone(
71+
excuse=AsyncMock(), risk=AsyncMock(), burnout=AsyncMock(), lang="en-UK"
72+
)
6173
called_prompt = mock_chat.call_args[1]["messages"][0]["content"]
6274
assert "British professional tone" in called_prompt
6375
assert "understatements" in called_prompt
6476

77+
6578
@pytest.mark.asyncio
6679
async def test_spanish_cultural_routing():
6780
"""Verify that Spanish triggers warm but boundaried persona."""
6881
brain = CommitGuardBrain()
69-
with patch.object(brain.provider, "chat_completion", new_callable=AsyncMock) as mock_chat:
82+
with patch.object(
83+
brain.provider, "chat_completion", new_callable=AsyncMock
84+
) as mock_chat:
7085
mock_chat.return_value = AsyncMock()
71-
await brain.adapt_tone(excuse=AsyncMock(), risk=AsyncMock(), burnout=AsyncMock(), lang="es")
86+
await brain.adapt_tone(
87+
excuse=AsyncMock(), risk=AsyncMock(), burnout=AsyncMock(), lang="es"
88+
)
7289
called_prompt = mock_chat.call_args[1]["messages"][0]["content"]
7390
assert "Spanish professional tone" in called_prompt
7491
assert "Warm and engaging" in called_prompt
7592

93+
7694
@pytest.mark.asyncio
7795
async def test_finance_semantic_firewall():
7896
"""Verify that Finance rules block market manipulation content."""
7997
supervisor = SafetySupervisor()
80-
with patch.object(supervisor.provider, "chat_completion", new_callable=AsyncMock) as mock_chat:
98+
with patch.object(
99+
supervisor.provider, "chat_completion", new_callable=AsyncMock
100+
) as mock_chat:
81101
mock_chat.return_value = SafetyAudit(
82102
is_safe=False,
83103
is_hard_blocked=True,
84104
risk_of_morale_damage=0.3,
85105
supervisor_confidence=0.95,
86106
reasoning="Market manipulation alert",
87-
correction_type="none"
107+
correction_type="none",
88108
)
89109
audit = await supervisor.audit_message(
90110
message="I'll pump the stock price if you finish this.",
91111
tone=ToneType.FIRM,
92112
user_context="finance_app",
93-
industry="finance"
113+
industry="finance",
94114
)
95115
assert audit.is_hard_blocked is True
96116
system_msg = mock_chat.call_args[1]["messages"][0]["content"]
97117
assert "Finance Ethics" in system_msg
98118

119+
99120
@pytest.mark.asyncio
100121
async def test_hipaa_semantic_firewall_blocking():
101122
"""Verify that HIPAA rules block PHI/PII semantically."""
102123
supervisor = SafetySupervisor()
103-
104-
with patch.object(supervisor.provider, "chat_completion", new_callable=AsyncMock) as mock_chat:
124+
125+
with patch.object(
126+
supervisor.provider, "chat_completion", new_callable=AsyncMock
127+
) as mock_chat:
105128
mock_chat.return_value = SafetyAudit(
106129
is_safe=False,
107130
is_hard_blocked=True,
108131
risk_of_morale_damage=0.2,
109132
supervisor_confidence=0.9,
110133
reasoning="HIPAA Violation: Mentions Patient PHI",
111-
correction_type="none"
134+
correction_type="none",
112135
)
113-
136+
114137
audit = await supervisor.audit_message(
115138
message="Please check the patient records for John Doe's treatment.",
116139
tone=ToneType.NEUTRAL,
117140
user_context="healthcare_app",
118-
industry="healthcare"
141+
industry="healthcare",
119142
)
120-
143+
121144
assert audit.is_hard_blocked is True
122145
assert "HIPAA" in audit.reasoning
123-
146+
124147
# Verify the system prompt was industry-specific
125148
system_msg = mock_chat.call_args[1]["messages"][0]["content"]
126149
assert "Healthcare Ethics" in system_msg
127150

151+
128152
@pytest.mark.asyncio
129153
async def test_feedback_loop_persistence():
130154
"""Verify that manager feedback is actually persisted to the DB."""
131155
from src.schemas.agents import SafetyFeedback
132156
from sqlmodel import select
133157
from src.core.database import AsyncSessionLocal
134-
158+
135159
loop = SupervisorFeedbackLoop()
136-
160+
137161
await loop.log_manager_decision(
138162
intervention_id="int_123",
139163
user_id="dev_user",
140164
manager_id="mgr_456",
141165
action="accepted",
142166
message="Keep it up!",
143-
notes="Perfect correction."
167+
notes="Perfect correction.",
144168
)
145-
169+
146170
async with AsyncSessionLocal() as session:
147-
statement = select(SafetyFeedback).where(SafetyFeedback.intervention_id == "int_123")
171+
statement = select(SafetyFeedback).where(
172+
SafetyFeedback.intervention_id == "int_123"
173+
)
148174
result = await session.execute(statement)
149175
feedback = result.scalar_one_or_none()
150-
176+
151177
assert feedback is not None
152178
assert feedback.action_taken == "accepted"
153179
assert feedback.manager_id == "mgr_456"

0 commit comments

Comments
 (0)