goodmem-semantic-kernel 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.
Files changed (24) hide show
  1. goodmem_semantic_kernel-0.1.0/.github/ISSUE_TEMPLATE/bug_report.md +32 -0
  2. goodmem_semantic_kernel-0.1.0/.github/ISSUE_TEMPLATE/feature_request.md +20 -0
  3. goodmem_semantic_kernel-0.1.0/.gitignore +46 -0
  4. goodmem_semantic_kernel-0.1.0/LICENSE +21 -0
  5. goodmem_semantic_kernel-0.1.0/PKG-INFO +307 -0
  6. goodmem_semantic_kernel-0.1.0/README.md +284 -0
  7. goodmem_semantic_kernel-0.1.0/examples/README.md +12 -0
  8. goodmem_semantic_kernel-0.1.0/examples/python/example_agent.py +199 -0
  9. goodmem_semantic_kernel-0.1.0/examples/python/example_agent_simple.py +168 -0
  10. goodmem_semantic_kernel-0.1.0/examples/python/example_single_collection.py +75 -0
  11. goodmem_semantic_kernel-0.1.0/examples/python/example_store.py +83 -0
  12. goodmem_semantic_kernel-0.1.0/goodmem_semantic_kernel/__init__.py +39 -0
  13. goodmem_semantic_kernel-0.1.0/goodmem_semantic_kernel/_client.py +331 -0
  14. goodmem_semantic_kernel-0.1.0/goodmem_semantic_kernel/collection.py +521 -0
  15. goodmem_semantic_kernel-0.1.0/goodmem_semantic_kernel/settings.py +37 -0
  16. goodmem_semantic_kernel-0.1.0/goodmem_semantic_kernel/store.py +143 -0
  17. goodmem_semantic_kernel-0.1.0/pyproject.toml +44 -0
  18. goodmem_semantic_kernel-0.1.0/tests/__init__.py +0 -0
  19. goodmem_semantic_kernel-0.1.0/tests/conftest.py +45 -0
  20. goodmem_semantic_kernel-0.1.0/tests/integration/__init__.py +0 -0
  21. goodmem_semantic_kernel-0.1.0/tests/integration/test_integration.py +224 -0
  22. goodmem_semantic_kernel-0.1.0/tests/unit/__init__.py +0 -0
  23. goodmem_semantic_kernel-0.1.0/tests/unit/test_collection.py +310 -0
  24. goodmem_semantic_kernel-0.1.0/tests/unit/test_store.py +144 -0
