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.
@@ -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
+ )