Add basic version: SemIf decision engine, urgency queue, skill tree, skill loop, decision log, dream pass, CLI

This commit is contained in:
semif-agent
2026-09-23 03:01:45 -05:00
commit 4e0e0534c4
21 changed files with 1629 additions and 0 deletions
+83
View File
@@ -0,0 +1,83 @@
"""Urgency priority queue.
Items are sorted by descending urgency weight, then FIFO arrival, then most
recent first. Queued items age upward so they cannot starve, and the queue has
a maximum depth.
"""
from __future__ import annotations
import heapq
import itertools
import time
from dataclasses import dataclass
from typing import Iterator
from .decisions import Request
MIN_RECENCY = 0.0
@dataclass
class QueueItem:
weight: float
seq: int
recency: float
request: Request
def key(self) -> tuple:
return (-self.weight, self.seq)
class UrgencyQueue:
"""A max-by-urgency priority queue with FIFO/recency tie-breaking."""
def __init__(self, max_size: int = 100, age_rate: float = 0.0):
self.max_size = max_size
self.age_rate = age_rate
self._entries: list[tuple] = []
self._counter = itertools.count()
def push(self, request: Request, weight: float, recency: float | None = None) -> bool:
"""Insert an item. Returns False if the queue is full."""
if len(self._entries) >= self.max_size:
return False
item = QueueItem(
weight=weight,
seq=next(self._counter),
recency=recency if recency is not None else time.time(),
request=request,
)
entry = (*item.key(), next(self._counter), item)
heapq.heappush(self._entries, entry)
return True
def pop(self) -> Request | None:
if not self._entries:
return None
return heapq.heappop(self._entries)[-1].request
def peek(self) -> Request | None:
if not self._entries:
return None
return self._entries[0][-1].request
def age(self, dt: float = 1.0) -> None:
"""Pull queued priorities toward critical so old items catch up."""
if self.age_rate <= 0:
return
rebuilt = []
for entry in self._entries:
item = entry[-1]
item.weight += self.age_rate * dt * (1.0 - item.weight)
rebuilt.append((*item.key(), next(self._counter), item))
self._entries = rebuilt
heapq.heapify(self._entries)
def __len__(self) -> int:
return len(self._entries)
def items(self) -> Iterator[tuple[float, Request]]:
for entry in sorted(self._entries, key=lambda e: e[:-1]):
item = entry[-1]
yield item.weight, item.request