cubememory 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.
@@ -0,0 +1,24 @@
1
+ Metadata-Version: 2.4
2
+ Name: cubememory
3
+ Version: 0.1.0
4
+ Summary: Official Python SDK for Cube Memory — semantic search with 100% exact recall
5
+ Author-email: Ronny Guerreiro <ynnor.guerreiro@gmail.com>
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://cubememory.com.br
8
+ Project-URL: Documentation, https://cubememory.com.br/docs
9
+ Project-URL: Repository, https://github.com/cubememory/sdk-python
10
+ Keywords: vector-search,semantic-search,rag,llm-memory,mcp,cube-memory
11
+ Classifier: Development Status :: 4 - Beta
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: Programming Language :: Python :: 3
14
+ Classifier: Programming Language :: Python :: 3.9
15
+ Classifier: Programming Language :: Python :: 3.10
16
+ Classifier: Programming Language :: Python :: 3.11
17
+ Classifier: Programming Language :: Python :: 3.12
18
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
19
+ Requires-Python: >=3.9
20
+ Requires-Dist: requests>=2.28
21
+ Provides-Extra: async
22
+ Requires-Dist: httpx>=0.25; extra == "async"
23
+ Provides-Extra: all
24
+ Requires-Dist: httpx>=0.25; extra == "all"
@@ -0,0 +1,24 @@
1
+ """
2
+ cubememory — Python SDK for Cube Memory
3
+ Semantic search with 100% exact recall · isolated islands · self-hosted
4
+ """
5
+
6
+ from .client import CubeClient, createCubeClient
7
+ from .index import IndexClient, SearchResult, InsertResult
8
+ from .memory import MemoryClient
9
+ from .notes import NotesClient, Note, NoteDetail
10
+ from .exceptions import CubeError
11
+
12
+ __version__ = "0.1.0"
13
+ __all__ = [
14
+ "CubeClient",
15
+ "createCubeClient",
16
+ "IndexClient",
17
+ "MemoryClient",
18
+ "NotesClient",
19
+ "SearchResult",
20
+ "InsertResult",
21
+ "Note",
22
+ "NoteDetail",
23
+ "CubeError",
24
+ ]
@@ -0,0 +1,118 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import Optional
4
+
5
+ from .http import SyncHttp, AsyncHttp
6
+ from .index import IndexClient, AsyncIndexClient
7
+ from .memory import MemoryClient, AsyncMemoryClient
8
+ from .notes import NotesClient, AsyncNotesClient
9
+
10
+
11
+ class CubeClient:
12
+ """Synchronous Cube Memory client.
13
+
14
+ Usage::
15
+
16
+ from cubememory import CubeClient
17
+
18
+ cube = CubeClient(
19
+ base_url="https://cubememory.com.br/gateway",
20
+ api_key="cm_live_xxx",
21
+ project_id="proj_xxx",
22
+ )
23
+
24
+ # Semantic search (100% exact recall)
25
+ results = cube.index("my-docs").search("calçado esportivo", limit=10)
26
+
27
+ # LLM Memory
28
+ cube.memory.store("User prefers dark mode")
29
+ ctx = cube.memory.search("user preferences")
30
+
31
+ # PKM Notes
32
+ cube.notes.upsert("Meeting Notes", "Discussed Q3 roadmap", tags=["meeting"])
33
+ """
34
+
35
+ def __init__(
36
+ self,
37
+ base_url: str,
38
+ api_key: str,
39
+ project_id: str = "",
40
+ timeout: float = 30.0,
41
+ ) -> None:
42
+ self._http = SyncHttp(base_url, api_key, project_id, timeout)
43
+ self.memory = MemoryClient(self._http)
44
+ self.notes = NotesClient(self._http)
45
+
46
+ def index(self, name: str) -> IndexClient:
47
+ return IndexClient(name, self._http)
48
+
49
+ def ping(self) -> bool:
50
+ try:
51
+ self._http.get("/v1/health")
52
+ return True
53
+ except Exception:
54
+ return False
55
+
56
+
57
+ class AsyncCubeClient:
58
+ """Async Cube Memory client (requires httpx).
59
+
60
+ Usage::
61
+
62
+ from cubememory import AsyncCubeClient
63
+
64
+ cube = AsyncCubeClient(
65
+ base_url="https://cubememory.com.br/gateway",
66
+ api_key="cm_live_xxx",
67
+ project_id="proj_xxx",
68
+ )
69
+
70
+ results = await cube.index("my-docs").search("calçado esportivo")
71
+ await cube.memory.store("User prefers dark mode")
72
+ """
73
+
74
+ def __init__(
75
+ self,
76
+ base_url: str,
77
+ api_key: str,
78
+ project_id: str = "",
79
+ timeout: float = 30.0,
80
+ ) -> None:
81
+ self._http = AsyncHttp(base_url, api_key, project_id, timeout)
82
+ self.memory = AsyncMemoryClient(self._http)
83
+ self.notes = AsyncNotesClient(self._http)
84
+
85
+ def index(self, name: str) -> AsyncIndexClient:
86
+ return AsyncIndexClient(name, self._http)
87
+
88
+ async def ping(self) -> bool:
89
+ try:
90
+ await self._http.get("/v1/health")
91
+ return True
92
+ except Exception:
93
+ return False
94
+
95
+
96
+ def createCubeClient(
97
+ base_url: str,
98
+ api_key: str,
99
+ project_id: str = "",
100
+ *,
101
+ async_mode: bool = False,
102
+ timeout: float = 30.0,
103
+ ) -> "CubeClient | AsyncCubeClient":
104
+ """Factory function — similar to Supabase's createClient().
105
+
106
+ Args:
107
+ base_url: Gateway URL, e.g. "https://cubememory.com.br/gateway"
108
+ api_key: Your API key (cm_live_xxx)
109
+ project_id: Your project ID (proj_xxx)
110
+ async_mode: Set True to get AsyncCubeClient (requires httpx)
111
+ timeout: Request timeout in seconds
112
+
113
+ Returns:
114
+ CubeClient (sync) or AsyncCubeClient (async)
115
+ """
116
+ if async_mode:
117
+ return AsyncCubeClient(base_url, api_key, project_id, timeout)
118
+ return CubeClient(base_url, api_key, project_id, timeout)
@@ -0,0 +1,8 @@
1
+ class CubeError(Exception):
2
+ def __init__(self, status: int, message: str) -> None:
3
+ super().__init__(message)
4
+ self.status = status
5
+ self.message = message
6
+
7
+ def __repr__(self) -> str:
8
+ return f"CubeError(status={self.status}, message={self.message!r})"
@@ -0,0 +1,82 @@
1
+ """HTTP client — sync (requests) + async (httpx) backends."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import Any, Dict, Optional
6
+
7
+ from .exceptions import CubeError
8
+
9
+ try:
10
+ import httpx
11
+ _HAS_HTTPX = True
12
+ except ImportError:
13
+ _HAS_HTTPX = False
14
+
15
+ try:
16
+ import requests as _requests
17
+ _HAS_REQUESTS = True
18
+ except ImportError:
19
+ _HAS_REQUESTS = False
20
+
21
+
22
+ class SyncHttp:
23
+ def __init__(self, base_url: str, api_key: str, project_id: str, timeout: float = 30.0) -> None:
24
+ if not _HAS_REQUESTS:
25
+ raise ImportError("Install 'requests' for sync usage: pip install cubememory[sync]")
26
+ self._base = base_url.rstrip("/")
27
+ self._headers = {
28
+ "Authorization": f"Bearer {api_key}",
29
+ "X-Project-Id": project_id,
30
+ "Content-Type": "application/json",
31
+ }
32
+ self._timeout = timeout
33
+
34
+ def get(self, path: str) -> Any:
35
+ r = _requests.get(f"{self._base}{path}", headers=self._headers, timeout=self._timeout)
36
+ if not r.ok:
37
+ raise CubeError(r.status_code, r.text)
38
+ return r.json()
39
+
40
+ def post(self, path: str, body: Dict[str, Any]) -> Any:
41
+ r = _requests.post(f"{self._base}{path}", json=body, headers=self._headers, timeout=self._timeout)
42
+ if not r.ok:
43
+ raise CubeError(r.status_code, r.text)
44
+ return r.json()
45
+
46
+ def delete(self, path: str) -> None:
47
+ r = _requests.delete(f"{self._base}{path}", headers=self._headers, timeout=self._timeout)
48
+ if not r.ok:
49
+ raise CubeError(r.status_code, r.text)
50
+
51
+
52
+ class AsyncHttp:
53
+ def __init__(self, base_url: str, api_key: str, project_id: str, timeout: float = 30.0) -> None:
54
+ if not _HAS_HTTPX:
55
+ raise ImportError("Install 'httpx' for async usage: pip install cubememory[async]")
56
+ self._base = base_url.rstrip("/")
57
+ self._headers = {
58
+ "Authorization": f"Bearer {api_key}",
59
+ "X-Project-Id": project_id,
60
+ "Content-Type": "application/json",
61
+ }
62
+ self._timeout = timeout
63
+
64
+ async def get(self, path: str) -> Any:
65
+ async with httpx.AsyncClient(timeout=self._timeout) as c:
66
+ r = await c.get(f"{self._base}{path}", headers=self._headers)
67
+ if not r.is_success:
68
+ raise CubeError(r.status_code, r.text)
69
+ return r.json()
70
+
71
+ async def post(self, path: str, body: Dict[str, Any]) -> Any:
72
+ async with httpx.AsyncClient(timeout=self._timeout) as c:
73
+ r = await c.post(f"{self._base}{path}", json=body, headers=self._headers)
74
+ if not r.is_success:
75
+ raise CubeError(r.status_code, r.text)
76
+ return r.json()
77
+
78
+ async def delete(self, path: str) -> None:
79
+ async with httpx.AsyncClient(timeout=self._timeout) as c:
80
+ r = await c.delete(f"{self._base}{path}", headers=self._headers)
81
+ if not r.is_success:
82
+ raise CubeError(r.status_code, r.text)
@@ -0,0 +1,122 @@
1
+ from __future__ import annotations
2
+
3
+ from dataclasses import dataclass, field
4
+ from typing import Any, Dict, List, Optional, Union
5
+
6
+ from .http import SyncHttp, AsyncHttp
7
+
8
+
9
+ @dataclass
10
+ class SearchResult:
11
+ id: str
12
+ score: float
13
+ text_preview: str = ""
14
+ metadata: Dict[str, Any] = field(default_factory=dict)
15
+
16
+ @classmethod
17
+ def _from(cls, d: Dict[str, Any]) -> "SearchResult":
18
+ return cls(
19
+ id=d.get("id", ""),
20
+ score=d.get("score", 0.0),
21
+ text_preview=d.get("text_preview", ""),
22
+ metadata=d.get("metadata", {}),
23
+ )
24
+
25
+
26
+ @dataclass
27
+ class InsertResult:
28
+ status: str
29
+ id: str
30
+ docs_total: Optional[int] = None
31
+
32
+ @classmethod
33
+ def _from(cls, d: Dict[str, Any]) -> "InsertResult":
34
+ return cls(status=d.get("status", "ok"), id=d.get("id", ""), docs_total=d.get("docs_total"))
35
+
36
+
37
+ # ─── Sync ─────────────────────────────────────────────────────────────────────
38
+
39
+ class IndexClient:
40
+ def __init__(self, name: str, http: SyncHttp) -> None:
41
+ self._name = name
42
+ self._http = http
43
+
44
+ def insert(self, id: str, text: str, metadata: Optional[Dict[str, Any]] = None) -> InsertResult:
45
+ body: Dict[str, Any] = {"id": id, "text": text}
46
+ if metadata:
47
+ body["metadata"] = metadata
48
+ return InsertResult._from(self._http.post(f"/v1/indexes/{self._name}/insert", body))
49
+
50
+ def insert_many(self, docs: List[Dict[str, Any]]) -> Dict[str, Any]:
51
+ return self._http.post(f"/v1/indexes/{self._name}/batch", {"docs": docs})
52
+
53
+ def search(
54
+ self,
55
+ query: str,
56
+ limit: int = 10,
57
+ threshold: Optional[float] = None,
58
+ filter: Optional[Dict[str, Any]] = None,
59
+ ) -> List[SearchResult]:
60
+ body: Dict[str, Any] = {"query": query, "limit": limit}
61
+ if threshold is not None:
62
+ body["threshold"] = threshold
63
+ if filter:
64
+ body["filter"] = filter
65
+ data = self._http.post(f"/v1/indexes/{self._name}/search", body)
66
+ return [SearchResult._from(r) for r in data.get("results", [])]
67
+
68
+ def get(self, id: str) -> Optional[Dict[str, Any]]:
69
+ try:
70
+ return self._http.get(f"/v1/indexes/{self._name}/docs/{id}")
71
+ except Exception:
72
+ return None
73
+
74
+ def delete(self, id: str) -> None:
75
+ self._http.delete(f"/v1/indexes/{self._name}/docs/{id}")
76
+
77
+ def stats(self) -> Dict[str, Any]:
78
+ return self._http.get(f"/v1/indexes/{self._name}/stats")
79
+
80
+
81
+ # ─── Async ────────────────────────────────────────────────────────────────────
82
+
83
+ class AsyncIndexClient:
84
+ def __init__(self, name: str, http: AsyncHttp) -> None:
85
+ self._name = name
86
+ self._http = http
87
+
88
+ async def insert(self, id: str, text: str, metadata: Optional[Dict[str, Any]] = None) -> InsertResult:
89
+ body: Dict[str, Any] = {"id": id, "text": text}
90
+ if metadata:
91
+ body["metadata"] = metadata
92
+ return InsertResult._from(await self._http.post(f"/v1/indexes/{self._name}/insert", body))
93
+
94
+ async def insert_many(self, docs: List[Dict[str, Any]]) -> Dict[str, Any]:
95
+ return await self._http.post(f"/v1/indexes/{self._name}/batch", {"docs": docs})
96
+
97
+ async def search(
98
+ self,
99
+ query: str,
100
+ limit: int = 10,
101
+ threshold: Optional[float] = None,
102
+ filter: Optional[Dict[str, Any]] = None,
103
+ ) -> List[SearchResult]:
104
+ body: Dict[str, Any] = {"query": query, "limit": limit}
105
+ if threshold is not None:
106
+ body["threshold"] = threshold
107
+ if filter:
108
+ body["filter"] = filter
109
+ data = await self._http.post(f"/v1/indexes/{self._name}/search", body)
110
+ return [SearchResult._from(r) for r in data.get("results", [])]
111
+
112
+ async def get(self, id: str) -> Optional[Dict[str, Any]]:
113
+ try:
114
+ return await self._http.get(f"/v1/indexes/{self._name}/docs/{id}")
115
+ except Exception:
116
+ return None
117
+
118
+ async def delete(self, id: str) -> None:
119
+ await self._http.delete(f"/v1/indexes/{self._name}/docs/{id}")
120
+
121
+ async def stats(self) -> Dict[str, Any]:
122
+ return await self._http.get(f"/v1/indexes/{self._name}/stats")
@@ -0,0 +1,44 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import Any, Dict, List, Literal, Optional
4
+
5
+ from .http import SyncHttp, AsyncHttp
6
+ from .index import SearchResult
7
+
8
+
9
+ class MemoryClient:
10
+ def __init__(self, http: SyncHttp) -> None:
11
+ self._http = http
12
+
13
+ def store(self, text: str, layer: Literal["long-term", "short-term"] = "long-term") -> Dict[str, Any]:
14
+ return self._http.post("/v1/memory", {"text": text, "layer": layer})
15
+
16
+ def search(self, query: str, limit: int = 5) -> List[SearchResult]:
17
+ data = self._http.post("/v1/memory/search", {"query": query, "limit": limit})
18
+ return [SearchResult._from(r) for r in data.get("results", [])]
19
+
20
+ def list(self, limit: int = 20) -> List[Dict[str, Any]]:
21
+ data = self._http.get(f"/v1/memory?limit={limit}")
22
+ return data.get("memories", [])
23
+
24
+ def delete(self, id: str) -> None:
25
+ self._http.delete(f"/v1/memory/{id}")
26
+
27
+
28
+ class AsyncMemoryClient:
29
+ def __init__(self, http: AsyncHttp) -> None:
30
+ self._http = http
31
+
32
+ async def store(self, text: str, layer: Literal["long-term", "short-term"] = "long-term") -> Dict[str, Any]:
33
+ return await self._http.post("/v1/memory", {"text": text, "layer": layer})
34
+
35
+ async def search(self, query: str, limit: int = 5) -> List[SearchResult]:
36
+ data = await self._http.post("/v1/memory/search", {"query": query, "limit": limit})
37
+ return [SearchResult._from(r) for r in data.get("results", [])]
38
+
39
+ async def list(self, limit: int = 20) -> List[Dict[str, Any]]:
40
+ data = await self._http.get(f"/v1/memory?limit={limit}")
41
+ return data.get("memories", [])
42
+
43
+ async def delete(self, id: str) -> None:
44
+ await self._http.delete(f"/v1/memory/{id}")
@@ -0,0 +1,115 @@
1
+ from __future__ import annotations
2
+
3
+ from dataclasses import dataclass, field
4
+ from typing import Any, Dict, List, Optional
5
+
6
+ from .http import SyncHttp, AsyncHttp
7
+
8
+
9
+ @dataclass
10
+ class Note:
11
+ id: str
12
+ title: str
13
+ tags: List[str] = field(default_factory=list)
14
+ text_preview: str = ""
15
+ updated_at: Optional[float] = None
16
+ score: Optional[float] = None
17
+ matched_section: Optional[str] = None
18
+
19
+ @classmethod
20
+ def _from(cls, d: Dict[str, Any]) -> "Note":
21
+ return cls(
22
+ id=d.get("id", ""),
23
+ title=d.get("title", "Untitled"),
24
+ tags=d.get("tags", []),
25
+ text_preview=d.get("text_preview", ""),
26
+ updated_at=d.get("updated_at"),
27
+ score=d.get("score"),
28
+ matched_section=d.get("matched_section"),
29
+ )
30
+
31
+
32
+ @dataclass
33
+ class NoteDetail(Note):
34
+ text: str = ""
35
+ tiptap_json: Optional[str] = None
36
+ related_notes: List[Dict[str, Any]] = field(default_factory=list)
37
+
38
+ @classmethod
39
+ def _from(cls, d: Dict[str, Any]) -> "NoteDetail": # type: ignore[override]
40
+ return cls(
41
+ id=d.get("id", ""),
42
+ title=d.get("title", "Untitled"),
43
+ tags=d.get("tags", []),
44
+ text_preview=d.get("text_preview", ""),
45
+ updated_at=d.get("updated_at"),
46
+ text=d.get("text", ""),
47
+ tiptap_json=d.get("tiptap_json"),
48
+ related_notes=d.get("related_notes", []),
49
+ )
50
+
51
+
52
+ class NotesClient:
53
+ def __init__(self, http: SyncHttp) -> None:
54
+ self._http = http
55
+
56
+ def upsert(
57
+ self,
58
+ title: str,
59
+ text: str,
60
+ id: Optional[str] = None,
61
+ tags: Optional[List[str]] = None,
62
+ ) -> Dict[str, Any]:
63
+ body: Dict[str, Any] = {"title": title, "text": text}
64
+ if id:
65
+ body["id"] = id
66
+ if tags:
67
+ body["tags"] = tags
68
+ return self._http.post("/v1/notes", body)
69
+
70
+ def list(self) -> List[Note]:
71
+ data = self._http.get("/v1/notes")
72
+ return [Note._from(n) for n in data.get("notes", [])]
73
+
74
+ def get(self, id: str) -> NoteDetail:
75
+ return NoteDetail._from(self._http.get(f"/v1/notes/{id}"))
76
+
77
+ def search(self, query: str, limit: int = 10) -> List[Note]:
78
+ data = self._http.post("/v1/notes/search", {"query": query, "limit": limit})
79
+ return [Note._from(n) for n in data.get("results", [])]
80
+
81
+ def delete(self, id: str) -> None:
82
+ self._http.delete(f"/v1/notes/{id}")
83
+
84
+
85
+ class AsyncNotesClient:
86
+ def __init__(self, http: AsyncHttp) -> None:
87
+ self._http = http
88
+
89
+ async def upsert(
90
+ self,
91
+ title: str,
92
+ text: str,
93
+ id: Optional[str] = None,
94
+ tags: Optional[List[str]] = None,
95
+ ) -> Dict[str, Any]:
96
+ body: Dict[str, Any] = {"title": title, "text": text}
97
+ if id:
98
+ body["id"] = id
99
+ if tags:
100
+ body["tags"] = tags
101
+ return await self._http.post("/v1/notes", body)
102
+
103
+ async def list(self) -> List[Note]:
104
+ data = await self._http.get("/v1/notes")
105
+ return [Note._from(n) for n in data.get("notes", [])]
106
+
107
+ async def get(self, id: str) -> NoteDetail:
108
+ return NoteDetail._from(await self._http.get(f"/v1/notes/{id}"))
109
+
110
+ async def search(self, query: str, limit: int = 10) -> List[Note]:
111
+ data = await self._http.post("/v1/notes/search", {"query": query, "limit": limit})
112
+ return [Note._from(n) for n in data.get("results", [])]
113
+
114
+ async def delete(self, id: str) -> None:
115
+ await self._http.delete(f"/v1/notes/{id}")
@@ -0,0 +1,24 @@
1
+ Metadata-Version: 2.4
2
+ Name: cubememory
3
+ Version: 0.1.0
4
+ Summary: Official Python SDK for Cube Memory — semantic search with 100% exact recall
5
+ Author-email: Ronny Guerreiro <ynnor.guerreiro@gmail.com>
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://cubememory.com.br
8
+ Project-URL: Documentation, https://cubememory.com.br/docs
9
+ Project-URL: Repository, https://github.com/cubememory/sdk-python
10
+ Keywords: vector-search,semantic-search,rag,llm-memory,mcp,cube-memory
11
+ Classifier: Development Status :: 4 - Beta
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: Programming Language :: Python :: 3
14
+ Classifier: Programming Language :: Python :: 3.9
15
+ Classifier: Programming Language :: Python :: 3.10
16
+ Classifier: Programming Language :: Python :: 3.11
17
+ Classifier: Programming Language :: Python :: 3.12
18
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
19
+ Requires-Python: >=3.9
20
+ Requires-Dist: requests>=2.28
21
+ Provides-Extra: async
22
+ Requires-Dist: httpx>=0.25; extra == "async"
23
+ Provides-Extra: all
24
+ Requires-Dist: httpx>=0.25; extra == "all"
@@ -0,0 +1,13 @@
1
+ pyproject.toml
2
+ cubememory/__init__.py
3
+ cubememory/client.py
4
+ cubememory/exceptions.py
5
+ cubememory/http.py
6
+ cubememory/index.py
7
+ cubememory/memory.py
8
+ cubememory/notes.py
9
+ cubememory.egg-info/PKG-INFO
10
+ cubememory.egg-info/SOURCES.txt
11
+ cubememory.egg-info/dependency_links.txt
12
+ cubememory.egg-info/requires.txt
13
+ cubememory.egg-info/top_level.txt
@@ -0,0 +1,7 @@
1
+ requests>=2.28
2
+
3
+ [all]
4
+ httpx>=0.25
5
+
6
+ [async]
7
+ httpx>=0.25
@@ -0,0 +1 @@
1
+ cubememory
@@ -0,0 +1,37 @@
1
+ [build-system]
2
+ requires = ["setuptools>=61", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "cubememory"
7
+ version = "0.1.0"
8
+ description = "Official Python SDK for Cube Memory — semantic search with 100% exact recall"
9
+ license = "MIT"
10
+ authors = [{ name = "Ronny Guerreiro", email = "ynnor.guerreiro@gmail.com" }]
11
+ keywords = ["vector-search", "semantic-search", "rag", "llm-memory", "mcp", "cube-memory"]
12
+ classifiers = [
13
+ "Development Status :: 4 - Beta",
14
+ "Intended Audience :: Developers",
15
+ "Programming Language :: Python :: 3",
16
+ "Programming Language :: Python :: 3.9",
17
+ "Programming Language :: Python :: 3.10",
18
+ "Programming Language :: Python :: 3.11",
19
+ "Programming Language :: Python :: 3.12",
20
+ "Topic :: Software Development :: Libraries :: Python Modules",
21
+ ]
22
+ requires-python = ">=3.9"
23
+ dependencies = [
24
+ "requests>=2.28",
25
+ ]
26
+
27
+ [project.optional-dependencies]
28
+ async = ["httpx>=0.25"]
29
+ all = ["httpx>=0.25"]
30
+
31
+ [project.urls]
32
+ Homepage = "https://cubememory.com.br"
33
+ Documentation = "https://cubememory.com.br/docs"
34
+ Repository = "https://github.com/cubememory/sdk-python"
35
+
36
+ [tool.hatch.build.targets.wheel]
37
+ packages = ["cubememory"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+