vector-task-mcp 1.2.7__tar.gz → 1.2.9__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.
- {vector_task_mcp-1.2.7 → vector_task_mcp-1.2.9}/PKG-INFO +2 -1
- {vector_task_mcp-1.2.7 → vector_task_mcp-1.2.9}/main.py +3 -2
- {vector_task_mcp-1.2.7 → vector_task_mcp-1.2.9}/pyproject.toml +2 -1
- vector_task_mcp-1.2.9/src/embeddings.py +151 -0
- {vector_task_mcp-1.2.7 → vector_task_mcp-1.2.9}/src/models.py +5 -1
- {vector_task_mcp-1.2.7 → vector_task_mcp-1.2.9}/src/task_store.py +124 -5
- {vector_task_mcp-1.2.7 → vector_task_mcp-1.2.9}/vector_task_mcp.egg-info/PKG-INFO +2 -1
- {vector_task_mcp-1.2.7 → vector_task_mcp-1.2.9}/vector_task_mcp.egg-info/requires.txt +1 -0
- vector_task_mcp-1.2.7/src/embeddings.py +0 -68
- {vector_task_mcp-1.2.7 → vector_task_mcp-1.2.9}/.mcp.json +0 -0
- {vector_task_mcp-1.2.7 → vector_task_mcp-1.2.9}/.python-version +0 -0
- {vector_task_mcp-1.2.7 → vector_task_mcp-1.2.9}/CLAUDE.md +0 -0
- {vector_task_mcp-1.2.7 → vector_task_mcp-1.2.9}/LICENSE +0 -0
- {vector_task_mcp-1.2.7 → vector_task_mcp-1.2.9}/MANIFEST.in +0 -0
- {vector_task_mcp-1.2.7 → vector_task_mcp-1.2.9}/README.md +0 -0
- {vector_task_mcp-1.2.7 → vector_task_mcp-1.2.9}/claude-desktop-config.example.json +0 -0
- {vector_task_mcp-1.2.7 → vector_task_mcp-1.2.9}/requirements.txt +0 -0
- {vector_task_mcp-1.2.7 → vector_task_mcp-1.2.9}/run-arm64.sh +0 -0
- {vector_task_mcp-1.2.7 → vector_task_mcp-1.2.9}/setup.cfg +0 -0
- {vector_task_mcp-1.2.7 → vector_task_mcp-1.2.9}/src/__init__.py +0 -0
- {vector_task_mcp-1.2.7 → vector_task_mcp-1.2.9}/src/security.py +0 -0
- {vector_task_mcp-1.2.7 → vector_task_mcp-1.2.9}/tests/test_task_store.py +0 -0
- {vector_task_mcp-1.2.7 → vector_task_mcp-1.2.9}/vector_task_mcp.egg-info/SOURCES.txt +0 -0
- {vector_task_mcp-1.2.7 → vector_task_mcp-1.2.9}/vector_task_mcp.egg-info/dependency_links.txt +0 -0
- {vector_task_mcp-1.2.7 → vector_task_mcp-1.2.9}/vector_task_mcp.egg-info/entry_points.txt +0 -0
- {vector_task_mcp-1.2.7 → vector_task_mcp-1.2.9}/vector_task_mcp.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: vector-task-mcp
|
|
3
|
-
Version: 1.2.
|
|
3
|
+
Version: 1.2.9
|
|
4
4
|
Summary: A secure, vector-based task management server for Claude Desktop using sqlite-vec and sentence-transformers
|
|
5
5
|
Author-email: Xsaven <xsaven@gmail.com>
|
|
6
6
|
License: MIT
|
|
@@ -23,6 +23,7 @@ License-File: LICENSE
|
|
|
23
23
|
Requires-Dist: mcp>=0.3.0
|
|
24
24
|
Requires-Dist: sqlite-vec>=0.1.6
|
|
25
25
|
Requires-Dist: sentence-transformers>=2.2.2
|
|
26
|
+
Requires-Dist: requests>=2.28.0
|
|
26
27
|
Dynamic: license-file
|
|
27
28
|
|
|
28
29
|
# Vector Task MCP Server
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "vector-task-mcp"
|
|
3
|
-
version = "1.2.
|
|
3
|
+
version = "1.2.9"
|
|
4
4
|
description = "A secure, vector-based task management server for Claude Desktop using sqlite-vec and sentence-transformers"
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
requires-python = ">=3.10"
|
|
@@ -34,6 +34,7 @@ dependencies = [
|
|
|
34
34
|
"mcp>=0.3.0",
|
|
35
35
|
"sqlite-vec>=0.1.6",
|
|
36
36
|
"sentence-transformers>=2.2.2",
|
|
37
|
+
"requests>=2.28.0",
|
|
37
38
|
]
|
|
38
39
|
|
|
39
40
|
[project.urls]
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Embedding Model Module
|
|
3
|
+
======================
|
|
4
|
+
|
|
5
|
+
Provides sentence transformer embedding functionality for semantic task search.
|
|
6
|
+
Uses lazy loading with background preload for fast server startup.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
import threading
|
|
10
|
+
from typing import List, Union, Optional
|
|
11
|
+
import numpy as np
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class EmbeddingModelNotReadyError(Exception):
|
|
15
|
+
"""Raised when embedding model not loaded and timeout exceeded."""
|
|
16
|
+
pass
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class LazyEmbeddingModel:
|
|
20
|
+
"""Lazy-loading embedding model with background initialization."""
|
|
21
|
+
|
|
22
|
+
def __init__(self, model_name: str, preload: bool = True):
|
|
23
|
+
"""
|
|
24
|
+
Initialize lazy embedding model.
|
|
25
|
+
|
|
26
|
+
Args:
|
|
27
|
+
model_name: HuggingFace model name (e.g., 'sentence-transformers/all-MiniLM-L6-v2')
|
|
28
|
+
preload: If True, start loading model in background immediately
|
|
29
|
+
"""
|
|
30
|
+
self.model_name = model_name
|
|
31
|
+
self._model = None
|
|
32
|
+
self._lock = threading.Lock()
|
|
33
|
+
self._ready_event = threading.Event()
|
|
34
|
+
self._load_error: Optional[Exception] = None
|
|
35
|
+
|
|
36
|
+
if preload:
|
|
37
|
+
self._start_background_load()
|
|
38
|
+
|
|
39
|
+
def _start_background_load(self) -> None:
|
|
40
|
+
"""Start model loading in background thread."""
|
|
41
|
+
thread = threading.Thread(target=self._load_model, daemon=True)
|
|
42
|
+
thread.start()
|
|
43
|
+
|
|
44
|
+
def _load_model(self) -> None:
|
|
45
|
+
"""Load the model (runs in background thread)."""
|
|
46
|
+
try:
|
|
47
|
+
# DEFERRED IMPORT - only happens in background thread
|
|
48
|
+
# This is the key optimization: sentence_transformers import is slow
|
|
49
|
+
from sentence_transformers import SentenceTransformer
|
|
50
|
+
model = SentenceTransformer(self.model_name)
|
|
51
|
+
with self._lock:
|
|
52
|
+
self._model = model
|
|
53
|
+
self._ready_event.set()
|
|
54
|
+
except Exception as e:
|
|
55
|
+
with self._lock:
|
|
56
|
+
self._load_error = e
|
|
57
|
+
self._ready_event.set()
|
|
58
|
+
|
|
59
|
+
def _ensure_model(self, timeout: float = 30.0):
|
|
60
|
+
"""
|
|
61
|
+
Ensure model is loaded, waiting up to timeout seconds.
|
|
62
|
+
|
|
63
|
+
Args:
|
|
64
|
+
timeout: Maximum seconds to wait for model
|
|
65
|
+
|
|
66
|
+
Returns:
|
|
67
|
+
Loaded SentenceTransformer model
|
|
68
|
+
|
|
69
|
+
Raises:
|
|
70
|
+
EmbeddingModelNotReadyError: If model not ready within timeout
|
|
71
|
+
"""
|
|
72
|
+
if not self._ready_event.wait(timeout=timeout):
|
|
73
|
+
raise EmbeddingModelNotReadyError(
|
|
74
|
+
f"Embedding model not ready after {timeout}s. Try again shortly."
|
|
75
|
+
)
|
|
76
|
+
if self._load_error:
|
|
77
|
+
raise EmbeddingModelNotReadyError(
|
|
78
|
+
f"Failed to load embedding model: {self._load_error}"
|
|
79
|
+
)
|
|
80
|
+
return self._model
|
|
81
|
+
|
|
82
|
+
def encode(self, text: Union[str, List[str]], timeout: float = 30.0) -> np.ndarray:
|
|
83
|
+
"""
|
|
84
|
+
Encode text to embedding vector(s).
|
|
85
|
+
|
|
86
|
+
Args:
|
|
87
|
+
text: Single text string or list of texts
|
|
88
|
+
timeout: Maximum seconds to wait for model
|
|
89
|
+
|
|
90
|
+
Returns:
|
|
91
|
+
Numpy array of embeddings (1D for single text, 2D for list)
|
|
92
|
+
|
|
93
|
+
Raises:
|
|
94
|
+
EmbeddingModelNotReadyError: If model not ready within timeout
|
|
95
|
+
"""
|
|
96
|
+
model = self._ensure_model(timeout)
|
|
97
|
+
embeddings = model.encode(text, convert_to_numpy=True)
|
|
98
|
+
# Ensure float32 for sqlite-vec compatibility
|
|
99
|
+
return embeddings.astype(np.float32)
|
|
100
|
+
|
|
101
|
+
def encode_single(self, text: str, timeout: float = 30.0) -> np.ndarray:
|
|
102
|
+
"""
|
|
103
|
+
Encode single text to embedding vector.
|
|
104
|
+
|
|
105
|
+
Args:
|
|
106
|
+
text: Single text string
|
|
107
|
+
timeout: Maximum seconds to wait for model
|
|
108
|
+
|
|
109
|
+
Returns:
|
|
110
|
+
Numpy array of embedding (1D float32)
|
|
111
|
+
|
|
112
|
+
Raises:
|
|
113
|
+
EmbeddingModelNotReadyError: If model not ready within timeout
|
|
114
|
+
"""
|
|
115
|
+
return self.encode(text, timeout)
|
|
116
|
+
|
|
117
|
+
def get_embedding_dim(self, timeout: float = 30.0) -> int:
|
|
118
|
+
"""
|
|
119
|
+
Get embedding dimension.
|
|
120
|
+
|
|
121
|
+
Args:
|
|
122
|
+
timeout: Maximum seconds to wait for model
|
|
123
|
+
|
|
124
|
+
Returns:
|
|
125
|
+
Embedding dimension (e.g., 384 for all-MiniLM-L6-v2)
|
|
126
|
+
"""
|
|
127
|
+
model = self._ensure_model(timeout)
|
|
128
|
+
return model.get_sentence_embedding_dimension()
|
|
129
|
+
|
|
130
|
+
def is_ready(self) -> bool:
|
|
131
|
+
"""
|
|
132
|
+
Check if model is loaded and ready.
|
|
133
|
+
|
|
134
|
+
Returns:
|
|
135
|
+
True if model is ready for encoding
|
|
136
|
+
"""
|
|
137
|
+
return self._ready_event.is_set() and self._model is not None and self._load_error is None
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
def get_embedding_model(model_name: str, preload: bool = True) -> LazyEmbeddingModel:
|
|
141
|
+
"""
|
|
142
|
+
Factory function to get embedding model instance.
|
|
143
|
+
|
|
144
|
+
Args:
|
|
145
|
+
model_name: HuggingFace model name
|
|
146
|
+
preload: If True, start loading model in background immediately
|
|
147
|
+
|
|
148
|
+
Returns:
|
|
149
|
+
Initialized LazyEmbeddingModel instance
|
|
150
|
+
"""
|
|
151
|
+
return LazyEmbeddingModel(model_name, preload=preload)
|
|
@@ -328,4 +328,8 @@ class Config:
|
|
|
328
328
|
# Database configuration
|
|
329
329
|
DB_NAME = "vector_tasks.db"
|
|
330
330
|
EMBEDDING_MODEL = "sentence-transformers/all-MiniLM-L6-v2"
|
|
331
|
-
EMBEDDING_DIM = 384
|
|
331
|
+
EMBEDDING_DIM = 384
|
|
332
|
+
|
|
333
|
+
# Embedding model configuration
|
|
334
|
+
EMBEDDING_LOAD_TIMEOUT = 30.0 # Seconds to wait for model during operations
|
|
335
|
+
EMBEDDING_PRELOAD = True # Start loading model in background on init
|
|
@@ -22,7 +22,7 @@ from .security import (
|
|
|
22
22
|
generate_content_hash, validate_file_path, validate_parent_id,
|
|
23
23
|
validate_bulk_tasks_params, validate_bulk_task_ids
|
|
24
24
|
)
|
|
25
|
-
from .embeddings import get_embedding_model
|
|
25
|
+
from .embeddings import get_embedding_model, EmbeddingModelNotReadyError
|
|
26
26
|
|
|
27
27
|
|
|
28
28
|
class TaskStore:
|
|
@@ -261,6 +261,76 @@ class TaskStore:
|
|
|
261
261
|
# Recursively propagate to grandparent
|
|
262
262
|
self._propagate_pending_to_parents(conn, parent_id)
|
|
263
263
|
|
|
264
|
+
def _propagate_in_progress_to_parents(self, conn: sqlite3.Connection, task_id: int) -> None:
|
|
265
|
+
"""
|
|
266
|
+
Recursively propagate 'in_progress' status to parent tasks when ANY child starts work.
|
|
267
|
+
|
|
268
|
+
Rules:
|
|
269
|
+
- Parent gets 'in_progress' when ANY child becomes 'in_progress'
|
|
270
|
+
- Only propagates if parent is currently 'pending' (not already in_progress/completed)
|
|
271
|
+
- Recursively propagates up the parent chain
|
|
272
|
+
|
|
273
|
+
Args:
|
|
274
|
+
conn: Active database connection (must be within transaction)
|
|
275
|
+
task_id: Current task ID whose status just changed to 'in_progress'
|
|
276
|
+
"""
|
|
277
|
+
# Get parent_id and parent status
|
|
278
|
+
cursor = conn.execute('SELECT parent_id FROM tasks WHERE id = ?', (task_id,))
|
|
279
|
+
row = cursor.fetchone()
|
|
280
|
+
if not row or not row[0]: # No parent
|
|
281
|
+
return
|
|
282
|
+
|
|
283
|
+
parent_id = row[0]
|
|
284
|
+
|
|
285
|
+
# Get parent's current status
|
|
286
|
+
parent_status_row = conn.execute('SELECT status FROM tasks WHERE id = ?', (parent_id,)).fetchone()
|
|
287
|
+
if not parent_status_row:
|
|
288
|
+
return
|
|
289
|
+
|
|
290
|
+
parent_status = parent_status_row[0]
|
|
291
|
+
|
|
292
|
+
# Only update parent if it's pending (not already working or completed)
|
|
293
|
+
if parent_status == 'pending':
|
|
294
|
+
conn.execute('UPDATE tasks SET status = ? WHERE id = ?', ('in_progress', parent_id))
|
|
295
|
+
# Recursively propagate to grandparent
|
|
296
|
+
self._propagate_in_progress_to_parents(conn, parent_id)
|
|
297
|
+
|
|
298
|
+
def _propagate_stopped_to_parents(self, conn: sqlite3.Connection, task_id: int) -> None:
|
|
299
|
+
"""
|
|
300
|
+
Recursively propagate status to parent tasks when a child is stopped.
|
|
301
|
+
|
|
302
|
+
Rules:
|
|
303
|
+
- When a child is stopped and parent has no other in_progress children,
|
|
304
|
+
parent reverts to 'pending' (no active work)
|
|
305
|
+
- Recursively propagates up the parent chain
|
|
306
|
+
|
|
307
|
+
Args:
|
|
308
|
+
conn: Active database connection (must be within transaction)
|
|
309
|
+
task_id: Current task ID whose status just changed to 'stopped'
|
|
310
|
+
"""
|
|
311
|
+
# Get parent_id
|
|
312
|
+
cursor = conn.execute('SELECT parent_id FROM tasks WHERE id = ?', (task_id,))
|
|
313
|
+
row = cursor.fetchone()
|
|
314
|
+
if not row or not row[0]: # No parent
|
|
315
|
+
return
|
|
316
|
+
|
|
317
|
+
parent_id = row[0]
|
|
318
|
+
|
|
319
|
+
# Check if parent has any other in_progress children
|
|
320
|
+
in_progress_children = conn.execute(
|
|
321
|
+
'SELECT COUNT(*) FROM tasks WHERE parent_id = ? AND status = ? AND id != ?',
|
|
322
|
+
(parent_id, 'in_progress', task_id)
|
|
323
|
+
).fetchone()[0]
|
|
324
|
+
|
|
325
|
+
# If no in_progress children remain, set parent to pending
|
|
326
|
+
if in_progress_children == 0:
|
|
327
|
+
# Get parent's current status
|
|
328
|
+
parent_status_row = conn.execute('SELECT status FROM tasks WHERE id = ?', (parent_id,)).fetchone()
|
|
329
|
+
if parent_status_row and parent_status_row[0] == 'in_progress':
|
|
330
|
+
conn.execute('UPDATE tasks SET status = ? WHERE id = ?', ('pending', parent_id))
|
|
331
|
+
# Recursively propagate to grandparent
|
|
332
|
+
self._propagate_stopped_to_parents(conn, parent_id)
|
|
333
|
+
|
|
264
334
|
def _start_time_session(self, conn: sqlite3.Connection, task_id: int, start_status: str) -> None:
|
|
265
335
|
"""
|
|
266
336
|
Create new time session record in task_time_log table.
|
|
@@ -403,7 +473,16 @@ class TaskStore:
|
|
|
403
473
|
}
|
|
404
474
|
|
|
405
475
|
# Generate embedding from title + content
|
|
406
|
-
|
|
476
|
+
try:
|
|
477
|
+
embedding = self.embedding_model.encode_single(combined, timeout=Config.EMBEDDING_LOAD_TIMEOUT)
|
|
478
|
+
except EmbeddingModelNotReadyError as e:
|
|
479
|
+
conn.close()
|
|
480
|
+
return {
|
|
481
|
+
"success": False,
|
|
482
|
+
"error": "model_loading",
|
|
483
|
+
"message": str(e),
|
|
484
|
+
"retry_after": 5
|
|
485
|
+
}
|
|
407
486
|
|
|
408
487
|
# Calculate or validate order
|
|
409
488
|
if validated_order is None:
|
|
@@ -556,7 +635,16 @@ class TaskStore:
|
|
|
556
635
|
}
|
|
557
636
|
|
|
558
637
|
# Batch generate embeddings for all valid tasks
|
|
559
|
-
|
|
638
|
+
try:
|
|
639
|
+
embeddings = self.embedding_model.encode(combined_texts, timeout=Config.EMBEDDING_LOAD_TIMEOUT)
|
|
640
|
+
except EmbeddingModelNotReadyError as e:
|
|
641
|
+
conn.close()
|
|
642
|
+
return {
|
|
643
|
+
"success": False,
|
|
644
|
+
"error": "model_loading",
|
|
645
|
+
"message": str(e),
|
|
646
|
+
"retry_after": 5
|
|
647
|
+
}
|
|
560
648
|
|
|
561
649
|
# Second pass: insert tasks and vectors in single transaction
|
|
562
650
|
for idx, metadata in enumerate(task_metadata):
|
|
@@ -840,7 +928,17 @@ class TaskStore:
|
|
|
840
928
|
update_values.append(new_hash)
|
|
841
929
|
|
|
842
930
|
# Generate new embedding
|
|
843
|
-
|
|
931
|
+
try:
|
|
932
|
+
embedding = self.embedding_model.encode_single(combined, timeout=Config.EMBEDDING_LOAD_TIMEOUT)
|
|
933
|
+
except EmbeddingModelNotReadyError as e:
|
|
934
|
+
conn.rollback()
|
|
935
|
+
conn.close()
|
|
936
|
+
return {
|
|
937
|
+
"success": False,
|
|
938
|
+
"error": "model_loading",
|
|
939
|
+
"message": str(e),
|
|
940
|
+
"retry_after": 5
|
|
941
|
+
}
|
|
844
942
|
embedding_blob = sqlite_vec.serialize_float32(embedding)
|
|
845
943
|
|
|
846
944
|
# Update vector
|
|
@@ -871,6 +969,10 @@ class TaskStore:
|
|
|
871
969
|
elif old_status == 'in_progress' and new_status != 'in_progress':
|
|
872
970
|
self._finish_time_session(conn, task_id, time_delta, new_status)
|
|
873
971
|
|
|
972
|
+
# Propagate 'in_progress' status to parent when ANY child starts work
|
|
973
|
+
if status_changed and new_status == 'in_progress':
|
|
974
|
+
self._propagate_in_progress_to_parents(conn, task_id)
|
|
975
|
+
|
|
874
976
|
# Propagate 'completed' status to parent when ALL children are finished
|
|
875
977
|
if status_changed and new_status in TaskStatus.finish_statuses():
|
|
876
978
|
self._propagate_completed_to_parents(conn, task_id)
|
|
@@ -879,6 +981,10 @@ class TaskStore:
|
|
|
879
981
|
if status_changed and new_status == 'pending':
|
|
880
982
|
self._propagate_pending_to_parents(conn, task_id)
|
|
881
983
|
|
|
984
|
+
# Propagate 'pending' to parent when child is stopped and no other in_progress children
|
|
985
|
+
if status_changed and new_status == 'stopped':
|
|
986
|
+
self._propagate_stopped_to_parents(conn, task_id)
|
|
987
|
+
|
|
882
988
|
conn.commit()
|
|
883
989
|
|
|
884
990
|
# Fetch updated task
|
|
@@ -1356,7 +1462,20 @@ class TaskStore:
|
|
|
1356
1462
|
query = sanitize_input(query)
|
|
1357
1463
|
|
|
1358
1464
|
# Generate query embedding
|
|
1359
|
-
|
|
1465
|
+
try:
|
|
1466
|
+
query_embedding = self.embedding_model.encode_single(query, timeout=Config.EMBEDDING_LOAD_TIMEOUT)
|
|
1467
|
+
except EmbeddingModelNotReadyError as e:
|
|
1468
|
+
conn.close()
|
|
1469
|
+
return {
|
|
1470
|
+
"tasks": [],
|
|
1471
|
+
"total_count": 0,
|
|
1472
|
+
"returned_count": 0,
|
|
1473
|
+
"offset": offset,
|
|
1474
|
+
"success": False,
|
|
1475
|
+
"error": "model_loading",
|
|
1476
|
+
"message": str(e),
|
|
1477
|
+
"retry_after": 5
|
|
1478
|
+
}
|
|
1360
1479
|
query_blob = sqlite_vec.serialize_float32(query_embedding)
|
|
1361
1480
|
|
|
1362
1481
|
# Build search query
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: vector-task-mcp
|
|
3
|
-
Version: 1.2.
|
|
3
|
+
Version: 1.2.9
|
|
4
4
|
Summary: A secure, vector-based task management server for Claude Desktop using sqlite-vec and sentence-transformers
|
|
5
5
|
Author-email: Xsaven <xsaven@gmail.com>
|
|
6
6
|
License: MIT
|
|
@@ -23,6 +23,7 @@ License-File: LICENSE
|
|
|
23
23
|
Requires-Dist: mcp>=0.3.0
|
|
24
24
|
Requires-Dist: sqlite-vec>=0.1.6
|
|
25
25
|
Requires-Dist: sentence-transformers>=2.2.2
|
|
26
|
+
Requires-Dist: requests>=2.28.0
|
|
26
27
|
Dynamic: license-file
|
|
27
28
|
|
|
28
29
|
# Vector Task MCP Server
|
|
@@ -1,68 +0,0 @@
|
|
|
1
|
-
"""
|
|
2
|
-
Embedding Model Module
|
|
3
|
-
======================
|
|
4
|
-
|
|
5
|
-
Provides sentence transformer embedding functionality for semantic task search.
|
|
6
|
-
"""
|
|
7
|
-
|
|
8
|
-
from typing import List, Union
|
|
9
|
-
import numpy as np
|
|
10
|
-
from sentence_transformers import SentenceTransformer
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
class EmbeddingModel:
|
|
14
|
-
"""Wrapper for sentence transformer embedding model."""
|
|
15
|
-
|
|
16
|
-
def __init__(self, model_name: str):
|
|
17
|
-
"""
|
|
18
|
-
Initialize embedding model.
|
|
19
|
-
|
|
20
|
-
Args:
|
|
21
|
-
model_name: HuggingFace model name (e.g., 'sentence-transformers/all-MiniLM-L6-v2')
|
|
22
|
-
"""
|
|
23
|
-
self.model_name = model_name
|
|
24
|
-
self.model = SentenceTransformer(model_name)
|
|
25
|
-
|
|
26
|
-
def encode(self, text: Union[str, List[str]]) -> np.ndarray:
|
|
27
|
-
"""
|
|
28
|
-
Encode text to embedding vector(s).
|
|
29
|
-
|
|
30
|
-
Args:
|
|
31
|
-
text: Single text string or list of texts
|
|
32
|
-
|
|
33
|
-
Returns:
|
|
34
|
-
Numpy array of embeddings (1D for single text, 2D for list)
|
|
35
|
-
"""
|
|
36
|
-
embeddings = self.model.encode(text, convert_to_numpy=True)
|
|
37
|
-
|
|
38
|
-
# Ensure float32 for sqlite-vec compatibility
|
|
39
|
-
return embeddings.astype(np.float32)
|
|
40
|
-
|
|
41
|
-
def encode_single(self, text: str) -> np.ndarray:
|
|
42
|
-
"""
|
|
43
|
-
Encode single text to embedding vector.
|
|
44
|
-
|
|
45
|
-
Args:
|
|
46
|
-
text: Single text string
|
|
47
|
-
|
|
48
|
-
Returns:
|
|
49
|
-
Numpy array of embedding (1D float32)
|
|
50
|
-
"""
|
|
51
|
-
return self.encode(text)
|
|
52
|
-
|
|
53
|
-
def get_embedding_dim(self) -> int:
|
|
54
|
-
"""Get embedding dimension."""
|
|
55
|
-
return self.model.get_sentence_embedding_dimension()
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
def get_embedding_model(model_name: str) -> EmbeddingModel:
|
|
59
|
-
"""
|
|
60
|
-
Factory function to get embedding model instance.
|
|
61
|
-
|
|
62
|
-
Args:
|
|
63
|
-
model_name: HuggingFace model name
|
|
64
|
-
|
|
65
|
-
Returns:
|
|
66
|
-
Initialized EmbeddingModel instance
|
|
67
|
-
"""
|
|
68
|
-
return EmbeddingModel(model_name)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{vector_task_mcp-1.2.7 → vector_task_mcp-1.2.9}/vector_task_mcp.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|