pdm-memory 0.1.2__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.
- pdm_memory/__init__.py +22 -0
- pdm_memory/auth/__init__.py +4 -0
- pdm_memory/auth/jwt_handler.py +119 -0
- pdm_memory/bench.py +5 -0
- pdm_memory/core/__init__.py +1 -0
- pdm_memory/core/math.py +329 -0
- pdm_memory/core/retrieval.py +362 -0
- pdm_memory/core/signature.py +257 -0
- pdm_memory/ingest/__init__.py +6 -0
- pdm_memory/ingest/auto_signature.py +202 -0
- pdm_memory/ingest/batch.py +164 -0
- pdm_memory/ingest/ingester.py +272 -0
- pdm_memory/integrations/__init__.py +21 -0
- pdm_memory/integrations/anthropic_adapter.py +179 -0
- pdm_memory/integrations/context_manager.py +134 -0
- pdm_memory/integrations/gemini_adapter.py +215 -0
- pdm_memory/integrations/groq_adapter.py +171 -0
- pdm_memory/integrations/ollama_adapter.py +184 -0
- pdm_memory/integrations/openai_adapter.py +191 -0
- pdm_memory/memory.py +552 -0
- pdm_memory/storage/__init__.py +5 -0
- pdm_memory/storage/base.py +123 -0
- pdm_memory/storage/cloud_driver.py +271 -0
- pdm_memory/storage/sqlite_driver.py +331 -0
- pdm_memory/sync.py +145 -0
- pdm_memory/tools/__init__.py +1 -0
- pdm_memory/tools/bench.py +294 -0
- pdm_memory/tools/cli.py +207 -0
- pdm_memory-0.1.2.dist-info/METADATA +485 -0
- pdm_memory-0.1.2.dist-info/RECORD +34 -0
- pdm_memory-0.1.2.dist-info/WHEEL +5 -0
- pdm_memory-0.1.2.dist-info/entry_points.txt +2 -0
- pdm_memory-0.1.2.dist-info/licenses/LICENSE +50 -0
- pdm_memory-0.1.2.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Ollama Adapter
|
|
3
|
+
|
|
4
|
+
Wraps official ollama client library for automatic PDM memory injection and saving.
|
|
5
|
+
|
|
6
|
+
Usage:
|
|
7
|
+
from pdm_memory.integrations import wrap_ollama
|
|
8
|
+
from pdm_memory import Memory
|
|
9
|
+
import ollama
|
|
10
|
+
|
|
11
|
+
mem = Memory(store="./my_app.db")
|
|
12
|
+
client = ollama.Client(host="http://localhost:11434")
|
|
13
|
+
wrapped_client = wrap_ollama(client, memory=mem)
|
|
14
|
+
reply = wrapped_client.chat("What are my preferences?")
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
from __future__ import annotations
|
|
18
|
+
|
|
19
|
+
import logging
|
|
20
|
+
from typing import Any, Optional
|
|
21
|
+
|
|
22
|
+
logger = logging.getLogger(__name__)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class PDMOllamaClient:
|
|
26
|
+
"""
|
|
27
|
+
Memory-augmented Ollama client.
|
|
28
|
+
|
|
29
|
+
All chat() calls automatically:
|
|
30
|
+
- Recall relevant memories before the API call.
|
|
31
|
+
- Inject them into the system prompt.
|
|
32
|
+
- Save both user message and AI reply as new memories.
|
|
33
|
+
"""
|
|
34
|
+
|
|
35
|
+
def __init__(
|
|
36
|
+
self,
|
|
37
|
+
ollama_client: Any,
|
|
38
|
+
memory: Any, # pdm_memory.Memory instance
|
|
39
|
+
model: str = "llama3",
|
|
40
|
+
max_memory_tokens: int = 1500,
|
|
41
|
+
recall_k: int = 5,
|
|
42
|
+
auto_save: bool = True,
|
|
43
|
+
system_prompt: str = "You are a helpful AI assistant.",
|
|
44
|
+
) -> None:
|
|
45
|
+
self._client = ollama_client
|
|
46
|
+
self._memory = memory
|
|
47
|
+
self._model = model
|
|
48
|
+
self._max_memory_tokens = max_memory_tokens
|
|
49
|
+
self._recall_k = recall_k
|
|
50
|
+
self._auto_save = auto_save
|
|
51
|
+
self._system_prompt = system_prompt
|
|
52
|
+
|
|
53
|
+
from pdm_memory.integrations.context_manager import ContextWindowManager
|
|
54
|
+
self._ctx_manager = ContextWindowManager(
|
|
55
|
+
max_tokens=max_memory_tokens,
|
|
56
|
+
model="gpt-4o-mini", # Use cl100k_base approximation
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
def chat(
|
|
60
|
+
self,
|
|
61
|
+
message: str,
|
|
62
|
+
model: Optional[str] = None,
|
|
63
|
+
system_prompt: Optional[str] = None,
|
|
64
|
+
recall_k: Optional[int] = None,
|
|
65
|
+
save_reply: Optional[bool] = None,
|
|
66
|
+
**ollama_kwargs: Any,
|
|
67
|
+
) -> str:
|
|
68
|
+
"""
|
|
69
|
+
Send a message to Ollama with automatic PDM memory injection.
|
|
70
|
+
|
|
71
|
+
Args:
|
|
72
|
+
message: The user's message.
|
|
73
|
+
model: Override the default model name.
|
|
74
|
+
system_prompt: Override the base system prompt.
|
|
75
|
+
recall_k: Override the number of memories to recall.
|
|
76
|
+
save_reply: Override whether to save this exchange to memory.
|
|
77
|
+
**ollama_kwargs: Passed through to ollama.chat API call.
|
|
78
|
+
|
|
79
|
+
Returns:
|
|
80
|
+
The assistant's reply text.
|
|
81
|
+
"""
|
|
82
|
+
k = recall_k if recall_k is not None else self._recall_k
|
|
83
|
+
should_save = save_reply if save_reply is not None else self._auto_save
|
|
84
|
+
base_system = system_prompt or self._system_prompt
|
|
85
|
+
|
|
86
|
+
# Step 1: Recall relevant memories
|
|
87
|
+
hits = self._memory.recall(query=message, k=k)
|
|
88
|
+
trimmed = self._ctx_manager.fit(hits)
|
|
89
|
+
|
|
90
|
+
# Step 2: Build system message with memory block
|
|
91
|
+
memory_block = self._ctx_manager.format_for_prompt(trimmed)
|
|
92
|
+
full_system = base_system
|
|
93
|
+
if memory_block:
|
|
94
|
+
full_system = f"{base_system}\n\n{memory_block}"
|
|
95
|
+
|
|
96
|
+
model_name = model or self._model
|
|
97
|
+
|
|
98
|
+
# Step 3: Call Ollama API
|
|
99
|
+
response = self._client.chat(
|
|
100
|
+
model=model_name,
|
|
101
|
+
messages=[
|
|
102
|
+
{"role": "system", "content": full_system},
|
|
103
|
+
{"role": "user", "content": message},
|
|
104
|
+
],
|
|
105
|
+
**ollama_kwargs,
|
|
106
|
+
)
|
|
107
|
+
|
|
108
|
+
# Handle both dict response and object response shapes
|
|
109
|
+
if isinstance(response, dict):
|
|
110
|
+
reply_text = response.get("message", {}).get("content", "")
|
|
111
|
+
else:
|
|
112
|
+
message_obj = getattr(response, "message", None)
|
|
113
|
+
if message_obj and hasattr(message_obj, "content"):
|
|
114
|
+
reply_text = message_obj.content
|
|
115
|
+
elif hasattr(response, "get"):
|
|
116
|
+
reply_text = response.get("message", {}).get("content", "")
|
|
117
|
+
else:
|
|
118
|
+
reply_text = str(response)
|
|
119
|
+
|
|
120
|
+
# Step 4: Save this turn to memory
|
|
121
|
+
if should_save:
|
|
122
|
+
self._save_turn(message, reply_text)
|
|
123
|
+
|
|
124
|
+
logger.debug(
|
|
125
|
+
"[PDM-Ollama] chat() | recalled=%d injected=%d model=%s",
|
|
126
|
+
len(hits), len(trimmed), model_name,
|
|
127
|
+
)
|
|
128
|
+
return reply_text
|
|
129
|
+
|
|
130
|
+
def _save_turn(self, user_msg: str, assistant_reply: str) -> None:
|
|
131
|
+
"""Save user message and assistant reply as memories."""
|
|
132
|
+
try:
|
|
133
|
+
if user_msg.strip():
|
|
134
|
+
self._memory.save(
|
|
135
|
+
text=user_msg[:500],
|
|
136
|
+
source="ollama_chat",
|
|
137
|
+
tags=["conversation", "user_input"],
|
|
138
|
+
p_magnitude=40.0,
|
|
139
|
+
)
|
|
140
|
+
except Exception as e:
|
|
141
|
+
logger.warning("[PDM-Ollama] Failed to save user message: %s", e)
|
|
142
|
+
|
|
143
|
+
try:
|
|
144
|
+
if assistant_reply.strip():
|
|
145
|
+
self._memory.save(
|
|
146
|
+
text=assistant_reply[:500],
|
|
147
|
+
source="ollama_chat",
|
|
148
|
+
tags=["conversation", "ai_reply"],
|
|
149
|
+
p_magnitude=35.0,
|
|
150
|
+
)
|
|
151
|
+
except Exception as e:
|
|
152
|
+
logger.warning("[PDM-Ollama] Failed to save assistant reply: %s", e)
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
def wrap_ollama(
|
|
156
|
+
client: Any,
|
|
157
|
+
memory: Any,
|
|
158
|
+
model: str = "llama3",
|
|
159
|
+
max_memory_tokens: int = 1500,
|
|
160
|
+
recall_k: int = 5,
|
|
161
|
+
system_prompt: str = "You are a helpful AI assistant.",
|
|
162
|
+
) -> PDMOllamaClient:
|
|
163
|
+
"""
|
|
164
|
+
Create a memory-augmented Ollama client.
|
|
165
|
+
|
|
166
|
+
Args:
|
|
167
|
+
client: ollama Client instance or ollama module itself.
|
|
168
|
+
memory: pdm_memory.Memory instance.
|
|
169
|
+
model: Default model (default: llama3).
|
|
170
|
+
max_memory_tokens: Token budget for injected memories.
|
|
171
|
+
recall_k: Number of memories to recall per turn.
|
|
172
|
+
system_prompt: Base system prompt.
|
|
173
|
+
|
|
174
|
+
Returns:
|
|
175
|
+
PDMOllamaClient wrapping the provided client/module.
|
|
176
|
+
"""
|
|
177
|
+
return PDMOllamaClient(
|
|
178
|
+
ollama_client=client,
|
|
179
|
+
memory=memory,
|
|
180
|
+
model=model,
|
|
181
|
+
max_memory_tokens=max_memory_tokens,
|
|
182
|
+
recall_k=recall_k,
|
|
183
|
+
system_prompt=system_prompt,
|
|
184
|
+
)
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
"""
|
|
2
|
+
OpenAI Adapter — Task 4.1
|
|
3
|
+
|
|
4
|
+
Wraps an OpenAI client so that PDM memories are automatically:
|
|
5
|
+
1. Injected into the system prompt before each AI call.
|
|
6
|
+
2. Saved back to memory after each AI response.
|
|
7
|
+
|
|
8
|
+
Usage:
|
|
9
|
+
from pdm_memory.integrations import wrap_openai
|
|
10
|
+
from pdm_memory import Memory
|
|
11
|
+
|
|
12
|
+
mem = Memory(store="./my_app.db")
|
|
13
|
+
client = wrap_openai(api_key="sk-...", memory=mem)
|
|
14
|
+
|
|
15
|
+
# Memory is handled invisibly
|
|
16
|
+
reply = client.chat("What units should I use?")
|
|
17
|
+
|
|
18
|
+
# Or with more control:
|
|
19
|
+
reply = client.chat(
|
|
20
|
+
"What units should I use?",
|
|
21
|
+
model="gpt-4o",
|
|
22
|
+
system_prompt="You are a helpful assistant.",
|
|
23
|
+
recall_k=5,
|
|
24
|
+
save_reply=True,
|
|
25
|
+
)
|
|
26
|
+
"""
|
|
27
|
+
|
|
28
|
+
from __future__ import annotations
|
|
29
|
+
|
|
30
|
+
import logging
|
|
31
|
+
from typing import Any, Optional
|
|
32
|
+
|
|
33
|
+
logger = logging.getLogger(__name__)
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
class PDMOpenAIClient:
|
|
37
|
+
"""
|
|
38
|
+
Memory-augmented OpenAI client.
|
|
39
|
+
|
|
40
|
+
All chat() calls automatically:
|
|
41
|
+
- Recall relevant memories before the API call.
|
|
42
|
+
- Inject them into the system prompt.
|
|
43
|
+
- Save both the user message and AI reply as new memories.
|
|
44
|
+
"""
|
|
45
|
+
|
|
46
|
+
def __init__(
|
|
47
|
+
self,
|
|
48
|
+
openai_client: Any,
|
|
49
|
+
memory: Any, # pdm_memory.Memory instance
|
|
50
|
+
model: str = "gpt-4o-mini",
|
|
51
|
+
max_memory_tokens: int = 1500,
|
|
52
|
+
recall_k: int = 5,
|
|
53
|
+
auto_save: bool = True,
|
|
54
|
+
system_prompt: str = "You are a helpful AI assistant.",
|
|
55
|
+
) -> None:
|
|
56
|
+
self._client = openai_client
|
|
57
|
+
self._memory = memory
|
|
58
|
+
self._model = model
|
|
59
|
+
self._max_memory_tokens = max_memory_tokens
|
|
60
|
+
self._recall_k = recall_k
|
|
61
|
+
self._auto_save = auto_save
|
|
62
|
+
self._system_prompt = system_prompt
|
|
63
|
+
|
|
64
|
+
from pdm_memory.integrations.context_manager import ContextWindowManager
|
|
65
|
+
self._ctx_manager = ContextWindowManager(
|
|
66
|
+
max_tokens=max_memory_tokens, model=model
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
def chat(
|
|
70
|
+
self,
|
|
71
|
+
message: str,
|
|
72
|
+
model: Optional[str] = None,
|
|
73
|
+
system_prompt: Optional[str] = None,
|
|
74
|
+
recall_k: Optional[int] = None,
|
|
75
|
+
save_reply: Optional[bool] = None,
|
|
76
|
+
**openai_kwargs: Any,
|
|
77
|
+
) -> str:
|
|
78
|
+
"""
|
|
79
|
+
Send a message to OpenAI with automatic PDM memory injection.
|
|
80
|
+
|
|
81
|
+
Args:
|
|
82
|
+
message: The user's message.
|
|
83
|
+
model: Override the default model.
|
|
84
|
+
system_prompt: Override the base system prompt.
|
|
85
|
+
recall_k: Override the number of memories to recall.
|
|
86
|
+
save_reply: Override whether to save this exchange to memory.
|
|
87
|
+
**openai_kwargs: Passed through to openai.chat.completions.create().
|
|
88
|
+
|
|
89
|
+
Returns:
|
|
90
|
+
The assistant's reply text.
|
|
91
|
+
"""
|
|
92
|
+
k = recall_k if recall_k is not None else self._recall_k
|
|
93
|
+
should_save = save_reply if save_reply is not None else self._auto_save
|
|
94
|
+
base_system = system_prompt or self._system_prompt
|
|
95
|
+
|
|
96
|
+
# Step 1: Recall relevant memories
|
|
97
|
+
hits = self._memory.recall(query=message, k=k)
|
|
98
|
+
trimmed = self._ctx_manager.fit(hits)
|
|
99
|
+
|
|
100
|
+
# Step 2: Build system message with memory block
|
|
101
|
+
memory_block = self._ctx_manager.format_for_prompt(trimmed)
|
|
102
|
+
full_system = base_system
|
|
103
|
+
if memory_block:
|
|
104
|
+
full_system = f"{base_system}\n\n{memory_block}"
|
|
105
|
+
|
|
106
|
+
# Step 3: Call OpenAI
|
|
107
|
+
response = self._client.chat.completions.create(
|
|
108
|
+
model=model or self._model,
|
|
109
|
+
messages=[
|
|
110
|
+
{"role": "system", "content": full_system},
|
|
111
|
+
{"role": "user", "content": message},
|
|
112
|
+
],
|
|
113
|
+
**openai_kwargs,
|
|
114
|
+
)
|
|
115
|
+
reply_text = response.choices[0].message.content or ""
|
|
116
|
+
|
|
117
|
+
# Step 4: Save this turn to memory
|
|
118
|
+
if should_save:
|
|
119
|
+
self._save_turn(message, reply_text)
|
|
120
|
+
|
|
121
|
+
logger.debug(
|
|
122
|
+
"[PDM-OpenAI] chat() | recalled=%d injected=%d model=%s",
|
|
123
|
+
len(hits), len(trimmed), model or self._model,
|
|
124
|
+
)
|
|
125
|
+
return reply_text
|
|
126
|
+
|
|
127
|
+
def _save_turn(self, user_msg: str, assistant_reply: str) -> None:
|
|
128
|
+
"""Save user message and assistant reply as memories."""
|
|
129
|
+
try:
|
|
130
|
+
if user_msg.strip():
|
|
131
|
+
self._memory.save(
|
|
132
|
+
text=user_msg[:500],
|
|
133
|
+
source="openai_chat",
|
|
134
|
+
tags=["conversation", "user_input"],
|
|
135
|
+
p_magnitude=40.0,
|
|
136
|
+
)
|
|
137
|
+
except Exception as e:
|
|
138
|
+
logger.warning("[PDM-OpenAI] Failed to save user message: %s", e)
|
|
139
|
+
|
|
140
|
+
try:
|
|
141
|
+
if assistant_reply.strip():
|
|
142
|
+
self._memory.save(
|
|
143
|
+
text=assistant_reply[:500],
|
|
144
|
+
source="openai_chat",
|
|
145
|
+
tags=["conversation", "ai_reply"],
|
|
146
|
+
p_magnitude=35.0,
|
|
147
|
+
)
|
|
148
|
+
except Exception as e:
|
|
149
|
+
logger.warning("[PDM-OpenAI] Failed to save assistant reply: %s", e)
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
def wrap_openai(
|
|
153
|
+
api_key: str,
|
|
154
|
+
memory: Any,
|
|
155
|
+
model: str = "gpt-4o-mini",
|
|
156
|
+
max_memory_tokens: int = 1500,
|
|
157
|
+
recall_k: int = 5,
|
|
158
|
+
system_prompt: str = "You are a helpful AI assistant.",
|
|
159
|
+
**openai_init_kwargs: Any,
|
|
160
|
+
) -> PDMOpenAIClient:
|
|
161
|
+
"""
|
|
162
|
+
Create a memory-augmented OpenAI client.
|
|
163
|
+
|
|
164
|
+
Args:
|
|
165
|
+
api_key: Your OpenAI API key.
|
|
166
|
+
memory: pdm_memory.Memory instance.
|
|
167
|
+
model: Default model (default: gpt-4o-mini).
|
|
168
|
+
max_memory_tokens: Token budget for injected memories.
|
|
169
|
+
recall_k: Number of memories to recall per turn.
|
|
170
|
+
system_prompt: Base system prompt.
|
|
171
|
+
**openai_init_kwargs: Passed to openai.OpenAI().
|
|
172
|
+
|
|
173
|
+
Returns:
|
|
174
|
+
PDMOpenAIClient wrapping an openai.OpenAI client.
|
|
175
|
+
"""
|
|
176
|
+
try:
|
|
177
|
+
import openai
|
|
178
|
+
except ImportError:
|
|
179
|
+
raise ImportError(
|
|
180
|
+
"openai package is required. Install it: pip install pdm-memory[openai]"
|
|
181
|
+
)
|
|
182
|
+
|
|
183
|
+
client = openai.OpenAI(api_key=api_key, **openai_init_kwargs)
|
|
184
|
+
return PDMOpenAIClient(
|
|
185
|
+
openai_client=client,
|
|
186
|
+
memory=memory,
|
|
187
|
+
model=model,
|
|
188
|
+
max_memory_tokens=max_memory_tokens,
|
|
189
|
+
recall_k=recall_k,
|
|
190
|
+
system_prompt=system_prompt,
|
|
191
|
+
)
|