gomemory 0.1.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.
- gomemory-0.1.0/PKG-INFO +10 -0
- gomemory-0.1.0/README.md +0 -0
- gomemory-0.1.0/gomemory/__init__.py +24 -0
- gomemory-0.1.0/gomemory/client.py +102 -0
- gomemory-0.1.0/gomemory/exceptions.py +11 -0
- gomemory-0.1.0/gomemory/models.py +55 -0
- gomemory-0.1.0/gomemory/py.typed +0 -0
- gomemory-0.1.0/gomemory.egg-info/PKG-INFO +10 -0
- gomemory-0.1.0/gomemory.egg-info/SOURCES.txt +13 -0
- gomemory-0.1.0/gomemory.egg-info/dependency_links.txt +1 -0
- gomemory-0.1.0/gomemory.egg-info/requires.txt +2 -0
- gomemory-0.1.0/gomemory.egg-info/top_level.txt +1 -0
- gomemory-0.1.0/pyproject.toml +23 -0
- gomemory-0.1.0/setup.cfg +4 -0
- gomemory-0.1.0/tests/test_client.py +65 -0
gomemory-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: gomemory
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Python SDK for the GoMemory AI memory layer
|
|
5
|
+
Author-email: Your Name <your.email@example.com>
|
|
6
|
+
Project-URL: Repository, https://github.com/yourusername/gomemory
|
|
7
|
+
Requires-Python: >=3.8
|
|
8
|
+
Description-Content-Type: text/markdown
|
|
9
|
+
Requires-Dist: requests>=2.31.0
|
|
10
|
+
Requires-Dist: pydantic>=2.0.0
|
gomemory-0.1.0/README.md
ADDED
|
File without changes
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
from .client import GoMemoryClient
|
|
2
|
+
from .exceptions import GoMemoryError, APIError
|
|
3
|
+
from .models import (
|
|
4
|
+
Role,
|
|
5
|
+
MemoryType,
|
|
6
|
+
Message,
|
|
7
|
+
UserResponse,
|
|
8
|
+
MemoryInsertionResponse,
|
|
9
|
+
Memory,
|
|
10
|
+
FullReqStatus
|
|
11
|
+
)
|
|
12
|
+
|
|
13
|
+
__all__ = [
|
|
14
|
+
"GoMemoryClient",
|
|
15
|
+
"GoMemoryError",
|
|
16
|
+
"APIError",
|
|
17
|
+
"Role",
|
|
18
|
+
"MemoryType",
|
|
19
|
+
"Message",
|
|
20
|
+
"UserResponse",
|
|
21
|
+
"MemoryInsertionResponse",
|
|
22
|
+
"Memory",
|
|
23
|
+
"FullReqStatus"
|
|
24
|
+
]
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
from typing import List, Optional, Union
|
|
2
|
+
import requests
|
|
3
|
+
from pydantic import ValidationError
|
|
4
|
+
|
|
5
|
+
from .exceptions import APIError, GoMemoryError
|
|
6
|
+
from .models import (
|
|
7
|
+
FullReqStatus,
|
|
8
|
+
InsertMemoryRequest,
|
|
9
|
+
Memory,
|
|
10
|
+
MemoryInsertionResponse,
|
|
11
|
+
MemoryRetrievalRequest,
|
|
12
|
+
Message,
|
|
13
|
+
UserResponse
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
class GoMemoryClient:
|
|
17
|
+
def __init__(self, base_url: str):
|
|
18
|
+
self.base_url = base_url.rstrip("/")
|
|
19
|
+
self.session = requests.Session()
|
|
20
|
+
|
|
21
|
+
def _handle_response(self, response: requests.Response):
|
|
22
|
+
"""Internal helper to catch APIError structs and raise them as Python exceptions."""
|
|
23
|
+
try:
|
|
24
|
+
data = response.json()
|
|
25
|
+
except ValueError:
|
|
26
|
+
data = response.text
|
|
27
|
+
|
|
28
|
+
if not response.ok:
|
|
29
|
+
# Check if it matches the APIError struct structure
|
|
30
|
+
if isinstance(data, dict) and "Status" in data and "Message" in data:
|
|
31
|
+
raise APIError(
|
|
32
|
+
status=data.get("Status", response.status_code),
|
|
33
|
+
message=data.get("Message", "Unknown error"),
|
|
34
|
+
error_details=str(data.get("Error", ""))
|
|
35
|
+
)
|
|
36
|
+
response.raise_for_status()
|
|
37
|
+
|
|
38
|
+
return data
|
|
39
|
+
|
|
40
|
+
def health_check(self) -> str:
|
|
41
|
+
"""GET /health"""
|
|
42
|
+
url = f"{self.base_url}/health"
|
|
43
|
+
response = self.session.get(url)
|
|
44
|
+
# Endpoint returns literal string via writeJSON
|
|
45
|
+
return self._handle_response(response)
|
|
46
|
+
|
|
47
|
+
def create_user(self) -> UserResponse:
|
|
48
|
+
"""POST /create/user"""
|
|
49
|
+
url = f"{self.base_url}/create/user"
|
|
50
|
+
response = self.session.post(url)
|
|
51
|
+
data = self._handle_response(response)
|
|
52
|
+
return UserResponse(**data)
|
|
53
|
+
|
|
54
|
+
def add_memory(self, user_id: str, messages: List[Message]) -> MemoryInsertionResponse:
|
|
55
|
+
"""POST /add_memory"""
|
|
56
|
+
url = f"{self.base_url}/add_memory"
|
|
57
|
+
|
|
58
|
+
# Pydantic validation on input
|
|
59
|
+
req_data = InsertMemoryRequest(userId=user_id, messages=messages)
|
|
60
|
+
|
|
61
|
+
response = self.session.post(
|
|
62
|
+
url,
|
|
63
|
+
json=req_data.model_dump(by_alias=True)
|
|
64
|
+
)
|
|
65
|
+
data = self._handle_response(response)
|
|
66
|
+
return MemoryInsertionResponse(**data)
|
|
67
|
+
|
|
68
|
+
def get_memory(
|
|
69
|
+
self,
|
|
70
|
+
user_id: str,
|
|
71
|
+
messages: Optional[List[Message]] = None,
|
|
72
|
+
query: Optional[str] = None,
|
|
73
|
+
threshold: Optional[float] = None
|
|
74
|
+
) -> List[Memory]:
|
|
75
|
+
"""POST /get_memory"""
|
|
76
|
+
url = f"{self.base_url}/get_memory"
|
|
77
|
+
|
|
78
|
+
# Pydantic handles the XOR validation for messages/query here
|
|
79
|
+
req_data = MemoryRetrievalRequest(
|
|
80
|
+
userId=user_id,
|
|
81
|
+
messages=messages,
|
|
82
|
+
query=query,
|
|
83
|
+
threshold=threshold
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
response = self.session.post(
|
|
87
|
+
url,
|
|
88
|
+
json=req_data.model_dump(by_alias=True, exclude_none=True)
|
|
89
|
+
)
|
|
90
|
+
data = self._handle_response(response)
|
|
91
|
+
|
|
92
|
+
# Expected to return a list of memories on success
|
|
93
|
+
if isinstance(data, list):
|
|
94
|
+
return [Memory(**m) for m in data]
|
|
95
|
+
raise GoMemoryError(f"Unexpected response format: {data}")
|
|
96
|
+
|
|
97
|
+
def get_status(self, req_id: str) -> FullReqStatus:
|
|
98
|
+
"""GET /get_status/{id}"""
|
|
99
|
+
url = f"{self.base_url}/get_status/{req_id}"
|
|
100
|
+
response = self.session.get(url)
|
|
101
|
+
data = self._handle_response(response)
|
|
102
|
+
return FullReqStatus(**data)
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
class GoMemoryError(Exception):
|
|
2
|
+
"""Base exception for the GoMemory SDK."""
|
|
3
|
+
pass
|
|
4
|
+
|
|
5
|
+
class APIError(GoMemoryError):
|
|
6
|
+
"""Exception raised when the server returns an APIError struct."""
|
|
7
|
+
def __init__(self, status: int, message: str, error_details: str = ""):
|
|
8
|
+
self.status = status
|
|
9
|
+
self.message = message
|
|
10
|
+
self.error_details = error_details
|
|
11
|
+
super().__init__(f"HTTP {status} - {message} ({error_details})")
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
from enum import Enum
|
|
2
|
+
from typing import List, Optional
|
|
3
|
+
from pydantic import BaseModel, Field, model_validator
|
|
4
|
+
|
|
5
|
+
class Role(str, Enum):
|
|
6
|
+
USER = "user"
|
|
7
|
+
ASSISTANT = "model"
|
|
8
|
+
SYSTEM = "system"
|
|
9
|
+
|
|
10
|
+
class MemoryType(str, Enum):
|
|
11
|
+
# Add actual memory types based on your Go definitions
|
|
12
|
+
CORE = "core"
|
|
13
|
+
GENERAL = "general"
|
|
14
|
+
|
|
15
|
+
class Message(BaseModel):
|
|
16
|
+
role: Role
|
|
17
|
+
content: str
|
|
18
|
+
|
|
19
|
+
class UserResponse(BaseModel):
|
|
20
|
+
user_id: str = Field(alias="userId")
|
|
21
|
+
|
|
22
|
+
class InsertMemoryRequest(BaseModel):
|
|
23
|
+
user_id: str = Field(alias="userId")
|
|
24
|
+
messages: List[Message]
|
|
25
|
+
|
|
26
|
+
class MemoryInsertionResponse(BaseModel):
|
|
27
|
+
req_id: str = Field(alias="ReqId")
|
|
28
|
+
msg: str = Field(alias="Msg")
|
|
29
|
+
|
|
30
|
+
class MemoryRetrievalRequest(BaseModel):
|
|
31
|
+
user_id: str = Field(alias="userId")
|
|
32
|
+
messages: Optional[List[Message]] = None
|
|
33
|
+
query: Optional[str] = None
|
|
34
|
+
threshold: Optional[float] = None
|
|
35
|
+
|
|
36
|
+
@model_validator(mode='after')
|
|
37
|
+
def check_query_or_messages(self):
|
|
38
|
+
# Enforces XOR logic: exactly one must be provided, but not both.
|
|
39
|
+
has_messages = bool(self.messages)
|
|
40
|
+
has_query = bool(self.query)
|
|
41
|
+
|
|
42
|
+
if has_messages == has_query:
|
|
43
|
+
raise ValueError("Exactly one of 'messages' or 'query' must be provided. Cannot provide both or neither.")
|
|
44
|
+
return self
|
|
45
|
+
|
|
46
|
+
class Memory(BaseModel):
|
|
47
|
+
memory_text: str = Field(alias="Memory_text")
|
|
48
|
+
type: str = Field(alias="Type") # Or MemoryType if enum is strictly enforced
|
|
49
|
+
memory_id: str = Field(alias="Memory_Id")
|
|
50
|
+
user_id: str = Field(alias="UserId")
|
|
51
|
+
|
|
52
|
+
class FullReqStatus(BaseModel):
|
|
53
|
+
status: str # Assuming types.ReqStatus resolves to a string. Use Enum if strict.
|
|
54
|
+
error: Optional[str] = None
|
|
55
|
+
created_at: str = Field(alias="createdAt")
|
|
File without changes
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: gomemory
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Python SDK for the GoMemory AI memory layer
|
|
5
|
+
Author-email: Your Name <your.email@example.com>
|
|
6
|
+
Project-URL: Repository, https://github.com/yourusername/gomemory
|
|
7
|
+
Requires-Python: >=3.8
|
|
8
|
+
Description-Content-Type: text/markdown
|
|
9
|
+
Requires-Dist: requests>=2.31.0
|
|
10
|
+
Requires-Dist: pydantic>=2.0.0
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
README.md
|
|
2
|
+
pyproject.toml
|
|
3
|
+
gomemory/__init__.py
|
|
4
|
+
gomemory/client.py
|
|
5
|
+
gomemory/exceptions.py
|
|
6
|
+
gomemory/models.py
|
|
7
|
+
gomemory/py.typed
|
|
8
|
+
gomemory.egg-info/PKG-INFO
|
|
9
|
+
gomemory.egg-info/SOURCES.txt
|
|
10
|
+
gomemory.egg-info/dependency_links.txt
|
|
11
|
+
gomemory.egg-info/requires.txt
|
|
12
|
+
gomemory.egg-info/top_level.txt
|
|
13
|
+
tests/test_client.py
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
gomemory
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=61.0", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "gomemory"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "Python SDK for the GoMemory AI memory layer"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.8"
|
|
11
|
+
authors = [
|
|
12
|
+
{name = "Your Name", email = "your.email@example.com"}
|
|
13
|
+
]
|
|
14
|
+
dependencies = [
|
|
15
|
+
"requests>=2.31.0",
|
|
16
|
+
"pydantic>=2.0.0"
|
|
17
|
+
]
|
|
18
|
+
|
|
19
|
+
[project.urls]
|
|
20
|
+
Repository = "https://github.com/yourusername/gomemory" # Update this to your actual GitHub repo
|
|
21
|
+
|
|
22
|
+
[tool.setuptools.packages.find]
|
|
23
|
+
include = ["gomemory*"]
|
gomemory-0.1.0/setup.cfg
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import pytest
|
|
3
|
+
from gomemory import GoMemoryClient, Role, Message
|
|
4
|
+
from gomemory.exceptions import APIError
|
|
5
|
+
from pydantic import ValidationError
|
|
6
|
+
|
|
7
|
+
# Default to localhost if not set in the environment
|
|
8
|
+
BASE_URL = os.getenv("GOMEMORY_BASE_URL", "http://localhost:9000")
|
|
9
|
+
|
|
10
|
+
@pytest.fixture(scope="module")
|
|
11
|
+
def client():
|
|
12
|
+
return GoMemoryClient(base_url=BASE_URL)
|
|
13
|
+
|
|
14
|
+
def test_health_check(client):
|
|
15
|
+
response = client.health_check()
|
|
16
|
+
assert response == "Server is healthy!"
|
|
17
|
+
|
|
18
|
+
def test_create_user(client):
|
|
19
|
+
response = client.create_user()
|
|
20
|
+
assert response.user_id is not None
|
|
21
|
+
assert isinstance(response.user_id, str)
|
|
22
|
+
|
|
23
|
+
def test_add_memory(client):
|
|
24
|
+
# Setup: need a user first
|
|
25
|
+
user = client.create_user()
|
|
26
|
+
|
|
27
|
+
messages = [
|
|
28
|
+
Message(role=Role.USER, content="Hey man what's up! My name is mario .. my brother's name is Wario!"),
|
|
29
|
+
Message(role=Role.ASSISTANT, content="I will remember that.")
|
|
30
|
+
]
|
|
31
|
+
|
|
32
|
+
response = client.add_memory(user_id=user.user_id, messages=messages)
|
|
33
|
+
assert response.req_id is not None
|
|
34
|
+
assert response.msg is not None
|
|
35
|
+
|
|
36
|
+
def test_get_memory_validation(client):
|
|
37
|
+
# Test the Pydantic XOR validation (should fail before hitting the server)
|
|
38
|
+
with pytest.raises(ValidationError) as exc_info:
|
|
39
|
+
client.get_memory(
|
|
40
|
+
user_id="4bdc091b-eaa5-4d42-bbba-3cd798b9d294",
|
|
41
|
+
)
|
|
42
|
+
assert "Exactly one of 'messages' or 'query' must be provided. Cannot provide both or neither." in str(exc_info.value)
|
|
43
|
+
|
|
44
|
+
def test_get_memory_success(client):
|
|
45
|
+
user = client.create_user()
|
|
46
|
+
|
|
47
|
+
# Optional: Wait for your Go server's worker pool to process embeddings
|
|
48
|
+
# if add_memory is asynchronous before calling get_memory.
|
|
49
|
+
|
|
50
|
+
# Using query
|
|
51
|
+
memories = client.get_memory(
|
|
52
|
+
user_id="4bdc091b-eaa5-4d42-bbba-3cd798b9d294",
|
|
53
|
+
query="I like dogs",
|
|
54
|
+
threshold=0.7
|
|
55
|
+
)
|
|
56
|
+
assert isinstance(memories, list)
|
|
57
|
+
|
|
58
|
+
def test_get_status(client):
|
|
59
|
+
user = client.create_user()
|
|
60
|
+
messages = [Message(role=Role.USER, content="Test memory status")]
|
|
61
|
+
insert_res = client.add_memory(user_id=user.user_id, messages=messages)
|
|
62
|
+
|
|
63
|
+
status_res = client.get_status(req_id=insert_res.req_id)
|
|
64
|
+
assert status_res.status is not None
|
|
65
|
+
assert status_res.created_at is not None
|