supermemory-agent 0.2.3__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.
Files changed (54) hide show
  1. storage/adapters/__init__.py +0 -0
  2. storage/adapters/file.py +397 -0
  3. storage/adapters/postgres_qdrant_redis.py +32 -0
  4. storage/adapters/sqlite_chroma.py +95 -0
  5. supermemory_agent-0.2.3.dist-info/METADATA +170 -0
  6. supermemory_agent-0.2.3.dist-info/RECORD +54 -0
  7. supermemory_agent-0.2.3.dist-info/WHEEL +4 -0
  8. supermemory_agent-0.2.3.dist-info/entry_points.txt +2 -0
  9. supermemory_agent-0.2.3.dist-info/licenses/LICENSE +21 -0
  10. supermemory_mcp/__init__.py +5 -0
  11. supermemory_mcp/bridge.py +35 -0
  12. supermemory_mcp/handlers.py +772 -0
  13. supermemory_mcp/server.py +522 -0
  14. supermemory_mcp/text.py +16 -0
  15. uall/__init__.py +0 -0
  16. uall/analytics/service.py +35 -0
  17. uall/collector/__init__.py +0 -0
  18. uall/collector/service.py +100 -0
  19. uall/distillation/distiller.py +80 -0
  20. uall/evaluation/engine.py +38 -0
  21. uall/experiments/manager.py +83 -0
  22. uall/memory/__init__.py +0 -0
  23. uall/memory/confidence.py +36 -0
  24. uall/memory/freshness.py +28 -0
  25. uall/memory/graph.py +24 -0
  26. uall/memory/namespaces.py +40 -0
  27. uall/memory/policies.py +44 -0
  28. uall/memory/provenance.py +23 -0
  29. uall/memory/pruning.py +55 -0
  30. uall/memory/retrieval.py +98 -0
  31. uall/memory/ttl.py +22 -0
  32. uall/memory/validator.py +144 -0
  33. uall/optimization/optimizers.py +59 -0
  34. uall/promotion/queue.py +107 -0
  35. uall/recommendations/engine.py +66 -0
  36. uall/reflection/engine.py +72 -0
  37. uall/rollback/manager.py +49 -0
  38. uall/service.py +572 -0
  39. uall/skills/library.py +19 -0
  40. uall/telemetry/retrieval.py +40 -0
  41. uall_core/__init__.py +0 -0
  42. uall_core/ports/storage.py +71 -0
  43. uall_core/providers/heuristic.py +58 -0
  44. uall_core/providers/llm.py +6 -0
  45. uall_core/schemas/__init__.py +0 -0
  46. uall_core/schemas/common.py +73 -0
  47. uall_core/schemas/events.py +75 -0
  48. uall_core/schemas/graph.py +32 -0
  49. uall_core/schemas/lesson.py +109 -0
  50. uall_core/schemas/namespace.py +76 -0
  51. uall_python/__init__.py +3 -0
  52. uall_python/client.py +337 -0
  53. uall_server/__init__.py +0 -0
  54. uall_server/main.py +284 -0
