llm-hippocampus 0.0.1__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 @@
1
+ # -*- coding: utf-8 -*-
@@ -0,0 +1 @@
1
+ # -*- coding: utf-8 -*-
@@ -0,0 +1,40 @@
1
+ # -*- coding: utf-8 -*-
2
+ from typing import Dict, Optional
3
+
4
+ class ContextManager:
5
+ _instance = None
6
+
7
+ def __new__(cls):
8
+ if cls._instance is None:
9
+ cls._instance = super().__new__(cls)
10
+ cls._instance._context = {}
11
+ return cls._instance
12
+
13
+ def set(self, key: str, value: str) -> None:
14
+ """添加或更新上下文键值对"""
15
+ self._context[key] = value
16
+
17
+ def get(self, key: str, default: Optional[str] = None) -> Optional[str]:
18
+ """获取上下文值"""
19
+ return self._context.get(key, default)
20
+
21
+ def delete(self, key: str) -> None:
22
+ """删除上下文键值对"""
23
+ if key in self._context:
24
+ del self._context[key]
25
+
26
+ def clear(self) -> None:
27
+ """清空所有上下文数据"""
28
+ self._context.clear()
29
+
30
+ def get_all(self) -> Dict[str, str]:
31
+ """获取所有上下文数据"""
32
+ return self._context.copy()
33
+
34
+
35
+ def test():
36
+ print()
37
+
38
+
39
+ if __name__ == '__main__':
40
+ None
@@ -0,0 +1,8 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ def test():
4
+ print()
5
+
6
+
7
+ if __name__ == '__main__':
8
+ None
@@ -0,0 +1,132 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ from typing import List, Callable
4
+ from enum import Enum
5
+
6
+ class ContextSource(Enum):
7
+ PROMPT_GENERATED = "prompt_generated"
8
+ EXTERNAL_KNOWLEDGE = "external_knowledge"
9
+ DYNAMIC_ASSEMBLED = "dynamic_assembled"
10
+
11
+ class ContextRetrieval:
12
+ def __init__(self, context_manager):
13
+ self.context_manager = context_manager
14
+ # 注册外部知识检索器(示例接口)
15
+ self.external_retrievers = {
16
+ "default": self._default_external_retriever
17
+ }
18
+
19
+ def generate_prompt_based_context(self, task_type: str, reasoning_framework: str = "basic") -> str:
20
+ """基于提示的生成:创建任务优化的指令框架
21
+ Args:
22
+ task_type: 任务类型,如"text_classification"、"nl2sql"、"summarization"
23
+ reasoning_framework: 推理框架类型,如"basic"、"chain_of_thought"、"tree_of_thought"
24
+ Returns:
25
+ 生成的提示上下文字符串
26
+ """
27
+ # 基础提示模板库
28
+ prompt_templates = {
29
+ "text_classification": """你是一个文本分类专家。基于以下上下文信息,将文本分类到预定义类别中:
30
+ {context}
31
+ 文本:{input}
32
+ 要求:输出最可能的类别名称,无需解释。""",
33
+ "nl2sql": """你是一个SQL生成专家。基于以下数据库模式信息,将自然语言转换为SQL查询:
34
+ {context}
35
+ 用户问题:{input}
36
+ 要求:输出标准SQL,不包含解释和多余文本。""",
37
+ "summarization": """你是一个文本摘要专家。基于以下上下文信息,生成简洁准确的摘要:
38
+ {context}
39
+ 文本:{input}
40
+ 要求:摘要长度不超过{max_length}字,保留关键信息。"""
41
+ }
42
+
43
+ # 推理框架增强
44
+ reasoning_enhancements = {
45
+ "basic": "",
46
+ "chain_of_thought": "思考过程:让我逐步分析...",
47
+ "tree_of_thought": "可能的思考方向:\n1. ...\n2. ...\n结论:..."
48
+ }
49
+
50
+ # 获取基础模板
51
+ base_template = prompt_templates.get(task_type, prompt_templates["text_classification"])
52
+ # 添加推理框架
53
+ enhanced_template = f"{base_template}\n{reasoning_enhancements.get(reasoning_framework, '')}"
54
+
55
+ # 存储生成的提示模板到上下文
56
+ self.context_manager.set(f"prompt_template_{task_type}", enhanced_template)
57
+ return enhanced_template
58
+
59
+ def retrieve_external_knowledge(self, query: str, retriever_name: str = "default", **kwargs) -> str:
60
+ """外部知识检索:访问动态信息源
61
+ Args:
62
+ query: 检索查询词
63
+ retriever_name: 检索器名称
64
+ **kwargs: 检索参数(如top_k、filter条件等)
65
+ Returns:
66
+ 检索到的知识文本
67
+ """
68
+ if retriever_name not in self.external_retrievers:
69
+ raise ValueError(f"Retriever {retriever_name} not registered")
70
+
71
+ # 调用注册的检索器
72
+ knowledge = self.external_retrievers[retriever_name](query, **kwargs)
73
+ # 存储检索结果到上下文
74
+ self.context_manager.set(f"external_knowledge_{query[:30]}", knowledge)
75
+ return knowledge
76
+
77
+ def assemble_dynamic_context(self, context_ids: List[str], assembly_strategy: str = "sequential") -> str:
78
+ """动态上下文组装:将多个上下文组件整合成优化的输入
79
+ Args:
80
+ context_ids: 上下文组件ID列表
81
+ assembly_strategy: 组装策略,如"sequential"、"priority_based"、"summary_based"
82
+ Returns:
83
+ 组装后的完整上下文
84
+ """
85
+ # 获取所有上下文组件
86
+ context_components = []
87
+ for cid in context_ids:
88
+ component = self.context_manager.get(cid)
89
+ if component:
90
+ context_components.append((cid, component))
91
+
92
+ # 应用组装策略
93
+ if assembly_strategy == "sequential":
94
+ assembled = "\n\n".join([f"[{cid}]: {comp}" for cid, comp in context_components])
95
+ elif assembly_strategy == "priority_based":
96
+ # 按ID中包含的优先级关键词排序
97
+ context_components.sort(key=lambda x: "priority" in x[0], reverse=True)
98
+ assembled = "\n\n".join([f"[{cid}]: {comp}" for cid, comp in context_components])
99
+ elif assembly_strategy == "summary_based":
100
+ # 生成组件摘要(简化实现)
101
+ summaries = [f"[{cid}摘要]: {comp[:100]}..." for cid, comp in context_components]
102
+ assembled = "\n\n".join(summaries + ["\n完整内容:\n" + "\n\n".join([comp for _, comp in context_components])])
103
+ else:
104
+ assembled = "\n\n".join([comp for _, comp in context_components])
105
+
106
+ # 存储组装结果
107
+ self.context_manager.set("assembled_context", assembled)
108
+ return assembled
109
+
110
+ def _default_external_retriever(self, query: str, top_k: int = 3) -> str:
111
+ """默认外部知识检索器(示例实现)
112
+ 实际应用中可替换为调用搜索引擎、数据库或API
113
+ """
114
+ # 模拟外部检索结果
115
+ mock_knowledge = [
116
+ f"[检索结果1] 关于'{query}'的信息:这是模拟的外部知识,实际应用中应替换为真实API调用。",
117
+ f"[检索结果2] 相关数据:{len(query)}个字符,{query.count(' ')+1}个词。",
118
+ f"[检索结果3] 时间戳:{self.context_manager.get('current_timestamp', '未知')}"
119
+ ]
120
+ return "\n\n".join(mock_knowledge[:top_k])
121
+
122
+ def register_retriever(self, name: str, retriever_func: Callable) -> None:
123
+ """注册自定义外部知识检索器"""
124
+ self.external_retrievers[name] = retriever_func
125
+
126
+
127
+ def test():
128
+ print()
129
+
130
+
131
+ if __name__ == '__main__':
132
+ None
llm_hippocampus/env.py ADDED
@@ -0,0 +1,24 @@
1
+ # -*- coding: utf-8 -*-
2
+ from dotenv import load_dotenv
3
+ from .shared.utils import str_to_bool
4
+ import os
5
+ load_dotenv()
6
+
7
+ TOP_K = int(os.environ.get("DEFAULT_TOP_K", 3))
8
+ DISTANCE_THRESHOLD = float(os.environ.get("DEFAULT_DISTANCE_THRESHOLD", 0.30))
9
+ REDIS_URL = os.environ.get("REDIS_URL", "redis://:redis@192.168.65.166:6377")
10
+ OPENAI_API_KEY = os.environ.get("OPENAI_API_KEY")
11
+ CHUNK_SIZE = int(os.environ.get("DEFAULT_CHUNK_SIZE", 500))
12
+ CHUNKING_TECHNIQUE = os.environ.get(
13
+ "DEFAULT_CHUNKING_TECHNIQUE", "Recursive Character"
14
+ )
15
+ USE_SEMANTIC_CACHE = str_to_bool(
16
+ os.environ.get("DEFAULT_USE_SEMANTIC_CACHE")
17
+ )
18
+ USE_RERANKERS = str_to_bool(os.environ.get("DEFAULT_USE_RERANKERS"))
19
+ RERANKER_MODEL = os.environ.get("DEFAULT_RERANKER_MODEL", "D:/model/Qwen3-Reranker-0.6B")
20
+ RERANKER_TYPE = os.environ.get("DEFAULT_RERANKER_TYPE", "HuggingFace")
21
+ USE_CHAT_HISTORY = str_to_bool(os.environ.get("DEFAULT_USE_CHAT_HISTORY"))
22
+ USE_RAGAS = str_to_bool(os.environ.get("DEFAULT_USE_RAGAS"))
23
+ EMBEDDING_MODEL_PROVIDER = "openai"
24
+ EMBEDDING_MODEL = os.environ.get("EMBEDDING_MODEL", "D:/model/Qwen3-Embedding-0.6B")
@@ -0,0 +1,247 @@
1
+ # -*- coding: utf-8 -*-
2
+ import os.path
3
+ from typing import List
4
+
5
+ from langchain.chains import create_retrieval_chain
6
+ from langchain.chains.combine_documents import create_stuff_documents_chain
7
+ from langchain_core.prompts import ChatPromptTemplate
8
+ from langchain_openai import (
9
+ OpenAIEmbeddings,
10
+ )
11
+ from langchain_redis import RedisChatMessageHistory, RedisVectorStore
12
+ from langchain_core.messages.chat import ChatMessage
13
+ from redisvl.extensions.llmcache import SemanticCache
14
+ from redisvl.utils.rerank import HFCrossEncoderReranker
15
+ from redisvl.utils.utils import create_ulid
16
+ from .shared.cached_llm import CachedLLM
17
+ from .shared.logger import logger
18
+ from . import env
19
+
20
+ os.environ["TOKENIZERS_PARALLELISM"] = "false"
21
+
22
+ class Session:
23
+ def __init__(self, llm, session_id = None, embedding = None, rerankers = None) -> None:
24
+ missing_vars = []
25
+ self.session_id = create_ulid() if session_id is None else session_id
26
+ self.redis_url = env.REDIS_URL
27
+ self.openai_api_key = env.OPENAI_API_KEY
28
+
29
+ self.initialized = False
30
+ self.RERANKERS = {}
31
+ if rerankers and env.USE_RERANKERS:
32
+ self.RERANKERS = rerankers
33
+
34
+ # Initialize non-API dependent variables
35
+ self.chunk_size = env.CHUNK_SIZE
36
+ self.chunking_technique = env.CHUNKING_TECHNIQUE
37
+ self.chat_history = None
38
+ self.N = 0
39
+ self.count = 0
40
+ self.use_semantic_cache = env.USE_SEMANTIC_CACHE
41
+ self.use_rerankers = env.USE_RERANKERS
42
+ self.top_k = env.TOP_K
43
+ self.distance_threshold = env.DISTANCE_THRESHOLD
44
+ self.use_chat_history = env.USE_CHAT_HISTORY
45
+ self.use_ragas = env.USE_RERANKERS
46
+ self.reranker_type = env.RERANKER_TYPE
47
+ logger.info("Initializing LLM")
48
+ self.llm = llm.get('llm')
49
+ self.llm_provider = llm.get('provider')
50
+ if not self.llm or not self.llm_provider:
51
+ missing_vars.append(llm)
52
+ self.cached_llm = None
53
+ self.vector_store = None
54
+ self.llmcache = None
55
+ self.index_name = None
56
+ if embedding:
57
+ self.embedding_model_provider = embedding['provider']
58
+ self.embedding = embedding['embedding']
59
+ else:
60
+ self.embedding = self.get_embedding_model()
61
+ self.embedding_model_provider = env.EMBEDDING_MODEL_PROVIDER
62
+
63
+ if missing_vars:
64
+ raise ValueError(f"模型初始化记忆发生异常,未设置必要的环境变量或传入的{missing_vars}不正确")
65
+
66
+ def initialize(self):
67
+ # Initialize rerankers
68
+ if self.use_rerankers:
69
+ logger.info("Initializing rerankers")
70
+
71
+ self.RERANKERS = {
72
+ "HuggingFace": HFCrossEncoderReranker(env.RERANKER_MODEL),
73
+ }
74
+ logger.info("Rerankers initialized")
75
+
76
+ # Init chat history if use_chat_history is True
77
+ if self.use_chat_history:
78
+ self.chat_history = RedisChatMessageHistory(
79
+ session_id=self.session_id,
80
+ redis_url=self.redis_url,
81
+ index_name="chat_history", # Use a common index for all chat histories
82
+ )
83
+ chats = {"session_id": self.session_id, "chat_history": self.chat_history}
84
+ logger.debug(f"加载对话历史,{chats}")
85
+ else:
86
+ self.chat_history = None
87
+
88
+ self.initialized = True
89
+
90
+ def get_embedding_model(self):
91
+ """Get the right embedding model based on settings and config"""
92
+ print(
93
+ f"Embeddings for provider: {env.EMBEDDING_MODEL_PROVIDER} and model: {env.EMBEDDING_MODEL}"
94
+ )
95
+ match env.EMBEDDING_MODEL_PROVIDER.lower():
96
+ case "openai":
97
+ return OpenAIEmbeddings(model=env.EMBEDDING_MODEL)
98
+
99
+ return None
100
+
101
+ def build_chain(self, history: List[ChatMessage]):
102
+ retriever = self.vector_store.as_retriever(search_kwargs={"k": self.top_k})
103
+
104
+ messages = [
105
+ (
106
+ "system",
107
+ """You are a helpful AI assistant. Use the following pieces of
108
+ context to answer the user's question. If you don't know the
109
+ answer, just say that you don't know, don't try to make up an
110
+ answer. Please be as detailed as possible with your
111
+ answers.""",
112
+ ),
113
+ ("system", "Context: {context}"),
114
+ ]
115
+
116
+ if self.use_chat_history:
117
+ for msg in history:
118
+ messages.append((msg["role"], msg["content"]))
119
+
120
+ messages.append(("human", "{input}"))
121
+ messages.append(
122
+ (
123
+ "system",
124
+ "Provide a helpful and accurate answer based on the given context and question:",
125
+ )
126
+ )
127
+ prompt = ChatPromptTemplate.from_messages(messages)
128
+
129
+ combine_docs_chain = create_stuff_documents_chain(self.cached_llm, prompt)
130
+ rag_chain = create_retrieval_chain(retriever, combine_docs_chain)
131
+
132
+ return rag_chain
133
+
134
+ def update_chat_history(
135
+ self, history: List[ChatMessage], use_chat_history: bool
136
+ ):
137
+ self.use_chat_history = use_chat_history
138
+
139
+ if self.use_chat_history:
140
+ if self.chat_history is None:
141
+ self.chat_history = RedisChatMessageHistory(
142
+ session_id=self.session_id,
143
+ redis_url=self.redis_url,
144
+ index_name="chat_history",
145
+ )
146
+
147
+ else:
148
+ if self.chat_history:
149
+ try:
150
+ self.chat_history.clear()
151
+ except Exception as e:
152
+ logger.debug(f"清理会话历史异常: {str(e)}")
153
+
154
+ history.clear()
155
+
156
+ return history
157
+
158
+ def get_chat_history(self):
159
+ if self.chat_history and self.use_chat_history:
160
+ messages = self.chat_history.messages
161
+ formatted_history = []
162
+ for msg in messages:
163
+ if msg.type == "human":
164
+ formatted_history.append(f"👤 **Human**: {msg.content}\n")
165
+ elif msg.type == "ai":
166
+ formatted_history.append(f"🤖 **AI**: {msg.content}\n")
167
+ return "\n".join(formatted_history)
168
+ return "No chat history available."
169
+
170
+ def update_top_k(self, new_top_k: int):
171
+ self.top_k = new_top_k
172
+
173
+ def make_semantic_cache(self) -> SemanticCache:
174
+ semantic_cache_index_name = f"llmcache:{self.index_name}"
175
+ return SemanticCache(
176
+ name=semantic_cache_index_name,
177
+ redis_url=self.redis_url,
178
+ distance_threshold=self.distance_threshold,
179
+ )
180
+
181
+ def clear_semantic_cache(self):
182
+ # Always make a new SemanticCache in case use_semantic_cache is False
183
+ semantic_cache = self.make_semantic_cache()
184
+ semantic_cache.clear()
185
+
186
+ def update_semantic_cache(self, use_semantic_cache: bool):
187
+ self.use_semantic_cache = use_semantic_cache
188
+ if self.use_semantic_cache and self.index_name:
189
+ self.llmcache = self.make_semantic_cache()
190
+ else:
191
+ self.llmcache = None
192
+
193
+
194
+ def update_distance_threshold(self, new_threshold: float):
195
+ self.distance_threshold = new_threshold
196
+ if self.index_name:
197
+ self.llmcache = self.make_semantic_cache()
198
+ self.update_llm()
199
+
200
+ def get_last_cache_status(self) -> bool:
201
+ if isinstance(self.cached_llm, CachedLLM):
202
+ return self.cached_llm.get_last_cache_status()
203
+ return False
204
+
205
+ def rerank_results(self, query, results):
206
+ if not self.use_reranker:
207
+ return results, None, None
208
+
209
+ reranker = self.RERANKERS[self.reranker_type]
210
+ original_results = [r.page_content for r in results]
211
+
212
+ reranked_results, scores = reranker.rank(query=query, docs=original_results)
213
+
214
+ # Reconstruct the results with reranked order, using fuzzy matching
215
+ reranked_docs = []
216
+ for reranked in reranked_results:
217
+ reranked_content = (
218
+ reranked["content"] if isinstance(reranked, dict) else reranked
219
+ )
220
+ best_match = max(
221
+ results, key=lambda r: self.similarity(r.page_content, reranked_content)
222
+ )
223
+ reranked_docs.append(best_match)
224
+
225
+ rerank_info = {
226
+ "original_order": original_results,
227
+ "reranked_order": [
228
+ r["content"] if isinstance(r, dict) else r for r in reranked_results
229
+ ],
230
+ "original_scores": [1.0]
231
+ * len(results), # Assuming original scores are not available
232
+ "reranked_scores": scores,
233
+ }
234
+
235
+ return reranked_docs, rerank_info, original_results
236
+
237
+ def similarity(self, s1, s2):
238
+ # Simple similarity measure based on common words
239
+ words1 = set(s1.lower().split())
240
+ words2 = set(s2.lower().split())
241
+ return len(words1.intersection(words2)) / len(words1.union(words2))
242
+
243
+ def rerankers(self):
244
+ return self.RERANKERS
245
+
246
+ def update_embedding_model_provider(self, new_provider: str):
247
+ self.embedding_model_provider = new_provider
@@ -0,0 +1 @@
1
+ # -*- coding: utf-8 -*-
@@ -0,0 +1,50 @@
1
+ # -*- coding: utf-8 -*-
2
+ from typing import Any, Optional
3
+
4
+ from langchain_core.prompt_values import ChatPromptValue, StringPromptValue
5
+ from langchain_core.runnables.base import Runnable
6
+ from langchain_core.runnables.config import RunnableConfig
7
+
8
+
9
+ class CachedLLM(Runnable):
10
+ def __init__(self, llm, llmcache):
11
+ self.llm = llm
12
+ self.llmcache = llmcache
13
+ self.last_is_cache_hit = False
14
+
15
+ def invoke(
16
+ self, input: Any, config: Optional[RunnableConfig] = None, **kwargs
17
+ ) -> str:
18
+ if isinstance(input, dict):
19
+ question = input.get("query") or input.get("input")
20
+ elif isinstance(input, str):
21
+ question = input
22
+ elif isinstance(input, StringPromptValue):
23
+ question = input.text
24
+ elif isinstance(input, ChatPromptValue):
25
+ # Extract the last human message from the chat history
26
+ human_message = next(
27
+ m for m in reversed(input.messages) if m.type == "human"
28
+ )
29
+ question = human_message.content.strip()
30
+ else:
31
+ raise ValueError(f"Unexpected input type: {type(input)}")
32
+
33
+ if not isinstance(question, str):
34
+ raise TypeError(f"Question must be a string, got {type(question)}")
35
+
36
+ cached_responses = self.llmcache.check(
37
+ prompt=question, return_fields=["prompt", "response", "metadata"]
38
+ )
39
+ if cached_responses:
40
+ self.last_is_cache_hit = True
41
+ return cached_responses[0]["response"]
42
+
43
+ self.last_is_cache_hit = False
44
+ response = self.llm.invoke(input, **kwargs)
45
+ text = response.content if hasattr(response, "content") else str(response)
46
+ self.llmcache.store(prompt=question, response=text)
47
+ return text
48
+
49
+ def get_last_cache_status(self) -> bool:
50
+ return self.last_is_cache_hit
@@ -0,0 +1,10 @@
1
+ # -*- coding: utf-8 -*-
2
+ import logging
3
+ import os
4
+ from dotenv import load_dotenv
5
+
6
+ load_dotenv()
7
+ log_level = os.environ.get("LOG_LEVEL", logging.DEBUG)
8
+
9
+ logger = logging.getLogger("app")
10
+ logger.setLevel(log_level)
@@ -0,0 +1,124 @@
1
+ # -*- coding: utf-8 -*-
2
+ from typing import List
3
+
4
+ from langchain_core.prompts import ChatPromptTemplate
5
+ from langchain_redis import RedisVectorStore
6
+ from redisvl.index import SearchIndex
7
+ from . import logger
8
+
9
+ class Memory:
10
+ index: SearchIndex
11
+ def __init__(self, redis_url: str):
12
+ logger.info(f"初始化记忆模块,Redis URL: {redis_url}")
13
+ self.redis_url = redis_url
14
+
15
+ @property
16
+ def client(self):
17
+ """Redis client accessor."""
18
+ return self.index.client
19
+
20
+ def _check_vector_store_exists(self, index_name: str) -> bool:
21
+ """Check if a vector store exists for the given index name."""
22
+ try:
23
+ self.client.ft(index_name).info()
24
+ return True
25
+ except Exception:
26
+ return False
27
+
28
+ def _cleanup_vector_store(self, index_name: str) -> bool:
29
+ """Clean up the vector store index and its documents."""
30
+ if not self._check_vector_store_exists(index_name):
31
+ return True
32
+
33
+ try:
34
+ self.client.ft(index_name).dropindex(delete_documents=True)
35
+ logger.info(f"Successfully cleaned up vector store: {index_name}")
36
+ return True
37
+ except Exception as e:
38
+ logger.warning(f"Could not clean up vector store {index_name}: {e}")
39
+ return False
40
+
41
+ def get_chat_history(chat_history, use_chat_history: bool):
42
+ if chat_history and use_chat_history:
43
+ messages = chat_history.messages
44
+ formatted_history = []
45
+ for msg in messages:
46
+ if msg.type == "human":
47
+ formatted_history.append(f"👤 **Human**: {msg.content}\n")
48
+ elif msg.type == "ai":
49
+ formatted_history.append(f"🤖 **AI**: {msg.content}\n")
50
+ return "\n".join(formatted_history)
51
+ return "No chat history available."
52
+
53
+
54
+ def build_chain(self, history: List[any]):
55
+ retriever = self.vector_store.as_retriever(search_kwargs={"k": self.top_k})
56
+
57
+ messages = [
58
+ (
59
+ "system",
60
+ """You are a helpful AI assistant. Use the following pieces of
61
+ context to answer the user's question. If you don't know the
62
+ answer, just say that you don't know, don't try to make up an
63
+ answer. Please be as detailed as possible with your
64
+ answers.""",
65
+ ),
66
+ ("system", "Context: {context}"),
67
+ ]
68
+
69
+ if self.use_chat_history:
70
+ for msg in history:
71
+ messages.append((msg["role"], msg["content"]))
72
+
73
+ messages.append(("human", "{input}"))
74
+ messages.append(
75
+ (
76
+ "system",
77
+ "Provide a helpful and accurate answer based on the given context and question:",
78
+ )
79
+ )
80
+ prompt = ChatPromptTemplate.from_messages(messages)
81
+
82
+ # combine_docs_chain = create_stuff_documents_chain(self.cached_llm, prompt)
83
+ # rag_chain = create_retrieval_chain(retriever, combine_docs_chain)
84
+
85
+ # return rag_chain
86
+
87
+ def load_vector_store(self, index_name: str, embeddings) -> RedisVectorStore:
88
+ try:
89
+ # Get the metadata
90
+ metadata = self.get_pdf_metadata(index_name)
91
+ if not metadata:
92
+ # Try to reprocess from file
93
+ return self._reprocess_from_file(index_name, embeddings)
94
+
95
+ # Check if vector store exists
96
+ documents = None
97
+ if not self._check_vector_store_exists(index_name):
98
+ logger.info(f"Vector store missing for {index_name}, reprocessing")
99
+ vector_store = RedisVectorStore.from_documents(
100
+ documents,
101
+ embeddings,
102
+ redis_url=self.redis_url,
103
+ index_name=index_name,
104
+ key_prefix=f"pdf:{index_name}",
105
+ )
106
+ logger.info(f"Created vector store during reprocessing for {index_name}")
107
+
108
+ # Vector store exists, load it
109
+ vector_store = RedisVectorStore(
110
+ embeddings,
111
+ redis_url=self.redis_url,
112
+ index_name=index_name,
113
+ key_prefix=f"pdf:{index_name}",
114
+ )
115
+
116
+ logger.info(f"Successfully loaded PDF: {metadata.filename}")
117
+ return vector_store
118
+
119
+ except Exception as e:
120
+ logger.error(f"Failed to load PDF {index_name}: {e}")
121
+ raise
122
+
123
+ if __name__ == '__main__':
124
+ None
@@ -0,0 +1,4 @@
1
+ # -*- coding: utf-8 -*-
2
+ import uuid
3
+ def str_to_bool(val):
4
+ return str(val).lower() in ["1", "yes", "true"]
@@ -0,0 +1,123 @@
1
+ Metadata-Version: 2.4
2
+ Name: llm-hippocampus
3
+ Version: 0.0.1
4
+ Summary: LLM Hippocampus — a Context Engineering playground
5
+ Home-page:
6
+ Author: joelz
7
+ Author-email: joelz <zhongbj_2621@163.com>
8
+ Project-URL: Repository, https://github.com/redis-developer/redis-rag-workbench.git
9
+ Project-URL: Bug Tracker, https://github.com/redis-developer/redis-rag-workbench/issues
10
+ Keywords: python,redis,langchain,openai
11
+ Classifier: Development Status :: 4 - Beta
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: Programming Language :: Python
14
+ Requires-Python: >=3.11
15
+ Description-Content-Type: text/markdown
16
+ License-File: LICENSE
17
+ Requires-Dist: openai>=1.63.0
18
+ Requires-Dist: python-dotenv>=1.0.0
19
+ Requires-Dist: langchain>=0.3.19
20
+ Requires-Dist: tiktoken>=0.9.0
21
+ Requires-Dist: redis>=5.2.1
22
+ Requires-Dist: langchain-community>=0.3.18
23
+ Requires-Dist: langchain-huggingface>=0.1.2
24
+ Requires-Dist: langchain-openai>=0.3.6
25
+ Requires-Dist: langchain-experimental>=0.3.4
26
+ Requires-Dist: python-ulid>=2.7.0
27
+ Requires-Dist: pandas==2.2.3
28
+ Requires-Dist: hf-xet>=1.1.8
29
+ Requires-Dist: redisvl>=0.8.2
30
+ Requires-Dist: sentence-transformers>=5.1.0
31
+ Requires-Dist: langchain-redis>=0.2.3
32
+ Provides-Extra: dev
33
+ Requires-Dist: mypy<2.0.0,>=1.8.0; extra == "dev"
34
+ Requires-Dist: ruff<1.0.0,>=0.2.2; extra == "dev"
35
+ Dynamic: author
36
+ Dynamic: license-file
37
+ Dynamic: requires-python
38
+
39
+ <div align="center">
40
+ <h1>🚀 LLM Hippocampus</h1>
41
+
42
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
43
+ ![Language](https://img.shields.io/github/languages/top/redis-developer/redis-rag-workbench)
44
+ ![GitHub last commit](https://img.shields.io/github/last-commit/redis-developer/redis-rag-workbench)
45
+ ![Python](https://img.shields.io/badge/python-3.11%2B-blue)
46
+
47
+ 🎯 Build up and manage the LLM 's memory
48
+
49
+ </div>
50
+
51
+ 🔥 **LLM Hippocampus** helping your project for building and experimenting with **Context Engineering** applications. harness the full power of Redis for **lightning-fast vector search**, **intelligent semantic caching**, **persistent LLM memory**, and **smart context engineering **.
52
+
53
+ ✨ **What makes this special?**
54
+ - 🚀 **One-command setup** - pip install llm-hippocampus
55
+ - ⚡ **LLM support** - OpenAI
56
+ - 🎯 **Redis-powered** - Vector search, caching, and memory management
57
+ - 🐳 **Docker ready** - Building...
58
+ - 🔧 **Developer-first** - Support to Hot load by installing llm-hippocampus
59
+
60
+ ---
61
+
62
+ ## Table of Contents
63
+
64
+ - [Quick Start](#quick-start)
65
+ - [Prerequisites](#prerequisites)
66
+ - [Getting Started](#getting-started)
67
+ - [Available Commands](#available-commands)
68
+ - [Development Workflows](#development-workflows)
69
+ - [Environment Configuration](#environment-configuration)
70
+ - [Using Google VertexAI](#using-google-vertexai)
71
+ - [Project Structure](#project-structure)
72
+ - [Connecting to Redis Cloud](#connecting-to-redis-cloud)
73
+ - [Troubleshooting](#troubleshooting)
74
+ - [Contributing](#contributing)
75
+ - [License](#license)
76
+ - [Learn More](#learn-more)
77
+
78
+
79
+ ## Quick Start
80
+
81
+ **Get up and install in your project:**
82
+
83
+ ```bash
84
+ pip install llm-hippocampus
85
+ or
86
+ uv add llm-hippocampus
87
+ ```
88
+
89
+ Welcome to LLM Hippocampus! 🎉
90
+
91
+ ---
92
+
93
+ ## Prerequisites
94
+
95
+ 1. Make sure you have the following tools available:
96
+ - [python](https://www.docker.com/products/docker-desktop/) 3.11+
97
+ - [uv](https://docs.astral.sh/uv/)
98
+ - [Redis Stack](https://redis.io/)
99
+ 2. Setup one or more of the following:
100
+ - [OpenAI API](https://platform.openai.com/)
101
+ - You will need an API Key
102
+
103
+ ## Getting Started
104
+
105
+
106
+ ### Development Workflows
107
+ - Building
108
+
109
+
110
+ ## Project Structure
111
+
112
+
113
+ ## Contributing
114
+
115
+ 🤝 Contributions are welcome! Please feel free to submit a Pull Request.
116
+
117
+ ## License
118
+
119
+ This project is licensed under the MIT License - see the LICENSE file for details.
120
+
121
+ ## Troubleshooting
122
+
123
+ ## Learn More
@@ -0,0 +1,17 @@
1
+ llm_hippocampus/__init__.py,sha256=lVL4VPz3c4kWCgjPtL4uuQLNmgwK92q1ffAsv5El02k,25
2
+ llm_hippocampus/env.py,sha256=C9OFLJPEcwAkZM2FeRNUScnKjyukXa9Dsi8GONDCkiA,1155
3
+ llm_hippocampus/session.py,sha256=UvtgI1i0zcmTPcUfSVTQYXZLy_qUSBorQlkwWqAcH5Q,9138
4
+ llm_hippocampus/context/__init__.py,sha256=lVL4VPz3c4kWCgjPtL4uuQLNmgwK92q1ffAsv5El02k,25
5
+ llm_hippocampus/context/context_manager.py,sha256=yojMwt2MSnukqGvBK_fpJ8QRcYmxiU5VpwvnUIcLYx8,1042
6
+ llm_hippocampus/context/context_processing.py,sha256=Siikctaa2qNS1KxjOlgGyRXl1bU17fEhSOO--5SxwBY,95
7
+ llm_hippocampus/context/context_retrieval.py,sha256=lFPTK2DKKZSRdSRwD_ONTC5wk53HvH4koxoxt0SV39s,5917
8
+ llm_hippocampus/shared/__init__.py,sha256=lVL4VPz3c4kWCgjPtL4uuQLNmgwK92q1ffAsv5El02k,25
9
+ llm_hippocampus/shared/cached_llm.py,sha256=vlJZ-A-2Qf_SOSQhpAF4Mk2xpsKl7s12pz9mUTqLY3M,1860
10
+ llm_hippocampus/shared/logger.py,sha256=0EDVmDmFB94MfJFpxLxHnZ-2NFf7xCQv6mRf5t0tu_A,212
11
+ llm_hippocampus/shared/memory.py,sha256=PuzW8m-jzTLfBtsy6F6hLOSaKoPwL1JiRmwPl2B_y9k,4658
12
+ llm_hippocampus/shared/utils.py,sha256=hbnk22Uh88g6y4AJBQ28bOUC--4U2wKS6c6RDTeVYn4,110
13
+ llm_hippocampus-0.0.1.dist-info/licenses/LICENSE,sha256=3326Yl1wrasHkY6vStpHeVbSsmPhmM8LLvOwJMlrbmM,1067
14
+ llm_hippocampus-0.0.1.dist-info/METADATA,sha256=7mg2TcMQZLYFxOTlCKPTWbtJJQpOz2LS0EdcKPl5d44,3893
15
+ llm_hippocampus-0.0.1.dist-info/WHEEL,sha256=ck4Vq1_RXyvS4Jt6SI0Vz6fyVs4GWg7AINwpsaGEgPE,91
16
+ llm_hippocampus-0.0.1.dist-info/top_level.txt,sha256=oylGwnbCAJaxKbvU1Khew5lhXTro7kFvyuYfSpA0iGQ,16
17
+ llm_hippocampus-0.0.1.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (80.0.0)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 Redis, 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 @@
1
+ llm_hippocampus