forked from BasedHardware/omi
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathjit_proactivity_store.py
More file actions
330 lines (301 loc) · 15.8 KB
/
Copy pathjit_proactivity_store.py
File metadata and controls
330 lines (301 loc) · 15.8 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
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
"""Atomic, content-free cross-device budget reservations for JIT proactivity."""
from __future__ import annotations
from datetime import datetime, time, timedelta, timezone
import hashlib
import json
from typing import Any
from zoneinfo import ZoneInfo, ZoneInfoNotFoundError
from database._client import data_plane_db as default_db_client
from database.account_deletion_policy import account_deletion_blocks_access, normalize_account_deletion_status
from database.memory_apply_store import transactional
from database.memory_collections import MemoryCollections
from database.read_boundary import parse_payload_strict, parse_snapshot_strict
from models.jit_proactivity import (
JIT_AMBIGUOUS_NANO_TRIAGES_PER_DAY,
JIT_FULL_TURNS_PER_CANDIDATE,
JIT_PLANNED_NOTIFICATIONS_PER_TRIGGER_PER_DAY,
JIT_TOTAL_FULL_TURNS_PER_DAY,
JIT_TOTAL_PROACTIVE_NOTIFICATIONS_PER_DAY,
JITProactivityEventReceipt,
JITProactivityOperation,
)
from models.memory_apply import MemoryControlState
from models.product_memory import MemoryItem
from utils.memory.jit_trigger_snapshot import is_authoritative_trigger_for_paid_work
class JITProactivityReservationError(RuntimeError):
pass
def _budget_day_for_timezone(at: datetime, timezone_name: str) -> str:
if at.tzinfo is None or at.utcoffset() is None:
raise JITProactivityReservationError("JIT reservation time is timezone-naive")
normalized = timezone_name.strip()
if not normalized:
raise JITProactivityReservationError("JIT user timezone is unavailable")
try:
user_timezone = ZoneInfo(normalized)
except (ZoneInfoNotFoundError, ValueError) as exc:
raise JITProactivityReservationError("JIT user timezone is invalid") from exc
return at.astimezone(user_timezone).date().isoformat()
def _next_local_midnight(at: datetime, timezone_name: str) -> datetime:
zone = ZoneInfo(timezone_name)
local = at.astimezone(zone)
next_date = local.date() + timedelta(days=1)
return datetime.combine(next_date, time.min, tzinfo=zone).astimezone(timezone.utc)
def _timezone_from_user_snapshot(snapshot: Any) -> str:
payload = _snapshot_payload(snapshot)
timezone_name = payload.get("time_zone")
if not isinstance(timezone_name, str):
raise JITProactivityReservationError("JIT user timezone is unavailable")
normalized = timezone_name.strip()
_budget_day_for_timezone(datetime.now(timezone.utc), normalized)
return normalized
def _snapshot_payload(snapshot: Any) -> dict[str, Any]:
payload = snapshot.to_dict() if getattr(snapshot, "exists", False) else None
if not isinstance(payload, dict):
raise JITProactivityReservationError("required JIT authority document is unavailable")
return payload
@transactional
def _reserve_transaction(
transaction: Any,
db_client: Any,
proposed: JITProactivityEventReceipt,
) -> tuple[JITProactivityEventReceipt, bool]:
uid = proposed.uid
collections = MemoryCollections(uid=uid)
user_snapshot = db_client.document(f"users/{uid}").get(transaction=transaction)
authoritative_timezone = _timezone_from_user_snapshot(user_snapshot)
if authoritative_timezone != proposed.budget_timezone:
raise JITProactivityReservationError("JIT user timezone authority changed")
deletion_ref = db_client.document(f"account_deletions/{uid}")
deletion_snapshot = deletion_ref.get(transaction=transaction)
deletion_payload = deletion_snapshot.to_dict() if getattr(deletion_snapshot, "exists", False) else {}
deletion_status = normalize_account_deletion_status(
marker_exists=bool(getattr(deletion_snapshot, "exists", False)),
raw_status=deletion_payload.get("wipe_status") if isinstance(deletion_payload, dict) else None,
)
if account_deletion_blocks_access(deletion_status):
raise JITProactivityReservationError("JIT reservation blocked by account deletion")
control_snapshot = db_client.document(collections.memory_apply_control_state).get(transaction=transaction)
control = parse_snapshot_strict(MemoryControlState, control_snapshot, payload_from_snapshot=_snapshot_payload)
if control.uid != uid or control.account_generation != proposed.account_generation:
raise JITProactivityReservationError("JIT reservation generation is stale")
event_ref = db_client.document(f"{collections.jit_proactivity_events}/{proposed.event_id}")
event_snapshot = event_ref.get(transaction=transaction)
parent: JITProactivityEventReceipt | None = None
if proposed.parent_event_id is not None:
parent_snapshot = db_client.document(f"{collections.jit_proactivity_events}/{proposed.parent_event_id}").get(
transaction=transaction
)
parent = parse_snapshot_strict(
JITProactivityEventReceipt,
parent_snapshot,
payload_from_snapshot=_snapshot_payload,
)
if (
parent.uid != uid
or parent.account_generation != proposed.account_generation
or parent.operation not in {"planned_notification", "ambient_notification"}
or parent.candidate_id != proposed.candidate_id
or parent.device_id != proposed.device_id
or parent.budget_day != proposed.budget_day
or parent.budget_timezone != proposed.budget_timezone
or parent.trigger_memory_id != proposed.trigger_memory_id
or parent.trigger_revision != proposed.trigger_revision
or parent.event_id == proposed.event_id
or parent.feedback_id is not None
):
raise JITProactivityReservationError("JIT full-turn admission authority is stale")
if proposed.trigger_memory_id is not None:
trigger_snapshot = db_client.document(f"{collections.memory_items}/{proposed.trigger_memory_id}").get(
transaction=transaction
)
trigger = parse_snapshot_strict(MemoryItem, trigger_snapshot, payload_from_snapshot=_snapshot_payload)
if (
trigger.uid != uid
or trigger.account_generation != proposed.account_generation
or trigger.item_revision != proposed.trigger_revision
or not is_authoritative_trigger_for_paid_work(trigger, proposed.created_at)
):
raise JITProactivityReservationError("JIT trigger authority is stale")
if getattr(event_snapshot, "exists", False):
existing = parse_snapshot_strict(
JITProactivityEventReceipt,
event_snapshot,
payload_from_snapshot=_snapshot_payload,
)
if existing.request_hash != proposed.request_hash:
raise JITProactivityReservationError("JIT event id was reused with a different payload")
return existing, False
budget_control_ref = db_client.document(f"{collections.user_root}/jit_proactivity_budget_control/current")
budget_control_snapshot = budget_control_ref.get(transaction=transaction)
if getattr(budget_control_snapshot, "exists", False):
budget_control = _snapshot_payload(budget_control_snapshot)
if (
budget_control.get("schema_version") != "jit_proactivity_budget_control.v1"
or budget_control.get("uid") != uid
or budget_control.get("account_generation") != proposed.account_generation
or not isinstance(budget_control.get("budget_timezone"), str)
or not isinstance(budget_control.get("budget_day"), str)
or not isinstance(budget_control.get("window_ends_at"), datetime)
):
raise JITProactivityReservationError("JIT budget timezone authority is malformed")
window_ends_at = budget_control["window_ends_at"]
if window_ends_at.tzinfo is None or window_ends_at.utcoffset() is None:
raise JITProactivityReservationError("JIT budget timezone authority is malformed")
if proposed.created_at < window_ends_at and (
budget_control["budget_timezone"] != proposed.budget_timezone
or budget_control["budget_day"] != proposed.budget_day
):
raise JITProactivityReservationError("JIT timezone change would split an active budget window")
budget_control_write = {
"schema_version": "jit_proactivity_budget_control.v1",
"uid": uid,
"account_generation": proposed.account_generation,
"budget_timezone": proposed.budget_timezone,
"budget_day": proposed.budget_day,
"window_ends_at": _next_local_midnight(proposed.created_at, proposed.budget_timezone),
"updated_at": proposed.created_at,
}
day_ref = db_client.document(f"{collections.jit_proactivity_daily_budgets}/{proposed.budget_day}")
day_snapshot = day_ref.get(transaction=transaction)
budget: dict[str, Any]
if getattr(day_snapshot, "exists", False):
budget = _snapshot_payload(day_snapshot)
prior_generation = budget.get("account_generation")
if type(prior_generation) is not int or prior_generation > proposed.account_generation:
raise JITProactivityReservationError("JIT daily budget authority is malformed")
if prior_generation < proposed.account_generation:
budget = {}
elif (
budget.get("schema_version") != "jit_proactivity_daily_budget.v1"
or budget.get("uid") != uid
or budget.get("budget_day") != proposed.budget_day
or budget.get("budget_timezone") != proposed.budget_timezone
):
raise JITProactivityReservationError("JIT daily budget authority is malformed")
else:
budget = {}
if not budget:
budget = {
"schema_version": "jit_proactivity_daily_budget.v1",
"uid": uid,
"account_generation": proposed.account_generation,
"budget_day": proposed.budget_day,
"budget_timezone": proposed.budget_timezone,
"total_notifications": 0,
"nano_triages": 0,
"full_turns": 0,
"planned_by_trigger": {},
}
operation = proposed.operation
if operation in {"planned_notification", "ambient_notification"}:
total = budget.get("total_notifications")
if type(total) is not int or total < 0:
raise JITProactivityReservationError("JIT notification budget is malformed")
if total >= JIT_TOTAL_PROACTIVE_NOTIFICATIONS_PER_DAY:
raise JITProactivityReservationError("JIT notification budget exhausted")
budget["total_notifications"] = total + 1
if operation == "planned_notification":
counts = budget.get("planned_by_trigger")
if not isinstance(counts, dict):
raise JITProactivityReservationError("JIT per-trigger budget is malformed")
assert proposed.trigger_memory_id is not None
if proposed.trigger_memory_id not in counts and len(counts) >= 500:
raise JITProactivityReservationError("JIT per-trigger budget is malformed")
used = counts.get(proposed.trigger_memory_id, 0)
if type(used) is not int or used < 0:
raise JITProactivityReservationError("JIT per-trigger budget is malformed")
if used >= JIT_PLANNED_NOTIFICATIONS_PER_TRIGGER_PER_DAY:
raise JITProactivityReservationError("JIT per-trigger budget exhausted")
budget["planned_by_trigger"] = {**counts, proposed.trigger_memory_id: used + 1}
elif operation == "nano_triage":
used = budget.get("nano_triages")
if type(used) is not int or used < 0:
raise JITProactivityReservationError("JIT nano-triage budget is malformed")
if used >= JIT_AMBIGUOUS_NANO_TRIAGES_PER_DAY:
raise JITProactivityReservationError("JIT nano-triage budget exhausted")
budget["nano_triages"] = used + 1
elif operation == "full_turn":
if parent is None: # pragma: no cover - the typed receipt owns this invariant.
raise JITProactivityReservationError("JIT full-turn admission authority is unavailable")
full_turns = budget.get("full_turns", 0)
if type(full_turns) is not int or full_turns < 0:
raise JITProactivityReservationError("JIT full-turn daily budget is malformed")
if full_turns >= JIT_TOTAL_FULL_TURNS_PER_DAY:
raise JITProactivityReservationError("JIT full-turn daily budget exhausted")
candidate_ref = db_client.document(f"{collections.jit_proactivity_candidate_turns}/{proposed.candidate_id}")
candidate_snapshot = candidate_ref.get(transaction=transaction)
if getattr(candidate_snapshot, "exists", False):
candidate_payload = _snapshot_payload(candidate_snapshot)
prior_generation = candidate_payload.get("account_generation")
if type(prior_generation) is not int or prior_generation > proposed.account_generation:
raise JITProactivityReservationError("JIT candidate full-turn authority is malformed")
if prior_generation == proposed.account_generation:
raise JITProactivityReservationError("JIT candidate full-turn budget exhausted")
budget["full_turns"] = full_turns + JIT_FULL_TURNS_PER_CANDIDATE
transaction.set(
candidate_ref,
{
"schema_version": "jit_proactivity_candidate_turn.v1",
"uid": uid,
"account_generation": proposed.account_generation,
"candidate_id": proposed.candidate_id,
"event_id": proposed.event_id,
"parent_event_id": proposed.parent_event_id,
"budget_day": proposed.budget_day,
"created_at": proposed.created_at,
},
)
else: # pragma: no cover - typed model owns this boundary.
raise JITProactivityReservationError("unsupported JIT reservation operation")
budget["updated_at"] = proposed.created_at
transaction.set(budget_control_ref, budget_control_write)
transaction.set(day_ref, budget)
transaction.set(event_ref, proposed.model_dump(mode="python"))
return proposed, True
def reserve_jit_proactivity_event(
uid: str,
*,
event_id: str,
candidate_id: str,
operation: JITProactivityOperation,
account_generation: int,
device_id: str,
trigger_memory_id: str | None = None,
trigger_revision: int | None = None,
parent_event_id: str | None = None,
now: datetime | None = None,
db_client: Any = None,
) -> tuple[JITProactivityEventReceipt, bool]:
client = db_client if db_client is not None else default_db_client
created_at = now or datetime.now(timezone.utc)
normalized_timezone = _timezone_from_user_snapshot(client.document(f"users/{uid}").get())
budget_day = _budget_day_for_timezone(created_at, normalized_timezone)
canonical_request = {
"schema_version": "jit_proactivity_event.v1",
"uid": uid.strip(),
"event_id": event_id.strip(),
"candidate_id": candidate_id.strip(),
"operation": operation,
"account_generation": account_generation,
"trigger_memory_id": trigger_memory_id.strip() if trigger_memory_id is not None else None,
"trigger_revision": trigger_revision,
"parent_event_id": parent_event_id.strip() if parent_event_id is not None else None,
"device_id": device_id.strip(),
"budget_day": budget_day,
"budget_timezone": normalized_timezone,
}
request_hash = hashlib.sha256(
json.dumps(canonical_request, sort_keys=True, separators=(",", ":")).encode()
).hexdigest()
proposed = parse_payload_strict(
JITProactivityEventReceipt,
{
**canonical_request,
"created_at": created_at,
"request_hash": request_hash,
},
document_path="<request>/jit_proactivity_event",
)
transaction = client.transaction()
return _reserve_transaction(transaction, client, proposed)
__all__ = ["JITProactivityReservationError", "reserve_jit_proactivity_event"]