feat: Add presence heartbeat for Matrix online status
- matrix-gateway: POST /internal/matrix/presence/online endpoint - usePresenceHeartbeat hook with activity tracking - Auto away after 5 min inactivity - Offline on page close/visibility change - Integrated in MatrixChatRoom component
This commit is contained in:
117
services/agent-filter/rules.py
Normal file
117
services/agent-filter/rules.py
Normal file
@@ -0,0 +1,117 @@
|
||||
from models import MessageCreatedEvent, FilterContext, FilterDecision
|
||||
from datetime import datetime, time
|
||||
import yaml
|
||||
import os
|
||||
|
||||
class FilterRules:
|
||||
def __init__(self, config_path: str = "config.yaml"):
|
||||
if os.path.exists(config_path):
|
||||
with open(config_path, 'r') as f:
|
||||
self.config = yaml.safe_load(f)
|
||||
else:
|
||||
# Default config
|
||||
self.config = {
|
||||
'rules': {
|
||||
'quiet_hours': {
|
||||
'start': '23:00',
|
||||
'end': '07:00'
|
||||
},
|
||||
'default_agents': {
|
||||
'microdao:daarion': 'agent:sofia',
|
||||
'microdao:7': 'agent:sofia'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
self.quiet_hours_start = datetime.strptime(
|
||||
self.config['rules']['quiet_hours']['start'],
|
||||
"%H:%M"
|
||||
).time()
|
||||
self.quiet_hours_end = datetime.strptime(
|
||||
self.config['rules']['quiet_hours']['end'],
|
||||
"%H:%M"
|
||||
).time()
|
||||
self.default_agents = self.config['rules'].get('default_agents', {})
|
||||
|
||||
def is_quiet_hours(self, dt: datetime) -> bool:
|
||||
"""Check if current time is in quiet hours"""
|
||||
current_time = dt.time()
|
||||
if self.quiet_hours_start > self.quiet_hours_end:
|
||||
# Overnight range (e.g., 23:00 - 07:00)
|
||||
return current_time >= self.quiet_hours_start or current_time <= self.quiet_hours_end
|
||||
else:
|
||||
return self.quiet_hours_start <= current_time <= self.quiet_hours_end
|
||||
|
||||
def decide(self, event: MessageCreatedEvent, ctx: FilterContext) -> FilterDecision:
|
||||
"""
|
||||
Apply filtering rules and decide if/which agent should respond
|
||||
|
||||
Rules:
|
||||
1. Block agent→agent loops
|
||||
2. Check if agent is disabled
|
||||
3. Find target agent (from allowed_agents or default)
|
||||
4. Apply quiet hours modifier
|
||||
5. Allow or deny
|
||||
"""
|
||||
base_decision = FilterDecision(
|
||||
channel_id=event.channel_id,
|
||||
message_id=event.message_id,
|
||||
matrix_event_id=event.matrix_event_id,
|
||||
microdao_id=event.microdao_id,
|
||||
decision="deny"
|
||||
)
|
||||
|
||||
# Rule 1: Block agent→agent loops
|
||||
if event.sender_type == "agent":
|
||||
print(f"[FILTER] Denying: sender is agent (loop prevention)")
|
||||
return base_decision
|
||||
|
||||
# Rule 2: Check if any agents are disabled
|
||||
if ctx.channel.disabled_agents:
|
||||
print(f"[FILTER] Warning: Some agents are disabled: {ctx.channel.disabled_agents}")
|
||||
|
||||
# Rule 3: Find target agent
|
||||
target_agent_id = None
|
||||
if ctx.channel.allowed_agents:
|
||||
target_agent_id = ctx.channel.allowed_agents[0]
|
||||
print(f"[FILTER] Target agent from allowed_agents: {target_agent_id}")
|
||||
elif event.microdao_id in self.default_agents:
|
||||
target_agent_id = self.default_agents[event.microdao_id]
|
||||
print(f"[FILTER] Target agent from default: {target_agent_id}")
|
||||
|
||||
if not target_agent_id:
|
||||
print(f"[FILTER] Denying: no target agent found for {event.microdao_id}")
|
||||
return base_decision
|
||||
|
||||
# Check if target agent is disabled
|
||||
if target_agent_id in ctx.channel.disabled_agents:
|
||||
print(f"[FILTER] Denying: target agent {target_agent_id} is disabled")
|
||||
return base_decision
|
||||
|
||||
# Rule 4: Check quiet hours
|
||||
if ctx.local_time and self.is_quiet_hours(ctx.local_time):
|
||||
print(f"[FILTER] Quiet hours active, modifying prompt")
|
||||
return FilterDecision(
|
||||
channel_id=event.channel_id,
|
||||
message_id=event.message_id,
|
||||
matrix_event_id=event.matrix_event_id,
|
||||
microdao_id=event.microdao_id,
|
||||
decision="modify",
|
||||
target_agent_id=target_agent_id,
|
||||
rewrite_prompt="Відповідай стисло і тільки якщо запит важливий. Не ініціюй розмову сам."
|
||||
)
|
||||
|
||||
# Rule 5: Allow
|
||||
print(f"[FILTER] Allowing: {target_agent_id} to respond")
|
||||
return FilterDecision(
|
||||
channel_id=event.channel_id,
|
||||
message_id=event.message_id,
|
||||
matrix_event_id=event.matrix_event_id,
|
||||
microdao_id=event.microdao_id,
|
||||
decision="allow",
|
||||
target_agent_id=target_agent_id
|
||||
)
|
||||
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user