Skip to content

Commit 77b1ea7

Browse files
authored
Merge pull request #8 from code0-tech/feat/#2
Alpine/musl compatibility
2 parents a6a94f7 + 32df502 commit 77b1ea7

8 files changed

Lines changed: 125 additions & 599 deletions

File tree

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,6 @@ dependencies = [
1414
"litellm==1.83.7",
1515
"pydantic==2.12.5",
1616
"qdrant-client>=1.16.1",
17-
"sentence-transformers>=5.1.2",
17+
"model2vec>=0.3.0",
1818
"tucana==0.0.72",
1919
]

src/endpoint/generation/generate_endpoint.py

Lines changed: 48 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -71,28 +71,6 @@ def Prompt(self, request: pb2.PromptRequest, context) -> pb2.FlowResponse:
7171
details=f"The specified model_identifier '{request.model_identifier}' does not exist. Please provide a valid model_identifier for flow generation."
7272
)
7373

74-
if len(
75-
self.function_store.get_all(
76-
group_identifier=str(request.project_id)
77-
)
78-
) <= 0 and (len(request.functions) <= 0):
79-
log.warning(f"[Prompt] Rejected — no functions for project={request.project_id}")
80-
context.abort(
81-
code=grpc.StatusCode.ABORTED,
82-
details="No functions found for the given project_id. Please add functions before requesting a prompt generation."
83-
)
84-
85-
if len(
86-
self.flow_type_store.get_all(
87-
group_identifier=str(request.project_id)
88-
)
89-
) <= 0 and (len(request.flow_types) <= 0):
90-
log.warning(f"[Prompt] Rejected — no flow types for project={request.project_id}")
91-
context.abort(
92-
code=grpc.StatusCode.ABORTED,
93-
details="No flow types found for the given project_id. Please add flow_types before requesting a prompt generation."
94-
)
95-
9674
functions = [
9775
map_to_function_schema(fn)
9876
for fn in request.functions
@@ -122,6 +100,30 @@ def Prompt(self, request: pb2.PromptRequest, context) -> pb2.FlowResponse:
122100
data_types=data_types
123101
)
124102

103+
stored_functions_count = len(self.function_store.get_all(group_identifier=str(request.project_id)))
104+
stored_flow_types_count = len(self.flow_type_store.get_all(group_identifier=str(request.project_id)))
105+
stored_few_shots_count = len(self.few_shots_store.get_all(group_identifier="global"))
106+
log.info(
107+
f"[Prompt] Store state — project={request.project_id} "
108+
f"functions={stored_functions_count} "
109+
f"flow_types={stored_flow_types_count} "
110+
f"few_shots={stored_few_shots_count}"
111+
)
112+
113+
if stored_functions_count <= 0:
114+
log.warning(f"[Prompt] Rejected — no functions for project={request.project_id}")
115+
context.abort(
116+
code=grpc.StatusCode.ABORTED,
117+
details="No functions found for the given project_id. Please add functions before requesting a prompt generation."
118+
)
119+
120+
if stored_flow_types_count <= 0:
121+
log.warning(f"[Prompt] Rejected — no flow types for project={request.project_id}")
122+
context.abort(
123+
code=grpc.StatusCode.ABORTED,
124+
details="No flow types found for the given project_id. Please add flow_types before requesting a prompt generation."
125+
)
126+
125127
prompt_functions = self.function_store.search(
126128
group_identifier=str(request.project_id),
127129
prompt=request.prompt,
@@ -272,28 +274,6 @@ def Flow(self, request: pb2.FlowRequest, context) -> pb2.FlowResponse:
272274
details=f"The specified model_identifier '{request.model_identifier}' does not exist. Please provide a valid model_identifier for flow generation."
273275
)
274276

275-
if len(
276-
self.function_store.get_all(
277-
group_identifier=str(request.project_id)
278-
)
279-
) <= 0 and (len(request.functions) <= 0):
280-
log.warning(f"[Flow] Rejected — no functions for project={request.project_id}")
281-
context.abort(
282-
code=grpc.StatusCode.ABORTED,
283-
details="No functions found for the given project_id. Please add functions before requesting a prompt generation."
284-
)
285-
286-
if len(
287-
self.flow_type_store.get_all(
288-
group_identifier=str(request.project_id)
289-
)
290-
) <= 0 and (len(request.flow_types) <= 0):
291-
log.warning(f"[Flow] Rejected — no flow types for project={request.project_id}")
292-
context.abort(
293-
code=grpc.StatusCode.ABORTED,
294-
details="No flow types found for the given project_id. Please add flow_types before requesting a prompt generation."
295-
)
296-
297277
functions = [
298278
map_to_function_schema(fn)
299279
for fn in request.functions
@@ -323,6 +303,30 @@ def Flow(self, request: pb2.FlowRequest, context) -> pb2.FlowResponse:
323303
data_types=data_types
324304
)
325305

