goodmem-agent-framework
GoodMem integration for the Microsoft Agent Framework.
This package gives Agent Framework agents persistent, semantic long-term memory backed by a GoodMem server. It exposes:
GoodMemClient— an async REST client for the GoodMem v1 API.GoodMemContextProvider— aBaseContextProviderthat automatically retrieves relevant memories before each agent run and stores conversations afterwards.create_goodmem_tools— a factory that returns ready-to-use function tools so the model itself can manage spaces and memories.
Installation
pip install goodmem-agent-framework
For local development:
pip install -e .
Quickstart
import asyncio
from goodmem_agent_framework import GoodMemClient, create_goodmem_tools
async def main():
client = GoodMemClient(
base_url="https://localhost:8080",
api_key="gm_xxxxxxxxxxxxxxxxxxxxxxxx",
verify_ssl=False, # self-signed local server
)
embedders = await client.list_embedders()
embedder_id = embedders[0]["embedderId"]
space = await client.create_space(name="quickstart", embedder_id=embedder_id)
space_id = space["spaceId"]
await client.create_memory(
space_id=space_id,
text_content="The capital of France is Paris.",
)
results = await client.retrieve_memories(
query="What is the capital of France?",
space_ids=[space_id],
max_results=3,
wait_for_indexing=True,
)
print(results)
await client.close()
asyncio.run(main())
Available tools
create_goodmem_tools(client) returns the following 11 function tools:
| Tool | Description |
|---|---|
goodmem_list_embedders |
List embedder models available on the server |
goodmem_list_spaces |
List all spaces accessible to the API key |
goodmem_get_space |
Fetch a space by ID |
goodmem_create_space |
Create a space, or reuse a same-name space that uses the same embedder |
goodmem_update_space |
Update a space's name/labels/visibility |
goodmem_delete_space |
Delete a space |
goodmem_create_memory |
Store text or a file as a memory |
goodmem_list_memories |
List memories in a space |
goodmem_retrieve_memories |
Semantic retrieval, with optional reranker/LLM |
goodmem_get_memory |
Fetch a memory by ID (with original content) |
goodmem_delete_memory |
Delete a memory |
Retrieval options
goodmem_retrieve_memories (and GoodMemClient.retrieve_memories) accept the
GoodMem post-processor parameters:
| Parameter | Type | Description |
|---|---|---|
reranker_id |
UUID | Reranker model to improve result ordering |
llm_id |
UUID | LLM used to generate a contextual abstract reply |
relevance_threshold |
0–1 | Minimum score for including a result |
llm_temperature |
0–2 | Creativity for the LLM post-processor |
max_results |
int | Cap on returned chunks |
chronological_resort |
bool | Reorder results by memory creation time |
Retrieval results and server statuses
retrieve_memories returns a dict (the tool returns the same dict as JSON):
| Key | Description |
|---|---|
success |
true unless the request itself failed (see Errors) |
results |
Matching chunks: chunkId, chunkText, memoryId, relevanceScore, memoryIndex |
memories |
Memory definitions (when include_memory_definition is true) |
totalResults |
Number of entries in results |
resultSetId |
The server's result set ID |
abstractReply |
The LLM's reply, when an llm_id was given and it succeeded |
partial |
true when the server reported a problem during this retrieval |
statuses |
The problems the server reported, in stream order; empty when partial is false |
message |
A readable summary when partial is true, or when wait_for_indexing gave up after 60 seconds |
Each entry in statuses is {"code", "message", "details"}, exactly as the
server sent it. These rules decide what counts as a problem:
FEATURE_DISABLEDandLLM_CAPABILITY_INFERREDare informational (an optional feature you did not configure). They are left out ofstatusesand never setpartial.- A code this package does not recognize is reported with
code: "UNKNOWN"and the server's code inoriginalCode, and setspartial. It is never dropped and never raises. - A problem with hits (for example a nonexistent
reranker_id): the hits are returned, withpartial: trueand the statuses. - A problem with no hits: an empty
results, withpartial: trueand the statuses. Nothing is raised.
With wait_for_indexing=True, polling stops as soon as the server reports a
problem, so a nonexistent reranker or LLM is reported at once instead of after
60 seconds. When the reranker fails (RERANKING_FAILED, or NOT_FOUND naming
the reranker) the server still returns the vector search's hits: their
relevanceScore values are vector-search scores, not reranker scores, and
message says so.
GoodMemContextProvider logs a WARNING with the statuses when a retrieval is
partial, and still uses any chunks that came back.
Space reuse
create_space (and goodmem_create_space) looks for a space with exactly the
requested name, across every page of the space listing:
- No such space: a new one is created (
reused: false). - One space, same embedder: it is reused (
reused: true).embedderIdandembedderIdsreport the space's real embedder (embedderIdisnullfor a space with several), andchunkingConfigits real chunking configuration (which may differ from the one requested). - One space, different embedder: nothing is created. The result has
success: false, anerrornaming the space, its ID and both embedders, plusexistingSpaceId,existingEmbedderIdsandrequestedEmbedderId. - Several spaces with that name: nothing is created. The result has
success: false, anerrorlisting each space and its embedders, andexistingSpaceIds.
If no embedder_id is given, an existing space with that name is reused
whatever its embedder, and a new space uses the server's first embedder.
Errors
Tools never raise: a failure is returned as {"success": false, "error": ...}.
When the server rejects a request, GoodMemClient raises
httpx.HTTPStatusError whose message includes the server's own error text,
for example HTTP 409 Conflict for POST /v1/spaces: A space with this name already exists, and the tools pass that text on in error.
Context provider
from agent_framework import Agent
from agent_framework.openai import OpenAIChatClient
from goodmem_agent_framework import GoodMemClient, GoodMemContextProvider
client = GoodMemClient(base_url="https://localhost:8080", api_key="gm_...", verify_ssl=False)
provider = GoodMemContextProvider(
client=client,
space_id=space_id,
max_results=5,
store_conversations=True,
)
agent = Agent(
client=OpenAIChatClient(model="gpt-4o"),
name="memory-agent",
instructions="You are a helpful assistant with persistent memory.",
context_providers=[provider],
)
Running the tests
The offline tests replay retrieval streams captured from a live GoodMem server
(tests/fixtures/) against a fake server, and need no credentials:
pip install -e ".[dev]"
pytest -v tests/test_retrieval_statuses.py tests/test_space_reuse.py
The live integration tests in tests/test_goodmem_integration.py run against
a real server and are skipped when GOODMEM_API_KEY is not set. They create
and delete their own spaces. GOODMEM_EMBEDDER_ID, GOODMEM_RERANKER_ID,
GOODMEM_LLM_ID and GOODMEM_PDF_PATH pick the models and file they use.
export GOODMEM_API_KEY=gm_xxxxxxxxxxxxxxxxxxxxxxxx
export GOODMEM_BASE_URL=https://localhost:8080
pip install -e ".[dev]"
pytest -m integration -v tests/test_goodmem_integration.py
Continuous integration
.github/workflows/ci.yml runs on every pull request and on pushes to main,
on Python 3.10, 3.11, 3.12 and 3.13. It installs the package with
pip install -e ".[dev]", compiles every module and imports the public API
(no linter is configured), fails if a GoodMem API key is committed, and runs
python -m pytest -v with no API key set, so the live tests are skipped.
Changes
See CHANGELOG.md.
Metadata
Release files for goodmem-agent-framework 0.3.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| goodmem_agent_framework-0.3.0.tar.gz | 20.3 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| goodmem_agent_framework-0.3.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 40.7 kB
Release files / goodmem_agent_framework-0.3.0.tar.gz
| Download URL | goodmem_agent_framework-0.3.0.tar.gz |
|---|---|
| Size | 20.3 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
e0377971e2884b97bc7eea4054f6b19337c61c4c2e127ce832f3d837f672d1b1
|
|
BLAKE2b-256 checksum How to use checksums |
700f13054263044d260eef949d6b1e9616d0b762564e2c9763daf61c86bdfa8a
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 29, 2026.
Transparency logRelease files / goodmem_agent_framework-0.3.0-py3-none-any.whl
| Download URL | goodmem_agent_framework-0.3.0-py3-none-any.whl |
|---|---|
| Size | 20.5 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
77c77df3f69de9798da6b3472724d24c3b7e6cd01a589778c690c023231d5ee6
|
|
BLAKE2b-256 checksum How to use checksums |
ce4bff53adfcfd52ecd04c2f8ae90a4f733156d8462d9b5188830a99bc950139
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 29, 2026.
Transparency log