TriCacheLLM-MMA 0.1.0__tar.gz → 0.1.2__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.
- {tricachellm_mma-0.1.0 → tricachellm_mma-0.1.2}/PKG-INFO +3 -1
- {tricachellm_mma-0.1.0 → tricachellm_mma-0.1.2}/README.md +2 -0
- tricachellm_mma-0.1.2/TriCacheLLM_MMA/__init__.py +16 -0
- {tricachellm_mma-0.1.0 → tricachellm_mma-0.1.2/TriCacheLLM_MMA}/portable_cache_Ai/portable_cache_rerankAi.py +2 -2
- {tricachellm_mma-0.1.0 → tricachellm_mma-0.1.2/TriCacheLLM_MMA}/portable_cache_bgWorkers/portable_cache_celery_conf.py +2 -2
- {tricachellm_mma-0.1.0 → tricachellm_mma-0.1.2/TriCacheLLM_MMA}/portable_cache_bgWorkers/portable_cache_workers.py +7 -7
- tricachellm_mma-0.1.2/TriCacheLLM_MMA/portable_cache_dbSchema.py +54 -0
- tricachellm_mma-0.1.2/TriCacheLLM_MMA/portable_cache_main.py +597 -0
- tricachellm_mma-0.1.2/TriCacheLLM_MMA/portable_cache_redis.py +65 -0
- {tricachellm_mma-0.1.0 → tricachellm_mma-0.1.2/TriCacheLLM_MMA}/portable_cache_schemas/portable_cache_dbConf.py +2 -2
- {tricachellm_mma-0.1.0 → tricachellm_mma-0.1.2/TriCacheLLM_MMA}/portable_cache_utils/portable_cache_embedding_model.py +1 -1
- {tricachellm_mma-0.1.0 → tricachellm_mma-0.1.2/TriCacheLLM_MMA}/portable_cache_utils/protable_cache_DynamicEnv_maker.py +1 -1
- {tricachellm_mma-0.1.0 → tricachellm_mma-0.1.2}/TriCacheLLM_MMA.egg-info/PKG-INFO +3 -1
- tricachellm_mma-0.1.2/TriCacheLLM_MMA.egg-info/SOURCES.txt +20 -0
- tricachellm_mma-0.1.2/TriCacheLLM_MMA.egg-info/top_level.txt +1 -0
- {tricachellm_mma-0.1.0 → tricachellm_mma-0.1.2}/pyproject.toml +3 -3
- tricachellm_mma-0.1.0/TriCacheLLM_MMA.egg-info/SOURCES.txt +0 -16
- tricachellm_mma-0.1.0/TriCacheLLM_MMA.egg-info/top_level.txt +0 -4
- {tricachellm_mma-0.1.0 → tricachellm_mma-0.1.2}/LICENSE +0 -0
- {tricachellm_mma-0.1.0 → tricachellm_mma-0.1.2/TriCacheLLM_MMA}/portable_cache_schemas/portable_cache_dbBase.py +0 -0
- {tricachellm_mma-0.1.0 → tricachellm_mma-0.1.2/TriCacheLLM_MMA}/portable_cache_schemas/portable_cache_schemas.py +0 -0
- {tricachellm_mma-0.1.0 → tricachellm_mma-0.1.2}/TriCacheLLM_MMA.egg-info/dependency_links.txt +0 -0
- {tricachellm_mma-0.1.0 → tricachellm_mma-0.1.2}/TriCacheLLM_MMA.egg-info/requires.txt +0 -0
- {tricachellm_mma-0.1.0 → tricachellm_mma-0.1.2}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: TriCacheLLM_MMA
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.2
|
|
4
4
|
Summary: A robust three-tier LLM-aware caching SDK using Redis, ChromaDB, and automatic SQLite bootstrap.
|
|
5
5
|
Author-email: Mohib Ashfaq Butt <inboxmohib@gmail.com>
|
|
6
6
|
Project-URL: Homepage, https://github.com/mohib-ash/TriCacheLLM_MMA
|
|
@@ -421,6 +421,8 @@ Or install the development version directly from the repository:
|
|
|
421
421
|
pip install .
|
|
422
422
|
```
|
|
423
423
|
|
|
424
|
+
PyPI link: https://pypi.org/project/TriCacheLLM-MMA/0.1.0/
|
|
425
|
+
|
|
424
426
|
---
|
|
425
427
|
|
|
426
428
|
# Redis
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
from .portable_cache_main import (
|
|
2
|
+
create_cache_system,
|
|
3
|
+
populate_cache,
|
|
4
|
+
check_cache,
|
|
5
|
+
close_cache_system,
|
|
6
|
+
check_tenant_creation_status
|
|
7
|
+
)
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
__all__ = [
|
|
11
|
+
"create_cache_system",
|
|
12
|
+
"populate_cache",
|
|
13
|
+
"check_cache",
|
|
14
|
+
"close_cache_system",
|
|
15
|
+
"check_tenant_creation_status",
|
|
16
|
+
]
|
|
@@ -5,8 +5,8 @@ from pathlib import Path
|
|
|
5
5
|
from sqlalchemy import create_engine
|
|
6
6
|
from sqlalchemy.orm import sessionmaker
|
|
7
7
|
from langchain_core.documents import Document as LangChainDocument
|
|
8
|
-
from portable_cache_utils.protable_cache_DynamicEnv_maker import get_settings
|
|
9
|
-
from portable_cache_dbSchema import Paths
|
|
8
|
+
from ..portable_cache_utils.protable_cache_DynamicEnv_maker import get_settings
|
|
9
|
+
from ..portable_cache_dbSchema import Paths
|
|
10
10
|
|
|
11
11
|
class CohereManager:
|
|
12
12
|
"""Encapsulates the Cohere client to prevent import-time crashes."""
|
|
@@ -3,8 +3,8 @@ from pathlib import Path
|
|
|
3
3
|
from celery import Celery
|
|
4
4
|
from sqlalchemy import create_engine
|
|
5
5
|
from sqlalchemy.orm import sessionmaker
|
|
6
|
-
from portable_cache_utils.protable_cache_DynamicEnv_maker import get_settings
|
|
7
|
-
from portable_cache_dbSchema import Paths
|
|
6
|
+
from ..portable_cache_utils.protable_cache_DynamicEnv_maker import get_settings
|
|
7
|
+
from ..portable_cache_dbSchema import Paths
|
|
8
8
|
|
|
9
9
|
def get_celery_redis_url() -> str:
|
|
10
10
|
"""Sneaks a peek into the SQLite registry DB to grab the true source-of-truth redis_url for Celery."""
|
|
@@ -2,18 +2,18 @@
|
|
|
2
2
|
import asyncio
|
|
3
3
|
from pathlib import Path
|
|
4
4
|
from typing import Any
|
|
5
|
-
from portable_cache_bgWorkers.portable_cache_celery_conf import celery_app
|
|
6
|
-
from portable_cache_schemas.portable_cache_dbConf import db_manager
|
|
5
|
+
from ..portable_cache_bgWorkers.portable_cache_celery_conf import celery_app
|
|
6
|
+
from ..portable_cache_schemas.portable_cache_dbConf import db_manager
|
|
7
7
|
import json
|
|
8
|
-
from portable_cache_dbSchema import CacheVDBResource
|
|
9
|
-
from portable_cache_utils.protable_cache_DynamicEnv_maker import get_settings
|
|
10
|
-
from portable_cache_schemas.portable_cache_schemas import CacheVDBStatus
|
|
8
|
+
from ..portable_cache_dbSchema import CacheVDBResource
|
|
9
|
+
from ..portable_cache_utils.protable_cache_DynamicEnv_maker import get_settings
|
|
10
|
+
from ..portable_cache_schemas.portable_cache_schemas import CacheVDBStatus
|
|
11
11
|
from langchain_core.documents import Document as LangChainDocument
|
|
12
12
|
from sqlalchemy import select
|
|
13
|
-
from portable_cache_utils.portable_cache_embedding_model import embedding_model
|
|
13
|
+
from ..portable_cache_utils.portable_cache_embedding_model import embedding_model
|
|
14
14
|
from langchain_chroma import Chroma
|
|
15
15
|
from datetime import datetime, timezone
|
|
16
|
-
from portable_cache_dbSchema import Paths
|
|
16
|
+
from ..portable_cache_dbSchema import Paths
|
|
17
17
|
from sqlalchemy import text
|
|
18
18
|
import time
|
|
19
19
|
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
# File: portable_cache_dbSchema.py
|
|
2
|
+
from sqlalchemy import Column, Integer, DateTime, Boolean, String, Text, ForeignKey, Enum as SQLEnum, text
|
|
3
|
+
from sqlalchemy.sql import func
|
|
4
|
+
from sqlalchemy.orm import relationship
|
|
5
|
+
from .portable_cache_schemas.portable_cache_dbBase import Base
|
|
6
|
+
from .portable_cache_schemas.portable_cache_schemas import CacheVDBStatus
|
|
7
|
+
|
|
8
|
+
class User(Base):
|
|
9
|
+
__tablename__ = "users"
|
|
10
|
+
|
|
11
|
+
user_id = Column(Integer, primary_key=True, autoincrement=True)
|
|
12
|
+
created_at = Column(DateTime(timezone=True), server_default=func.now(), nullable=False)
|
|
13
|
+
is_banned = Column(Boolean, nullable=False, server_default=text("0"))
|
|
14
|
+
|
|
15
|
+
cache_vdb_resource = relationship("CacheVDBResource", back_populates="user", uselist=False, cascade="all, delete-orphan")
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class CacheVDBResource(Base):
|
|
19
|
+
__tablename__ = "cache_vdb_resources"
|
|
20
|
+
|
|
21
|
+
cache_vdb_id = Column(Integer, primary_key=True, autoincrement=True)
|
|
22
|
+
user_id = Column(Integer, ForeignKey("users.user_id", ondelete="CASCADE"), nullable=False, unique=True, index=True)
|
|
23
|
+
|
|
24
|
+
status = Column(SQLEnum(CacheVDBStatus), default=CacheVDBStatus.PENDING, nullable=False, index=True)
|
|
25
|
+
version = Column(Integer, default=0, server_default="0", nullable=False)
|
|
26
|
+
|
|
27
|
+
vdb_path = Column(String(512), nullable=True)
|
|
28
|
+
failure_reason = Column(Text, nullable=True)
|
|
29
|
+
|
|
30
|
+
created_at = Column(
|
|
31
|
+
DateTime(timezone=True),
|
|
32
|
+
server_default=func.now(),
|
|
33
|
+
nullable=False,
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
updated_at = Column(
|
|
37
|
+
DateTime(timezone=True),
|
|
38
|
+
server_default=func.now(),
|
|
39
|
+
onupdate=func.now(),
|
|
40
|
+
nullable=False,
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
user = relationship("User", back_populates="cache_vdb_resource")
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
class Paths(Base):
|
|
47
|
+
__tablename__ = "paths"
|
|
48
|
+
|
|
49
|
+
id = Column(Integer, primary_key=True, default=1)
|
|
50
|
+
portable_env_path = Column(String(512), nullable=False)
|
|
51
|
+
chroma_db_dir = Column(String(512), nullable=False)
|
|
52
|
+
redis_url = Column(String(512), nullable=False)
|
|
53
|
+
db_path = Column(String(512), nullable=False)
|
|
54
|
+
cohere_api_key = Column(String(512), nullable=False)
|
|
@@ -0,0 +1,597 @@
|
|
|
1
|
+
# File: portable_cache_main.py
|
|
2
|
+
import asyncio
|
|
3
|
+
import hashlib
|
|
4
|
+
import json
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
from typing import Any
|
|
7
|
+
from .portable_cache_schemas.portable_cache_dbConf import init_cache_database, init_db_tables, db_manager
|
|
8
|
+
from .portable_cache_schemas.portable_cache_schemas import CacheVDBStatus
|
|
9
|
+
from .portable_cache_dbSchema import CacheVDBResource
|
|
10
|
+
from sqlalchemy import select
|
|
11
|
+
from .portable_cache_bgWorkers.portable_cache_workers import create_cache_vdb_worker, push_responce_in_cache_worker
|
|
12
|
+
from .portable_cache_utils.portable_cache_embedding_model import embedding_model
|
|
13
|
+
from redis.asyncio import Redis
|
|
14
|
+
from redis.commands.search.field import TagField, VectorField
|
|
15
|
+
from redis.commands.search.index_definition import (
|
|
16
|
+
IndexDefinition,
|
|
17
|
+
IndexType,
|
|
18
|
+
)
|
|
19
|
+
from redis.commands.search.query import Query
|
|
20
|
+
from redis.exceptions import ResponseError
|
|
21
|
+
from pydantic import BaseModel
|
|
22
|
+
import numpy as np
|
|
23
|
+
from langchain_chroma import Chroma
|
|
24
|
+
from langchain_core.documents import Document as LangChainDocument
|
|
25
|
+
import random
|
|
26
|
+
from .portable_cache_Ai.portable_cache_rerankAi import portable_cache_cohere_rerank
|
|
27
|
+
from typing import Optional
|
|
28
|
+
from .portable_cache_redis import get_redis
|
|
29
|
+
from .portable_cache_redis import redis_manager
|
|
30
|
+
from .portable_cache_utils.protable_cache_DynamicEnv_maker import system_key
|
|
31
|
+
from .portable_cache_dbSchema import Paths
|
|
32
|
+
|
|
33
|
+
def create_vector_index_schema(dim: int, distance_metric: str = "COSINE", m: int = 16, ef_construction: int = 200, ef_runtime: int = 10) -> list:
|
|
34
|
+
return [
|
|
35
|
+
VectorField(
|
|
36
|
+
"vector",
|
|
37
|
+
"HNSW",
|
|
38
|
+
{
|
|
39
|
+
"TYPE": "FLOAT32",
|
|
40
|
+
"DIM": dim,
|
|
41
|
+
"DISTANCE_METRIC": distance_metric,
|
|
42
|
+
"M": m,
|
|
43
|
+
"EF_CONSTRUCTION": ef_construction,
|
|
44
|
+
"EF_RUNTIME": ef_runtime
|
|
45
|
+
}
|
|
46
|
+
),
|
|
47
|
+
TagField("scope"),
|
|
48
|
+
]
|
|
49
|
+
|
|
50
|
+
async def generate_embedding(text: str) -> list[float]:
|
|
51
|
+
return await asyncio.to_thread(embedding_model.embed_query, text)
|
|
52
|
+
|
|
53
|
+
def generate_cache_key(question: str, user_id: Any = 0) -> str:
|
|
54
|
+
normalized_q = question.strip().lower()
|
|
55
|
+
payload = f"{user_id}:{normalized_q}"
|
|
56
|
+
hash_sig = hashlib.sha256(payload.encode("utf-8")).hexdigest()
|
|
57
|
+
return f"ai_hot_cache:{user_id}:{hash_sig}"
|
|
58
|
+
|
|
59
|
+
async def create_cache_vdb_inishiator(user_id, db) -> dict[str, Any] | None:
|
|
60
|
+
stmt = select(CacheVDBResource).where(
|
|
61
|
+
CacheVDBResource.user_id == user_id,
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
result = await db.execute(stmt)
|
|
65
|
+
cache_resource = result.scalar_one_or_none()
|
|
66
|
+
|
|
67
|
+
if cache_resource is None:
|
|
68
|
+
print(f"CACHE_VDB_CREATING_PENDING where user_id: {user_id}")
|
|
69
|
+
cache_resource = CacheVDBResource(
|
|
70
|
+
user_id=user_id,
|
|
71
|
+
status=CacheVDBStatus.PENDING,
|
|
72
|
+
version=0,
|
|
73
|
+
)
|
|
74
|
+
db.add(cache_resource)
|
|
75
|
+
await db.commit()
|
|
76
|
+
await db.refresh(cache_resource)
|
|
77
|
+
|
|
78
|
+
proceed = cache_resource.status in (
|
|
79
|
+
CacheVDBStatus.PENDING,
|
|
80
|
+
CacheVDBStatus.FAILED,
|
|
81
|
+
)
|
|
82
|
+
|
|
83
|
+
if not proceed:
|
|
84
|
+
print(f"CACHE_VDB_STATUS_SKIPPED where user_id: {user_id}")
|
|
85
|
+
return None
|
|
86
|
+
|
|
87
|
+
print(f"CACHE_VDB_LOCKED_PROCESSING where user_id: {user_id}")
|
|
88
|
+
cache_resource.status = CacheVDBStatus.PROCESSING
|
|
89
|
+
await db.commit()
|
|
90
|
+
await db.refresh(cache_resource)
|
|
91
|
+
|
|
92
|
+
print(f"CACHE_VDB_WORKER_FIRED where user_id: {user_id}")
|
|
93
|
+
task_id = create_cache_vdb_worker.delay(user_id=user_id)
|
|
94
|
+
|
|
95
|
+
data: dict = {
|
|
96
|
+
"success": True,
|
|
97
|
+
"task_id": task_id.id if hasattr(task_id, "id") else str(task_id) if task_id else None,
|
|
98
|
+
"status": "PROCESSING"
|
|
99
|
+
}
|
|
100
|
+
return data
|
|
101
|
+
|
|
102
|
+
async def create_redis_vector_index(user_id, redis_client: Redis, dim: int = 384):
|
|
103
|
+
flag_key = f"meta:index_initialized:{user_id}"
|
|
104
|
+
index_name = f"idx:user_cache:{user_id}"
|
|
105
|
+
print(f"REDIS_INDEX_CHECK_STARTED where user_id: {user_id}")
|
|
106
|
+
|
|
107
|
+
if await redis_client.exists(flag_key):
|
|
108
|
+
try:
|
|
109
|
+
await redis_client.ft(index_name).info()
|
|
110
|
+
print(f"REDIS_INDEX_EXISTS where user_id: {user_id}")
|
|
111
|
+
return
|
|
112
|
+
except ResponseError:
|
|
113
|
+
print(f"REDIS_INDEX_FLAG_STALE where user_id: {user_id}")
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
schema = (
|
|
117
|
+
VectorField(
|
|
118
|
+
"vector",
|
|
119
|
+
"HNSW",
|
|
120
|
+
{
|
|
121
|
+
"TYPE": "FLOAT32",
|
|
122
|
+
"DIM": dim,
|
|
123
|
+
"DISTANCE_METRIC": "COSINE",
|
|
124
|
+
"M": 16,
|
|
125
|
+
"EF_CONSTRUCTION": 200,
|
|
126
|
+
"EF_RUNTIME": 10
|
|
127
|
+
}
|
|
128
|
+
),
|
|
129
|
+
TagField("scope"),
|
|
130
|
+
)
|
|
131
|
+
|
|
132
|
+
try:
|
|
133
|
+
print(f"REDIS_INDEX_CREATION_STARTED where user_id: {user_id}")
|
|
134
|
+
await redis_client.ft(index_name).create_index(
|
|
135
|
+
schema,
|
|
136
|
+
definition=IndexDefinition(
|
|
137
|
+
prefix=[f"cache:item:{user_id}:"],
|
|
138
|
+
index_type=IndexType.HASH
|
|
139
|
+
)
|
|
140
|
+
)
|
|
141
|
+
print(f"REDIS_INDEX_CREATED_SUCCESS where user_id: {user_id}")
|
|
142
|
+
except ResponseError as e:
|
|
143
|
+
if "Index already exists" not in str(e):
|
|
144
|
+
print(f"REDIS_INDEX_CREATION_ERROR where error: {str(e)} and user_id: {user_id}")
|
|
145
|
+
raise e
|
|
146
|
+
print(f"REDIS_INDEX_ALREADY_EXISTS where user_id: {user_id}")
|
|
147
|
+
|
|
148
|
+
await redis_client.set(flag_key, "1")
|
|
149
|
+
print(f"REDIS_INDEX_INITIALIZATION_COMPLETE where user_id: {user_id}")
|
|
150
|
+
|
|
151
|
+
def portable_cache_normalize_scope(scope: Any = None) -> str:
|
|
152
|
+
if scope is None:
|
|
153
|
+
return "global"
|
|
154
|
+
if isinstance(scope, list):
|
|
155
|
+
return "__".join(sorted(str(s).strip().lower() for s in scope))
|
|
156
|
+
if isinstance(scope, tuple):
|
|
157
|
+
return "__".join(sorted(str(s).strip().lower() for s in scope))
|
|
158
|
+
return str(scope).strip().lower()
|
|
159
|
+
|
|
160
|
+
def escape_redis_tag(value: str) -> str:
|
|
161
|
+
special_chars = r",.<>{}[]\"':;!@#$%^&*()-+=~|"
|
|
162
|
+
return "".join(
|
|
163
|
+
f"\\{char}" if char in special_chars or char == " " else char
|
|
164
|
+
for char in value
|
|
165
|
+
)
|
|
166
|
+
|
|
167
|
+
async def save_to_redis_vector_cache(user_id,
|
|
168
|
+
question: str,
|
|
169
|
+
response_payload: dict | BaseModel | Any,
|
|
170
|
+
redis_client: Redis,
|
|
171
|
+
scope_input: Any = None
|
|
172
|
+
):
|
|
173
|
+
print(f"REDIS_VECTOR_SAVE_STARTED where user_id: {user_id}")
|
|
174
|
+
try:
|
|
175
|
+
await create_redis_vector_index(user_id=user_id, redis_client=redis_client)
|
|
176
|
+
print(f"REDIS_VECTOR_INDEX_ENSURED where user_id: {user_id}")
|
|
177
|
+
|
|
178
|
+
vector = await generate_embedding(question)
|
|
179
|
+
print(f"REDIS_VECTOR_EMBEDDING_GENERATED where user_id: {user_id}")
|
|
180
|
+
vector_bytes = np.array(vector, dtype=np.float32).tobytes()
|
|
181
|
+
|
|
182
|
+
if isinstance(response_payload, BaseModel):
|
|
183
|
+
payload_data = response_payload.model_dump_json()
|
|
184
|
+
else:
|
|
185
|
+
payload_data = json.dumps(response_payload)
|
|
186
|
+
print(f"REDIS_VECTOR_PAYLOAD_SERIALIZED where user_id: {user_id}")
|
|
187
|
+
|
|
188
|
+
normalized_question = question.strip().lower()
|
|
189
|
+
scope = portable_cache_normalize_scope(scope_input)
|
|
190
|
+
|
|
191
|
+
scope_payload = f"{normalized_question}:{scope}"
|
|
192
|
+
entry_id = hashlib.sha256(scope_payload.encode("utf-8")).hexdigest()
|
|
193
|
+
redis_key = f"cache:item:{user_id}:{entry_id}"
|
|
194
|
+
|
|
195
|
+
await redis_client.hset(
|
|
196
|
+
redis_key,
|
|
197
|
+
mapping={
|
|
198
|
+
"vector": vector_bytes,
|
|
199
|
+
"response_data": payload_data,
|
|
200
|
+
"scope": scope,
|
|
201
|
+
}
|
|
202
|
+
)
|
|
203
|
+
print(f"REDIS_VECTOR_HASH_SAVED where user_id: {user_id}")
|
|
204
|
+
|
|
205
|
+
await asyncio.create_task(redis_client.expire(redis_key, 86400))
|
|
206
|
+
print(f"REDIS_VECTOR_TTL_SET where user_id: {user_id}")
|
|
207
|
+
|
|
208
|
+
except Exception as e:
|
|
209
|
+
print(f"REDIS_VECTOR_SAVE_ERROR where error: {str(e)} and user_id: {user_id}")
|
|
210
|
+
raise
|
|
211
|
+
|
|
212
|
+
async def get_user_cache_vdb(user_id, db) -> Chroma | None:
|
|
213
|
+
print(f"CHECKING_USER_VDB where user_id: {user_id}")
|
|
214
|
+
|
|
215
|
+
stmt = select(CacheVDBResource).where(CacheVDBResource.user_id == user_id)
|
|
216
|
+
result = await db.execute(stmt)
|
|
217
|
+
cache_res = result.scalar_one_or_none()
|
|
218
|
+
|
|
219
|
+
if cache_res is None or cache_res.status != CacheVDBStatus.READY or not cache_res.vdb_path:
|
|
220
|
+
print(f"VDB_NOT_FOUND where user_id: {user_id}")
|
|
221
|
+
return None
|
|
222
|
+
|
|
223
|
+
print(f"VDB_FOUND where user_id: {user_id}")
|
|
224
|
+
|
|
225
|
+
return await asyncio.to_thread(
|
|
226
|
+
lambda: Chroma(
|
|
227
|
+
persist_directory=cache_res.vdb_path,
|
|
228
|
+
collection_name=f"question_cache_{user_id}",
|
|
229
|
+
embedding_function=embedding_model,
|
|
230
|
+
)
|
|
231
|
+
)
|
|
232
|
+
|
|
233
|
+
async def get_cached(question: str, cache_vdb: Chroma, user_id) -> dict[str, Any]:
|
|
234
|
+
print(f"CHROMA_SIMILARITY_SEARCH_STARTED where user_id: {user_id}")
|
|
235
|
+
try:
|
|
236
|
+
fetched_and_scores: list[tuple[LangChainDocument, float]] = await asyncio.to_thread(
|
|
237
|
+
cache_vdb.similarity_search_with_score,
|
|
238
|
+
query=question,
|
|
239
|
+
k=10
|
|
240
|
+
)
|
|
241
|
+
except Exception as exc:
|
|
242
|
+
print(f"CHROMA_SIMILARITY_SEARCH_ERROR where error: {str(exc)} and user_id: {user_id}")
|
|
243
|
+
return {"success": False, "data": None}
|
|
244
|
+
|
|
245
|
+
if not fetched_and_scores:
|
|
246
|
+
print(f"CHROMA_CANDIDATES_NOT_FOUND where user_id: {user_id}")
|
|
247
|
+
return {"success": False, "data": None}
|
|
248
|
+
|
|
249
|
+
documents_for_rerank = [
|
|
250
|
+
LangChainDocument(
|
|
251
|
+
page_content=(
|
|
252
|
+
f"Cached Question:\n{doc.metadata.get('question')}\n\n"
|
|
253
|
+
f"Cached Answer:\n{doc.metadata.get('llm_response')}"
|
|
254
|
+
),
|
|
255
|
+
metadata=doc.metadata
|
|
256
|
+
)
|
|
257
|
+
for doc, score in fetched_and_scores
|
|
258
|
+
]
|
|
259
|
+
|
|
260
|
+
print(f"CHROMA_RERANK_STARTED where user_id: {user_id}")
|
|
261
|
+
rerank_response: dict = await portable_cache_cohere_rerank(
|
|
262
|
+
question=question,
|
|
263
|
+
user_id=user_id,
|
|
264
|
+
received_docs=documents_for_rerank,
|
|
265
|
+
top_k=3
|
|
266
|
+
)
|
|
267
|
+
|
|
268
|
+
if not rerank_response["success"]:
|
|
269
|
+
print(f"CHROMA_RERANK_FAILED where user_id: {user_id}")
|
|
270
|
+
return {"success": False, "data": None}
|
|
271
|
+
|
|
272
|
+
best_doc = rerank_response["data"][0]
|
|
273
|
+
best_score = best_doc.metadata.get("rerank_score", 0.0)
|
|
274
|
+
|
|
275
|
+
RELEVANCE_THRESHOLD = 0.85
|
|
276
|
+
DELIBERATE_MISS_RATE = 0.10
|
|
277
|
+
|
|
278
|
+
if best_score < RELEVANCE_THRESHOLD:
|
|
279
|
+
print(f"CHROMA_RELEVANCE_THRESHOLD_FAILED where user_id: {user_id}")
|
|
280
|
+
return {"success": False, "data": None}
|
|
281
|
+
|
|
282
|
+
if random.random() < DELIBERATE_MISS_RATE:
|
|
283
|
+
print(f"CHROMA_DELIBERATE_MISS_TRIGGERED where user_id: {user_id}")
|
|
284
|
+
return {"success": False, "data": None}
|
|
285
|
+
|
|
286
|
+
print(f"CHROMA_CACHE_HIT where user_id: {user_id}")
|
|
287
|
+
raw_response = best_doc.metadata.get("full_llm_response") or best_doc.metadata.get("llm_response")
|
|
288
|
+
parsed_answer = json.loads(raw_response) if isinstance(raw_response, str) else raw_response
|
|
289
|
+
|
|
290
|
+
return {"success": True, "data": parsed_answer}
|
|
291
|
+
|
|
292
|
+
async def get_redis_vector_cached(question: str, user_id, redis_client: Redis, top_k: int = 1, scope: Any = None) -> dict[str, Any]:
|
|
293
|
+
print(f"REDIS_VECTOR_SEARCH_STARTED where user_id: {user_id}")
|
|
294
|
+
try:
|
|
295
|
+
print(f"REDIS_VECTOR_GENERATING_EMBEDDING where user_id: {user_id}")
|
|
296
|
+
question_vector: list[float] = await generate_embedding(question)
|
|
297
|
+
|
|
298
|
+
vector_bytes = np.array(question_vector, dtype=np.float32).tobytes()
|
|
299
|
+
index_name = f"idx:user_cache:{user_id}"
|
|
300
|
+
|
|
301
|
+
normalized_scope: str = portable_cache_normalize_scope(scope)
|
|
302
|
+
print(f"REDIS_VECTOR_SCOPE_NORMALIZED where user_id: {user_id}")
|
|
303
|
+
|
|
304
|
+
escaped_scope = escape_redis_tag(normalized_scope)
|
|
305
|
+
query_str = (
|
|
306
|
+
f"(@scope:{{{escaped_scope}}})"
|
|
307
|
+
f"=>[KNN {top_k} @vector $vec AS vector_score]"
|
|
308
|
+
)
|
|
309
|
+
|
|
310
|
+
query = (
|
|
311
|
+
Query(query_str)
|
|
312
|
+
.return_fields("response_data", "vector_score",)
|
|
313
|
+
.sort_by("vector_score")
|
|
314
|
+
.dialect(2)
|
|
315
|
+
)
|
|
316
|
+
|
|
317
|
+
print(f"REDIS_VECTOR_QUERY_EXECUTING where user_id: {user_id}")
|
|
318
|
+
results = await redis_client.ft(index_name).search(
|
|
319
|
+
query,
|
|
320
|
+
query_params={"vec": vector_bytes}
|
|
321
|
+
)
|
|
322
|
+
|
|
323
|
+
if results and results.docs:
|
|
324
|
+
doc = results.docs[0]
|
|
325
|
+
|
|
326
|
+
score = float(doc.vector_score)
|
|
327
|
+
if score > 0.20:
|
|
328
|
+
# print(f"REDIS_VECTOR_SCORE where user_id: {user_id} "f"score={score}")
|
|
329
|
+
print(f"REDIS_VECTOR_SCORE_MISMATCH where user_id: {user_id}")
|
|
330
|
+
return {"success": False, "data": None}
|
|
331
|
+
|
|
332
|
+
|
|
333
|
+
|
|
334
|
+
print(f"REDIS_VECTOR_HIT_FOUND where user_id: {user_id}")
|
|
335
|
+
return {"success": True, "data": json.loads(doc.response_data)}
|
|
336
|
+
|
|
337
|
+
print(f"REDIS_VECTOR_CACHE_MISS user_id: {user_id}")
|
|
338
|
+
return {"success": False, "data": None}
|
|
339
|
+
|
|
340
|
+
except Exception as e:
|
|
341
|
+
print(f"REDIS_VECTOR_ERROR where error: {str(e)} and user_id: {user_id}")
|
|
342
|
+
return {"success": False, "data": None}
|
|
343
|
+
|
|
344
|
+
async def multi_tier_cache_system(question: str, user_id, normal_redis_instance: Redis, db, cache_key: str, scope: Any = None, top_k: int = 1) -> dict[str, Any]:
|
|
345
|
+
#Tier 1:
|
|
346
|
+
cached_payload = await normal_redis_instance.get(cache_key)
|
|
347
|
+
if cached_payload:
|
|
348
|
+
print(f"CACHE_TIER1_EXACT_HIT where user_id: {user_id}")
|
|
349
|
+
return {"success": True, "data": json.loads(cached_payload)}
|
|
350
|
+
|
|
351
|
+
#Tier 2:
|
|
352
|
+
print(f"CACHE_POLICY_CACHEABLE where user_id: {user_id}")
|
|
353
|
+
redis_vector_result: dict = await get_redis_vector_cached(question=question, user_id=user_id, redis_client=normal_redis_instance, scope=scope, top_k=top_k)
|
|
354
|
+
|
|
355
|
+
if redis_vector_result["success"]:
|
|
356
|
+
print(f"CACHE_TIER2_VECTOR_HIT where user_id: {user_id}")
|
|
357
|
+
asyncio.create_task(
|
|
358
|
+
normal_redis_instance.setex(cache_key, 86400, json.dumps(redis_vector_result["data"]))
|
|
359
|
+
)
|
|
360
|
+
return {"success": True, "data": redis_vector_result["data"]}
|
|
361
|
+
|
|
362
|
+
|
|
363
|
+
#Tier 3:
|
|
364
|
+
cache_vdb: Chroma | None = await get_user_cache_vdb(user_id=user_id, db=db)
|
|
365
|
+
if cache_vdb is not None:
|
|
366
|
+
chroma_result: dict = await get_cached(question=question, cache_vdb=cache_vdb, user_id=user_id)
|
|
367
|
+
|
|
368
|
+
if chroma_result["success"]:
|
|
369
|
+
print(f"CACHE_TIER3_CHROMA_HIT where user_id: {user_id}")
|
|
370
|
+
async def run_both():
|
|
371
|
+
await asyncio.gather(
|
|
372
|
+
normal_redis_instance.setex(cache_key, 86400, json.dumps(chroma_result["data"])),
|
|
373
|
+
save_to_redis_vector_cache(
|
|
374
|
+
user_id=user_id,
|
|
375
|
+
question=question,
|
|
376
|
+
response_payload=chroma_result["data"],
|
|
377
|
+
redis_client=normal_redis_instance,
|
|
378
|
+
scope_input=scope,
|
|
379
|
+
),
|
|
380
|
+
return_exceptions=True
|
|
381
|
+
)
|
|
382
|
+
|
|
383
|
+
asyncio.create_task(run_both())
|
|
384
|
+
return {"data": chroma_result["data"], "success": True}
|
|
385
|
+
else:
|
|
386
|
+
print(f"CACHE_SYSTEM_MISS_FALLBACK where user_id: {user_id}")
|
|
387
|
+
return {"success": False, "data": None}
|
|
388
|
+
else:
|
|
389
|
+
print(f"CACHE_VDB_NOT_FOUND where user_id: {user_id}")
|
|
390
|
+
return {"success": False, "data": None}
|
|
391
|
+
|
|
392
|
+
async def push_responce_in_cache_inishiator(model_output: Any, question: str, user_id) -> dict:
|
|
393
|
+
model_dict = model_output.model_dump() if hasattr(model_output, "model_dump") else model_output
|
|
394
|
+
|
|
395
|
+
task = push_responce_in_cache_worker.delay(
|
|
396
|
+
user_id=user_id,
|
|
397
|
+
question=question,
|
|
398
|
+
model_output_dict=model_dict,
|
|
399
|
+
)
|
|
400
|
+
return {
|
|
401
|
+
"success": True,
|
|
402
|
+
"task_id": task.id if hasattr(task, "id") else str(task.id),
|
|
403
|
+
"status": "PROCESSING"
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
|
|
407
|
+
|
|
408
|
+
|
|
409
|
+
|
|
410
|
+
|
|
411
|
+
async def create_cache_system(
|
|
412
|
+
redis_url: Optional[str] = None,
|
|
413
|
+
cohere_api_key: Optional[str] = None,
|
|
414
|
+
chroma_db_dir: Optional[str] = None,
|
|
415
|
+
db_path: Optional[str] = None,
|
|
416
|
+
user_id: Any = 0
|
|
417
|
+
) -> dict[str, bool | str] | None:
|
|
418
|
+
|
|
419
|
+
print(f"CACHE_SYSTEM_INIT_STARTED where user_id: {user_id}")
|
|
420
|
+
try:
|
|
421
|
+
if user_id == 0:
|
|
422
|
+
if not all([
|
|
423
|
+
redis_url,
|
|
424
|
+
cohere_api_key,
|
|
425
|
+
chroma_db_dir,
|
|
426
|
+
db_path,
|
|
427
|
+
]):
|
|
428
|
+
raise ValueError(
|
|
429
|
+
"System initialization requires redis_url, "
|
|
430
|
+
"cohere_api_key, chroma_db_dir and db_path."
|
|
431
|
+
)
|
|
432
|
+
|
|
433
|
+
#these are to be made for the first time
|
|
434
|
+
db_file = Path(db_path)
|
|
435
|
+
init_cache_database(db_file) #this creates all session mamanger for db_manager object!
|
|
436
|
+
await init_db_tables() #this will stay out regardless in case of server cold restart
|
|
437
|
+
|
|
438
|
+
key_res: dict = system_key(redis_url=redis_url, chroma_db_dir=chroma_db_dir, cohere_api_key=cohere_api_key, portable_cache_registry_db=Path(db_path))
|
|
439
|
+
if not key_res["success"]:
|
|
440
|
+
raise ValueError("Failed to initialize system keys. Verify if the keys are correct.")
|
|
441
|
+
|
|
442
|
+
return #user_id=0 doesnt need its own vdb! if you want to be it user-self be user_id=1 -> single user
|
|
443
|
+
#if you want multi-tanent then keep sending in user_ids lol
|
|
444
|
+
|
|
445
|
+
|
|
446
|
+
async with db_manager.async_session() as db:
|
|
447
|
+
ans: None | dict = await create_cache_vdb_inishiator(user_id=user_id, db=db)
|
|
448
|
+
|
|
449
|
+
if ans is None:
|
|
450
|
+
print(f"CACHE_SYSTEM_INIT_SKIPPED: Already exists or in progress for user_id: {user_id}")
|
|
451
|
+
return {
|
|
452
|
+
"success": False,
|
|
453
|
+
"task_id": None,
|
|
454
|
+
"status_msg": "already created or is being created"
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
if ans.get("success"):
|
|
458
|
+
task_id = ans.get("task_id")
|
|
459
|
+
print(f"CACHE_SYSTEM_INIT_STARTED_ASYNC with task_id: {task_id} for user_id: {user_id}")
|
|
460
|
+
return {
|
|
461
|
+
"success": True,
|
|
462
|
+
"task_id": task_id,
|
|
463
|
+
"status_msg": "Started making"
|
|
464
|
+
}
|
|
465
|
+
else:
|
|
466
|
+
print(f"CACHE_SYSTEM_INIT_FAILED for user_id: {user_id}")
|
|
467
|
+
return {
|
|
468
|
+
"success": False,
|
|
469
|
+
"task_id": None,
|
|
470
|
+
"status_msg": "Initialization failed upstream"
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
except Exception as exc:
|
|
474
|
+
print(f"CACHE_SYSTEM_INIT_ERROR where error: {str(exc)} and user_id: {user_id}")
|
|
475
|
+
return {
|
|
476
|
+
"success": False,
|
|
477
|
+
"task_id": None,
|
|
478
|
+
"status_msg": f"Error: {str(exc)}"
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
async def check_cache(user_input: str, scope: Optional[str] = None, user_id: Any = 0) -> dict | None:
|
|
482
|
+
print(f"CHECKING_MULTI_TIER_CACHE where user_id: {user_id}")
|
|
483
|
+
try:
|
|
484
|
+
async with db_manager.async_session() as db:
|
|
485
|
+
path_record = await db.get(Paths, 1) #get me 1st row as this is consumer only thus singletn row, btw consumer can have n users dw!
|
|
486
|
+
if not path_record or not path_record.redis_url:
|
|
487
|
+
raise ValueError("System registry not initialized! Run create_cache_system first.")
|
|
488
|
+
|
|
489
|
+
redis_url = path_record.redis_url
|
|
490
|
+
cache_key: str = generate_cache_key(question=user_input, user_id=user_id)
|
|
491
|
+
|
|
492
|
+
normal_redis = await get_redis(redis_url=redis_url) #dw this will work as when we import portable_cache_redis.py all loads
|
|
493
|
+
cache_res: dict = await multi_tier_cache_system(
|
|
494
|
+
scope=scope,
|
|
495
|
+
cache_key=cache_key,
|
|
496
|
+
normal_redis_instance=normal_redis,
|
|
497
|
+
question=user_input,
|
|
498
|
+
db=db,
|
|
499
|
+
user_id=user_id
|
|
500
|
+
)
|
|
501
|
+
|
|
502
|
+
if cache_res and cache_res.get("success"):
|
|
503
|
+
return cache_res.get("data")
|
|
504
|
+
|
|
505
|
+
return None
|
|
506
|
+
except Exception as e:
|
|
507
|
+
print(f"Error checking cache for user_id {user_id}: {e}")
|
|
508
|
+
return None
|
|
509
|
+
|
|
510
|
+
async def populate_cache(to_cache_answer, to_cache_question, user_id: Any = 0) -> dict | None:
|
|
511
|
+
print(f"BACKGROUND_CACHE_POPULATION_FIRED where user_id: {user_id}")
|
|
512
|
+
try:
|
|
513
|
+
async with db_manager.async_session() as db:
|
|
514
|
+
cache_vdb: Chroma | None = await get_user_cache_vdb(user_id=user_id, db=db)
|
|
515
|
+
if cache_vdb is not None:
|
|
516
|
+
ans: dict = await push_responce_in_cache_inishiator(model_output=to_cache_answer, user_id=user_id, question=to_cache_question)
|
|
517
|
+
if ans["success"]:
|
|
518
|
+
return ans
|
|
519
|
+
return None
|
|
520
|
+
except Exception as e:
|
|
521
|
+
print(f"Error populating cache for user_id {user_id}: {e}")
|
|
522
|
+
return None
|
|
523
|
+
|
|
524
|
+
async def close_cache_system():
|
|
525
|
+
print("SHUTTING_DOWN_PORTABLE_CACHE: Cleaning up resources...")
|
|
526
|
+
|
|
527
|
+
from portable_cache_schemas.portable_cache_dbConf import db_manager
|
|
528
|
+
|
|
529
|
+
if db_manager.norma_engine is not None:
|
|
530
|
+
try:
|
|
531
|
+
await db_manager.norma_engine.dispose()
|
|
532
|
+
print("Norma database engine disposed successfully.")
|
|
533
|
+
except Exception as e:
|
|
534
|
+
print(f"Error disposing norma_engine: {e}")
|
|
535
|
+
|
|
536
|
+
if db_manager.celery_engine is not None:
|
|
537
|
+
try:
|
|
538
|
+
await db_manager.celery_engine.dispose()
|
|
539
|
+
print("Celery database engine disposed successfully.")
|
|
540
|
+
except Exception as e:
|
|
541
|
+
print(f"Error disposing celery_engine: {e}")
|
|
542
|
+
|
|
543
|
+
try:
|
|
544
|
+
await redis_manager.close_redis_pool()
|
|
545
|
+
print("Redis connection pool disconnected successfully.")
|
|
546
|
+
except Exception as e:
|
|
547
|
+
print(f"Error disconnecting Redis pool: {e}")
|
|
548
|
+
|
|
549
|
+
print("Portable Cache shutdown complete!")
|
|
550
|
+
|
|
551
|
+
def check_tenant_creation_status(task_id, user_id) -> dict:
|
|
552
|
+
task_id: str = str(task_id)
|
|
553
|
+
user_id: int = int(user_id)
|
|
554
|
+
|
|
555
|
+
from celery.result import AsyncResult
|
|
556
|
+
from .portable_cache_bgWorkers.portable_cache_celery_conf import celery_app
|
|
557
|
+
from celery.states import SUCCESS, FAILURE, RETRY
|
|
558
|
+
|
|
559
|
+
async_result = AsyncResult(task_id, app=celery_app)
|
|
560
|
+
state = async_result.state
|
|
561
|
+
|
|
562
|
+
if state == SUCCESS:
|
|
563
|
+
data={
|
|
564
|
+
"status": "completed",
|
|
565
|
+
"task_id": task_id,
|
|
566
|
+
"state": state,
|
|
567
|
+
"failed": False
|
|
568
|
+
}
|
|
569
|
+
return data
|
|
570
|
+
|
|
571
|
+
elif state == FAILURE:
|
|
572
|
+
data={
|
|
573
|
+
"status": "failed",
|
|
574
|
+
"task_id": task_id,
|
|
575
|
+
"state": state,
|
|
576
|
+
"failed": True
|
|
577
|
+
}
|
|
578
|
+
return data
|
|
579
|
+
|
|
580
|
+
elif state == RETRY:
|
|
581
|
+
data={
|
|
582
|
+
"status": "retrying",
|
|
583
|
+
"task_id": task_id,
|
|
584
|
+
"state": state,
|
|
585
|
+
"failed": False
|
|
586
|
+
}
|
|
587
|
+
return data
|
|
588
|
+
|
|
589
|
+
else:
|
|
590
|
+
data={
|
|
591
|
+
"status": "processing",
|
|
592
|
+
"task_id": task_id,
|
|
593
|
+
"state": state,
|
|
594
|
+
"failed": False
|
|
595
|
+
}
|
|
596
|
+
return data
|
|
597
|
+
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
# File: portable_cache_redis.py
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
import redis.asyncio as aioredis
|
|
4
|
+
from typing import Optional
|
|
5
|
+
from .portable_cache_utils.protable_cache_DynamicEnv_maker import get_settings
|
|
6
|
+
from .portable_cache_dbSchema import Paths
|
|
7
|
+
from sqlalchemy import create_engine
|
|
8
|
+
from sqlalchemy.orm import sessionmaker
|
|
9
|
+
|
|
10
|
+
class RedisManager:
|
|
11
|
+
"""Encapsulates the Redis connection pool without relying on unsafe global state."""
|
|
12
|
+
|
|
13
|
+
def __init__(self):
|
|
14
|
+
self._pool: Optional[aioredis.ConnectionPool] = None
|
|
15
|
+
self._current_url: Optional[str] = None
|
|
16
|
+
|
|
17
|
+
def _get_redis_url_from_registry(self) -> Optional[str]:
|
|
18
|
+
"""Sneaks a peek into the SQLite registry DB to grab the true source-of-truth redis_url."""
|
|
19
|
+
try:
|
|
20
|
+
settings = get_settings()
|
|
21
|
+
db_path = Path(settings.portable_cache_registry_db)
|
|
22
|
+
if not db_path.exists():
|
|
23
|
+
return None
|
|
24
|
+
|
|
25
|
+
sync_engine = create_engine(f"sqlite:///{db_path}")
|
|
26
|
+
SessionLocal = sessionmaker(bind=sync_engine)
|
|
27
|
+
with SessionLocal() as session:
|
|
28
|
+
path_record = session.query(Paths).filter_by(id=1).first()
|
|
29
|
+
|
|
30
|
+
sync_engine.dispose()
|
|
31
|
+
if path_record and path_record.redis_url:
|
|
32
|
+
return path_record.redis_url
|
|
33
|
+
except Exception:
|
|
34
|
+
pass
|
|
35
|
+
return None
|
|
36
|
+
|
|
37
|
+
async def get_client(self, redis_url: Optional[str] = None) -> aioredis.Redis:
|
|
38
|
+
"""Lazily initializes and returns a connection-pooled Redis client."""
|
|
39
|
+
settings = get_settings()
|
|
40
|
+
|
|
41
|
+
target_url = redis_url or self._get_redis_url_from_registry() or settings.portable_cache_redis_url
|
|
42
|
+
|
|
43
|
+
if self._pool is None or self._current_url != target_url:
|
|
44
|
+
if self._pool:
|
|
45
|
+
await self._pool.disconnect()
|
|
46
|
+
|
|
47
|
+
self._current_url = target_url
|
|
48
|
+
self._pool = aioredis.ConnectionPool.from_url(
|
|
49
|
+
target_url,
|
|
50
|
+
decode_responses=False #false coz vectors are stored in bytes!
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
return aioredis.Redis(connection_pool=self._pool)
|
|
54
|
+
|
|
55
|
+
async def close_redis_pool(self):
|
|
56
|
+
"""Clean shutdown for app teardown/lifespan events."""
|
|
57
|
+
if self._pool:
|
|
58
|
+
await self._pool.disconnect()
|
|
59
|
+
self._pool = None
|
|
60
|
+
|
|
61
|
+
redis_manager = RedisManager()
|
|
62
|
+
|
|
63
|
+
async def get_redis(redis_url: Optional[str] = None) -> aioredis.Redis:
|
|
64
|
+
"""Helper function to cleanly fetch the client."""
|
|
65
|
+
return await redis_manager.get_client(redis_url=redis_url)
|
|
@@ -3,8 +3,8 @@ import sqlite3
|
|
|
3
3
|
from pathlib import Path
|
|
4
4
|
from sqlalchemy.ext.asyncio import create_async_engine, AsyncSession, async_sessionmaker
|
|
5
5
|
from sqlalchemy import event
|
|
6
|
-
from portable_cache_schemas.portable_cache_dbBase import Base
|
|
7
|
-
from portable_cache_utils.protable_cache_DynamicEnv_maker import get_settings
|
|
6
|
+
from ..portable_cache_schemas.portable_cache_dbBase import Base
|
|
7
|
+
from ..portable_cache_utils.protable_cache_DynamicEnv_maker import get_settings
|
|
8
8
|
|
|
9
9
|
|
|
10
10
|
class DatabaseManager:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# # File: portable_cache_utils/portable_cache_embedding_model.py
|
|
2
2
|
from langchain_huggingface import HuggingFaceEmbeddings
|
|
3
|
-
from portable_cache_utils.protable_cache_DynamicEnv_maker import get_settings
|
|
3
|
+
from ..portable_cache_utils.protable_cache_DynamicEnv_maker import get_settings
|
|
4
4
|
|
|
5
5
|
embedding_model = HuggingFaceEmbeddings(
|
|
6
6
|
model_name=get_settings().portable_cache_cache_proj_embedding_model
|
|
@@ -3,7 +3,7 @@ import os
|
|
|
3
3
|
from pathlib import Path
|
|
4
4
|
from typing import Any, Optional
|
|
5
5
|
from pydantic_settings import BaseSettings, SettingsConfigDict
|
|
6
|
-
from portable_cache_dbSchema import Paths
|
|
6
|
+
from ..portable_cache_dbSchema import Paths
|
|
7
7
|
from sqlalchemy import create_engine, select
|
|
8
8
|
from pydantic import model_validator
|
|
9
9
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: TriCacheLLM_MMA
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.2
|
|
4
4
|
Summary: A robust three-tier LLM-aware caching SDK using Redis, ChromaDB, and automatic SQLite bootstrap.
|
|
5
5
|
Author-email: Mohib Ashfaq Butt <inboxmohib@gmail.com>
|
|
6
6
|
Project-URL: Homepage, https://github.com/mohib-ash/TriCacheLLM_MMA
|
|
@@ -421,6 +421,8 @@ Or install the development version directly from the repository:
|
|
|
421
421
|
pip install .
|
|
422
422
|
```
|
|
423
423
|
|
|
424
|
+
PyPI link: https://pypi.org/project/TriCacheLLM-MMA/0.1.0/
|
|
425
|
+
|
|
424
426
|
---
|
|
425
427
|
|
|
426
428
|
# Redis
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
LICENSE
|
|
2
|
+
README.md
|
|
3
|
+
pyproject.toml
|
|
4
|
+
TriCacheLLM_MMA/__init__.py
|
|
5
|
+
TriCacheLLM_MMA/portable_cache_dbSchema.py
|
|
6
|
+
TriCacheLLM_MMA/portable_cache_main.py
|
|
7
|
+
TriCacheLLM_MMA/portable_cache_redis.py
|
|
8
|
+
TriCacheLLM_MMA.egg-info/PKG-INFO
|
|
9
|
+
TriCacheLLM_MMA.egg-info/SOURCES.txt
|
|
10
|
+
TriCacheLLM_MMA.egg-info/dependency_links.txt
|
|
11
|
+
TriCacheLLM_MMA.egg-info/requires.txt
|
|
12
|
+
TriCacheLLM_MMA.egg-info/top_level.txt
|
|
13
|
+
TriCacheLLM_MMA/portable_cache_Ai/portable_cache_rerankAi.py
|
|
14
|
+
TriCacheLLM_MMA/portable_cache_bgWorkers/portable_cache_celery_conf.py
|
|
15
|
+
TriCacheLLM_MMA/portable_cache_bgWorkers/portable_cache_workers.py
|
|
16
|
+
TriCacheLLM_MMA/portable_cache_schemas/portable_cache_dbBase.py
|
|
17
|
+
TriCacheLLM_MMA/portable_cache_schemas/portable_cache_dbConf.py
|
|
18
|
+
TriCacheLLM_MMA/portable_cache_schemas/portable_cache_schemas.py
|
|
19
|
+
TriCacheLLM_MMA/portable_cache_utils/portable_cache_embedding_model.py
|
|
20
|
+
TriCacheLLM_MMA/portable_cache_utils/protable_cache_DynamicEnv_maker.py
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
TriCacheLLM_MMA
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "TriCacheLLM_MMA"
|
|
7
|
-
version = "0.1.
|
|
7
|
+
version = "0.1.2"
|
|
8
8
|
description = "A robust three-tier LLM-aware caching SDK using Redis, ChromaDB, and automatic SQLite bootstrap."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.11"
|
|
@@ -62,5 +62,5 @@ Homepage = "https://github.com/mohib-ash/TriCacheLLM_MMA"
|
|
|
62
62
|
Repository = "https://github.com/mohib-ash/TriCacheLLM_MMA"
|
|
63
63
|
Issues = "https://github.com/mohib-ash/TriCacheLLM_MMA/issues"
|
|
64
64
|
|
|
65
|
-
[tool.setuptools
|
|
66
|
-
|
|
65
|
+
[tool.setuptools]
|
|
66
|
+
packages = ["TriCacheLLM_MMA", "TriCacheLLM_MMA.portable_cache_Ai", "TriCacheLLM_MMA.portable_cache_bgWorkers", "TriCacheLLM_MMA.portable_cache_schemas", "TriCacheLLM_MMA.portable_cache_utils"]
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
LICENSE
|
|
2
|
-
README.md
|
|
3
|
-
pyproject.toml
|
|
4
|
-
TriCacheLLM_MMA.egg-info/PKG-INFO
|
|
5
|
-
TriCacheLLM_MMA.egg-info/SOURCES.txt
|
|
6
|
-
TriCacheLLM_MMA.egg-info/dependency_links.txt
|
|
7
|
-
TriCacheLLM_MMA.egg-info/requires.txt
|
|
8
|
-
TriCacheLLM_MMA.egg-info/top_level.txt
|
|
9
|
-
portable_cache_Ai/portable_cache_rerankAi.py
|
|
10
|
-
portable_cache_bgWorkers/portable_cache_celery_conf.py
|
|
11
|
-
portable_cache_bgWorkers/portable_cache_workers.py
|
|
12
|
-
portable_cache_schemas/portable_cache_dbBase.py
|
|
13
|
-
portable_cache_schemas/portable_cache_dbConf.py
|
|
14
|
-
portable_cache_schemas/portable_cache_schemas.py
|
|
15
|
-
portable_cache_utils/portable_cache_embedding_model.py
|
|
16
|
-
portable_cache_utils/protable_cache_DynamicEnv_maker.py
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{tricachellm_mma-0.1.0 → tricachellm_mma-0.1.2}/TriCacheLLM_MMA.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|