306+
stored_functions_count = len(self.function_store.get_all(group_identifier=str(request.project_id)))
307+
stored_flow_types_count = len(self.flow_type_store.get_all(group_identifier=str(request.project_id)))
308+
stored_few_shots_count = len(self.few_shots_store.get_all(group_identifier="global"))
309+
log.info(
310+
f"[Flow] Store state — project={request.project_id} "
311+
f"functions={stored_functions_count} "
312+
f"flow_types={stored_flow_types_count} "
313+
f"few_shots={stored_few_shots_count}"
314+
)
315+
316+
if stored_functions_count <= 0:
317+
log.warning(f"[Flow] Rejected — no functions for project={request.project_id}")
318+
context.abort(
319+
code=grpc.StatusCode.ABORTED,
320+
details="No functions found for the given project_id. Please add functions before requesting a prompt generation."
321+
)
322+
323+
if stored_flow_types_count <= 0:
324+
log.warning(f"[Flow] Rejected — no flow types for project={request.project_id}")
325+
context.abort(
326+
code=grpc.StatusCode.ABORTED,
327+
details="No flow types found for the given project_id. Please add flow_types before requesting a prompt generation."
328+
)
329+
326330
prompt_functions = self.function_store.search(
327331
group_identifier=str(request.project_id),
328332
prompt=request.prompt,

src/model.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
1-
from sentence_transformers import SentenceTransformer
1+
from model2vec import StaticModel
22

3-
MODEL_NAME = 'all-MiniLM-L6-v2'
3+
MODEL_NAME = 'minishlab/potion-base-8M'
4+
EMBEDDING_DIM = 256
45

56

6-
def load_vector_model() -> SentenceTransformer:
7-
return SentenceTransformer(MODEL_NAME)
7+
def load_vector_model() -> StaticModel:
8+
return StaticModel.from_pretrained(MODEL_NAME)

src/store/few_shots_store.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@
22
from pathlib import Path
33
from typing import Any, List
44

5+
from model2vec import StaticModel
56
from qdrant_client import QdrantClient
6-
from sentence_transformers import SentenceTransformer
77

88
from src.logger import get_logger
99
from src.schema.few_shot_schema import FewShot
@@ -13,13 +13,14 @@
1313

1414

1515
class FewShotsStore(Store):
16-
def __init__(self, memory_client: QdrantClient, vector_model: SentenceTransformer):
16+
def __init__(self, memory_client: QdrantClient, vector_model: StaticModel):
1717
super().__init__(
1818
memory_client,
1919
vector_model,
2020
'few_shots',
2121
"identifier",
22-
"identifier"
22+
"identifier",
23+
time_to_live=None,
2324
)
2425
self._load_models_from_json()
2526

@@ -50,7 +51,7 @@ def validate(self, payload: Any) -> FewShot:
5051
return FewShot.model_validate(payload)
5152

5253
def search(self, group_identifier: str, prompt: str, limit=5) -> List[FewShot]:
53-
return super().search(prompt, group_identifier, limit)
54+
return super().search(group_identifier, prompt, limit)
5455

5556
def find(self, group_identifier: str, identifier: str) -> FewShot | None:
5657
return super().find(group_identifier, identifier)

src/store/flow_type_store.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
import re
22
from typing import List, Any
33

4+
from model2vec import StaticModel
45
from qdrant_client import QdrantClient
5-
from sentence_transformers import SentenceTransformer
66

77
from src.schema.data_type_schema import DataType
88
from src.schema.flow_type_schema import FlowType
@@ -81,7 +81,7 @@ def replacement(match):
8181

8282

8383
class FlowTypeStore(Store):
84-
def __init__(self, memory_client: QdrantClient, vector_model: SentenceTransformer):
84+
def __init__(self, memory_client: QdrantClient, vector_model: StaticModel):
8585
super().__init__(
8686
memory_client,
8787
vector_model,
@@ -105,7 +105,7 @@ def validate(self, payload: Any) -> FlowType:
105105
return FlowType.model_validate(payload)
106106

107107
def search(self, group_identifier: str, prompt: str, limit=5) -> List[FlowType]:
108-
return super().search(prompt, group_identifier, limit)
108+
return super().search(group_identifier, prompt, limit)
109109

110110
def find(self, group_identifier: str, identifier: str) -> FlowType | None:
111111
return super().find(group_identifier, identifier)

src/store/function_store.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
import re
22
from typing import List, Any
33

4+
from model2vec import StaticModel
45
from qdrant_client import QdrantClient
5-
from sentence_transformers import SentenceTransformer
66

77
from src.schema.data_type_schema import DataType
88
from src.schema.function_schema import FunctionDefinition
@@ -81,7 +81,7 @@ def replacement(match):
8181

8282

8383
class FunctionStore(Store):
84-
def __init__(self, memory_client: QdrantClient, vector_model: SentenceTransformer):
84+
def __init__(self, memory_client: QdrantClient, vector_model: StaticModel):
8585
super().__init__(
8686
memory_client,
8787
vector_model,
@@ -105,7 +105,7 @@ def validate(self, payload: Any) -> FunctionDefinition:
105105
return FunctionDefinition.model_validate(payload)
106106

107107
def search(self, group_identifier: str, prompt: str, limit=5) -> List[FunctionDefinition]:
108-
return super().search(prompt, group_identifier, limit)
108+
return super().search(group_identifier, prompt, limit)
109109

110110
def find(self, group_identifier: str, identifier: str) -> FunctionDefinition | None:
111111
return super().find(group_identifier, identifier)

src/store/vector_store.py

Lines changed: 27 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,9 @@
55
from apscheduler.schedulers.background import BackgroundScheduler
66
from qdrant_client import QdrantClient
77
from qdrant_client.models import Distance, VectorParams, PointStruct, MatchAny, Filter, FieldCondition, MatchValue
8-
from sentence_transformers import SentenceTransformer
8+
from model2vec import StaticModel
9+
10+
from src.model import EMBEDDING_DIM
911

1012
from src.logger import get_logger
1113

@@ -16,11 +18,11 @@ class Store(ABC):
1618
def __init__(
1719
self,
1820
client: QdrantClient,
19-
model: SentenceTransformer,
21+
model: StaticModel,
2022
collection_name: str,
2123
payload_identifier: str,
2224
group_identifier: str,
23-
time_to_live: int = 60 * 5
25+
time_to_live: int | None = 60 * 5
2426

2527
):
2628
self.client = client
@@ -32,14 +34,15 @@ def __init__(
3234
self._id_counter = 0
3335
self._scheduler = None
3436
self._setup_collection()
35-
self._start_garbage_collector()
37+
if self.time_to_live is not None:
38+
self._start_garbage_collector()
3639

3740
def _setup_collection(self):
3841
if not self.client.collection_exists(collection_name=self.collection_name):
3942
self.client.create_collection(
4043
collection_name=self.collection_name,
4144
vectors_config=VectorParams(
42-
size=384,
45+
size=EMBEDDING_DIM,
4346
distance=Distance.COSINE
4447
),
4548
)
@@ -81,13 +84,21 @@ def _garbage_collect_all(self):
8184
log.error(f"[GC] Error in collection '{self.collection_name}': {e}")
8285

8386
def _reset_time_to_live(self, group_identifier: str):
84-
items = self.get_all(group_identifier)
85-
current_time = time.time()
87+
if self.time_to_live is None:
88+
return
8689

87-
for item in items:
88-
item_dict = item.model_dump() if hasattr(item, 'model_dump') else item
89-
item_dict["created_at"] = current_time
90-
self.insert(group_identifier, item_dict)
90+
self.client.set_payload(
91+
collection_name=self.collection_name,
92+
payload={"created_at": time.time()},
93+
points=Filter(
94+
must=[
95+
FieldCondition(
96+
key=self.group_identifier,
97+
match=MatchValue(value=group_identifier)
98+
)
99+
]
100+
),
101+
)
91102

92103
@abstractmethod
93104
def validate(self, payload: Any) -> Any:
@@ -101,8 +112,11 @@ def insert(self, group_identifier: str, payload: Any) -> None:
101112
if "created_at" not in payload:
102113
payload["created_at"] = time.time()
103114

104-
payload_str = str(payload)
105-
vector = self.model.encode(payload_str).tolist()
115+
semantic_payload = {
116+
k: v for k, v in payload.items()
117+
if k not in ("created_at", self.group_identifier)
118+
}
119+
vector = self.model.encode(str(semantic_payload)).tolist()
106120

107121
payload[self.group_identifier] = group_identifier
108122

0 commit comments

Comments
 (0)