File size: 3,552 Bytes
a28cff7
 
ce17628
a28cff7
ce17628
 
a28cff7
 
 
 
 
 
58debbc
 
 
 
 
a28cff7
 
 
 
58debbc
a28cff7
58debbc
 
a28cff7
 
4819352
a28cff7
 
 
 
 
 
 
 
 
58debbc
 
 
 
 
 
 
 
a28cff7
 
 
 
 
 
 
37fcd19
a28cff7
 
 
 
58debbc
 
 
 
 
 
 
 
 
 
 
a28cff7
 
 
 
69812a2
58debbc
 
 
 
 
 
 
 
 
69812a2
 
 
a28cff7
58debbc
 
 
 
 
a28cff7
 
 
 
 
 
 
 
58debbc
 
 
 
 
a28cff7
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
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