Skip to content

Commit b1542d4

Browse files
GeneAIclaude
authored andcommitted
feat: Add async pattern detection and workflow tests
- Implement _detect_patterns_async method in core.py for Level 3 proactive interactions (detects sequential, preference, conditional patterns in background without blocking main response) - Add 32 tests for PRReviewWorkflow (parallel execution, scoring, verdict determination, findings merge) - Add 29 tests for DependencyCheckWorkflow (parsing, vulnerability assessment, risk scoring, LLM integration) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <[email protected]>
1 parent 32691e1 commit b1542d4

File tree

3 files changed

+1304
-3
lines changed

3 files changed

+1304
-3
lines changed

empathy_llm_toolkit/core.py

Lines changed: 116 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
Licensed under Fair Source 0.9
88
"""
99

10+
import asyncio
1011
import logging
1112
import time
1213
from typing import Any
@@ -24,7 +25,7 @@
2425
from .levels import EmpathyLevel
2526
from .providers import AnthropicProvider, BaseLLMProvider, LocalProvider, OpenAIProvider
2627
from .routing import ModelRouter
27-
from .state import CollaborationState, UserPattern
28+
from .state import CollaborationState, PatternType, UserPattern
2829

2930
logger = logging.getLogger(__name__)
3031

@@ -589,8 +590,8 @@ async def _level_3_proactive(
589590
proactive = False
590591
pattern_info = None
591592

592-
# TODO: Run pattern detection in background
593-
# await self._detect_patterns_async(state)
593+
# Run pattern detection in background (non-blocking)
594+
asyncio.create_task(self._detect_patterns_async(state, user_input))
594595

595596
generate_kwargs: dict[str, Any] = {
596597
"messages": messages,
@@ -728,6 +729,118 @@ async def _level_5_systems(
728729
},
729730
}
730731

732+
async def _detect_patterns_async(
733+
self,
734+
state: CollaborationState,
735+
current_input: str,
736+
) -> None:
737+
"""
738+
Detect user behavior patterns in background.
739+
740+
Analyzes conversation history to identify:
741+
- Sequential patterns: User always does X then Y
742+
- Preference patterns: User prefers certain formats/styles
743+
- Temporal patterns: User does X at specific times
744+
- Conditional patterns: When Z happens, user does X
745+
746+
This runs asynchronously to avoid blocking the main response.
747+
Detected patterns enable Level 3 proactive interactions.
748+
"""
749+
try:
750+
from datetime import datetime
751+
752+
interactions = state.interactions
753+
if len(interactions) < 3:
754+
# Need at least 3 interactions to detect patterns
755+
return
756+
757+
# Analyze recent interactions for sequential patterns
758+
recent = interactions[-10:] # Last 10 interactions
759+
user_messages = [i for i in recent if i.role == "user"]
760+
761+
if len(user_messages) < 2:
762+
return
763+
764+
# Pattern 1: Sequential patterns (X followed by Y)
765+
for i in range(len(user_messages) - 1):
766+
current = user_messages[i].content.lower()
767+
next_msg = user_messages[i + 1].content.lower()
768+
769+
# Detect common sequential patterns
770+
sequential_triggers = [
771+
("review", "fix"), # Review then fix
772+
("debug", "test"), # Debug then test
773+
("implement", "test"), # Implement then test
774+
("refactor", "review"), # Refactor then review
775+
]
776+
777+
for trigger, action in sequential_triggers:
778+
if trigger in current and action in next_msg:
779+
pattern = UserPattern(
780+
pattern_type=PatternType.SEQUENTIAL,
781+
trigger=trigger,
782+
action=f"Typically follows with {action}",
783+
confidence=0.6 + (0.1 * min(i, 3)), # Increase with occurrences
784+
occurrences=1,
785+
last_seen=datetime.now(),
786+
context={"detected_from": "sequential_analysis"},
787+
)
788+
state.add_pattern(pattern)
789+
790+
# Pattern 2: Preference patterns
791+
preference_indicators = {
792+
"concise": "brief, concise responses",
793+
"detailed": "comprehensive, detailed responses",
794+
"example": "responses with examples",
795+
"step by step": "step-by-step explanations",
796+
"code": "code-focused responses",
797+
}
798+
799+
for indicator, preference in preference_indicators.items():
800+
occurrences = sum(1 for m in user_messages if indicator in m.content.lower())
801+
if occurrences >= 2:
802+
pattern = UserPattern(
803+
pattern_type=PatternType.PREFERENCE,
804+
trigger=indicator,
805+
action=f"User prefers {preference}",
806+
confidence=min(0.9, 0.5 + (0.1 * occurrences)),
807+
occurrences=occurrences,
808+
last_seen=datetime.now(),
809+
context={"preference_type": indicator},
810+
)
811+
state.add_pattern(pattern)
812+
813+
# Pattern 3: Conditional patterns (error -> debug)
814+
conditional_triggers = [
815+
("error", "debug", "When errors occur, user asks for debugging"),
816+
("failed", "fix", "When tests fail, user asks for fixes"),
817+
("slow", "optimize", "When performance issues arise, user asks for optimization"),
818+
]
819+
820+
for condition, response_keyword, description in conditional_triggers:
821+
for i, msg in enumerate(user_messages[:-1]):
822+
if condition in msg.content.lower():
823+
next_msg = user_messages[i + 1].content.lower()
824+
if response_keyword in next_msg:
825+
pattern = UserPattern(
826+
pattern_type=PatternType.CONDITIONAL,
827+
trigger=condition,
828+
action=description,
829+
confidence=0.7,
830+
occurrences=1,
831+
last_seen=datetime.now(),
832+
context={"condition": condition, "response": response_keyword},
833+
)
834+
state.add_pattern(pattern)
835+
836+
logger.debug(
837+
f"Pattern detection complete. Detected {len(state.detected_patterns)} patterns."
838+
)
839+
840+
except Exception as e:
841+
# Pattern detection should never break the main flow
842+
logger.warning(f"Pattern detection error (non-critical): {e}")
843+
731844
def update_trust(self, user_id: str, outcome: str, magnitude: float = 1.0):
732845
"""
733846
Update trust level based on interaction outcome.

0 commit comments

Comments
 (0)