membase-strands 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,5 @@
1
+ .venv/
2
+ dist/
3
+ __pycache__/
4
+ .pytest_cache/
5
+ .ruff_cache/
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Unibase
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,186 @@
1
+ Metadata-Version: 2.5
2
+ Name: membase-strands
3
+ Version: 0.1.0
4
+ Summary: Membase memory for Strands Agents: a MemoryStore with time-boxed recall and deliberate search/save/profile tools.
5
+ Project-URL: Homepage, https://membase.com
6
+ Project-URL: Documentation, https://github.com/unibaseio/membase/tree/main/integrations/strands#readme
7
+ Project-URL: Repository, https://github.com/unibaseio/membase
8
+ Project-URL: Issues, https://github.com/unibaseio/membase/issues
9
+ Author: Unibase
10
+ License-Expression: MIT
11
+ License-File: LICENSE
12
+ Keywords: agents,long-term-memory,membase,memory,strands,strands-agents
13
+ Classifier: Development Status :: 4 - Beta
14
+ Classifier: Intended Audience :: Developers
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Programming Language :: Python :: 3.10
17
+ Classifier: Programming Language :: Python :: 3.11
18
+ Classifier: Programming Language :: Python :: 3.12
19
+ Classifier: Programming Language :: Python :: 3.13
20
+ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
21
+ Requires-Python: >=3.10
22
+ Requires-Dist: strands-agents>=1.45.0
23
+ Requires-Dist: unibaseio-membase>=0.3.2
24
+ Provides-Extra: local
25
+ Requires-Dist: unibaseio-membase[local]>=0.3.2; extra == 'local'
26
+ Description-Content-Type: text/markdown
27
+
28
+ # membase-strands
29
+
30
+ [Membase](https://membase.com) memory for [Strands Agents](https://strandsagents.com) (Python).
31
+
32
+ `MembaseMemoryStore` implements Strands' `MemoryStore` protocol, so it plugs into
33
+ `strands.memory.MemoryManager` like the other memory stores:
34
+
35
+ - **Recall before each user turn.** The manager's context injection calls `search`, and the
36
+ passages it returns are folded into that model call. Each one names the Membase memory
37
+ (container) it came from: `[Project decisions] We chose Postgres for the ledger.`
38
+ - **Time-boxed, fail-open recall.** A Membase search runs inside the account's own memory
39
+ container, and the first one after a quiet spell takes longer while it wakes. The
40
+ automatic recall gets 8 seconds by default (`recall_timeout`). If it runs out, the manager
41
+ logs it and the turn goes ahead without memory. The request keeps running in the
42
+ background, so the memory is awake for the next turn. While it is still running, later
43
+ recalls skip immediately instead of piling up.
44
+ - **Deliberate writes.** `get_tools()` gives the model `membase_search`, `membase_save` and
45
+ `membase_profile`. Saving is a tool the model calls when the user asked it to remember
46
+ something. Saving every turn automatically (`capture=True`) is opt-in.
47
+
48
+ ```bash
49
+ pip install membase-strands
50
+ ```
51
+
52
+ ## Quick start
53
+
54
+ Create a developer key in the Membase app under **Connect › Developer keys**. Use
55
+ *Read & write* if the agent should save, and tick the profile if it should read it.
56
+
57
+ ```python
58
+ from strands import Agent
59
+ from strands.memory import MemoryManager
60
+ from membase_strands import MembaseMemoryStore
61
+
62
+ store = MembaseMemoryStore(api_key="mbk_...") # or set MEMBASE_API_KEY
63
+
64
+ agent = Agent(
65
+ memory_manager=MemoryManager(
66
+ stores=[store],
67
+ search_tool_config=False, # the model gets membase_search instead (see below)
68
+ ),
69
+ )
70
+
71
+ agent("Which database did we pick for the ledger?")
72
+ # Before the model call, the manager searches Membase and injects:
73
+ # <memory>
74
+ # <entry source="membase">[Project decisions] We chose Postgres for the ledger.</entry>
75
+ # </memory>
76
+
77
+ agent("Remember that we moved the launch to May.")
78
+ # The model calls membase_save(content="We moved the launch to May.", container="Project decisions")
79
+ ```
80
+
81
+ ### Why `search_tool_config=False`
82
+
83
+ The manager's built-in `search_memory` tool calls the same time-boxed `search` as injection.
84
+ On a cold start it would give up after 8 seconds and show the model an empty list. The
85
+ store's own `membase_search` tool waits for the SDK's full timeout (90 s), so a cold start
86
+ gets an answer. It also lets the model pick one Memory by name, and it says when a Memory
87
+ could not answer. You can keep both tools if you prefer.
88
+
89
+ ## Tools
90
+
91
+ | Tool | What it does |
92
+ |---|---|
93
+ | `membase_search(query, container?, limit?)` | Searches every Memory in reach, or one Memory by name or id. Each passage is cited as `[Memory name] …`. A Memory that could not answer is listed as such, so it is not mistaken for "no memory". |
94
+ | `membase_save(content, container?, standing_fact?)` | Saves a note in the user's words. `standing_fact=true` records a lasting fact in the user's profile. If the key reaches several Memories and none is named, the tool returns their names so the model can ask which one. |
95
+ | `membase_profile(topic?)` | Reads the user's standing facts and recent changes. This needs the owner to have ticked the profile for the key. |
96
+
97
+ Pick the tools with `tools=("search", "profile")`. For example, drop `"save"` for a Read key.
98
+ There is no delete or forget tool, and the store never confirms a destructive operation for
99
+ the user.
100
+
101
+ ## Options
102
+
103
+ ```python
104
+ MembaseMemoryStore(
105
+ client=None, # a ready membase.Membase client, or:
106
+ api_key=None, # MEMBASE_API_KEY
107
+ base_url=None, # MEMBASE_BASE_URL, default https://api.app.membase.com
108
+ local=None, # a directory for local mode (pip install 'membase-strands[local]')
109
+ container=None, # one Memory (name or id) to search and write to; default: all in reach
110
+ name="membase", # store name in the manager
111
+ max_search_results=None, # passages per search when the caller passes none (default 5)
112
+ recall_timeout=8.0, # budget for the automatic recall; None = SDK timeout
113
+ capture=False, # let the manager write (add_memory tool, extraction)
114
+ extraction=None, # the manager's extraction config; needs capture=True
115
+ capture_roles=("user",), # which messages captured turns keep
116
+ tools=("search", "save", "profile"),
117
+ )
118
+ ```
119
+
120
+ ### Automatic capture (opt-in)
121
+
122
+ By default the store is not `writable`, so the manager never writes to Membase. The only way
123
+ to save is the model calling `membase_save` when the user asks. Membase learns from what it
124
+ is given, and a transcript of every turn would teach it the assistant's own answers as facts.
125
+
126
+ To capture anyway:
127
+
128
+ ```python
129
+ store = MembaseMemoryStore(container="Conversations", capture=True, extraction=True)
130
+ agent = Agent(memory_manager=MemoryManager(stores=[store], search_tool_config=False))
131
+ ```
132
+
133
+ With `extraction=True` the manager hands batches of turns to `add_messages`, by default every
134
+ 5 turns and at the end of each synchronous `agent(...)` call. The store saves the user's
135
+ messages from the batch as one note in `container`, and Membase learns from it in the
136
+ background. `capture_roles=("user", "assistant")` keeps both sides. `capture=True` also lets
137
+ you enable the manager's own `add_memory` tool (`MemoryManager(..., add_tool_config=True)`).
138
+ A retried batch can be saved twice.
139
+
140
+ ## One store per person
141
+
142
+ Membase has no `user_id`. One key, or one local directory, is one person's account. The
143
+ `MemoryStore` protocol does not pass a user or session to `search` either. So a store built
144
+ from a single key suits **a personal agent**. In a multi-user app, build the store and the
145
+ agent per user, from that user's own key or directory, and never route one user's turn
146
+ through another user's key:
147
+
148
+ ```python
149
+ def agent_for(user) -> Agent:
150
+ store = MembaseMemoryStore(api_key=keys.get(user.id)) # that user's key
151
+ # or: MembaseMemoryStore(local=f"/data/membase/{user.id}") # one directory per user
152
+ return Agent(memory_manager=MemoryManager(stores=[store], search_tool_config=False))
153
+ ```
154
+
155
+ ## Errors
156
+
157
+ - **403** means the key's owner narrowed or revoked it. Automatic recall raises
158
+ `MembaseAccessError`; the manager logs it and continues without memory. The tools return
159
+ "this Membase key was narrowed or revoked…" so the model can tell the user. Nothing retries
160
+ with that key.
161
+ - **A Memory that could not answer** (no working model, dormant) is reported. Recall returns
162
+ an entry that says so, and `membase_search` lists it under "Not searched".
163
+ - **A recall that runs out of time** raises `MembaseRecallTimeout` (a `TimeoutError`); the
164
+ manager logs it and continues. The tools use the SDK timeout and, if that runs out too,
165
+ return a "try again in a minute" sentence.
166
+ - Captured writes (`add`, `add_messages`) raise on failure so the manager can report or retry
167
+ them. The tools turn errors into sentences.
168
+
169
+ The key is never logged.
170
+
171
+ ## Compatibility
172
+
173
+ Python 3.10+, `strands-agents>=1.45.0` (the first release with `strands.memory`) and
174
+ `unibaseio-membase>=0.3.2`. The offline tests pass on strands-agents 1.45.0, 1.50.0, 1.55.0
175
+ and 1.61.1.
176
+
177
+ ## Development
178
+
179
+ ```bash
180
+ uv venv && uv pip install -e . pytest pytest-asyncio ruff
181
+ .venv/bin/python -m pytest -q # offline: real Strands Agent, Membase SDK on httpx.MockTransport
182
+ MEMBASE_API_KEY=mbk_... .venv/bin/python examples/live_smoke.py
183
+ uv build
184
+ ```
185
+
186
+ MIT licence.
@@ -0,0 +1,159 @@
1
+ # membase-strands
2
+
3
+ [Membase](https://membase.com) memory for [Strands Agents](https://strandsagents.com) (Python).
4
+
5
+ `MembaseMemoryStore` implements Strands' `MemoryStore` protocol, so it plugs into
6
+ `strands.memory.MemoryManager` like the other memory stores:
7
+
8
+ - **Recall before each user turn.** The manager's context injection calls `search`, and the
9
+ passages it returns are folded into that model call. Each one names the Membase memory
10
+ (container) it came from: `[Project decisions] We chose Postgres for the ledger.`
11
+ - **Time-boxed, fail-open recall.** A Membase search runs inside the account's own memory
12
+ container, and the first one after a quiet spell takes longer while it wakes. The
13
+ automatic recall gets 8 seconds by default (`recall_timeout`). If it runs out, the manager
14
+ logs it and the turn goes ahead without memory. The request keeps running in the
15
+ background, so the memory is awake for the next turn. While it is still running, later
16
+ recalls skip immediately instead of piling up.
17
+ - **Deliberate writes.** `get_tools()` gives the model `membase_search`, `membase_save` and
18
+ `membase_profile`. Saving is a tool the model calls when the user asked it to remember
19
+ something. Saving every turn automatically (`capture=True`) is opt-in.
20
+
21
+ ```bash
22
+ pip install membase-strands
23
+ ```
24
+
25
+ ## Quick start
26
+
27
+ Create a developer key in the Membase app under **Connect › Developer keys**. Use
28
+ *Read & write* if the agent should save, and tick the profile if it should read it.
29
+
30
+ ```python
31
+ from strands import Agent
32
+ from strands.memory import MemoryManager
33
+ from membase_strands import MembaseMemoryStore
34
+
35
+ store = MembaseMemoryStore(api_key="mbk_...") # or set MEMBASE_API_KEY
36
+
37
+ agent = Agent(
38
+ memory_manager=MemoryManager(
39
+ stores=[store],
40
+ search_tool_config=False, # the model gets membase_search instead (see below)
41
+ ),
42
+ )
43
+
44
+ agent("Which database did we pick for the ledger?")
45
+ # Before the model call, the manager searches Membase and injects:
46
+ # <memory>
47
+ # <entry source="membase">[Project decisions] We chose Postgres for the ledger.</entry>
48
+ # </memory>
49
+
50
+ agent("Remember that we moved the launch to May.")
51
+ # The model calls membase_save(content="We moved the launch to May.", container="Project decisions")
52
+ ```
53
+
54
+ ### Why `search_tool_config=False`
55
+
56
+ The manager's built-in `search_memory` tool calls the same time-boxed `search` as injection.
57
+ On a cold start it would give up after 8 seconds and show the model an empty list. The
58
+ store's own `membase_search` tool waits for the SDK's full timeout (90 s), so a cold start
59
+ gets an answer. It also lets the model pick one Memory by name, and it says when a Memory
60
+ could not answer. You can keep both tools if you prefer.
61
+
62
+ ## Tools
63
+
64
+ | Tool | What it does |
65
+ |---|---|
66
+ | `membase_search(query, container?, limit?)` | Searches every Memory in reach, or one Memory by name or id. Each passage is cited as `[Memory name] …`. A Memory that could not answer is listed as such, so it is not mistaken for "no memory". |
67
+ | `membase_save(content, container?, standing_fact?)` | Saves a note in the user's words. `standing_fact=true` records a lasting fact in the user's profile. If the key reaches several Memories and none is named, the tool returns their names so the model can ask which one. |
68
+ | `membase_profile(topic?)` | Reads the user's standing facts and recent changes. This needs the owner to have ticked the profile for the key. |
69
+
70
+ Pick the tools with `tools=("search", "profile")`. For example, drop `"save"` for a Read key.
71
+ There is no delete or forget tool, and the store never confirms a destructive operation for
72
+ the user.
73
+
74
+ ## Options
75
+
76
+ ```python
77
+ MembaseMemoryStore(
78
+ client=None, # a ready membase.Membase client, or:
79
+ api_key=None, # MEMBASE_API_KEY
80
+ base_url=None, # MEMBASE_BASE_URL, default https://api.app.membase.com
81
+ local=None, # a directory for local mode (pip install 'membase-strands[local]')
82
+ container=None, # one Memory (name or id) to search and write to; default: all in reach
83
+ name="membase", # store name in the manager
84
+ max_search_results=None, # passages per search when the caller passes none (default 5)
85
+ recall_timeout=8.0, # budget for the automatic recall; None = SDK timeout
86
+ capture=False, # let the manager write (add_memory tool, extraction)
87
+ extraction=None, # the manager's extraction config; needs capture=True
88
+ capture_roles=("user",), # which messages captured turns keep
89
+ tools=("search", "save", "profile"),
90
+ )
91
+ ```
92
+
93
+ ### Automatic capture (opt-in)
94
+
95
+ By default the store is not `writable`, so the manager never writes to Membase. The only way
96
+ to save is the model calling `membase_save` when the user asks. Membase learns from what it
97
+ is given, and a transcript of every turn would teach it the assistant's own answers as facts.
98
+
99
+ To capture anyway:
100
+
101
+ ```python
102
+ store = MembaseMemoryStore(container="Conversations", capture=True, extraction=True)
103
+ agent = Agent(memory_manager=MemoryManager(stores=[store], search_tool_config=False))
104
+ ```
105
+
106
+ With `extraction=True` the manager hands batches of turns to `add_messages`, by default every
107
+ 5 turns and at the end of each synchronous `agent(...)` call. The store saves the user's
108
+ messages from the batch as one note in `container`, and Membase learns from it in the
109
+ background. `capture_roles=("user", "assistant")` keeps both sides. `capture=True` also lets
110
+ you enable the manager's own `add_memory` tool (`MemoryManager(..., add_tool_config=True)`).
111
+ A retried batch can be saved twice.
112
+
113
+ ## One store per person
114
+
115
+ Membase has no `user_id`. One key, or one local directory, is one person's account. The
116
+ `MemoryStore` protocol does not pass a user or session to `search` either. So a store built
117
+ from a single key suits **a personal agent**. In a multi-user app, build the store and the
118
+ agent per user, from that user's own key or directory, and never route one user's turn
119
+ through another user's key:
120
+
121
+ ```python
122
+ def agent_for(user) -> Agent:
123
+ store = MembaseMemoryStore(api_key=keys.get(user.id)) # that user's key
124
+ # or: MembaseMemoryStore(local=f"/data/membase/{user.id}") # one directory per user
125
+ return Agent(memory_manager=MemoryManager(stores=[store], search_tool_config=False))
126
+ ```
127
+
128
+ ## Errors
129
+
130
+ - **403** means the key's owner narrowed or revoked it. Automatic recall raises
131
+ `MembaseAccessError`; the manager logs it and continues without memory. The tools return
132
+ "this Membase key was narrowed or revoked…" so the model can tell the user. Nothing retries
133
+ with that key.
134
+ - **A Memory that could not answer** (no working model, dormant) is reported. Recall returns
135
+ an entry that says so, and `membase_search` lists it under "Not searched".
136
+ - **A recall that runs out of time** raises `MembaseRecallTimeout` (a `TimeoutError`); the
137
+ manager logs it and continues. The tools use the SDK timeout and, if that runs out too,
138
+ return a "try again in a minute" sentence.
139
+ - Captured writes (`add`, `add_messages`) raise on failure so the manager can report or retry
140
+ them. The tools turn errors into sentences.
141
+
142
+ The key is never logged.
143
+
144
+ ## Compatibility
145
+
146
+ Python 3.10+, `strands-agents>=1.45.0` (the first release with `strands.memory`) and
147
+ `unibaseio-membase>=0.3.2`. The offline tests pass on strands-agents 1.45.0, 1.50.0, 1.55.0
148
+ and 1.61.1.
149
+
150
+ ## Development
151
+
152
+ ```bash
153
+ uv venv && uv pip install -e . pytest pytest-asyncio ruff
154
+ .venv/bin/python -m pytest -q # offline: real Strands Agent, Membase SDK on httpx.MockTransport
155
+ MEMBASE_API_KEY=mbk_... .venv/bin/python examples/live_smoke.py
156
+ uv build
157
+ ```
158
+
159
+ MIT licence.
@@ -0,0 +1,90 @@
1
+ """Live smoke test against the real Membase API.
2
+
3
+ Needs MEMBASE_API_KEY (a developer key from Connect > Developer keys in the Membase app).
4
+ Without a model key it checks the store and its tools directly; with AWS credentials for
5
+ Bedrock (Strands' default model) or OPENAI_API_KEY plus `pip install 'strands-agents[openai]'`,
6
+ it also runs one agent turn so injection is exercised end to end.
7
+
8
+ MEMBASE_API_KEY=mbk_... python examples/live_smoke.py
9
+ MEMBASE_API_KEY=mbk_... MEMBASE_SMOKE_SAVE=1 python examples/live_smoke.py # also saves one note
10
+
11
+ Saving is off unless MEMBASE_SMOKE_SAVE=1, because it writes to a real person's memory.
12
+ """
13
+
14
+ from __future__ import annotations
15
+
16
+ import asyncio
17
+ import logging
18
+ import os
19
+ import sys
20
+ import time
21
+
22
+ from membase_strands import MembaseMemoryStore
23
+
24
+
25
+ def tool(store: MembaseMemoryStore, name: str):
26
+ return next(t for t in store.get_tools() if t.tool_name == name)
27
+
28
+
29
+ async def main() -> int:
30
+ if not os.environ.get("MEMBASE_API_KEY"):
31
+ print("MEMBASE_API_KEY is not set; nothing to do.")
32
+ return 0
33
+ logging.basicConfig(level=logging.INFO, format="%(levelname)s %(name)s: %(message)s")
34
+
35
+ store = MembaseMemoryStore() # MEMBASE_API_KEY / MEMBASE_BASE_URL from the environment
36
+ containers = store.client.containers.list()["containers"]
37
+ print("Memories in reach:", [c["name"] for c in containers])
38
+
39
+ # 1. Explicit search through the tool (SDK timeout, so a cold start is waited out).
40
+ started = time.monotonic()
41
+ out = await tool(store, "membase_search")(query="what do I care about?")
42
+ print(f"\nmembase_search ({time.monotonic() - started:.1f}s):\n{out}")
43
+
44
+ # 2. Automatic recall path: time-boxed; now warm, it should answer within budget.
45
+ started = time.monotonic()
46
+ try:
47
+ entries = await store.search("what do I care about?")
48
+ print(f"\nstore.search ({time.monotonic() - started:.1f}s): {len(entries)} entries")
49
+ for e in entries:
50
+ print(" ", e.content[:160])
51
+ except TimeoutError as error:
52
+ print(f"\nstore.search timed out as designed: {error}")
53
+
54
+ # 3. Profile (403 if the owner did not share the profile with this key).
55
+ print("\nmembase_profile:\n" + await tool(store, "membase_profile")())
56
+
57
+ if os.environ.get("MEMBASE_SMOKE_SAVE") == "1":
58
+ target = containers[0]["name"] if containers else None
59
+ out = await tool(store, "membase_save")(
60
+ content="Membase Strands smoke test: this note can be deleted.", container=target
61
+ )
62
+ print("\nmembase_save:", out)
63
+
64
+ # 4. One real agent turn, if a model is configured.
65
+ model = None
66
+ if os.environ.get("OPENAI_API_KEY"):
67
+ from strands.models.openai import OpenAIModel
68
+
69
+ model = OpenAIModel(model_id=os.environ.get("OPENAI_MODEL", "gpt-4o-mini"))
70
+ elif os.environ.get("AWS_ACCESS_KEY_ID") or os.environ.get("AWS_PROFILE"):
71
+ model = None # Strands' default Bedrock model
72
+ else:
73
+ print("\nNo model credentials; skipping the agent turn.")
74
+ return 0
75
+
76
+ from strands import Agent
77
+ from strands.memory import MemoryManager
78
+
79
+ agent = Agent(
80
+ model=model,
81
+ memory_manager=MemoryManager(stores=[store], search_tool_config=False),
82
+ system_prompt="Answer from the user's memory when it helps, and name the Memory you used.",
83
+ )
84
+ result = await agent.invoke_async("What do you remember about me? One sentence.")
85
+ print("\nAgent:", result)
86
+ return 0
87
+
88
+
89
+ if __name__ == "__main__":
90
+ sys.exit(asyncio.run(main()))
@@ -0,0 +1,64 @@
1
+ [build-system]
2
+ requires = ["hatchling"]
3
+ build-backend = "hatchling.build"
4
+
5
+ [project]
6
+ name = "membase-strands"
7
+ version = "0.1.0"
8
+ description = "Membase memory for Strands Agents: a MemoryStore with time-boxed recall and deliberate search/save/profile tools."
9
+ readme = "README.md"
10
+ requires-python = ">=3.10"
11
+ license = "MIT"
12
+ license-files = ["LICENSE"]
13
+ authors = [{ name = "Unibase" }]
14
+ keywords = ["strands", "strands-agents", "agents", "memory", "membase", "long-term-memory"]
15
+ classifiers = [
16
+ "Development Status :: 4 - Beta",
17
+ "Intended Audience :: Developers",
18
+ "Programming Language :: Python :: 3",
19
+ "Programming Language :: Python :: 3.10",
20
+ "Programming Language :: Python :: 3.11",
21
+ "Programming Language :: Python :: 3.12",
22
+ "Programming Language :: Python :: 3.13",
23
+ "Topic :: Scientific/Engineering :: Artificial Intelligence",
24
+ ]
25
+ # strands-agents>=1.45.0: first release with strands.memory (MemoryStore / MemoryManager,
26
+ # injection, get_tools). The offline tests pass on 1.45.0, 1.50.0, 1.55.0 and 1.61.1.
27
+ dependencies = [
28
+ "strands-agents>=1.45.0",
29
+ "unibaseio-membase>=0.3.2",
30
+ ]
31
+
32
+ [project.optional-dependencies]
33
+ local = ["unibaseio-membase[local]>=0.3.2"]
34
+
35
+ [project.urls]
36
+ Homepage = "https://membase.com"
37
+ Documentation = "https://github.com/unibaseio/membase/tree/main/integrations/strands#readme"
38
+ Repository = "https://github.com/unibaseio/membase"
39
+ Issues = "https://github.com/unibaseio/membase/issues"
40
+
41
+ [dependency-groups]
42
+ dev = [
43
+ "pytest>=8",
44
+ "pytest-asyncio>=0.25",
45
+ "ruff>=0.11",
46
+ ]
47
+
48
+ [tool.hatch.build.targets.wheel]
49
+ packages = ["src/membase_strands"]
50
+
51
+ [tool.hatch.build.targets.sdist]
52
+ include = ["src", "tests", "examples", "README.md", "LICENSE", "pyproject.toml"]
53
+
54
+ [tool.pytest.ini_options]
55
+ testpaths = ["tests"]
56
+ pythonpath = ["src"]
57
+ asyncio_mode = "auto"
58
+
59
+ [tool.ruff]
60
+ line-length = 120
61
+ target-version = "py310"
62
+
63
+ [tool.ruff.lint]
64
+ select = ["E", "F", "I", "B", "UP"]
@@ -0,0 +1,16 @@
1
+ """Membase memory for Strands Agents: a ``MemoryStore`` plus deliberate search/save/profile tools."""
2
+
3
+ from .store import (
4
+ DEFAULT_RECALL_TIMEOUT,
5
+ MembaseAccessError,
6
+ MembaseMemoryStore,
7
+ MembaseRecallTimeout,
8
+ )
9
+
10
+ __all__ = [
11
+ "DEFAULT_RECALL_TIMEOUT",
12
+ "MembaseAccessError",
13
+ "MembaseMemoryStore",
14
+ "MembaseRecallTimeout",
15
+ ]
16
+ __version__ = "0.1.0"
File without changes