@@ -0,0 +1,71 @@
1
+ from typing import Any, Protocol
2
+
3
+ from uall_core.schemas.common import (
4
+ Experiment,
5
+ PolicyVersion,
6
+ RetrievalTelemetryEvent,
7
+ Skill,
8
+ VersionRecord,
9
+ )
10
+ from uall_core.schemas.events import Event, Feedback, RunEnd, RunStart
11
+ from uall_core.schemas.lesson import Lesson, MemorySearchRequest, PendingLesson
12
+
13
+
14
+ class StoragePort(Protocol):
15
+ """Pluggable storage interface for all UALL tiers."""
16
+
17
+ async def init(self) -> None: ...
18
+
19
+ # Runs & events
20
+ async def save_run_start(self, run: RunStart) -> None: ...
21
+ async def save_run_end(self, run: RunEnd) -> None: ...
22
+ async def save_event(self, event: Event) -> None: ...
23
+ async def get_event(self, event_id: str) -> dict[str, Any] | None: ...
24
+ async def save_feedback(self, feedback: Feedback) -> str: ...
25
+ async def get_run(self, run_id: str) -> dict[str, Any] | None: ...
26
+ async def list_runs(self) -> list[dict[str, Any]]: ...
27
+
28
+ # Lessons & memory
29
+ async def save_lesson(self, lesson: Lesson) -> str: ...
30
+ async def get_lesson(self, lesson_id: str) -> Lesson | None: ...
31
+ async def list_lessons(self, status: str = "active") -> list[Lesson]: ...
32
+ async def update_lesson(self, lesson: Lesson) -> None: ...
33
+ async def delete_lesson(self, lesson_id: str) -> None: ...
34
+ async def search_lessons(self, request: MemorySearchRequest) -> list[Lesson]: ...
35
+
36
+ # Pending promotion queue
37
+ async def save_pending(self, pending: PendingLesson) -> str: ...
38
+ async def get_pending(self, pending_id: str) -> PendingLesson | None: ...
39
+ async def list_pending(self, status: str = "pending") -> list[PendingLesson]: ...
40
+ async def update_pending(self, pending: PendingLesson) -> None: ...
41
+
42
+ # Policies
43
+ async def save_policy(self, policy: PolicyVersion) -> str: ...
44
+ async def get_active_policies(self) -> list[PolicyVersion]: ...
45
+ async def list_policy_versions(self, policy_id: str) -> list[PolicyVersion]: ...
46
+
47
+ # Skills
48
+ async def save_skill(self, skill: Skill) -> str: ...
49
+ async def get_skill(self, skill_id: str) -> Skill | None: ...
50
+ async def search_skills(self, query: str) -> list[Skill]: ...
51
+
52
+ # Telemetry
53
+ async def save_telemetry(self, event: RetrievalTelemetryEvent) -> str: ...
54
+ async def list_telemetry(self, lesson_id: str | None = None) -> list[RetrievalTelemetryEvent]: ...
55
+
56
+ # Experiments
57
+ async def save_experiment(self, experiment: Experiment) -> str: ...
58
+ async def get_experiment(self, experiment_id: str) -> Experiment | None: ...
59
+ async def update_experiment(self, experiment: Experiment) -> None: ...
60
+
61
+ # Versions (rollback)
62
+ async def save_version(self, record: VersionRecord) -> str: ...
63
+ async def list_versions(self, resource_type: str, resource_id: str) -> list[VersionRecord]: ...
64
+
65
+ # Reflections
66
+ async def save_reflection(self, data: dict[str, Any]) -> str: ...
67
+ async def get_reflection(self, reflection_id: str) -> dict[str, Any] | None: ...
68
+
69
+ # Metrics / analytics raw
70
+ async def save_metrics(self, name: str, data: dict[str, Any]) -> None: ...
71
+ async def get_metrics(self, name: str) -> dict[str, Any] | None: ...
@@ -0,0 +1,58 @@
1
+ """Heuristic LLM provider — zero API keys required for local dev."""
2
+
3
+ import hashlib
4
+ import math
5
+ import re
6
+
7
+
8
+ class HeuristicLLMProvider:
9
+ async def complete(self, prompt: str, max_tokens: int = 500) -> str:
10
+ text = prompt.lower()
11
+ failure = _extract_field(prompt, "failure") or "unknown failure"
12
+ root = _extract_field(prompt, "root cause") or _infer_root(failure)
13
+ fix = _extract_field(prompt, "fix") or _infer_fix(failure, root)
14
+ return f"FAILURE: {failure}\nROOT_CAUSE: {root}\nFIX: {fix}\nCONFIDENCE: 0.85"
15
+
16
+ async def embed(self, text: str) -> list[float]:
17
+ tokens = re.findall(r"\w+", text.lower())
18
+ vec = [0.0] * 128
19
+ for tok in tokens:
20
+ h = int(hashlib.md5(tok.encode()).hexdigest(), 16)
21
+ for i in range(128):
22
+ vec[i] += math.sin(h * (i + 1) * 0.001)
23
+ norm = math.sqrt(sum(x * x for x in vec)) or 1.0
24
+ return [x / norm for x in vec]
25
+
26
+
27
+ def _extract_field(prompt: str, label: str) -> str | None:
28
+ for line in prompt.splitlines():
29
+ if label.lower() in line.lower() and ":" in line:
30
+ return line.split(":", 1)[1].strip()[:200]
31
+ return None
32
+
33
+
34
+ def _infer_root(failure: str) -> str:
35
+ if "ocr" in failure.lower():
36
+ return "Routing logic did not inspect PDF text layer before choosing OCR"
37
+ if "sql" in failure.lower() or "join" in failure.lower():
38
+ return "Schema relationships were not verified before generating SQL"
39
+ return "Insufficient validation before action"
40
+
41
+
42
+ def _infer_fix(failure: str, root: str) -> str:
43
+ if "ocr" in failure.lower() or "pdf" in failure.lower():
44
+ return "Inspect PDF text layer first; use OCR only for scanned documents"
45
+ if "sql" in failure.lower():
46
+ return "Inspect foreign keys and schema before generating joins"
47
+ return f"Add validation step to prevent: {root[:80]}"
48
+
49
+
50
+ def cosine_similarity(a: list[float], b: list[float]) -> float:
51
+ if not a or not b or len(a) != len(b):
52
+ return 0.0
53
+ dot = sum(x * y for x, y in zip(a, b))
54
+ na = math.sqrt(sum(x * x for x in a))
55
+ nb = math.sqrt(sum(y * y for y in b))
56
+ if na == 0 or nb == 0:
57
+ return 0.0
58
+ return dot / (na * nb)
@@ -0,0 +1,6 @@
1
+ from typing import Protocol
2
+
3
+
4
+ class LLMProvider(Protocol):
5
+ async def complete(self, prompt: str, max_tokens: int = 500) -> str: ...
6
+ async def embed(self, text: str) -> list[float]: ...
File without changes
@@ -0,0 +1,73 @@
1
+ from datetime import datetime
2
+ from typing import Any
3
+
4
+ from pydantic import BaseModel, Field
5
+
6
+
7
+ class PolicyVersion(BaseModel):
8
+ policy_id: str
9
+ version: str
10
+ effective_at: datetime = Field(default_factory=datetime.utcnow)
11
+ rules: list[str] = Field(default_factory=list)
12
+ priority: int = 100
13
+
14
+
15
+ class Skill(BaseModel):
16
+ skill_id: str
17
+ name: str
18
+ version: str = "1.0.0"
19
+ description: str = ""
20
+ steps: list[str] = Field(default_factory=list)
21
+ lesson_ids: list[str] = Field(default_factory=list)
22
+ tool_bindings: list[str] = Field(default_factory=list)
23
+ metadata: dict[str, Any] = Field(default_factory=dict)
24
+ created_at: datetime = Field(default_factory=datetime.utcnow)
25
+
26
+
27
+ class ExperimentMetrics(BaseModel):
28
+ success_rate: float = 0.0
29
+ retry_count: float = 0.0
30
+ cost: float = 0.0
31
+ correction_rate: float = 0.0
32
+ latency_p50: float = 0.0
33
+ latency_p95: float = 0.0
34
+ token_usage: float = 0.0
35
+ human_approval_rate: float = 0.0
36
+ rollback_frequency: float = 0.0
37
+ downstream_failure_rate: float = 0.0
38
+ sample_size: int = 0
39
+
40
+
41
+ class Experiment(BaseModel):
42
+ experiment_id: str
43
+ resource_type: str # prompt, workflow
44
+ resource_id: str
45
+ variant_a: str
46
+ variant_b: str
47
+ traffic_split: float = 0.1
48
+ status: str = "running" # running, concluded, rolled_back
49
+ metrics_a: ExperimentMetrics = Field(default_factory=ExperimentMetrics)
50
+ metrics_b: ExperimentMetrics = Field(default_factory=ExperimentMetrics)
51
+ winner: str | None = None
52
+ created_at: datetime = Field(default_factory=datetime.utcnow)
53
+ concluded_at: datetime | None = None
54
+
55
+
56
+ class RetrievalTelemetryEvent(BaseModel):
57
+ telemetry_id: str
58
+ lesson_id: str
59
+ run_id: str | None = None
60
+ retrieved: bool = True
61
+ used: bool = False
62
+ accepted: bool = False
63
+ outcome_improved: bool | None = None
64
+ timestamp: datetime = Field(default_factory=datetime.utcnow)
65
+
66
+
67
+ class VersionRecord(BaseModel):
68
+ resource_type: str
69
+ resource_id: str
70
+ version: str
71
+ content: dict[str, Any] = Field(default_factory=dict)
72
+ created_at: datetime = Field(default_factory=datetime.utcnow)
73
+ promoted: bool = False
@@ -0,0 +1,75 @@
1
+ from datetime import datetime
2
+ from enum import Enum
3
+ from typing import Any
4
+
5
+ from pydantic import BaseModel, Field, field_validator
6
+
7
+ MAX_PAYLOAD_BYTES = 2048
8
+
9
+
10
+ class EventType(str, Enum):
11
+ WORKFLOW_START = "workflow_start"
12
+ WORKFLOW_STEP = "workflow_step"
13
+ WORKFLOW_END = "workflow_end"
14
+ FAILURE = "failure"
15
+ CORRECTION = "correction"
16
+ SUGGESTION = "suggestion"
17
+ TOOL_OUTCOME = "tool_outcome"
18
+
19
+
20
+ class StageMetadata(BaseModel):
21
+ workflow: str | None = None
22
+ step: str | None = None
23
+ tool: str | None = None
24
+ agent: str | None = None
25
+ domain: str | None = None
26
+ language: str | None = None
27
+ environment: str | None = None
28
+ namespace: str | None = None
29
+ namespace_id: str | None = None
30
+
31
+
32
+ class Event(BaseModel):
33
+ event_id: str
34
+ event_type: EventType
35
+ run_id: str
36
+ timestamp: datetime = Field(default_factory=datetime.utcnow)
37
+ stage: StageMetadata = Field(default_factory=StageMetadata)
38
+ tags: list[str] = Field(default_factory=list)
39
+ payload: dict[str, Any] = Field(default_factory=dict)
40
+ metadata: dict[str, Any] = Field(default_factory=dict)
41
+
42
+ @field_validator("payload")
43
+ @classmethod
44
+ def validate_payload_size(cls, v: dict) -> dict:
45
+ import json
46
+
47
+ if len(json.dumps(v, default=str)) > MAX_PAYLOAD_BYTES:
48
+ raise ValueError(f"Payload exceeds {MAX_PAYLOAD_BYTES} bytes")
49
+ return v
50
+
51
+
52
+ class RunStart(BaseModel):
53
+ run_id: str
54
+ workflow_id: str
55
+ agents: list[str] = Field(default_factory=list)
56
+ workflow_graph: dict[str, Any] = Field(default_factory=dict)
57
+ stage: StageMetadata = Field(default_factory=StageMetadata)
58
+
59
+
60
+ class RunEnd(BaseModel):
61
+ run_id: str
62
+ success: bool
63
+ metrics: dict[str, Any] = Field(default_factory=dict)
64
+ lessons_used: list[str] = Field(default_factory=list)
65
+ score: float | None = None
66
+
67
+
68
+ class Feedback(BaseModel):
69
+ run_id: str | None = None
70
+ lesson_id: str | None = None
71
+ rating: str # positive, negative, correction
72
+ comment: str | None = None
73
+ correction_before: str | None = None
74
+ correction_after: str | None = None
75
+ intent: str | None = None
@@ -0,0 +1,32 @@
1
+ from enum import Enum
2
+
3
+ from pydantic import BaseModel, Field
4
+
5
+
6
+ class GraphEdgeType(str, Enum):
7
+ CAUSED_BY = "caused_by"
8
+ FIXED_BY = "fixed_by"
9
+ SUPERSEDES = "supersedes"
10
+ RELATED_TO = "related_to"
11
+ DEPENDS_ON = "depends_on"
12
+ CONFLICTS_WITH = "conflicts_with"
13
+ DERIVED_FROM = "derived_from"
14
+ GENERALIZES = "generalizes"
15
+ SPECIALIZES = "specializes"
16
+
17
+
18
+ class GraphEdge(BaseModel):
19
+ edge_type: GraphEdgeType
20
+ target_id: str
21
+
22
+
23
+ class KnowledgeGraph(BaseModel):
24
+ lesson_id: str
25
+ edges: list[GraphEdge] = Field(default_factory=list)
26
+
27
+ def get_targets(self, edge_type: GraphEdgeType) -> list[str]:
28
+ return [e.target_id for e in self.edges if e.edge_type == edge_type]
29
+
30
+ def add_edge(self, edge_type: GraphEdgeType, target_id: str) -> None:
31
+ if not any(e.edge_type == edge_type and e.target_id == target_id for e in self.edges):
32
+ self.edges.append(GraphEdge(edge_type=edge_type, target_id=target_id))
@@ -0,0 +1,109 @@
1
+ from datetime import datetime
2
+ from typing import Any
3
+
4
+ from pydantic import BaseModel, Field
5
+
6
+ from uall_core.schemas.events import StageMetadata
7
+ from uall_core.schemas.graph import KnowledgeGraph
8
+ from uall_core.schemas.namespace import (
9
+ ConfidenceDimensions,
10
+ FreshnessMetrics,
11
+ MemoryType,
12
+ NamespaceRef,
13
+ Provenance,
14
+ TTLConfig,
15
+ )
16
+
17
+
18
+ class Reflection(BaseModel):
19
+ reflection_id: str
20
+ failure: str
21
+ root_cause: str
22
+ fix: str
23
+ confidence: float = 0.8
24
+ memory_type: MemoryType = MemoryType.FAILURE
25
+ run_id: str | None = None
26
+ event_ids: list[str] = Field(default_factory=list)
27
+ created_at: datetime = Field(default_factory=datetime.utcnow)
28
+
29
+
30
+ class Lesson(BaseModel):
31
+ lesson_id: str
32
+ failure: str = ""
33
+ root_cause: str = ""
34
+ fix: str = ""
35
+ memory_type: MemoryType = MemoryType.FAILURE
36
+ stage: StageMetadata = Field(default_factory=StageMetadata)
37
+ namespace: NamespaceRef = Field(default_factory=NamespaceRef)
38
+ confidence: ConfidenceDimensions = Field(default_factory=ConfidenceDimensions)
39
+ freshness: FreshnessMetrics = Field(default_factory=FreshnessMetrics)
40
+ ttl: TTLConfig = Field(default_factory=TTLConfig)
41
+ provenance: Provenance = Field(default_factory=Provenance)
42
+ graph: KnowledgeGraph | None = None
43
+ occurrence_count: int = 1
44
+ quality_score: float = 0.5
45
+ embedding: list[float] | None = None
46
+ status: str = "active" # active, archived, pending_revalidation
47
+ metadata: dict[str, Any] = Field(default_factory=dict)
48
+
49
+ def to_search_text(self) -> str:
50
+ return f"{self.failure} {self.root_cause} {self.fix}"
51
+
52
+
53
+ class CandidateLesson(BaseModel):
54
+ """Pre-validation lesson from reflection/distillation."""
55
+ reflection_id: str
56
+ failure: str
57
+ root_cause: str
58
+ fix: str
59
+ memory_type: MemoryType = MemoryType.FAILURE
60
+ stage: StageMetadata = Field(default_factory=StageMetadata)
61
+ namespace: NamespaceRef = Field(default_factory=NamespaceRef)
62
+ confidence: float = 0.8
63
+ run_id: str | None = None
64
+ event_ids: list[str] = Field(default_factory=list)
65
+ evidence_payload: dict = Field(default_factory=dict)
66
+
67
+
68
+ class MemorySearchRequest(BaseModel):
69
+ query: str
70
+ workflow: str | None = None
71
+ step: str | None = None
72
+ tool: str | None = None
73
+ agent: str | None = None
74
+ domain: str | None = None
75
+ namespace: str | None = None
76
+ namespace_id: str | None = None
77
+ max_tokens: int = 800
78
+ top_k: int = 5
79
+
80
+
81
+ class MemorySearchResult(BaseModel):
82
+ lesson: Lesson
83
+ score: float
84
+ telemetry_id: str | None = None
85
+
86
+
87
+ class ValidatorAction(str):
88
+ REJECT = "reject"
89
+ MERGE = "merge"
90
+ REWRITE = "rewrite"
91
+ APPROVE = "approve"
92
+
93
+
94
+ class ValidationResult(BaseModel):
95
+ action: str
96
+ candidate: CandidateLesson
97
+ quality_score: float = 0.0
98
+ merge_target_id: str | None = None
99
+ rewritten_fix: str | None = None
100
+ reason: str = ""
101
+
102
+
103
+ class PendingLesson(BaseModel):
104
+ pending_id: str
105
+ candidate: CandidateLesson
106
+ validation_result: ValidationResult
107
+ status: str = "pending" # pending, evaluating, promoted, discarded
108
+ created_at: datetime = Field(default_factory=datetime.utcnow)
109
+ processed_at: datetime | None = None
@@ -0,0 +1,76 @@
1
+ from datetime import datetime
2
+ from enum import Enum
3
+
4
+ from pydantic import BaseModel, Field
5
+
6
+
7
+ class MemoryType(str, Enum):
8
+ EPISODIC = "episodic"
9
+ SEMANTIC = "semantic"
10
+ PROCEDURAL = "procedural"
11
+ USER = "user"
12
+ ORGANIZATIONAL = "organizational"
13
+ FAILURE = "failure"
14
+ TOOL = "tool"
15
+
16
+
17
+ class NamespaceLevel(str, Enum):
18
+ GLOBAL = "global"
19
+ ORGANIZATION = "organization"
20
+ TEAM = "team"
21
+ PROJECT = "project"
22
+ USER = "user"
23
+ SESSION = "session"
24
+
25
+
26
+ NAMESPACE_PRIORITY = [
27
+ NamespaceLevel.ORGANIZATION,
28
+ NamespaceLevel.TEAM,
29
+ NamespaceLevel.PROJECT,
30
+ NamespaceLevel.USER,
31
+ NamespaceLevel.SESSION,
32
+ NamespaceLevel.GLOBAL,
33
+ ]
34
+
35
+
36
+ class NamespaceRef(BaseModel):
37
+ level: NamespaceLevel = NamespaceLevel.PROJECT
38
+ namespace_id: str = "default"
39
+
40
+
41
+ class ConfidenceDimensions(BaseModel):
42
+ evidence: float = 0.5
43
+ retrieval_success: float = 0.5
44
+ human_verified: bool = False
45
+ overall: float = 0.5
46
+
47
+ def recalculate_overall(self) -> float:
48
+ bonus = 1.0 if self.human_verified else 0.0
49
+ self.overall = round(
50
+ 0.4 * self.evidence + 0.4 * self.retrieval_success + 0.2 * bonus, 4
51
+ )
52
+ return self.overall
53
+
54
+
55
+ class TTLConfig(BaseModel):
56
+ expires_at: datetime | None = None
57
+ auto_revalidate_after_days: int | None = None
58
+
59
+
60
+ class FreshnessMetrics(BaseModel):
61
+ created_at: datetime = Field(default_factory=datetime.utcnow)
62
+ last_used: datetime | None = None
63
+ last_confirmed: datetime | None = None
64
+ usage_count: int = 0
65
+ success_after_use: int = 0
66
+ failure_after_use: int = 0
67
+ staleness_score: float = 0.0
68
+
69
+
70
+ class Provenance(BaseModel):
71
+ run_id: str | None = None
72
+ reflection_id: str | None = None
73
+ event_ids: list[str] = Field(default_factory=list)
74
+ policy_version: str | None = None
75
+ validator_action: str | None = None
76
+ promoted_at: datetime | None = None
@@ -0,0 +1,3 @@
1
+ from uall_python.client import UALLClient, RunContext
2
+
3
+ __all__ = ["UALLClient", "RunContext"]