hyperstack-py 1.0.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.
hyperstack/__init__.py ADDED
@@ -0,0 +1,221 @@
1
+ """
2
+ HyperStack Python SDK
3
+ Cloud memory for AI agents. 3 lines to integrate.
4
+
5
+ Usage:
6
+ from hyperstack import HyperStack
7
+
8
+ hs = HyperStack("hs_your_key")
9
+ hs.store("project-api", "API", "FastAPI on AWS", stack="projects", keywords=["fastapi", "python"])
10
+ results = hs.search("python")
11
+ cards = hs.list()
12
+ hs.delete("project-api")
13
+ stats = hs.stats()
14
+
15
+ Install:
16
+ pip install hyperstack-py
17
+
18
+ Docs: https://cascadeai.dev
19
+ """
20
+
21
+ import json
22
+ import urllib.request
23
+ import urllib.error
24
+ import urllib.parse
25
+ from typing import Optional, List, Dict, Any
26
+
27
+ __version__ = "1.0.0"
28
+
29
+ DEFAULT_API_URL = "https://hyperstack-cloud.vercel.app"
30
+
31
+
32
+ class HyperStackError(Exception):
33
+ """Base exception for HyperStack errors."""
34
+ def __init__(self, message: str, status: int = 0):
35
+ super().__init__(message)
36
+ self.status = status
37
+
38
+
39
+ class HyperStack:
40
+ """
41
+ HyperStack client — cloud memory for AI agents.
42
+
43
+ Args:
44
+ api_key: Your HyperStack API key (starts with hs_)
45
+ workspace: Workspace name (default: "default")
46
+ api_url: API base URL (default: production)
47
+ """
48
+
49
+ def __init__(
50
+ self,
51
+ api_key: str,
52
+ workspace: str = "default",
53
+ api_url: str = DEFAULT_API_URL,
54
+ ):
55
+ if not api_key:
56
+ raise HyperStackError("API key required. Get one free at https://cascadeai.dev")
57
+ self.api_key = api_key
58
+ self.workspace = workspace
59
+ self.api_url = api_url.rstrip("/")
60
+
61
+ def _request(self, endpoint: str, method: str = "GET", body: Optional[dict] = None, params: Optional[dict] = None) -> dict:
62
+ """Make an API request."""
63
+ url = f"{self.api_url}/api/{endpoint}?workspace={urllib.parse.quote(self.workspace)}"
64
+ if params:
65
+ for k, v in params.items():
66
+ url += f"&{urllib.parse.quote(k)}={urllib.parse.quote(str(v))}"
67
+
68
+ headers = {
69
+ "X-API-Key": self.api_key,
70
+ "Content-Type": "application/json",
71
+ }
72
+
73
+ data = json.dumps(body).encode("utf-8") if body else None
74
+ req = urllib.request.Request(url, data=data, headers=headers, method=method)
75
+
76
+ try:
77
+ with urllib.request.urlopen(req) as resp:
78
+ return json.loads(resp.read().decode("utf-8"))
79
+ except urllib.error.HTTPError as e:
80
+ body_text = e.read().decode("utf-8", errors="replace")
81
+ try:
82
+ err_data = json.loads(body_text)
83
+ msg = err_data.get("error", body_text)
84
+ except Exception:
85
+ msg = body_text
86
+ raise HyperStackError(msg, status=e.code)
87
+ except urllib.error.URLError as e:
88
+ raise HyperStackError(f"Connection failed: {e.reason}")
89
+
90
+ def store(
91
+ self,
92
+ slug: str,
93
+ title: str,
94
+ body: str,
95
+ stack: str = "general",
96
+ keywords: Optional[List[str]] = None,
97
+ ) -> dict:
98
+ """
99
+ Store or update a memory card.
100
+
101
+ Args:
102
+ slug: Unique ID (kebab-case, e.g. "project-webapp")
103
+ title: Short title
104
+ body: The knowledge to remember
105
+ stack: Category (projects|people|decisions|preferences|workflows|general)
106
+ keywords: Tags for search
107
+
108
+ Returns:
109
+ Card data dict
110
+ """
111
+ return self._request("cards", method="POST", body={
112
+ "slug": slug,
113
+ "title": title,
114
+ "body": body,
115
+ "stack": stack,
116
+ "keywords": keywords or [],
117
+ })
118
+
119
+ def search(self, query: str) -> List[dict]:
120
+ """
121
+ Search memory cards.
122
+
123
+ Args:
124
+ query: Search term
125
+
126
+ Returns:
127
+ List of matching cards
128
+ """
129
+ result = self._request("search", params={"q": query})
130
+ return result.get("cards", [])
131
+
132
+ def list(self, stack: Optional[str] = None) -> List[dict]:
133
+ """
134
+ List all memory cards.
135
+
136
+ Args:
137
+ stack: Filter by stack (optional)
138
+
139
+ Returns:
140
+ List of all cards
141
+ """
142
+ result = self._request("cards")
143
+ cards = result.get("cards", [])
144
+ if stack:
145
+ cards = [c for c in cards if c.get("stack") == stack]
146
+ return cards
147
+
148
+ def get(self, slug: str) -> Optional[dict]:
149
+ """
150
+ Get a specific card by slug.
151
+
152
+ Args:
153
+ slug: Card slug
154
+
155
+ Returns:
156
+ Card dict or None
157
+ """
158
+ cards = self.list()
159
+ for card in cards:
160
+ if card.get("slug") == slug:
161
+ return card
162
+ return None
163
+
164
+ def delete(self, slug: str) -> dict:
165
+ """
166
+ Delete a memory card.
167
+
168
+ Args:
169
+ slug: Card slug to delete
170
+
171
+ Returns:
172
+ Response dict
173
+ """
174
+ return self._request("cards", method="DELETE", params={"id": slug})
175
+
176
+ def stats(self) -> dict:
177
+ """
178
+ Get memory usage stats.
179
+
180
+ Returns:
181
+ Dict with card count, plan, limit, token stats
182
+ """
183
+ result = self._request("cards")
184
+ cards = result.get("cards", [])
185
+ total_tokens = sum(c.get("tokens", 0) for c in cards)
186
+ without_hs = len(cards) * 6000
187
+
188
+ stacks: Dict[str, int] = {}
189
+ for c in cards:
190
+ s = c.get("stack", "general")
191
+ stacks[s] = stacks.get(s, 0) + 1
192
+
193
+ return {
194
+ "cards": len(cards),
195
+ "plan": result.get("plan", "FREE"),
196
+ "limit": result.get("limit", 50),
197
+ "tokens_stored": total_tokens,
198
+ "tokens_without_hs": without_hs,
199
+ "savings_pct": round((1 - total_tokens / without_hs) * 100) if without_hs > 0 else 0,
200
+ "stacks": stacks,
201
+ }
202
+
203
+ def ingest(self, text: str, source: str = "conversation") -> dict:
204
+ """
205
+ Auto-extract memory cards from raw text.
206
+
207
+ Args:
208
+ text: Raw conversation or document text
209
+ source: Source identifier
210
+
211
+ Returns:
212
+ Dict with extracted count and created cards
213
+ """
214
+ return self._request("ingest", method="POST", body={
215
+ "text": text,
216
+ "workspace": self.workspace,
217
+ "source": source,
218
+ })
219
+
220
+ def __repr__(self) -> str:
221
+ return f"HyperStack(workspace='{self.workspace}', key='...{self.api_key[-6:]}')"
@@ -0,0 +1,95 @@
1
+ Metadata-Version: 2.4
2
+ Name: hyperstack-py
3
+ Version: 1.0.0
4
+ Summary: Cloud memory for AI agents. 3 lines to integrate. No SDK dependencies.
5
+ Home-page: https://cascadeai.dev
6
+ Author: CascadeAI
7
+ Author-email: deeq.yaqub1@gmail.com
8
+ License: MIT
9
+ Project-URL: Documentation, https://cascadeai.dev
10
+ Project-URL: Source, https://github.com/deeqyaqub1-cmd/hyperstack-py
11
+ Project-URL: Discord, https://discord.gg/tdnXaV6e
12
+ Keywords: ai memory agent llm hyperstack mcp claude cursor
13
+ Classifier: Development Status :: 4 - Beta
14
+ Classifier: Intended Audience :: Developers
15
+ Classifier: License :: OSI Approved :: MIT License
16
+ Classifier: Programming Language :: Python :: 3
17
+ Classifier: Topic :: Software Development :: Libraries
18
+ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
19
+ Requires-Python: >=3.7
20
+ Description-Content-Type: text/markdown
21
+ Dynamic: author
22
+ Dynamic: author-email
23
+ Dynamic: classifier
24
+ Dynamic: description
25
+ Dynamic: description-content-type
26
+ Dynamic: home-page
27
+ Dynamic: keywords
28
+ Dynamic: license
29
+ Dynamic: project-url
30
+ Dynamic: requires-python
31
+ Dynamic: summary
32
+
33
+ # 🃏 HyperStack Python SDK
34
+
35
+ **Cloud memory for AI agents. Zero dependencies. 3 lines to integrate.**
36
+
37
+ ## Install
38
+
39
+ ```bash
40
+ pip install hyperstack-py
41
+ ```
42
+
43
+ ## Quick Start
44
+
45
+ ```python
46
+ from hyperstack import HyperStack
47
+
48
+ hs = HyperStack("hs_your_key")
49
+
50
+ # Store a memory
51
+ hs.store("project-api", "API", "FastAPI 3.12 on AWS", stack="projects", keywords=["fastapi", "python"])
52
+
53
+ # Search memories
54
+ results = hs.search("python")
55
+
56
+ # List all cards
57
+ cards = hs.list()
58
+
59
+ # Delete a card
60
+ hs.delete("project-api")
61
+
62
+ # Get usage stats
63
+ stats = hs.stats()
64
+ print(f"Saving {stats['savings_pct']}% on tokens!")
65
+
66
+ # Auto-extract from conversation text
67
+ hs.ingest("Alice is a senior engineer. We decided to use FastAPI over Django.")
68
+ ```
69
+
70
+ ## Why HyperStack?
71
+
72
+ - **Zero dependencies** — just Python stdlib
73
+ - **No LLM costs** — memory ops are free
74
+ - **94% token savings** — ~350 tokens vs ~6,000 per message
75
+ - **30-second setup** — get key at [cascadeai.dev](https://cascadeai.dev)
76
+
77
+ ## API Reference
78
+
79
+ | Method | Description |
80
+ |--------|-------------|
81
+ | `store(slug, title, body, stack, keywords)` | Create/update a card |
82
+ | `search(query)` | Search cards |
83
+ | `list(stack=None)` | List all cards |
84
+ | `get(slug)` | Get one card |
85
+ | `delete(slug)` | Delete a card |
86
+ | `stats()` | Usage summary |
87
+ | `ingest(text)` | Auto-extract memories |
88
+
89
+ ## Get a free key
90
+
91
+ → [cascadeai.dev](https://cascadeai.dev) — 50 cards free, no credit card.
92
+
93
+ ## License
94
+
95
+ MIT © [CascadeAI](https://cascadeai.dev)
@@ -0,0 +1,5 @@
1
+ hyperstack/__init__.py,sha256=HtBk0gfwJEHU3RK2d65kOiYUV5W1pqVXWbATdec4MC8,6324
2
+ hyperstack_py-1.0.0.dist-info/METADATA,sha256=Ty1WnwtxWG_FxNyf7R2CPI7wgGAZ_DoIhkFsQH_MOIo,2631
3
+ hyperstack_py-1.0.0.dist-info/WHEEL,sha256=YCfwYGOYMi5Jhw2fU4yNgwErybb2IX5PEwBKV4ZbdBo,91
4
+ hyperstack_py-1.0.0.dist-info/top_level.txt,sha256=njn3-XmjCMziM6_3QadnDQbqsVh2KYw4J1IysqyY0HI,11
5
+ hyperstack_py-1.0.0.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (82.0.0)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1 @@
1
+ hyperstack