minder-cli 0.2.0__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.
- minder/__init__.py +12 -0
- minder/api/routers/prompts.py +177 -0
- minder/application/__init__.py +1 -0
- minder/application/admin/__init__.py +11 -0
- minder/application/admin/dto.py +453 -0
- minder/application/admin/jobs.py +327 -0
- minder/application/admin/use_cases.py +1895 -0
- minder/auth/__init__.py +12 -0
- minder/auth/context.py +26 -0
- minder/auth/middleware.py +70 -0
- minder/auth/principal.py +59 -0
- minder/auth/rate_limiter.py +89 -0
- minder/auth/rbac.py +60 -0
- minder/auth/service.py +541 -0
- minder/bootstrap/__init__.py +9 -0
- minder/bootstrap/providers.py +109 -0
- minder/bootstrap/transport.py +807 -0
- minder/cache/__init__.py +10 -0
- minder/cache/providers.py +140 -0
- minder/chunking/__init__.py +4 -0
- minder/chunking/code_splitter.py +184 -0
- minder/chunking/splitter.py +136 -0
- minder/cli.py +1542 -0
- minder/config.py +179 -0
- minder/continuity.py +363 -0
- minder/dev.py +160 -0
- minder/embedding/__init__.py +9 -0
- minder/embedding/base.py +7 -0
- minder/embedding/local.py +65 -0
- minder/embedding/openai.py +7 -0
- minder/graph/__init__.py +11 -0
- minder/graph/edges.py +13 -0
- minder/graph/executor.py +127 -0
- minder/graph/graph.py +263 -0
- minder/graph/nodes/__init__.py +27 -0
- minder/graph/nodes/evaluator.py +21 -0
- minder/graph/nodes/guard.py +64 -0
- minder/graph/nodes/llm.py +59 -0
- minder/graph/nodes/planning.py +30 -0
- minder/graph/nodes/reasoning.py +87 -0
- minder/graph/nodes/reranker.py +141 -0
- minder/graph/nodes/retriever.py +86 -0
- minder/graph/nodes/verification.py +230 -0
- minder/graph/nodes/workflow_planner.py +250 -0
- minder/graph/runtime.py +15 -0
- minder/graph/state.py +26 -0
- minder/llm/__init__.py +5 -0
- minder/llm/base.py +14 -0
- minder/llm/local.py +381 -0
- minder/llm/openai.py +89 -0
- minder/models/__init__.py +109 -0
- minder/models/base.py +10 -0
- minder/models/client.py +137 -0
- minder/models/document.py +34 -0
- minder/models/error.py +32 -0
- minder/models/graph.py +114 -0
- minder/models/history.py +32 -0
- minder/models/job.py +62 -0
- minder/models/prompt.py +41 -0
- minder/models/repository.py +62 -0
- minder/models/rule.py +68 -0
- minder/models/session.py +51 -0
- minder/models/skill.py +52 -0
- minder/models/user.py +41 -0
- minder/models/workflow.py +35 -0
- minder/observability/__init__.py +57 -0
- minder/observability/audit.py +243 -0
- minder/observability/logging.py +253 -0
- minder/observability/metrics.py +448 -0
- minder/observability/tracing.py +215 -0
- minder/presentation/__init__.py +1 -0
- minder/presentation/http/__init__.py +1 -0
- minder/presentation/http/admin/__init__.py +3 -0
- minder/presentation/http/admin/api.py +1309 -0
- minder/presentation/http/admin/context.py +94 -0
- minder/presentation/http/admin/dashboard.py +111 -0
- minder/presentation/http/admin/jobs.py +208 -0
- minder/presentation/http/admin/memories.py +185 -0
- minder/presentation/http/admin/prompts.py +219 -0
- minder/presentation/http/admin/routes.py +127 -0
- minder/presentation/http/admin/runtime.py +650 -0
- minder/presentation/http/admin/search.py +368 -0
- minder/presentation/http/admin/skills.py +230 -0
- minder/prompts/__init__.py +646 -0
- minder/prompts/formatter.py +142 -0
- minder/resources/__init__.py +318 -0
- minder/retrieval/__init__.py +5 -0
- minder/retrieval/hybrid.py +178 -0
- minder/retrieval/mmr.py +116 -0
- minder/retrieval/multi_hop.py +115 -0
- minder/runtime.py +15 -0
- minder/server.py +145 -0
- minder/store/__init__.py +64 -0
- minder/store/document.py +115 -0
- minder/store/error.py +82 -0
- minder/store/feedback.py +114 -0
- minder/store/graph.py +588 -0
- minder/store/history.py +57 -0
- minder/store/interfaces.py +512 -0
- minder/store/milvus/__init__.py +11 -0
- minder/store/milvus/client.py +26 -0
- minder/store/milvus/collections.py +15 -0
- minder/store/milvus/vector_store.py +232 -0
- minder/store/mongodb/__init__.py +11 -0
- minder/store/mongodb/client.py +49 -0
- minder/store/mongodb/indexes.py +90 -0
- minder/store/mongodb/operational_store.py +993 -0
- minder/store/relational.py +1087 -0
- minder/store/repo_state.py +58 -0
- minder/store/rule.py +93 -0
- minder/store/vector.py +79 -0
- minder/tools/__init__.py +47 -0
- minder/tools/auth.py +94 -0
- minder/tools/graph.py +839 -0
- minder/tools/ingest.py +353 -0
- minder/tools/memory.py +381 -0
- minder/tools/query.py +307 -0
- minder/tools/registry.py +269 -0
- minder/tools/repo_scanner.py +1266 -0
- minder/tools/search.py +15 -0
- minder/tools/session.py +316 -0
- minder/tools/skills.py +899 -0
- minder/tools/workflow.py +215 -0
- minder/transport/__init__.py +4 -0
- minder/transport/base.py +286 -0
- minder/transport/sse.py +252 -0
- minder/transport/stdio.py +29 -0
- minder_cli-0.2.0.dist-info/METADATA +318 -0
- minder_cli-0.2.0.dist-info/RECORD +132 -0
- minder_cli-0.2.0.dist-info/WHEEL +4 -0
- minder_cli-0.2.0.dist-info/entry_points.txt +2 -0
- minder_cli-0.2.0.dist-info/licenses/LICENSE +201 -0
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Milvus Vector Store — implements IVectorStore using PyMilvus.
|
|
3
|
+
|
|
4
|
+
All operations execute in a thread pool since PyMilvus is synchronous.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
import asyncio
|
|
10
|
+
import logging
|
|
11
|
+
import uuid
|
|
12
|
+
from typing import Any
|
|
13
|
+
|
|
14
|
+
from minder.store.interfaces import IDocumentRepository
|
|
15
|
+
from minder.store.milvus.client import MilvusClient
|
|
16
|
+
from minder.store.milvus.collections import get_document_schema
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
logger = logging.getLogger(__name__)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class MilvusVectorStore:
|
|
23
|
+
def __init__(
|
|
24
|
+
self,
|
|
25
|
+
client: MilvusClient,
|
|
26
|
+
document_store: IDocumentRepository,
|
|
27
|
+
prefix: str = "minder_",
|
|
28
|
+
dimensions: int = 768,
|
|
29
|
+
) -> None:
|
|
30
|
+
self._client = client
|
|
31
|
+
self._document_store = document_store
|
|
32
|
+
self._prefix = prefix
|
|
33
|
+
self._dimensions = dimensions
|
|
34
|
+
self._doc_collection = f"{prefix}documents"
|
|
35
|
+
self._collection_ready = False
|
|
36
|
+
self._collection_lock = asyncio.Lock()
|
|
37
|
+
|
|
38
|
+
async def setup(self) -> None:
|
|
39
|
+
await self._ensure_collection_ready(force=True)
|
|
40
|
+
|
|
41
|
+
async def _ensure_collection_ready(self, *, force: bool = False) -> None:
|
|
42
|
+
if self._collection_ready and not force:
|
|
43
|
+
return
|
|
44
|
+
|
|
45
|
+
async with self._collection_lock:
|
|
46
|
+
if self._collection_ready and not force:
|
|
47
|
+
return
|
|
48
|
+
|
|
49
|
+
loop = asyncio.get_running_loop()
|
|
50
|
+
|
|
51
|
+
def _setup() -> None:
|
|
52
|
+
if self._client.client.has_collection(self._doc_collection):
|
|
53
|
+
metadata = self._client.client.describe_collection(self._doc_collection)
|
|
54
|
+
current_dim = self._extract_embedding_dimension(metadata)
|
|
55
|
+
if current_dim is not None and current_dim != self._dimensions:
|
|
56
|
+
logger.warning(
|
|
57
|
+
"Milvus collection %s dimension mismatch: existing=%s expected=%s; recreating collection for reindex",
|
|
58
|
+
self._doc_collection,
|
|
59
|
+
current_dim,
|
|
60
|
+
self._dimensions,
|
|
61
|
+
)
|
|
62
|
+
self._client.client.drop_collection(self._doc_collection)
|
|
63
|
+
|
|
64
|
+
if not self._client.client.has_collection(self._doc_collection):
|
|
65
|
+
self._create_collection()
|
|
66
|
+
|
|
67
|
+
await loop.run_in_executor(None, _setup)
|
|
68
|
+
self._collection_ready = True
|
|
69
|
+
|
|
70
|
+
def _create_collection(self) -> None:
|
|
71
|
+
schema = get_document_schema(self._dimensions)
|
|
72
|
+
index_params = self._client.client.prepare_index_params()
|
|
73
|
+
index_params.add_index(
|
|
74
|
+
field_name="embedding",
|
|
75
|
+
index_type="AUTOINDEX",
|
|
76
|
+
metric_type="COSINE",
|
|
77
|
+
)
|
|
78
|
+
self._client.client.create_collection(
|
|
79
|
+
collection_name=self._doc_collection,
|
|
80
|
+
schema=schema,
|
|
81
|
+
index_params=index_params,
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
@staticmethod
|
|
85
|
+
def _extract_embedding_dimension(metadata: Any) -> int | None:
|
|
86
|
+
if not isinstance(metadata, dict):
|
|
87
|
+
return None
|
|
88
|
+
fields = metadata.get("fields", [])
|
|
89
|
+
if not isinstance(fields, list):
|
|
90
|
+
return None
|
|
91
|
+
|
|
92
|
+
for field in fields:
|
|
93
|
+
if not isinstance(field, dict):
|
|
94
|
+
continue
|
|
95
|
+
field_name = field.get("name") or field.get("field_name")
|
|
96
|
+
if field_name != "embedding":
|
|
97
|
+
continue
|
|
98
|
+
|
|
99
|
+
for key in ("params", "type_params", "element_type_params"):
|
|
100
|
+
params = field.get(key)
|
|
101
|
+
if isinstance(params, dict) and params.get("dim") is not None:
|
|
102
|
+
return int(params["dim"])
|
|
103
|
+
|
|
104
|
+
if field.get("dim") is not None:
|
|
105
|
+
return int(field["dim"])
|
|
106
|
+
|
|
107
|
+
return None
|
|
108
|
+
|
|
109
|
+
def _validate_embedding_length(self, embedding: list[float]) -> None:
|
|
110
|
+
if len(embedding) != self._dimensions:
|
|
111
|
+
raise ValueError(
|
|
112
|
+
f"Embedding length {len(embedding)} does not match configured Milvus dimension {self._dimensions}"
|
|
113
|
+
)
|
|
114
|
+
|
|
115
|
+
@staticmethod
|
|
116
|
+
def _serialize_payload(payload: dict[str, Any]) -> dict[str, Any]:
|
|
117
|
+
return {
|
|
118
|
+
"title": str(payload.get("title", "")),
|
|
119
|
+
"source_path": str(payload.get("source_path", payload.get("path", ""))),
|
|
120
|
+
"doc_type": str(payload.get("doc_type", "")),
|
|
121
|
+
"project": str(payload.get("project", "")),
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
async def upsert_document(
|
|
125
|
+
self,
|
|
126
|
+
doc_id: uuid.UUID,
|
|
127
|
+
embedding: list[float],
|
|
128
|
+
payload: dict[str, Any],
|
|
129
|
+
) -> None:
|
|
130
|
+
self._validate_embedding_length(embedding)
|
|
131
|
+
await self._ensure_collection_ready()
|
|
132
|
+
loop = asyncio.get_running_loop()
|
|
133
|
+
|
|
134
|
+
def _upsert() -> None:
|
|
135
|
+
self._client.client.upsert(
|
|
136
|
+
collection_name=self._doc_collection,
|
|
137
|
+
data=[
|
|
138
|
+
{
|
|
139
|
+
"id": str(doc_id),
|
|
140
|
+
"embedding": embedding,
|
|
141
|
+
"project": payload.get("project", ""),
|
|
142
|
+
"doc_type": payload.get("doc_type", ""),
|
|
143
|
+
"payload": self._serialize_payload(payload),
|
|
144
|
+
}
|
|
145
|
+
],
|
|
146
|
+
)
|
|
147
|
+
|
|
148
|
+
await loop.run_in_executor(None, _upsert)
|
|
149
|
+
|
|
150
|
+
async def delete_documents(self, doc_ids: list[uuid.UUID]) -> None:
|
|
151
|
+
if not doc_ids:
|
|
152
|
+
return
|
|
153
|
+
loop = asyncio.get_running_loop()
|
|
154
|
+
|
|
155
|
+
def _delete() -> None:
|
|
156
|
+
id_list = [f"'{did}'" for did in doc_ids]
|
|
157
|
+
expr = f"id in [{', '.join(id_list)}]"
|
|
158
|
+
self._client.client.delete(
|
|
159
|
+
collection_name=self._doc_collection,
|
|
160
|
+
filter=expr,
|
|
161
|
+
)
|
|
162
|
+
|
|
163
|
+
await loop.run_in_executor(None, _delete)
|
|
164
|
+
|
|
165
|
+
async def search_documents(
|
|
166
|
+
self,
|
|
167
|
+
query_embedding: list[float],
|
|
168
|
+
*,
|
|
169
|
+
project: str | None = None,
|
|
170
|
+
doc_types: set[str] | None = None,
|
|
171
|
+
limit: int = 5,
|
|
172
|
+
score_threshold: float = 0.0,
|
|
173
|
+
) -> list[dict[str, Any]]:
|
|
174
|
+
self._validate_embedding_length(query_embedding)
|
|
175
|
+
await self._ensure_collection_ready()
|
|
176
|
+
loop = asyncio.get_running_loop()
|
|
177
|
+
|
|
178
|
+
filter_expr = ""
|
|
179
|
+
filters = []
|
|
180
|
+
if project:
|
|
181
|
+
filters.append(f'project == "{project}"')
|
|
182
|
+
if doc_types:
|
|
183
|
+
types_str = ", ".join(f'"{t}"' for t in doc_types)
|
|
184
|
+
filters.append(f"doc_type in [{types_str}]")
|
|
185
|
+
|
|
186
|
+
if filters:
|
|
187
|
+
filter_expr = " and ".join(filters)
|
|
188
|
+
|
|
189
|
+
def _search() -> Any:
|
|
190
|
+
return self._client.client.search(
|
|
191
|
+
collection_name=self._doc_collection,
|
|
192
|
+
data=[query_embedding],
|
|
193
|
+
filter=filter_expr,
|
|
194
|
+
limit=limit,
|
|
195
|
+
output_fields=["payload"],
|
|
196
|
+
)
|
|
197
|
+
|
|
198
|
+
results = await loop.run_in_executor(None, _search)
|
|
199
|
+
|
|
200
|
+
hits_with_payload: list[tuple[Any, dict[str, Any]]] = []
|
|
201
|
+
doc_ids: list[uuid.UUID] = []
|
|
202
|
+
ranked: list[dict[str, Any]] = []
|
|
203
|
+
if results and len(results) > 0:
|
|
204
|
+
for hit in results[0]:
|
|
205
|
+
if hit.distance < score_threshold:
|
|
206
|
+
continue
|
|
207
|
+
payload = hit.entity.get("payload", {})
|
|
208
|
+
doc_id = uuid.UUID(hit.id) if isinstance(hit.id, str) else hit.id
|
|
209
|
+
if isinstance(doc_id, uuid.UUID):
|
|
210
|
+
doc_ids.append(doc_id)
|
|
211
|
+
hits_with_payload.append((hit, payload if isinstance(payload, dict) else {}))
|
|
212
|
+
|
|
213
|
+
docs_by_id = {
|
|
214
|
+
doc.id: doc for doc in await self._document_store.get_documents_by_ids(doc_ids)
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
for hit, payload in hits_with_payload:
|
|
218
|
+
doc_id = uuid.UUID(hit.id) if isinstance(hit.id, str) else hit.id
|
|
219
|
+
doc = docs_by_id.get(doc_id) if isinstance(doc_id, uuid.UUID) else None
|
|
220
|
+
ranked.append(
|
|
221
|
+
{
|
|
222
|
+
"id": doc_id,
|
|
223
|
+
"title": getattr(doc, "title", None) or payload.get("title", ""),
|
|
224
|
+
"path": getattr(doc, "source_path", None)
|
|
225
|
+
or payload.get("source_path", payload.get("path", "")),
|
|
226
|
+
"content": getattr(doc, "content", None) or "",
|
|
227
|
+
"score": round(hit.distance, 4),
|
|
228
|
+
"doc_type": getattr(doc, "doc_type", None) or payload.get("doc_type", ""),
|
|
229
|
+
}
|
|
230
|
+
)
|
|
231
|
+
|
|
232
|
+
return ranked
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
"""
|
|
2
|
+
MongoDB Store Package — Motor-based async adapters implementing domain interfaces.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from minder.store.mongodb.client import MongoClient
|
|
6
|
+
from minder.store.mongodb.operational_store import MongoOperationalStore
|
|
7
|
+
|
|
8
|
+
__all__ = [
|
|
9
|
+
"MongoClient",
|
|
10
|
+
"MongoOperationalStore",
|
|
11
|
+
]
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
"""
|
|
2
|
+
MongoDB Async Client — Motor singleton with connection pooling.
|
|
3
|
+
|
|
4
|
+
Usage:
|
|
5
|
+
client = MongoClient(uri="mongodb://localhost:27017", database="minder")
|
|
6
|
+
db = client.db
|
|
7
|
+
await client.health_check()
|
|
8
|
+
await client.close()
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from __future__ import annotations
|
|
12
|
+
|
|
13
|
+
from motor.motor_asyncio import AsyncIOMotorClient, AsyncIOMotorDatabase
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class MongoClient:
|
|
17
|
+
"""Thin wrapper around Motor's async client with lifecycle helpers."""
|
|
18
|
+
|
|
19
|
+
def __init__(
|
|
20
|
+
self,
|
|
21
|
+
uri: str = "mongodb://localhost:27017",
|
|
22
|
+
database: str = "minder",
|
|
23
|
+
*,
|
|
24
|
+
min_pool_size: int = 5,
|
|
25
|
+
max_pool_size: int = 50,
|
|
26
|
+
) -> None:
|
|
27
|
+
self._client: AsyncIOMotorClient = AsyncIOMotorClient( # type: ignore[type-arg]
|
|
28
|
+
uri,
|
|
29
|
+
minPoolSize=min_pool_size,
|
|
30
|
+
maxPoolSize=max_pool_size,
|
|
31
|
+
serverSelectionTimeoutMS=5000,
|
|
32
|
+
)
|
|
33
|
+
self._db: AsyncIOMotorDatabase = self._client[database] # type: ignore[type-arg]
|
|
34
|
+
|
|
35
|
+
@property
|
|
36
|
+
def db(self) -> AsyncIOMotorDatabase: # type: ignore[type-arg]
|
|
37
|
+
return self._db
|
|
38
|
+
|
|
39
|
+
async def health_check(self) -> bool:
|
|
40
|
+
"""Ping the MongoDB server."""
|
|
41
|
+
try:
|
|
42
|
+
await self._client.admin.command("ping")
|
|
43
|
+
return True
|
|
44
|
+
except Exception:
|
|
45
|
+
return False
|
|
46
|
+
|
|
47
|
+
async def close(self) -> None:
|
|
48
|
+
"""Close the Motor client."""
|
|
49
|
+
self._client.close()
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
"""
|
|
2
|
+
MongoDB collection indexes — called once at application startup.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from __future__ import annotations
|
|
6
|
+
|
|
7
|
+
from motor.motor_asyncio import AsyncIOMotorDatabase
|
|
8
|
+
from pymongo import ASCENDING
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
async def ensure_indexes(db: AsyncIOMotorDatabase) -> None: # type: ignore[type-arg]
|
|
12
|
+
"""Create all required MongoDB indexes (idempotent)."""
|
|
13
|
+
|
|
14
|
+
# Users
|
|
15
|
+
users = db["users"]
|
|
16
|
+
await users.create_index([("email", ASCENDING)], unique=True)
|
|
17
|
+
await users.create_index([("username", ASCENDING)], unique=True)
|
|
18
|
+
await users.create_index([("company_id", ASCENDING)])
|
|
19
|
+
|
|
20
|
+
# Clients
|
|
21
|
+
clients = db["clients"]
|
|
22
|
+
await clients.create_index([("slug", ASCENDING)], unique=True)
|
|
23
|
+
await clients.create_index([("created_by_user_id", ASCENDING)])
|
|
24
|
+
|
|
25
|
+
client_api_keys = db["client_api_keys"]
|
|
26
|
+
await client_api_keys.create_index([("client_id", ASCENDING)])
|
|
27
|
+
await client_api_keys.create_index([("status", ASCENDING)])
|
|
28
|
+
|
|
29
|
+
client_sessions = db["client_sessions"]
|
|
30
|
+
await client_sessions.create_index([("client_id", ASCENDING)])
|
|
31
|
+
await client_sessions.create_index([("access_token_id", ASCENDING)], unique=True)
|
|
32
|
+
|
|
33
|
+
audit_logs = db["audit_logs"]
|
|
34
|
+
await audit_logs.create_index([("actor_id", ASCENDING)])
|
|
35
|
+
await audit_logs.create_index([("event_type", ASCENDING)])
|
|
36
|
+
|
|
37
|
+
admin_jobs = db["admin_jobs"]
|
|
38
|
+
await admin_jobs.create_index([("job_type", ASCENDING)])
|
|
39
|
+
await admin_jobs.create_index([("status", ASCENDING)])
|
|
40
|
+
await admin_jobs.create_index([("requested_by_user_id", ASCENDING)])
|
|
41
|
+
await admin_jobs.create_index([("created_at", ASCENDING)])
|
|
42
|
+
|
|
43
|
+
# Skills
|
|
44
|
+
skills = db["skills"]
|
|
45
|
+
await skills.create_index([("company_id", ASCENDING)])
|
|
46
|
+
await skills.create_index([("title", ASCENDING)])
|
|
47
|
+
await skills.create_index([("language", ASCENDING)])
|
|
48
|
+
|
|
49
|
+
# Prompts
|
|
50
|
+
prompts = db["prompts"]
|
|
51
|
+
await prompts.create_index([("company_id", ASCENDING)])
|
|
52
|
+
await prompts.create_index([("name", ASCENDING)], unique=True)
|
|
53
|
+
|
|
54
|
+
# Sessions
|
|
55
|
+
sessions = db["sessions"]
|
|
56
|
+
await sessions.create_index([("company_id", ASCENDING)])
|
|
57
|
+
await sessions.create_index([("user_id", ASCENDING)])
|
|
58
|
+
await sessions.create_index([("repo_id", ASCENDING)])
|
|
59
|
+
|
|
60
|
+
# Workflows
|
|
61
|
+
workflows = db["workflows"]
|
|
62
|
+
await workflows.create_index([("company_id", ASCENDING)])
|
|
63
|
+
await workflows.create_index([("name", ASCENDING)])
|
|
64
|
+
|
|
65
|
+
# Repositories
|
|
66
|
+
repos = db["repositories"]
|
|
67
|
+
await repos.create_index([("company_id", ASCENDING)])
|
|
68
|
+
await repos.create_index([("repo_name", ASCENDING)])
|
|
69
|
+
|
|
70
|
+
# Repository Workflow States
|
|
71
|
+
workflow_states = db["repository_workflow_states"]
|
|
72
|
+
await workflow_states.create_index([("repo_id", ASCENDING)])
|
|
73
|
+
await workflow_states.create_index([("session_id", ASCENDING)])
|
|
74
|
+
|
|
75
|
+
# Documents
|
|
76
|
+
documents = db["documents"]
|
|
77
|
+
await documents.create_index([("doc_type", ASCENDING)])
|
|
78
|
+
await documents.create_index([("project", ASCENDING)])
|
|
79
|
+
await documents.create_index(
|
|
80
|
+
[("source_path", ASCENDING), ("project", ASCENDING)],
|
|
81
|
+
unique=True,
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
# History
|
|
85
|
+
history = db["history"]
|
|
86
|
+
await history.create_index([("session_id", ASCENDING)])
|
|
87
|
+
|
|
88
|
+
# Errors
|
|
89
|
+
errors = db["errors"]
|
|
90
|
+
await errors.create_index([("error_code", ASCENDING)])
|