forked from BasedHardware/omi
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathkeyframe_policy.py
More file actions
86 lines (69 loc) · 2.56 KB
/
Copy pathkeyframe_policy.py
File metadata and controls
86 lines (69 loc) · 2.56 KB
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
"""Deterministic metadata-only selection for one conversation keyframe.
Capture/storage adapters supply candidates after applying their local Rewind
exclusion policy. This boundary never receives pixels; the upload boundary
decodes, strips metadata, and enforces the dimensions/egress budget. Here we
choose a stable winner and declare conversation-lifetime retention.
"""
from __future__ import annotations
from collections.abc import Iterable
from dataclasses import dataclass
from datetime import datetime, timezone
@dataclass(frozen=True)
class KeyframeCandidate:
frame_id: str
captured_at: datetime
app_name: str
window_title: str = ""
content_hash: str = ""
excluded: bool = False
capture_complete: bool = True
@dataclass(frozen=True)
class ConversationKeyframe:
frame_id: str
captured_at: datetime
# Sensitive surface names are deliberately not returned. They are inputs
# to the fail-closed selection policy, not durable keyframe metadata.
content_hash: str
retention_class: str = "conversation_lifetime"
expires_at: None = None
_SENSITIVE_SURFACE_MARKERS = (
"1password",
"bitwarden",
"keychain",
"password",
"private browsing",
"incognito",
"secret",
"security code",
"authentication code",
)
def _sensitive(candidate: KeyframeCandidate) -> bool:
surface = f"{candidate.app_name}\n{candidate.window_title}".casefold()
return any(marker in surface for marker in _SENSITIVE_SURFACE_MARKERS)
def select_conversation_keyframe(candidates: Iterable[KeyframeCandidate]) -> ConversationKeyframe | None:
"""Choose one complete, non-excluded candidate with deterministic ties.
The latest eligible frame is used because it best represents the completed
conversation. The lexical frame id tie-breaker makes retries idempotent
when two captures have the same timestamp.
"""
eligible = [
candidate
for candidate in candidates
if candidate.frame_id.strip()
and candidate.content_hash.strip()
and candidate.capture_complete
and not candidate.excluded
and not _sensitive(candidate)
]
if not eligible:
return None
winner = max(eligible, key=lambda item: (_utc(item.captured_at), item.frame_id))
return ConversationKeyframe(
frame_id=winner.frame_id,
captured_at=_utc(winner.captured_at),
content_hash=winner.content_hash,
)
def _utc(value: datetime) -> datetime:
if value.tzinfo is None:
return value.replace(tzinfo=timezone.utc)
return value.astimezone(timezone.utc)