upp-python 0.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
upp/__init__.py ADDED
@@ -0,0 +1,183 @@
1
+ """UPP — Universal Personalization Protocol Python Reference Implementation.
2
+
3
+ This package provides the Python reference implementation of the Universal
4
+ Personalization Protocol (UPP) data models, backend interfaces, JSON-RPC
5
+ support, and a high-level client.
6
+
7
+ Quick Start::
8
+
9
+ from upp import UPPClient, OntologyUserV1
10
+ from upp import Event, StoredEvent, LabelDefinition
11
+ from upp import EventStatus, SourceType, SensitivityTier
12
+
13
+ Backend Protocols::
14
+
15
+ from upp import IngestBackend, RetrieverBackend, OntologyBackend
16
+
17
+ All public types are re-exported from sub-packages for convenience.
18
+ """
19
+
20
+ from importlib.metadata import version as _pkg_version
21
+
22
+ __version__ = _pkg_version("upp-python")
23
+
24
+ # --- Backend Protocols ---
25
+ from upp.backends import (
26
+ IngestBackend,
27
+ OntologyBackend,
28
+ RetrieverBackend,
29
+ )
30
+
31
+ # --- Client ---
32
+ from upp.client import UPPClient
33
+
34
+ # --- Models ---
35
+ from upp.models import (
36
+ Cardinality,
37
+ ContextualizeResult,
38
+ Durability,
39
+ Event,
40
+ EventStatus,
41
+ LabelDefinition,
42
+ SensitivityTier,
43
+ SourceType,
44
+ StoredEvent,
45
+ TaskResult,
46
+ TaskStatus,
47
+ )
48
+
49
+ # --- Ontologies ---
50
+ from upp.ontologies.user_v1 import OntologyUserV1
51
+
52
+ # --- RPC ---
53
+ from upp.rpc import (
54
+ ALL_METHODS,
55
+ EXTRACTION_FAILED,
56
+ INGEST_FAILED,
57
+ INTERNAL_ERROR,
58
+ INVALID_PARAMS,
59
+ INVALID_REQUEST,
60
+ METHOD_NOT_FOUND,
61
+ ONTOLOGY_NOT_FOUND,
62
+ PARSE_ERROR,
63
+ UPP_CONTEXTUALIZE,
64
+ UPP_DELETE,
65
+ UPP_EVENTS,
66
+ UPP_EXPORT,
67
+ UPP_GET_TASKS,
68
+ UPP_IMPORT,
69
+ UPP_INFO,
70
+ UPP_INGEST,
71
+ UPP_LABELS,
72
+ UPP_RETRIEVE,
73
+ USER_NOT_FOUND,
74
+ ContextualizeRequest,
75
+ ContextualizeResponse,
76
+ DeleteRequest,
77
+ DeleteResponse,
78
+ EventsRequest,
79
+ EventsResponse,
80
+ ExportRequest,
81
+ ExportResponse,
82
+ GetTasksRequest,
83
+ GetTasksResponse,
84
+ ImportRequest,
85
+ ImportResponse,
86
+ InfoRequest,
87
+ InfoResponse,
88
+ IngestRequest,
89
+ IngestResponse,
90
+ JsonRpcError,
91
+ JsonRpcNotification,
92
+ JsonRpcRequest,
93
+ JsonRpcResponse,
94
+ LabelsRequest,
95
+ LabelsResponse,
96
+ RetrieveRequest,
97
+ RetrieveResponse,
98
+ UppError,
99
+ decode_request,
100
+ decode_response,
101
+ encode_request,
102
+ encode_response,
103
+ )
104
+
105
+ __all__ = [
106
+ "__version__",
107
+ # Enumerations
108
+ "Cardinality",
109
+ "Durability",
110
+ "EventStatus",
111
+ "SensitivityTier",
112
+ "SourceType",
113
+ "TaskStatus",
114
+ # Core entities
115
+ "ContextualizeResult",
116
+ "Event",
117
+ "LabelDefinition",
118
+ "StoredEvent",
119
+ "TaskResult",
120
+ # Backend protocols
121
+ "OntologyBackend",
122
+ "RetrieverBackend",
123
+ "IngestBackend",
124
+ # RPC message types
125
+ "JsonRpcError",
126
+ "JsonRpcNotification",
127
+ "JsonRpcRequest",
128
+ "JsonRpcResponse",
129
+ # RPC operation models
130
+ "ContextualizeRequest",
131
+ "ContextualizeResponse",
132
+ "DeleteRequest",
133
+ "DeleteResponse",
134
+ "EventsRequest",
135
+ "EventsResponse",
136
+ "ExportRequest",
137
+ "ExportResponse",
138
+ "GetTasksRequest",
139
+ "GetTasksResponse",
140
+ "ImportRequest",
141
+ "ImportResponse",
142
+ "InfoRequest",
143
+ "InfoResponse",
144
+ "LabelsRequest",
145
+ "LabelsResponse",
146
+ "IngestRequest",
147
+ "IngestResponse",
148
+ "RetrieveRequest",
149
+ "RetrieveResponse",
150
+ # RPC method constants
151
+ "ALL_METHODS",
152
+ "UPP_CONTEXTUALIZE",
153
+ "UPP_DELETE",
154
+ "UPP_EVENTS",
155
+ "UPP_EXPORT",
156
+ "UPP_GET_TASKS",
157
+ "UPP_IMPORT",
158
+ "UPP_INFO",
159
+ "UPP_LABELS",
160
+ "UPP_INGEST",
161
+ "UPP_RETRIEVE",
162
+ # RPC error codes
163
+ "EXTRACTION_FAILED",
164
+ "INTERNAL_ERROR",
165
+ "INVALID_PARAMS",
166
+ "INVALID_REQUEST",
167
+ "METHOD_NOT_FOUND",
168
+ "ONTOLOGY_NOT_FOUND",
169
+ "INGEST_FAILED",
170
+ "PARSE_ERROR",
171
+ "USER_NOT_FOUND",
172
+ # Exception
173
+ "UppError",
174
+ # Codec
175
+ "decode_request",
176
+ "decode_response",
177
+ "encode_request",
178
+ "encode_response",
179
+ # Client
180
+ "UPPClient",
181
+ # Ontology
182
+ "OntologyUserV1",
183
+ ]
upp/backends/.gitkeep ADDED
File without changes
@@ -0,0 +1,23 @@
1
+ """UPP Backend Interfaces.
2
+
3
+ This package defines the abstract backend protocols for the Universal
4
+ Personalization Protocol. Each backend is a :class:`typing.Protocol`
5
+ decorated with :func:`typing.runtime_checkable`.
6
+
7
+ Backend Protocols:
8
+ IngestBackend — Persists and retrieves stored events.
9
+ RetrieverBackend — Intelligent retrieval of relevant context.
10
+ OntologyBackend — Label definitions and server metadata.
11
+ """
12
+
13
+ from __future__ import annotations
14
+
15
+ from upp.backends.ingest import IngestBackend
16
+ from upp.backends.ontology import OntologyBackend
17
+ from upp.backends.retriever import RetrieverBackend
18
+
19
+ __all__ = [
20
+ "OntologyBackend",
21
+ "RetrieverBackend",
22
+ "IngestBackend",
23
+ ]
upp/backends/ingest.py ADDED
@@ -0,0 +1,122 @@
1
+ """UPP Ingest Backend Protocol.
2
+
3
+ Defines the :class:`IngestBackend` protocol for extracting and persisting
4
+ personal events from free text. The backend manages the full lifecycle of
5
+ events following an immutable event-sourcing pattern.
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ from typing import Protocol, runtime_checkable
11
+
12
+ from upp.models.events import Event, StoredEvent, TaskResult
13
+
14
+ __all__ = [
15
+ "IngestBackend",
16
+ ]
17
+
18
+
19
+ @runtime_checkable
20
+ class IngestBackend(Protocol):
21
+ """Protocol for pluggable event ingestion.
22
+
23
+ An ingest backend receives free text, extracts relevant personal
24
+ facts, classifies them with ontology labels, handles supersession
25
+ for singular-cardinality labels, and persists the resulting events.
26
+
27
+ Implementations may use any extraction strategy (LLM, NLP, rules)
28
+ and any backing storage (in-memory, SQLite, PostgreSQL, Redis, etc.).
29
+ """
30
+
31
+ async def ingest(
32
+ self,
33
+ entity_key: str,
34
+ text: str,
35
+ ) -> list[StoredEvent]:
36
+ """Extract and persist events from free text.
37
+
38
+ The backend MUST:
39
+
40
+ 1. Analyze the input text and extract relevant personal facts.
41
+ 2. Classify each fact with one or more ontology labels.
42
+ 3. Assign a UUID v4 ``id`` and a UTC ``created_at`` timestamp.
43
+ 4. For singular-cardinality labels, mark existing valid events
44
+ with the same label as superseded.
45
+
46
+ Args:
47
+ entity_key: Unique identifier of the user.
48
+ text: Free text from which to extract events.
49
+
50
+ Returns:
51
+ A list of :class:`StoredEvent` objects with server-assigned metadata.
52
+ """
53
+ ...
54
+
55
+ async def delete_events(
56
+ self,
57
+ entity_key: str,
58
+ event_ids: list[str] | None = None,
59
+ ) -> int:
60
+ """Delete events for a user.
61
+
62
+ If ``event_ids`` is ``None``, deletes ALL events for the user
63
+ (right to erasure). Otherwise, deletes only the specified events.
64
+
65
+ Args:
66
+ entity_key: Unique identifier of the user.
67
+ event_ids: Optional list of specific event IDs to delete.
68
+ If ``None``, all events for the user are deleted.
69
+
70
+ Returns:
71
+ Number of events deleted.
72
+ """
73
+ ...
74
+
75
+ async def import_events(
76
+ self,
77
+ entity_key: str,
78
+ events: list[Event],
79
+ ) -> list[StoredEvent]:
80
+ """Import events for a user.
81
+
82
+ Persists events that were previously exported from another
83
+ UPP-compatible server.
84
+
85
+ Args:
86
+ entity_key: Unique identifier of the user.
87
+ events: Events to import.
88
+
89
+ Returns:
90
+ A list of :class:`StoredEvent` objects with server-assigned metadata.
91
+ """
92
+ ...
93
+
94
+ async def schedule_ingest(
95
+ self,
96
+ entity_key: str,
97
+ text: str,
98
+ ) -> str:
99
+ """Schedule an ingest operation to run in the background.
100
+
101
+ Args:
102
+ entity_key: Unique identifier of the user.
103
+ text: Free text from which to extract events.
104
+
105
+ Returns:
106
+ A task_id that can be used with get_tasks to check status.
107
+ """
108
+ ...
109
+
110
+ async def get_tasks(
111
+ self,
112
+ task_ids: list[str],
113
+ ) -> list[TaskResult]:
114
+ """Check the status of background tasks.
115
+
116
+ Args:
117
+ task_ids: One or more task IDs to check.
118
+
119
+ Returns:
120
+ A list of TaskResult objects with status and results.
121
+ """
122
+ ...
@@ -0,0 +1,41 @@
1
+ """UPP Ontology Backend Protocol.
2
+
3
+ Defines the :class:`OntologyBackend` protocol for accessing ontology
4
+ metadata, label definitions, and server information.
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ from typing import Protocol, runtime_checkable
10
+
11
+ from upp.models.labels import LabelDefinition
12
+
13
+ __all__ = [
14
+ "OntologyBackend",
15
+ ]
16
+
17
+
18
+ @runtime_checkable
19
+ class OntologyBackend(Protocol):
20
+ """Protocol for pluggable ontology backends.
21
+
22
+ An ontology backend provides access to label definitions, the
23
+ ontology identifier, and server information for the ``upp/info``
24
+ operation.
25
+ """
26
+
27
+ def get_labels(self) -> list[LabelDefinition]:
28
+ """Return all label definitions for the ontology.
29
+
30
+ Returns:
31
+ A list of :class:`LabelDefinition` objects.
32
+ """
33
+ ...
34
+
35
+ def get_version(self) -> str:
36
+ """Return the ontology identifier for this server instance.
37
+
38
+ Returns:
39
+ The ontology identifier string (e.g., ``"user/v1"``).
40
+ """
41
+ ...
@@ -0,0 +1,79 @@
1
+ """UPP Retriever Backend Protocol.
2
+
3
+ Defines the :class:`RetrieverBackend` protocol for retrieving relevant
4
+ structured context given a free-text query. The retriever is responsible
5
+ for scoring, ranking, and selecting the most relevant events.
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ from typing import Protocol, runtime_checkable
11
+
12
+ from upp.models.events import StoredEvent
13
+
14
+ __all__ = [
15
+ "RetrieverBackend",
16
+ ]
17
+
18
+
19
+ @runtime_checkable
20
+ class RetrieverBackend(Protocol):
21
+ """Protocol for pluggable intelligent retrieval.
22
+
23
+ A retriever takes a user key and a free-text query, then returns
24
+ the most relevant events ranked by the implementation's scoring
25
+ algorithm.
26
+ """
27
+
28
+ async def retrieve(
29
+ self,
30
+ entity_key: str,
31
+ query: str,
32
+ ) -> list[StoredEvent]:
33
+ """Retrieve relevant events for a user given a query.
34
+
35
+ The retriever MUST interpret the query and return events
36
+ ranked by relevance.
37
+
38
+ Args:
39
+ entity_key: Unique identifier of the user.
40
+ query: Free-text query describing what information is needed.
41
+
42
+ Returns:
43
+ A list of :class:`StoredEvent` objects ranked by relevance.
44
+ """
45
+ ...
46
+
47
+ async def get_events(
48
+ self,
49
+ entity_key: str,
50
+ ) -> list[StoredEvent]:
51
+ """Get all stored events for a user.
52
+
53
+ Returns events of all statuses for transparency.
54
+
55
+ Args:
56
+ entity_key: Unique identifier of the user.
57
+
58
+ Returns:
59
+ All stored events for the user.
60
+ """
61
+ ...
62
+
63
+ async def export_events(
64
+ self,
65
+ entity_key: str,
66
+ ) -> list[StoredEvent]:
67
+ """Export all events for a user.
68
+
69
+ Returns events of all statuses (valid, staged, superseded) to
70
+ provide a complete audit trail suitable for migration between
71
+ vendors.
72
+
73
+ Args:
74
+ entity_key: Unique identifier of the user.
75
+
76
+ Returns:
77
+ All stored events for export.
78
+ """
79
+ ...
upp/client.py ADDED
@@ -0,0 +1,148 @@
1
+ """UPP Client — High-Level Protocol Client.
2
+
3
+ Provides :class:`UPPClient`, the primary entry point for the UPP Python
4
+ reference implementation. The client accepts pluggable backends and
5
+ exposes methods for all ten UPP operations.
6
+
7
+ """
8
+
9
+ from __future__ import annotations
10
+
11
+ import logging
12
+ from importlib.metadata import version as _pkg_version
13
+ from typing import Any
14
+
15
+ from upp.backends.ingest import IngestBackend
16
+ from upp.backends.ontology import OntologyBackend
17
+ from upp.backends.retriever import RetrieverBackend
18
+ from upp.models.client import UPPClientProtocol
19
+ from upp.models.events import ContextualizeResult, Event, StoredEvent, TaskResult
20
+ from upp.models.labels import LabelDefinition
21
+
22
+ __all__ = [
23
+ "UPPClient",
24
+ ]
25
+
26
+ logger = logging.getLogger(__name__)
27
+
28
+
29
+ class UPPClient(UPPClientProtocol):
30
+ """High-level UPP protocol client.
31
+
32
+ Orchestrates backend interfaces to implement all ten UPP
33
+ operations. The client is backend-agnostic — any combination of
34
+ backends satisfying the respective Protocol interfaces can be
35
+ injected.
36
+ """
37
+
38
+ def __init__(
39
+ self,
40
+ *,
41
+ ingest: IngestBackend,
42
+ retriever: RetrieverBackend,
43
+ ontology: OntologyBackend,
44
+ ) -> None:
45
+ """Initialize the UPP client with pluggable backends.
46
+
47
+ Args:
48
+ ingest: Backend for event ingestion.
49
+ retriever: Backend for intelligent retrieval.
50
+ ontology: Backend for ontology access and server metadata.
51
+ """
52
+ self._ingest = ingest
53
+ self._retriever = retriever
54
+ self._ontology = ontology
55
+
56
+ # ── Core (write) ─────────────────────────────────────────────────
57
+
58
+ async def ingest(
59
+ self,
60
+ entity_key: str,
61
+ text: str,
62
+ ) -> list[StoredEvent]:
63
+ """upp/ingest — Extract and ingest events from text."""
64
+ logger.info(f"Ingesting text for entity '{entity_key}'")
65
+ return await self._ingest.ingest(entity_key, text)
66
+
67
+ async def delete_events(
68
+ self,
69
+ entity_key: str,
70
+ event_ids: list[str] | None = None,
71
+ ) -> int:
72
+ """upp/delete_events — Delete events (GDPR/CCPA compliance)."""
73
+ logger.info(f"Deleting events for entity '{entity_key}' with event_ids={event_ids}")
74
+ return await self._ingest.delete_events(entity_key, event_ids)
75
+
76
+ # ── Core (read) ──────────────────────────────────────────────────
77
+
78
+ async def retrieve(
79
+ self,
80
+ entity_key: str,
81
+ query: str,
82
+ ) -> list[StoredEvent]:
83
+ """upp/retrieve — Intelligent search for relevant events."""
84
+ logger.info(f"Retrieving events for entity '{entity_key}'")
85
+ return await self._retriever.retrieve(entity_key, query)
86
+
87
+ async def get_events(
88
+ self,
89
+ entity_key: str,
90
+ ) -> list[StoredEvent]:
91
+ """upp/get_events — Raw listing of stored events."""
92
+ logger.info(f"Getting events for entity '{entity_key}'")
93
+ return await self._retriever.get_events(entity_key)
94
+
95
+ async def contextualize(
96
+ self,
97
+ entity_key: str,
98
+ text: str,
99
+ ) -> ContextualizeResult:
100
+ """upp/contextualize — Retrieve context and ingest in the background."""
101
+ logger.info(f"Contextualizing text for entity '{entity_key}'")
102
+ # Retrieve existing relevant events synchronously
103
+ events = await self._retriever.retrieve(entity_key, text)
104
+ # Schedule background ingest and get task reference
105
+ task_id = await self._ingest.schedule_ingest(entity_key, text)
106
+ return ContextualizeResult(events=events, task_id=task_id)
107
+
108
+ # ── Discovery ────────────────────────────────────────────────────
109
+
110
+ def info(self) -> dict[str, Any]:
111
+ """upp/info — Server metadata and capabilities."""
112
+ logger.info("Fetching server info")
113
+ return {
114
+ "protocol_version": _pkg_version("upp-python"),
115
+ "ontology": self._ontology.get_version(),
116
+ }
117
+
118
+ def get_labels(self) -> list[LabelDefinition]:
119
+ """upp/get_labels — Available labels in the ontology."""
120
+ logger.info("Fetching label definitions from ontology")
121
+ return self._ontology.get_labels()
122
+
123
+ async def get_tasks(
124
+ self,
125
+ task_ids: list[str],
126
+ ) -> list[TaskResult]:
127
+ """upp/get_tasks — Check status of background tasks."""
128
+ logger.info(f"Getting task status for {len(task_ids)} task(s)")
129
+ return await self._ingest.get_tasks(task_ids)
130
+
131
+ # ── Portability ──────────────────────────────────────────────────
132
+
133
+ async def export_events(
134
+ self,
135
+ entity_key: str,
136
+ ) -> list[StoredEvent]:
137
+ """upp/export_events — Export events for migration."""
138
+ logger.info(f"Exporting events for entity '{entity_key}'")
139
+ return await self._retriever.export_events(entity_key)
140
+
141
+ async def import_events(
142
+ self,
143
+ entity_key: str,
144
+ events: list[Event],
145
+ ) -> list[StoredEvent]:
146
+ """upp/import_events — Import events from another server."""
147
+ logger.info(f"Importing {len(events)} events for entity '{entity_key}'")
148
+ return await self._ingest.import_events(entity_key, events)
upp/models/.gitkeep ADDED
File without changes
upp/models/__init__.py ADDED
@@ -0,0 +1,38 @@
1
+ """UPP Data Models.
2
+
3
+ This package contains all data model definitions for the Universal
4
+ Personalization Protocol (UPP) Python reference implementation.
5
+
6
+ Enumerations:
7
+ EventStatus, SourceType, SensitivityTier, Cardinality, Durability
8
+
9
+ Core Entities:
10
+ Event, StoredEvent, LabelDefinition
11
+ """
12
+
13
+ from upp.models.enums import (
14
+ Cardinality,
15
+ Durability,
16
+ EventStatus,
17
+ SensitivityTier,
18
+ SourceType,
19
+ TaskStatus,
20
+ )
21
+ from upp.models.events import ContextualizeResult, Event, StoredEvent, TaskResult
22
+ from upp.models.labels import LabelDefinition
23
+
24
+ __all__ = [
25
+ # Enumerations
26
+ "Cardinality",
27
+ "Durability",
28
+ "EventStatus",
29
+ "SensitivityTier",
30
+ "SourceType",
31
+ "TaskStatus",
32
+ # Core entities
33
+ "ContextualizeResult",
34
+ "Event",
35
+ "LabelDefinition",
36
+ "StoredEvent",
37
+ "TaskResult",
38
+ ]