@@ -0,0 +1,32 @@
1
+ ---
2
+ name: Bug report
3
+ about: Create a report to help us improve
4
+ title: "[BUG]"
5
+ labels: bug
6
+ assignees: ''
7
+
8
+ ---
9
+
10
+ ## Describe the bug
11
+ A clear and concise description of what the bug is.
12
+
13
+ ## To Reproduce
14
+ Steps to reproduce the behavior:
15
+ 1. Go to '...'
16
+ 2. Click on '....'
17
+ 3. Scroll down to '....'
18
+ 4. See error
19
+
20
+ ## Expected behavior
21
+ A clear and concise description of what you expected to happen.
22
+
23
+ ## Screenshots
24
+ If applicable, add screenshots to help explain your problem.
25
+
26
+ ## Environment
27
+ - Language: (Python, Java, .NET or other)
28
+ - OS: [ex: Windows, macOS, Ubuntu 22.04, etc.]
29
+ - Version [ex: 0.1.0]
30
+
31
+ ## Additional context
32
+ Add any other context about the problem here.
@@ -0,0 +1,20 @@
1
+ ---
2
+ name: Feature request
3
+ about: Suggest an idea for this project
4
+ title: ''
5
+ labels: ''
6
+ assignees: ''
7
+
8
+ ---
9
+
10
+ **Is your feature request related to a problem? Please describe.**
11
+ A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
12
+
13
+ **Describe the solution you'd like**
14
+ A clear and concise description of what you want to happen.
15
+
16
+ **Describe alternatives you've considered**
17
+ A clear and concise description of any alternative solutions or features you've considered.
18
+
19
+ **Additional context**
20
+ Add any other context or screenshots about the feature request here.
@@ -0,0 +1,46 @@
1
+ # Claude
2
+ CLAUDE.md
3
+ .claudeignore
4
+
5
+ # Python
6
+ __pycache__/
7
+ *.py[cod]
8
+ *.pyo
9
+ *.pyd
10
+ .Python
11
+ *.egg-info/
12
+ dist/
13
+ build/
14
+ *.egg
15
+ .eggs/
16
+
17
+ # Virtual environments
18
+ venv/
19
+ .venv/
20
+ env/
21
+ .env/
22
+
23
+ # Environment / secrets
24
+ .env
25
+ .env.*
26
+
27
+ # Testing
28
+ .pytest_cache/
29
+ .coverage
30
+ htmlcov/
31
+ .tox/
32
+
33
+ # Type checking
34
+ .mypy_cache/
35
+ .pyright/
36
+
37
+ # Editors
38
+ .vscode/
39
+ .idea/
40
+ *.swp
41
+ *.swo
42
+ *~
43
+
44
+ # OS
45
+ .DS_Store
46
+ Thumbs.db
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 PAIR Systems, Inc.
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,307 @@
1
+ Metadata-Version: 2.4
2
+ Name: goodmem-semantic-kernel
3
+ Version: 0.1.0
4
+ Summary: GoodMem connector for Microsoft Semantic Kernel
5
+ Project-URL: Homepage, https://github.com/PAIR-Systems-Inc/goodmem-semantic-kernel
6
+ Project-URL: Repository, https://github.com/PAIR-Systems-Inc/goodmem-semantic-kernel
7
+ Project-URL: Documentation, https://goodmem.ai/docs
8
+ License: MIT
9
+ License-File: LICENSE
10
+ Keywords: agents,goodmem,llm,memory,semantic-kernel,vector-store
11
+ Classifier: Programming Language :: Python :: 3
12
+ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
13
+ Requires-Python: >=3.10
14
+ Requires-Dist: httpx>=0.27
15
+ Requires-Dist: pydantic-settings>=2.0
16
+ Requires-Dist: pydantic>=2.0
17
+ Requires-Dist: semantic-kernel>=1.0
18
+ Provides-Extra: dev
19
+ Requires-Dist: pytest-asyncio>=0.23; extra == 'dev'
20
+ Requires-Dist: pytest-mock>=3.12; extra == 'dev'
21
+ Requires-Dist: pytest>=8.0; extra == 'dev'
22
+ Description-Content-Type: text/markdown
23
+
24
+ # goodmem-semantic-kernel
25
+
26
+ A [GoodMem](https://goodmem.ai) connector for [Microsoft Semantic Kernel](https://github.com/microsoft/semantic-kernel).
27
+
28
+ Implements Semantic Kernel's `VectorStoreCollection` and `VectorStore` interfaces so agents built on Semantic Kernel can store and retrieve memories from a GoodMem server — no local embedding model required.
29
+
30
+ ## What is GoodMem?
31
+
32
+ GoodMem is a centralized memory API for AI agents and LLMs. The point of GoodMem is so that you can easily and efficiently store and retrieve your data/memories through semantic searching, ai summaries, and context-aware results.
33
+
34
+ GoodMem stores text memories as semantic embeddings in PostgreSQL (via `pgvector`) and retrieves them by semantic similarity. Because it runs as a shared service, multiple agents can read and write to the same memory spaces simultaneously.
35
+
36
+ > Embeddings are computed **server-side**, so this connector never needs an `embedding_generator`.
37
+
38
+ ### Conceptual Overview
39
+
40
+ In GoodMem all data is hosted in a "**Space**", an abstract storage unit in GoodMem.
41
+ Each **Space** can be configured with embedders and/or chunking strategies. Each Space holds "**Memories**".
42
+
43
+ **Memories** are stored content with associated metadata that are automatically chunked and embedded for efficient retrieval. All **Memories** belong to a **Space**.
44
+
45
+ **Embedders** convert your data into a vectorized format. GoodMem supports multiple embedding models & providers.
46
+
47
+ ---
48
+
49
+ ## Installation
50
+
51
+ **Requirements:** Python 3.10+ and a running GoodMem server.
52
+
53
+ ```bash
54
+ pip install goodmem-semantic-kernel
55
+ ```
56
+
57
+ Or from source (note the hyphens, rather than underscores):
58
+
59
+ ```bash
60
+ git clone https://github.com/PAIR-Systems-Inc/goodmem-semantic-kernel
61
+ pip install -e goodmem-semantic-kernel
62
+ # alternative methods if facing issues
63
+ # pip install -e ".[dev]"
64
+ # pip install -e .
65
+ ```
66
+
67
+ ## Configuration
68
+
69
+ All settings are read from environment variables with the `GOODMEM_` prefix, or passed directly via `GoodMemSettings`.
70
+
71
+ ```bash
72
+ export GOODMEM_API_KEY=your_key_here
73
+ export GOODMEM_BASE_URL=https://your_goodmem_server:8080
74
+ export GOODMEM_VERIFY_SSL=true_or_false
75
+ ```
76
+
77
+ | Variable | Required | Default | Description |
78
+ |---|---|---|---|
79
+ | `GOODMEM_API_KEY` | Yes | — | API key for the GoodMem server |
80
+ | `GOODMEM_BASE_URL` | No | `http://localhost:8080` | GoodMem server base URL |
81
+ | `GOODMEM_EMBEDDER_ID` | No | auto-detected | UUID of the embedder to use |
82
+ | `GOODMEM_VERIFY_SSL` | No | `false` | Set to `false` for self-signed certs, set to `true` if you setup custom TLS certs|
83
+
84
+ ## Quickstart
85
+
86
+ ### Define a data model
87
+
88
+ ```python
89
+ from dataclasses import dataclass
90
+ from typing import Annotated
91
+ from semantic_kernel.data.vector import VectorStoreField, vectorstoremodel
92
+
93
+ @vectorstoremodel
94
+ @dataclass
95
+ class Note:
96
+ id: Annotated[str | None, VectorStoreField("key")] = None
97
+ content: Annotated[str, VectorStoreField("data", type="str")] = ""
98
+ source: Annotated[str | None, VectorStoreField("data")] = None
99
+ ```
100
+
101
+ - Exactly one `"key"` field (the memory ID — `None` lets the server generate a UUID).
102
+ - One `"data"` field named `content` becomes the embedded text (`originalContent` in GoodMem).
103
+ - All other `"data"` fields are stored as metadata and returned on search results.
104
+ - `"vector"` fields are accepted for interface compatibility but ignored — GoodMem embeds server-side.
105
+
106
+ We have three example patterns provided.
107
+
108
+ Option A is the recommended pattern for production agents since the LLM decides when to call memory and what to search for, rather than the application hardcoding those decisions.
109
+
110
+ ### Option A — Wired into a Semantic Kernel agent
111
+
112
+ ```python
113
+ from semantic_kernel.agents import AgentThread, ChatCompletionAgent
114
+ from semantic_kernel.connectors.ai import FunctionChoiceBehavior
115
+ from semantic_kernel.connectors.ai.open_ai import OpenAIChatCompletion
116
+ from semantic_kernel.functions import KernelParameterMetadata, KernelPlugin
117
+ from goodmem_semantic_kernel import GoodMemCollection
118
+
119
+ async def main():
120
+ async with GoodMemCollection(record_type=Note, collection_name="agent-memory") as coll:
121
+ await coll.ensure_collection_exists()
122
+ await coll.upsert([...]) # seed your memories
123
+
124
+ memory_plugin = KernelPlugin(
125
+ name="memory",
126
+ functions=[
127
+ coll.create_search_function(
128
+ function_name="recall",
129
+ description="Search long-term memory for relevant facts.",
130
+ string_mapper=lambda r: r.record.content,
131
+ )
132
+ ],
133
+ )
134
+
135
+ agent = ChatCompletionAgent(
136
+ name="MemoryAgent",
137
+ service=OpenAIChatCompletion(),
138
+ instructions="Always search memory before answering factual questions.",
139
+ function_choice_behavior=FunctionChoiceBehavior.Auto(),
140
+ plugins=[memory_plugin],
141
+ )
142
+
143
+ thread: AgentThread | None = None
144
+ result = await agent.get_response(messages="Where is the Eiffel Tower?", thread=thread)
145
+ print(result.content)
146
+ ```
147
+
148
+ ### Option B — Single collection
149
+
150
+ ```python
151
+ import asyncio
152
+ from goodmem_semantic_kernel import GoodMemCollection
153
+
154
+ async def main():
155
+ async with GoodMemCollection(record_type=Note, collection_name="my-notes") as coll:
156
+ await coll.ensure_collection_exists()
157
+
158
+ keys = await coll.upsert([
159
+ Note(content="The Eiffel Tower is in Paris", source="facts"),
160
+ Note(content="Buy milk and eggs", source="shopping"),
161
+ ])
162
+
163
+ await asyncio.sleep(3) # wait for server-side embedding
164
+
165
+ results = await coll.search("grocery list", top=5)
166
+ async for r in results.results:
167
+ print(f"[{r.score:.3f}] {r.record.content} (source={r.record.source})")
168
+
169
+ asyncio.run(main())
170
+ ```
171
+
172
+ ### Option C — Store (multiple collections, shared connection)
173
+
174
+ ```python
175
+ from goodmem_semantic_kernel import GoodMemStore
176
+
177
+ async def main():
178
+ async with GoodMemStore() as store:
179
+ print(await store.list_collection_names())
180
+
181
+ notes = store.get_collection(Note, collection_name="notes")
182
+ todos = store.get_collection(Note, collection_name="todos")
183
+
184
+ await notes.ensure_collection_exists()
185
+ await todos.ensure_collection_exists()
186
+
187
+ await notes.upsert(Note(content="Mount Fuji is in Japan"))
188
+ await todos.upsert(Note(content="Call the dentist"))
189
+ ```
190
+
191
+ ## Running the examples
192
+
193
+ ```bash
194
+ cd goodmem_semantic_kernel/examples/python
195
+
196
+ # Option A — agent with memory tool (also requires OPENAI_API_KEY)
197
+ OPENAI_API_KEY=your_openai_key_here
198
+ python example_agent.py
199
+
200
+ # Option B — single collection
201
+ python example_single_collection.py
202
+
203
+ # Option C — store with multiple collections
204
+ python example_store.py
205
+ ```
206
+
207
+ if you failed to run an example successfully, double check [Configuration](#configuration)
208
+
209
+ or try running inside a python virtual environment if there is a dependency conflict
210
+
211
+ ```bash
212
+ python3 -m venv venv
213
+ source venv/bin/activate
214
+ ```
215
+
216
+ ## Testing
217
+
218
+ ```bash
219
+ # Unit tests (no server required)
220
+ pytest tests/unit/
221
+
222
+ # Integration tests (requires a live GoodMem server)
223
+ GOODMEM_API_KEY=your_key_here pytest -m integration
224
+ ```
225
+
226
+ ## Behavior notes
227
+
228
+ **No local embedding.** Never pass an `embedding_generator` — GoodMem embeds content server-side. The parameter is accepted for interface compatibility and silently ignored.
229
+
230
+ **Upsert semantics.** GoodMem memories are immutable. If you `upsert` a record with an existing `id`, the connector deletes the old memory and creates a new one.
231
+
232
+ **`content` is write-only in GoodMem.** The server does not return `originalContent` in search responses. Retrieved text comes from `chunkText` (a chunk of the original), which the connector maps back to your `content` field transparently.
233
+
234
+ **Score convention.** `relevanceScore` from the GoodMem API is a raw pgvector value where lower means more similar. The connector negates it before returning, so SK's standard convention (higher = more relevant) is preserved.
235
+
236
+ **Filters not supported.** Passing `filter=` to `search()` raises `VectorStoreOperationNotSupportedException`. Post-filter results in application code if needed.
237
+
238
+ **Pre-computed vectors not supported.** Passing `vector=` to `search()` raises the same exception. Pass text only.
239
+
240
+ ## Project structure
241
+
242
+ ```
243
+ goodmem-semantic-kernel/ ← repo root
244
+ ├── goodmem_semantic_kernel/ ← importable package
245
+ │ ├── __init__.py # Public exports: GoodMemCollection, GoodMemStore, GoodMemSettings
246
+ │ ├── _client.py # Async HTTP wrapper around the GoodMem REST API
247
+ │ ├── collection.py # VectorStoreCollection + VectorSearch implementation
248
+ │ ├── settings.py # GoodMemSettings (Pydantic, reads GOODMEM_* env vars)
249
+ │ ├── store.py # VectorStore implementation
250
+ ├── tests/
251
+ │ ├── unit/ # Mocked unit tests (no server required)
252
+ │ └── integration/ # Live integration tests (require GoodMem server)
253
+ ├── examples/ # Runnable examples (Options A, B, C)
254
+ └── pyproject.toml
255
+ ```
256
+
257
+ ## API reference
258
+
259
+ ### `GoodMemCollection`
260
+
261
+ The core class. Implements `VectorStoreCollection[str, TModel]` and `VectorSearch[str, TModel]`.
262
+
263
+ ```python
264
+ GoodMemCollection(
265
+ record_type=MyModel,
266
+ collection_name="my-space", # maps to a GoodMem Space
267
+ settings=GoodMemSettings(), # optional; reads GOODMEM_* env vars by default
268
+ client=None, # optional; inject a pre-built GoodMemAsyncClient
269
+ )
270
+ ```
271
+
272
+ | Method | Description |
273
+ |---|---|
274
+ | `ensure_collection_exists()` | Create the GoodMem space if it doesn't exist |
275
+ | `ensure_collection_deleted()` | Delete the space and all its memories |
276
+ | `collection_exists()` | Return `True` if the space exists |
277
+ | `upsert(records)` | Write one or a list of records; returns the memory ID(s) |
278
+ | `get(key=...)` / `get(keys=[...])` | Fetch memories by ID |
279
+ | `delete(keys=[...])` | Delete memories by ID |
280
+ | `search(query, top=5)` | Semantic search; returns `KernelSearchResults` |
281
+ | `create_search_function(...)` | Wrap search as a `KernelFunction` for use in agent plugins |
282
+
283
+ ### `GoodMemStore`
284
+
285
+ Factory for collections. All collections from the same store share one HTTP connection.
286
+
287
+ ```python
288
+ GoodMemStore(settings=GoodMemSettings())
289
+ ```
290
+
291
+ | Method | Description |
292
+ |---|---|
293
+ | `get_collection(record_type, collection_name=...)` | Return a `GoodMemCollection` |
294
+ | `list_collection_names()` | List all GoodMem spaces visible to this API key |
295
+
296
+ ### `GoodMemSettings`
297
+
298
+ Pydantic settings class; reads `GOODMEM_*` environment variables.
299
+
300
+ ```python
301
+ GoodMemSettings(
302
+ base_url="https://localhost:8080",
303
+ api_key="your_key_here",
304
+ embedder_id=None, # auto-detected if omitted
305
+ verify_ssl=false,
306
+ )
307
+ ```