Add /daemon/status endpoint to track internal semiosis
- Add daemon state tracking globals (mode, is_ruminating, cycles_by_type) - Track trigger type and timestamp on each /cycle call - Add DaemonStatusResponse model - Add GET /daemon/status endpoint returning: - mode: idle | conversation | autonomous - is_ruminating: true when in rumination_free or corpus cycles - last_trigger: type and timestamp - cycles_breakdown: count by trigger type - cycles_since_last_user: autonomous cycles since last user interaction - time_since_last_user_seconds: elapsed time Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -62,6 +62,18 @@ _authority: Optional[Authority] = None
|
||||
_startup_time: Optional[datetime] = None
|
||||
_directions: List[Dict] = [] # 109 directions from Weaviate
|
||||
|
||||
# Daemon state tracking
|
||||
_daemon_mode: str = "idle" # idle, conversation, autonomous
|
||||
_is_ruminating: bool = False
|
||||
_last_trigger_type: Optional[str] = None
|
||||
_last_trigger_time: Optional[datetime] = None
|
||||
_cycles_by_type: Dict[str, int] = {
|
||||
"user": 0,
|
||||
"veille": 0,
|
||||
"corpus": 0,
|
||||
"rumination_free": 0,
|
||||
}
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# REQUEST/RESPONSE MODELS
|
||||
@@ -139,6 +151,16 @@ class ProfileResponse(BaseModel):
|
||||
david_similarity: float
|
||||
|
||||
|
||||
class DaemonStatusResponse(BaseModel):
|
||||
"""Statut du daemon (sémiose interne)."""
|
||||
mode: str # idle, conversation, autonomous
|
||||
is_ruminating: bool
|
||||
last_trigger: Optional[Dict[str, Any]] = None
|
||||
cycles_breakdown: Dict[str, int]
|
||||
cycles_since_last_user: int
|
||||
time_since_last_user_seconds: Optional[float] = None
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# INITIALIZATION
|
||||
# =============================================================================
|
||||
@@ -634,10 +656,27 @@ async def run_cycle(request: CycleRequest):
|
||||
3. Appliquer la fixation
|
||||
4. Mettre à jour l'état
|
||||
"""
|
||||
global _current_state
|
||||
global _current_state, _daemon_mode, _is_ruminating, _last_trigger_type, _last_trigger_time, _cycles_by_type
|
||||
|
||||
start_time = time.time()
|
||||
|
||||
# Track daemon state
|
||||
_last_trigger_type = request.trigger_type
|
||||
_last_trigger_time = datetime.now()
|
||||
if request.trigger_type in _cycles_by_type:
|
||||
_cycles_by_type[request.trigger_type] += 1
|
||||
|
||||
# Update daemon mode based on trigger type
|
||||
if request.trigger_type == "user":
|
||||
_daemon_mode = "conversation"
|
||||
_is_ruminating = False
|
||||
elif request.trigger_type in ("rumination_free", "corpus"):
|
||||
_daemon_mode = "autonomous"
|
||||
_is_ruminating = True
|
||||
else:
|
||||
_daemon_mode = "conversation"
|
||||
_is_ruminating = False
|
||||
|
||||
try:
|
||||
# 1. Vectoriser l'entrée
|
||||
e_input = _embedding_model.encode([request.content])[0]
|
||||
@@ -788,15 +827,62 @@ async def get_metrics():
|
||||
@app.post("/reset")
|
||||
async def reset_state():
|
||||
"""Réinitialiser l'état à S(0)."""
|
||||
global _current_state
|
||||
global _current_state, _cycles_by_type, _daemon_mode, _is_ruminating
|
||||
|
||||
_current_state = _initial_state.copy()
|
||||
_vigilance.reset_cumulative()
|
||||
_metrics.reset()
|
||||
|
||||
# Reset daemon tracking
|
||||
_cycles_by_type = {"user": 0, "veille": 0, "corpus": 0, "rumination_free": 0}
|
||||
_daemon_mode = "idle"
|
||||
_is_ruminating = False
|
||||
|
||||
return {"status": "ok", "state_id": _current_state.state_id}
|
||||
|
||||
|
||||
@app.get("/daemon/status", response_model=DaemonStatusResponse)
|
||||
async def get_daemon_status():
|
||||
"""
|
||||
Récupérer le statut du daemon (sémiose interne).
|
||||
|
||||
Permet de savoir si Ikario est en train de:
|
||||
- Répondre à un utilisateur (conversation)
|
||||
- Ruminer seul (autonomous)
|
||||
- En attente (idle)
|
||||
"""
|
||||
# Calculate cycles since last user interaction
|
||||
cycles_since_user = sum(
|
||||
count for trigger, count in _cycles_by_type.items()
|
||||
if trigger != "user"
|
||||
)
|
||||
|
||||
# Calculate time since last user interaction
|
||||
time_since_user = None
|
||||
if _last_trigger_time and _last_trigger_type == "user":
|
||||
time_since_user = (datetime.now() - _last_trigger_time).total_seconds()
|
||||
elif _last_trigger_time:
|
||||
# If last trigger was not user, count from then
|
||||
time_since_user = (datetime.now() - _last_trigger_time).total_seconds()
|
||||
|
||||
# Build last trigger info
|
||||
last_trigger = None
|
||||
if _last_trigger_type and _last_trigger_time:
|
||||
last_trigger = {
|
||||
"type": _last_trigger_type,
|
||||
"timestamp": _last_trigger_time.isoformat(),
|
||||
}
|
||||
|
||||
return DaemonStatusResponse(
|
||||
mode=_daemon_mode,
|
||||
is_ruminating=_is_ruminating,
|
||||
last_trigger=last_trigger,
|
||||
cycles_breakdown=_cycles_by_type,
|
||||
cycles_since_last_user=cycles_since_user,
|
||||
time_since_last_user_seconds=time_since_user,
|
||||
)
|
||||
|
||||
|
||||
@app.get("/profile", response_model=ProfileResponse)
|
||||
async def get_profile():
|
||||
"""
|
||||
|
||||
Reference in New Issue
Block a user