| from __future__ import annotations |
| """ |
| Behavioral ์ด๋ฒคํธ ๊ณต์ ์ ์ฅ์ (์๋๋ฆฌ์ค ๋ฌด๊ด). |
| |
| ์ธ์
๋ณ ํ๋ ์ด๋ฒคํธ๋ฅผ ๋์ ํ๊ณ 5๋ถ window๋ก ์กฐํํ๋ค. |
| Pattern Feature ์ฐ์ถ(์๋๋ฆฌ์ค๋ณ ์ง๊ณ)์ ๊ฐ ์์ง(cs/bundle/worker)์ด ๋ด๋น. |
| """ |
| from datetime import datetime, timedelta |
| from typing import Any |
|
|
|
|
| def _now() -> datetime: |
| """ํ์ฌ UTC ์๊ฐ์ ๋ฐํํ๋ค (์ด๋ฒคํธ ํ์์คํฌํยทwindow ์ปท์คํ ๊ธฐ์ค). |
| |
| Returns: |
| ํ์ฌ UTC datetime. |
| """ |
| return datetime.utcnow() |
|
|
|
|
| class BehavioralPatternExtractor: |
| """์ธ์
๋ณ ํ๋ ์ด๋ฒคํธ๋ฅผ ๋์ ํ๊ณ window ๋จ์๋ก ์กฐํํ๋ค. |
| |
| WebSocket ์ธ์
๋จ์๋ก ์ธ์คํด์ค๋ฅผ ๋ณด์กดํ๋ฉฐ, ๋งค ํ๋๋ง๋ค add_event()๋ฅผ ํธ์ถํ๋ค. |
| Pattern Feature ์ฐ์ถ์ ๊ฐ ์๋๋ฆฌ์ค ์์ง์ด ๋ด๋นํ๋ค. |
| """ |
|
|
| def __init__(self) -> None: |
| self._events_by_session: dict[str, list[dict[str, Any]]] = {} |
|
|
| def add_event( |
| self, |
| session_id: str, |
| event_type: str, |
| entity: str, |
| occurred_at: datetime | None = None, |
| ) -> None: |
| """์ธ์
์ ํ๋ ์ด๋ฒคํธ 1๊ฑด์ ๋์ ํ๋ค. |
| |
| Args: |
| session_id: ์ด๋ฒคํธ๋ฅผ ๋์ ํ ์ธ์
ID. |
| event_type: ์ด๋ฒคํธ ์ข
๋ฅ. |
| entity: ์ด๋ฒคํธ ๋์ ์ํฐํฐ. |
| occurred_at: ์ด๋ฒคํธ ๋ฐ์ ์๊ฐ. ๋ฏธ์ง์ ์ ํ์ฌ UTC. |
| """ |
| ts = occurred_at or _now() |
| self._events_by_session.setdefault(session_id, []).append({ |
| "event_type": event_type, |
| "entity": entity, |
| "occurred_at": ts, |
| }) |
|
|
| def events_within( |
| self, |
| session_id: str, |
| window_seconds: int = 300, |
| ) -> list[dict[str, Any]]: |
| """์ธ์
์ ์ต๊ทผ window ๋ด ์ด๋ฒคํธ๋ฅผ ์กฐํํ๋ค. |
| |
| ์์ง๋ณ Pattern ๊ณ์ฐ ์
๋ ฅ์ผ๋ก ์ฌ์ฉ๋๋ค. |
| |
| Args: |
| session_id: ์กฐํํ ์ธ์
ID. |
| window_seconds: ์ปท์คํ window ๊ธธ์ด(์ด). |
| |
| Returns: |
| ์ปท์คํ ์ดํ ๋ฐ์ํ ์ด๋ฒคํธ ๋ฆฌ์คํธ. |
| """ |
| events = self._events_by_session.get(session_id, []) |
| cutoff = _now() - timedelta(seconds=window_seconds) |
| return [e for e in events if e["occurred_at"] >= cutoff] |
|
|
| def recent_events(self, session_id: str, n: int) -> list[dict[str, Any]]: |
| """์ธ์
์ ์ต๊ทผ n๊ฐ ์ด๋ฒคํธ๋ฅผ ์กฐํํ๋ค (ํด๋ฆญ ๊ธฐ๋ฐ ์๋์ฐ). |
| |
| Args: |
| session_id: ์กฐํํ ์ธ์
ID. |
| n: ์กฐํํ ์ต๊ทผ ์ด๋ฒคํธ ๊ฐ์. 0 ์ดํ์ด๋ฉด ์ ์ฒด. |
| |
| Returns: |
| ์ต๊ทผ n๊ฐ ์ด๋ฒคํธ ๋ฆฌ์คํธ. |
| """ |
| events = self._events_by_session.get(session_id, []) |
| return events[-n:] if n and n > 0 else list(events) |
|
|
| def reset(self, session_id: str) -> None: |
| """์ธ์
์ ๋์ ์ด๋ฒคํธ๋ฅผ ์ ๊ฑฐํ๋ค. |
| |
| Args: |
| session_id: ์ด๊ธฐํํ ์ธ์
ID. |
| """ |
| self._events_by_session.pop(session_id, None) |
|
|
|
|
| |
| _extractor: BehavioralPatternExtractor | None = None |
|
|
|
|
| def get_extractor() -> BehavioralPatternExtractor: |
| """๊ณต์ ์ด๋ฒคํธ ์ ์ฅ์ ์ฑ๊ธํค์ ๋ฐํํ๋ค. |
| |
| Returns: |
| ํ๋ก์ธ์ค ์ ์ญ BehavioralPatternExtractor ์ธ์คํด์ค. |
| """ |
| global _extractor |
| if _extractor is None: |
| _extractor = BehavioralPatternExtractor() |
| return _extractor |
|
|