synapse-layer 0.7.0__tar.gz
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.
- synapse_layer-0.7.0/.gitignore +45 -0
- synapse_layer-0.7.0/PKG-INFO +40 -0
- synapse_layer-0.7.0/pyproject.toml +145 -0
- synapse_layer-0.7.0/synapse_layer/__init__.py +92 -0
- synapse_layer-0.7.0/synapse_layer/a2a_client.py +337 -0
- synapse_layer-0.7.0/synapse_layer/crewai_tools.py +432 -0
- synapse_layer-0.7.0/synapse_layer/langchain_memory.py +281 -0
- synapse_layer-0.7.0/synapse_layer/plugins/__init__.py +8 -0
- synapse_layer-0.7.0/synapse_layer/plugins/base.py +153 -0
- synapse_layer-0.7.0/synapse_layer/plugins/medical_resolver.py +137 -0
- synapse_layer-0.7.0/synapse_memory.py +815 -0
- synapse_layer-0.7.0/tests/__init__.py +1 -0
- synapse_layer-0.7.0/tests/conftest.py +260 -0
- synapse_layer-0.7.0/tests/test_a2a_client.py +571 -0
- synapse_layer-0.7.0/tests/test_langchain_memory.py +404 -0
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# ── Dependencies ──────────────────────────────────────────────────
|
|
2
|
+
node_modules/
|
|
3
|
+
.pnp.*
|
|
4
|
+
.yarn/
|
|
5
|
+
|
|
6
|
+
# ── Build outputs ─────────────────────────────────────────────────
|
|
7
|
+
dist/
|
|
8
|
+
build/
|
|
9
|
+
*.js.map
|
|
10
|
+
|
|
11
|
+
# ── Environment & Secrets ─────────────────────────────────────────
|
|
12
|
+
.env
|
|
13
|
+
.env.local
|
|
14
|
+
.env.*.local
|
|
15
|
+
|
|
16
|
+
# ── IDE ───────────────────────────────────────────────────────────
|
|
17
|
+
.vscode/
|
|
18
|
+
.idea/
|
|
19
|
+
*.swp
|
|
20
|
+
*.swo
|
|
21
|
+
*~
|
|
22
|
+
|
|
23
|
+
# ── OS ────────────────────────────────────────────────────────────
|
|
24
|
+
.DS_Store
|
|
25
|
+
Thumbs.db
|
|
26
|
+
|
|
27
|
+
# ── Python ────────────────────────────────────────────────────────
|
|
28
|
+
__pycache__/
|
|
29
|
+
*.py[cod]
|
|
30
|
+
*.egg-info/
|
|
31
|
+
.eggs/
|
|
32
|
+
venv/
|
|
33
|
+
.venv/
|
|
34
|
+
*.whl
|
|
35
|
+
|
|
36
|
+
# ── Testing ───────────────────────────────────────────────────────
|
|
37
|
+
coverage/
|
|
38
|
+
.jest/
|
|
39
|
+
|
|
40
|
+
# ── Logs ──────────────────────────────────────────────────────────
|
|
41
|
+
*.log
|
|
42
|
+
npm-debug.log*
|
|
43
|
+
|
|
44
|
+
# ── Supabase ──────────────────────────────────────────────────────
|
|
45
|
+
supabase/.temp/
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: synapse-layer
|
|
3
|
+
Version: 0.7.0
|
|
4
|
+
Summary: Universal memory layer for AI agents - Persistent, Private, Model-agnostic, Open-source
|
|
5
|
+
Project-URL: Homepage, https://synapse-layer.org
|
|
6
|
+
Project-URL: Documentation, https://docs.synapse-layer.org
|
|
7
|
+
Project-URL: Repository, https://github.com/synapse-layer/synapse-layer
|
|
8
|
+
Project-URL: Issues, https://github.com/synapse-layer/synapse-layer/issues
|
|
9
|
+
Author-email: Ismael Marchi <ismael@synapse-layer.org>
|
|
10
|
+
License: Apache-2.0
|
|
11
|
+
Keywords: a2a,agents,ai,crewai,encryption,langchain,memory,persistence
|
|
12
|
+
Classifier: Development Status :: 4 - Beta
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: License :: OSI Approved :: Apache Software License
|
|
15
|
+
Classifier: Natural Language :: English
|
|
16
|
+
Classifier: Programming Language :: Python :: 3
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
21
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
22
|
+
Requires-Python: >=3.9
|
|
23
|
+
Requires-Dist: httpx>=0.27.0
|
|
24
|
+
Requires-Dist: pydantic>=2.0.0
|
|
25
|
+
Provides-Extra: all
|
|
26
|
+
Requires-Dist: crewai>=0.1.0; extra == 'all'
|
|
27
|
+
Requires-Dist: langchain-core>=0.1.0; extra == 'all'
|
|
28
|
+
Provides-Extra: crewai
|
|
29
|
+
Requires-Dist: crewai>=0.1.0; extra == 'crewai'
|
|
30
|
+
Provides-Extra: dev
|
|
31
|
+
Requires-Dist: black>=23.0.0; extra == 'dev'
|
|
32
|
+
Requires-Dist: isort>=5.0.0; extra == 'dev'
|
|
33
|
+
Requires-Dist: mypy>=1.0.0; extra == 'dev'
|
|
34
|
+
Requires-Dist: pytest-asyncio>=0.21.0; extra == 'dev'
|
|
35
|
+
Requires-Dist: pytest-cov>=4.0.0; extra == 'dev'
|
|
36
|
+
Requires-Dist: pytest>=7.0.0; extra == 'dev'
|
|
37
|
+
Requires-Dist: ruff>=0.1.0; extra == 'dev'
|
|
38
|
+
Requires-Dist: sphinx>=6.0.0; extra == 'dev'
|
|
39
|
+
Provides-Extra: langchain
|
|
40
|
+
Requires-Dist: langchain-core>=0.1.0; extra == 'langchain'
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "synapse-layer"
|
|
7
|
+
version = "0.7.0"
|
|
8
|
+
description = "Universal memory layer for AI agents - Persistent, Private, Model-agnostic, Open-source"
|
|
9
|
+
license = {text = "Apache-2.0"}
|
|
10
|
+
authors = [
|
|
11
|
+
{name = "Ismael Marchi", email = "ismael@synapse-layer.org"},
|
|
12
|
+
]
|
|
13
|
+
keywords = [
|
|
14
|
+
"ai",
|
|
15
|
+
"agents",
|
|
16
|
+
"memory",
|
|
17
|
+
"persistence",
|
|
18
|
+
"langchain",
|
|
19
|
+
"crewai",
|
|
20
|
+
"a2a",
|
|
21
|
+
"encryption",
|
|
22
|
+
]
|
|
23
|
+
classifiers = [
|
|
24
|
+
"Development Status :: 4 - Beta",
|
|
25
|
+
"Intended Audience :: Developers",
|
|
26
|
+
"License :: OSI Approved :: Apache Software License",
|
|
27
|
+
"Natural Language :: English",
|
|
28
|
+
"Programming Language :: Python :: 3",
|
|
29
|
+
"Programming Language :: Python :: 3.9",
|
|
30
|
+
"Programming Language :: Python :: 3.10",
|
|
31
|
+
"Programming Language :: Python :: 3.11",
|
|
32
|
+
"Programming Language :: Python :: 3.12",
|
|
33
|
+
"Topic :: Software Development :: Libraries :: Python Modules",
|
|
34
|
+
]
|
|
35
|
+
requires-python = ">=3.9"
|
|
36
|
+
|
|
37
|
+
dependencies = [
|
|
38
|
+
"httpx>=0.27.0",
|
|
39
|
+
"pydantic>=2.0.0",
|
|
40
|
+
]
|
|
41
|
+
|
|
42
|
+
[project.optional-dependencies]
|
|
43
|
+
langchain = [
|
|
44
|
+
"langchain-core>=0.1.0",
|
|
45
|
+
]
|
|
46
|
+
crewai = [
|
|
47
|
+
"crewai>=0.1.0",
|
|
48
|
+
]
|
|
49
|
+
all = [
|
|
50
|
+
"langchain-core>=0.1.0",
|
|
51
|
+
"crewai>=0.1.0",
|
|
52
|
+
]
|
|
53
|
+
dev = [
|
|
54
|
+
"pytest>=7.0.0",
|
|
55
|
+
"pytest-asyncio>=0.21.0",
|
|
56
|
+
"pytest-cov>=4.0.0",
|
|
57
|
+
"ruff>=0.1.0",
|
|
58
|
+
"mypy>=1.0.0",
|
|
59
|
+
"black>=23.0.0",
|
|
60
|
+
"isort>=5.0.0",
|
|
61
|
+
"sphinx>=6.0.0",
|
|
62
|
+
]
|
|
63
|
+
|
|
64
|
+
[project.urls]
|
|
65
|
+
Homepage = "https://synapse-layer.org"
|
|
66
|
+
Documentation = "https://docs.synapse-layer.org"
|
|
67
|
+
Repository = "https://github.com/synapse-layer/synapse-layer"
|
|
68
|
+
Issues = "https://github.com/synapse-layer/synapse-layer/issues"
|
|
69
|
+
|
|
70
|
+
[tool.hatch.build.targets.wheel]
|
|
71
|
+
packages = ["synapse_layer"]
|
|
72
|
+
|
|
73
|
+
[tool.pytest.ini_options]
|
|
74
|
+
testpaths = ["tests"]
|
|
75
|
+
python_files = ["test_*.py"]
|
|
76
|
+
python_classes = ["Test*"]
|
|
77
|
+
python_functions = ["test_*"]
|
|
78
|
+
asyncio_mode = "auto"
|
|
79
|
+
addopts = "-v --tb=short --strict-markers"
|
|
80
|
+
markers = [
|
|
81
|
+
"asyncio: marks tests as async (deselect with '-m \"not asyncio\"')",
|
|
82
|
+
"integration: marks tests as integration tests (deselect with '-m \"not integration\"')",
|
|
83
|
+
"slow: marks tests as slow (deselect with '-m \"not slow\"')",
|
|
84
|
+
]
|
|
85
|
+
|
|
86
|
+
[tool.ruff]
|
|
87
|
+
line-length = 100
|
|
88
|
+
target-version = "py39"
|
|
89
|
+
exclude = [
|
|
90
|
+
".git",
|
|
91
|
+
".venv",
|
|
92
|
+
"build",
|
|
93
|
+
"dist",
|
|
94
|
+
".eggs",
|
|
95
|
+
"__pycache__",
|
|
96
|
+
]
|
|
97
|
+
|
|
98
|
+
[tool.ruff.lint]
|
|
99
|
+
select = [
|
|
100
|
+
"E",
|
|
101
|
+
"F",
|
|
102
|
+
"W",
|
|
103
|
+
"I",
|
|
104
|
+
"N",
|
|
105
|
+
"UP",
|
|
106
|
+
"B",
|
|
107
|
+
"A",
|
|
108
|
+
"C4",
|
|
109
|
+
"SIM",
|
|
110
|
+
]
|
|
111
|
+
ignore = [
|
|
112
|
+
"E501",
|
|
113
|
+
"E203",
|
|
114
|
+
]
|
|
115
|
+
|
|
116
|
+
[tool.mypy]
|
|
117
|
+
python_version = "3.9"
|
|
118
|
+
check_untyped_defs = true
|
|
119
|
+
strict_optional = true
|
|
120
|
+
warn_redundant_casts = true
|
|
121
|
+
warn_unused_configs = true
|
|
122
|
+
warn_unused_ignores = true
|
|
123
|
+
|
|
124
|
+
[tool.isort]
|
|
125
|
+
profile = "black"
|
|
126
|
+
line_length = 100
|
|
127
|
+
skip_gitignore = true
|
|
128
|
+
|
|
129
|
+
[tool.black]
|
|
130
|
+
line-length = 100
|
|
131
|
+
target-version = ["py39"]
|
|
132
|
+
include = '\.pyi?$'
|
|
133
|
+
extend-exclude = '''
|
|
134
|
+
/(
|
|
135
|
+
# directories
|
|
136
|
+
\.eggs
|
|
137
|
+
| \.git
|
|
138
|
+
| \.hg
|
|
139
|
+
| \.mypy_cache
|
|
140
|
+
| \.tox
|
|
141
|
+
| \.venv
|
|
142
|
+
| build
|
|
143
|
+
| dist
|
|
144
|
+
)/
|
|
145
|
+
'''
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Synapse Layer - Universal Memory Layer for AI Agents
|
|
3
|
+
|
|
4
|
+
A persistent, private, model-agnostic, open-source memory solution for AI agents.
|
|
5
|
+
Provides seamless integration with LangChain, CrewAI, and custom Agent-to-Agent (A2A) protocols.
|
|
6
|
+
|
|
7
|
+
Modules:
|
|
8
|
+
a2a_client: JSON-RPC 2.0 client for Agent-to-Agent communication
|
|
9
|
+
langchain_memory: LangChain adapters (SynapseMemory, SynapseChatHistory)
|
|
10
|
+
crewai_tools: CrewAI tools (SynapseStoreMemoryTool, SynapseRecallMemoryTool, SynapseHandoverTool)
|
|
11
|
+
|
|
12
|
+
Key Features:
|
|
13
|
+
- Persistent encrypted memory storage
|
|
14
|
+
- Trust Quotient (TQ) weighting: TQ = (confidence_score * 0.4) + (recency_score * 0.3) + (usage_normalized * 0.3)
|
|
15
|
+
- Semantic search with pgvector HNSW
|
|
16
|
+
- Neural Handover with HMAC-SHA256 signatures
|
|
17
|
+
- GDPR/LGPD compliance (soft-delete with audit trail)
|
|
18
|
+
- Zero-Knowledge Context preservation
|
|
19
|
+
|
|
20
|
+
Author: Ismael Marchi
|
|
21
|
+
License: Apache 2.0
|
|
22
|
+
Version: 0.7.0
|
|
23
|
+
|
|
24
|
+
Example:
|
|
25
|
+
>>> from synapse_layer import SynapseA2AClient
|
|
26
|
+
>>> async with SynapseA2AClient(api_key="your-key") as client:
|
|
27
|
+
... result = await client.store_memory(
|
|
28
|
+
... user_id="user-123",
|
|
29
|
+
... content="Important context",
|
|
30
|
+
... source_type="inference",
|
|
31
|
+
... confidence=0.9
|
|
32
|
+
... )
|
|
33
|
+
... print(result)
|
|
34
|
+
"""
|
|
35
|
+
|
|
36
|
+
__version__ = "0.7.0"
|
|
37
|
+
__author__ = "Ismael Marchi"
|
|
38
|
+
__license__ = "Apache-2.0"
|
|
39
|
+
__all__ = [
|
|
40
|
+
# Core client
|
|
41
|
+
"SynapseA2AClient",
|
|
42
|
+
# Data types
|
|
43
|
+
"TaskResult",
|
|
44
|
+
"TaskState",
|
|
45
|
+
# Constants
|
|
46
|
+
"DEFAULT_BASE_URL",
|
|
47
|
+
"VALID_SKILL_IDS",
|
|
48
|
+
# Version
|
|
49
|
+
"__version__",
|
|
50
|
+
"__author__",
|
|
51
|
+
"__license__",
|
|
52
|
+
]
|
|
53
|
+
|
|
54
|
+
# Import core components
|
|
55
|
+
from .a2a_client import (
|
|
56
|
+
DEFAULT_BASE_URL,
|
|
57
|
+
VALID_SKILL_IDS,
|
|
58
|
+
SynapseA2AClient,
|
|
59
|
+
TaskResult,
|
|
60
|
+
TaskState,
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
# Try to import optional adapters
|
|
64
|
+
try:
|
|
65
|
+
from .langchain_memory import SynapseMemory, SynapseChatHistory
|
|
66
|
+
|
|
67
|
+
__all__.extend(["SynapseMemory", "SynapseChatHistory"])
|
|
68
|
+
except ImportError:
|
|
69
|
+
pass
|
|
70
|
+
|
|
71
|
+
try:
|
|
72
|
+
from .crewai_tools import (
|
|
73
|
+
SynapseHandoverTool,
|
|
74
|
+
SynapseRecallMemoryTool,
|
|
75
|
+
SynapseStoreMemoryTool,
|
|
76
|
+
HandoverInput,
|
|
77
|
+
RecallMemoryInput,
|
|
78
|
+
StoreMemoryInput,
|
|
79
|
+
)
|
|
80
|
+
|
|
81
|
+
__all__.extend(
|
|
82
|
+
[
|
|
83
|
+
"SynapseStoreMemoryTool",
|
|
84
|
+
"SynapseRecallMemoryTool",
|
|
85
|
+
"SynapseHandoverTool",
|
|
86
|
+
"StoreMemoryInput",
|
|
87
|
+
"RecallMemoryInput",
|
|
88
|
+
"HandoverInput",
|
|
89
|
+
]
|
|
90
|
+
)
|
|
91
|
+
except ImportError:
|
|
92
|
+
pass
|
|
@@ -0,0 +1,337 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Synapse Layer A2A Client — JSON-RPC 2.0 over HTTPS
|
|
3
|
+
|
|
4
|
+
Provides async client for Agent-to-Agent (A2A) communication with Synapse Layer MCP server.
|
|
5
|
+
Implements Trust Quotient (TQ) formula for memory confidence evaluation:
|
|
6
|
+
|
|
7
|
+
TQ = (confidence_score * 0.4) + (recency_score * 0.3) + (usage_normalized * 0.3)
|
|
8
|
+
|
|
9
|
+
where:
|
|
10
|
+
- confidence_score: Precision of memory (0.0-1.0)
|
|
11
|
+
- recency_score: How recent the memory is (0.0-1.0, decays over time)
|
|
12
|
+
- usage_normalized: Normalized count of times memory was recalled (0.0-1.0)
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
import asyncio
|
|
16
|
+
import json
|
|
17
|
+
from dataclasses import dataclass
|
|
18
|
+
from enum import Enum
|
|
19
|
+
from typing import Any, Dict, Optional
|
|
20
|
+
import aiohttp
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
# Constants
|
|
24
|
+
DEFAULT_BASE_URL = "https://rbeycxzizrrdmxpilepc.supabase.co/functions/v1/mcp-server"
|
|
25
|
+
VALID_SKILL_IDS = {
|
|
26
|
+
"store_memory",
|
|
27
|
+
"recall_memory",
|
|
28
|
+
"create_handover",
|
|
29
|
+
"resolve_conflict",
|
|
30
|
+
"forget_memory"
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
# Enums
|
|
35
|
+
class TaskState(str, Enum):
|
|
36
|
+
"""State enum for A2A task lifecycle."""
|
|
37
|
+
SUBMITTED = "submitted"
|
|
38
|
+
WORKING = "working"
|
|
39
|
+
COMPLETED = "completed"
|
|
40
|
+
FAILED = "failed"
|
|
41
|
+
CANCELLED = "cancelled"
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
# Dataclasses
|
|
45
|
+
@dataclass
|
|
46
|
+
class TaskResult:
|
|
47
|
+
"""Result of a skill invocation."""
|
|
48
|
+
task_id: str
|
|
49
|
+
status: TaskState
|
|
50
|
+
output: Optional[Dict[str, Any]] = None
|
|
51
|
+
error: Optional[str] = None
|
|
52
|
+
|
|
53
|
+
def __post_init__(self):
|
|
54
|
+
"""Convert status string to TaskState enum if needed."""
|
|
55
|
+
if isinstance(self.status, str):
|
|
56
|
+
self.status = TaskState(self.status)
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
# Main Client Class
|
|
60
|
+
class SynapseA2AClient:
|
|
61
|
+
"""
|
|
62
|
+
Async client for Synapse Layer A2A protocol (JSON-RPC 2.0).
|
|
63
|
+
|
|
64
|
+
Provides methods to interact with Synapse Layer's memory, handover, and
|
|
65
|
+
conflict resolution skills through the MCP server.
|
|
66
|
+
|
|
67
|
+
Trust Quotient (TQ) formula applied to all memory operations:
|
|
68
|
+
TQ = (confidence_score * 0.4) + (recency_score * 0.3) + (usage_normalized * 0.3)
|
|
69
|
+
|
|
70
|
+
Example:
|
|
71
|
+
async with SynapseA2AClient(api_key="...") as client:
|
|
72
|
+
result = await client.store_memory(
|
|
73
|
+
user_id="user-123",
|
|
74
|
+
content="My preferences...",
|
|
75
|
+
source_type="user_input",
|
|
76
|
+
confidence=0.95
|
|
77
|
+
)
|
|
78
|
+
"""
|
|
79
|
+
|
|
80
|
+
def __init__(
|
|
81
|
+
self,
|
|
82
|
+
api_key: str,
|
|
83
|
+
base_url: str = DEFAULT_BASE_URL,
|
|
84
|
+
timeout: int = 30
|
|
85
|
+
):
|
|
86
|
+
"""
|
|
87
|
+
Initialize A2A client.
|
|
88
|
+
|
|
89
|
+
Args:
|
|
90
|
+
api_key: Synapse Layer API key for authentication
|
|
91
|
+
base_url: MCP server base URL (defaults to production Supabase Edge Function)
|
|
92
|
+
timeout: Request timeout in seconds
|
|
93
|
+
"""
|
|
94
|
+
self.api_key = api_key
|
|
95
|
+
self.base_url = base_url.rstrip("/")
|
|
96
|
+
self.timeout = timeout
|
|
97
|
+
self.session: Optional[aiohttp.ClientSession] = None
|
|
98
|
+
self._request_id_counter = 0
|
|
99
|
+
|
|
100
|
+
async def __aenter__(self):
|
|
101
|
+
"""Async context manager entry."""
|
|
102
|
+
self.session = aiohttp.ClientSession()
|
|
103
|
+
return self
|
|
104
|
+
|
|
105
|
+
async def __aexit__(self, exc_type, exc_val, exc_tb):
|
|
106
|
+
"""Async context manager exit."""
|
|
107
|
+
if self.session:
|
|
108
|
+
await self.session.close()
|
|
109
|
+
|
|
110
|
+
def _get_next_request_id(self) -> int:
|
|
111
|
+
"""Get next JSON-RPC request ID."""
|
|
112
|
+
self._request_id_counter += 1
|
|
113
|
+
return self._request_id_counter
|
|
114
|
+
|
|
115
|
+
async def send_task(self, skill_id: str, params: Dict[str, Any]) -> TaskResult:
|
|
116
|
+
"""
|
|
117
|
+
Send a skill invocation task via JSON-RPC 2.0.
|
|
118
|
+
|
|
119
|
+
Core method for all A2A communication. Validates skill_id and formats
|
|
120
|
+
the JSON-RPC request according to MCP spec.
|
|
121
|
+
|
|
122
|
+
Args:
|
|
123
|
+
skill_id: One of VALID_SKILL_IDS
|
|
124
|
+
params: Skill-specific parameters dict
|
|
125
|
+
|
|
126
|
+
Returns:
|
|
127
|
+
TaskResult with task_id, status, output or error
|
|
128
|
+
|
|
129
|
+
Raises:
|
|
130
|
+
ValueError: If skill_id is invalid
|
|
131
|
+
aiohttp.ClientError: If HTTP request fails
|
|
132
|
+
"""
|
|
133
|
+
if skill_id not in VALID_SKILL_IDS:
|
|
134
|
+
raise ValueError(f"Unknown skill: {skill_id}. Valid: {VALID_SKILL_IDS}")
|
|
135
|
+
|
|
136
|
+
if not self.session:
|
|
137
|
+
raise RuntimeError("Client not in async context. Use 'async with' syntax.")
|
|
138
|
+
|
|
139
|
+
# Build JSON-RPC 2.0 request
|
|
140
|
+
request_id = self._get_next_request_id()
|
|
141
|
+
rpc_payload = {
|
|
142
|
+
"jsonrpc": "2.0",
|
|
143
|
+
"method": skill_id,
|
|
144
|
+
"params": params,
|
|
145
|
+
"id": request_id
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
headers = {
|
|
149
|
+
"Authorization": f"Bearer {self.api_key}",
|
|
150
|
+
"Content-Type": "application/json"
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
try:
|
|
154
|
+
async with self.session.post(
|
|
155
|
+
self.base_url,
|
|
156
|
+
json=rpc_payload,
|
|
157
|
+
headers=headers,
|
|
158
|
+
timeout=aiohttp.ClientTimeout(total=self.timeout)
|
|
159
|
+
) as response:
|
|
160
|
+
body = await response.json()
|
|
161
|
+
return self._parse_response(body, request_id)
|
|
162
|
+
except asyncio.TimeoutError:
|
|
163
|
+
return TaskResult(
|
|
164
|
+
task_id=f"req-{request_id}",
|
|
165
|
+
status=TaskState.FAILED,
|
|
166
|
+
error="Request timeout"
|
|
167
|
+
)
|
|
168
|
+
except Exception as e:
|
|
169
|
+
return TaskResult(
|
|
170
|
+
task_id=f"req-{request_id}",
|
|
171
|
+
status=TaskState.FAILED,
|
|
172
|
+
error=str(e)
|
|
173
|
+
)
|
|
174
|
+
|
|
175
|
+
@staticmethod
|
|
176
|
+
def _parse_response(body: Dict[str, Any], request_id: int) -> TaskResult:
|
|
177
|
+
"""
|
|
178
|
+
Parse JSON-RPC 2.0 response.
|
|
179
|
+
|
|
180
|
+
Handles both success (result) and error responses according to JSON-RPC spec.
|
|
181
|
+
|
|
182
|
+
Args:
|
|
183
|
+
body: Response body from server
|
|
184
|
+
request_id: Original request ID for correlation
|
|
185
|
+
|
|
186
|
+
Returns:
|
|
187
|
+
TaskResult with parsed output or error
|
|
188
|
+
"""
|
|
189
|
+
task_id = body.get("id", request_id)
|
|
190
|
+
|
|
191
|
+
if "error" in body:
|
|
192
|
+
error_obj = body["error"]
|
|
193
|
+
error_msg = error_obj.get("message", "Unknown error")
|
|
194
|
+
return TaskResult(
|
|
195
|
+
task_id=str(task_id),
|
|
196
|
+
status=TaskState.FAILED,
|
|
197
|
+
error=error_msg
|
|
198
|
+
)
|
|
199
|
+
|
|
200
|
+
if "result" in body:
|
|
201
|
+
result = body["result"]
|
|
202
|
+
return TaskResult(
|
|
203
|
+
task_id=str(task_id),
|
|
204
|
+
status=TaskState.COMPLETED,
|
|
205
|
+
output=result
|
|
206
|
+
)
|
|
207
|
+
|
|
208
|
+
return TaskResult(
|
|
209
|
+
task_id=str(task_id),
|
|
210
|
+
status=TaskState.FAILED,
|
|
211
|
+
error="Invalid JSON-RPC response"
|
|
212
|
+
)
|
|
213
|
+
|
|
214
|
+
# Skill Convenience Wrappers
|
|
215
|
+
|
|
216
|
+
async def store_memory(
|
|
217
|
+
self,
|
|
218
|
+
user_id: str,
|
|
219
|
+
content: str,
|
|
220
|
+
source_type: str,
|
|
221
|
+
confidence: float
|
|
222
|
+
) -> TaskResult:
|
|
223
|
+
"""
|
|
224
|
+
Store encrypted memory with semantic embedding.
|
|
225
|
+
|
|
226
|
+
TQ formula applied: confidence contributes 40% to final Trust Quotient.
|
|
227
|
+
|
|
228
|
+
Args:
|
|
229
|
+
user_id: UUID of memory owner
|
|
230
|
+
content: Plaintext content to encrypt and store
|
|
231
|
+
source_type: Origin type (user_input, api_response, inference, handover, system)
|
|
232
|
+
confidence: Confidence score (0.0-1.0)
|
|
233
|
+
|
|
234
|
+
Returns:
|
|
235
|
+
TaskResult with memory_id, fact_hash, and tq_score
|
|
236
|
+
"""
|
|
237
|
+
return await self.send_task("store_memory", {
|
|
238
|
+
"user_id": user_id,
|
|
239
|
+
"content": content,
|
|
240
|
+
"source_type": source_type,
|
|
241
|
+
"confidence": confidence
|
|
242
|
+
})
|
|
243
|
+
|
|
244
|
+
async def recall_memory(
|
|
245
|
+
self,
|
|
246
|
+
user_id: str,
|
|
247
|
+
query: str,
|
|
248
|
+
limit: int = 10
|
|
249
|
+
) -> TaskResult:
|
|
250
|
+
"""
|
|
251
|
+
Semantic search over encrypted memories with TQ weighting.
|
|
252
|
+
|
|
253
|
+
Results ordered by Trust Quotient = (confidence * 0.4) + (recency * 0.3) + (usage * 0.3).
|
|
254
|
+
|
|
255
|
+
Args:
|
|
256
|
+
user_id: UUID of agent requesting recall
|
|
257
|
+
query: Natural language query for semantic search
|
|
258
|
+
limit: Max memories to return (default 10, max 100)
|
|
259
|
+
|
|
260
|
+
Returns:
|
|
261
|
+
TaskResult with memories array (memory_id, similarity, tq_score)
|
|
262
|
+
"""
|
|
263
|
+
return await self.send_task("recall_memory", {
|
|
264
|
+
"user_id": user_id,
|
|
265
|
+
"query": query,
|
|
266
|
+
"limit": min(limit, 100)
|
|
267
|
+
})
|
|
268
|
+
|
|
269
|
+
async def create_handover(
|
|
270
|
+
self,
|
|
271
|
+
user_id: str,
|
|
272
|
+
target_model: str,
|
|
273
|
+
summary: str
|
|
274
|
+
) -> TaskResult:
|
|
275
|
+
"""
|
|
276
|
+
Create Neural Handover package for context transfer to another model.
|
|
277
|
+
|
|
278
|
+
Package includes HMAC-SHA256 signature and list of transferred memories.
|
|
279
|
+
|
|
280
|
+
Args:
|
|
281
|
+
user_id: UUID of agent context to transfer
|
|
282
|
+
target_model: Target model identifier (claude-3-opus, gpt-4, gemini-2.5, etc)
|
|
283
|
+
summary: Brief context summary for handover
|
|
284
|
+
|
|
285
|
+
Returns:
|
|
286
|
+
TaskResult with handover_package (Base64), signature, and memories_count
|
|
287
|
+
"""
|
|
288
|
+
return await self.send_task("create_handover", {
|
|
289
|
+
"user_id": user_id,
|
|
290
|
+
"target_model": target_model,
|
|
291
|
+
"summary": summary
|
|
292
|
+
})
|
|
293
|
+
|
|
294
|
+
async def resolve_conflict(
|
|
295
|
+
self,
|
|
296
|
+
user_id: str,
|
|
297
|
+
memory_id: str
|
|
298
|
+
) -> TaskResult:
|
|
299
|
+
"""
|
|
300
|
+
Detect and auto-resolve conflicting memories using Consensus Engine.
|
|
301
|
+
|
|
302
|
+
Uses Trust Quotient to select winner among conflicts:
|
|
303
|
+
TQ = (confidence * 0.4) + (recency * 0.3) + (usage * 0.3)
|
|
304
|
+
|
|
305
|
+
Args:
|
|
306
|
+
user_id: UUID of memory vault
|
|
307
|
+
memory_id: Primary memory ID to check for conflicts
|
|
308
|
+
|
|
309
|
+
Returns:
|
|
310
|
+
TaskResult with conflict_detected, winner_id, winner_tq, resolution_method
|
|
311
|
+
"""
|
|
312
|
+
return await self.send_task("resolve_conflict", {
|
|
313
|
+
"user_id": user_id,
|
|
314
|
+
"memory_id": memory_id
|
|
315
|
+
})
|
|
316
|
+
|
|
317
|
+
async def forget_memory(
|
|
318
|
+
self,
|
|
319
|
+
user_id: str,
|
|
320
|
+
memory_id: str
|
|
321
|
+
) -> TaskResult:
|
|
322
|
+
"""
|
|
323
|
+
Soft-delete memory with GDPR/LGPD compliance.
|
|
324
|
+
|
|
325
|
+
Memory marked inactive but audit trail preserved for compliance.
|
|
326
|
+
|
|
327
|
+
Args:
|
|
328
|
+
user_id: UUID of memory owner
|
|
329
|
+
memory_id: UUID of memory to delete
|
|
330
|
+
|
|
331
|
+
Returns:
|
|
332
|
+
TaskResult with deleted flag, deletion_timestamp, and audit_id
|
|
333
|
+
"""
|
|
334
|
+
return await self.send_task("forget_memory", {
|
|
335
|
+
"user_id": user_id,
|
|
336
|
+
"memory_id": memory_id
|
|
337
|
+
})
|