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,219 @@
|
|
|
1
|
+
import uuid
|
|
2
|
+
import logging
|
|
3
|
+
from typing import Any, List
|
|
4
|
+
|
|
5
|
+
from pydantic import BaseModel, Field
|
|
6
|
+
from starlette.requests import Request
|
|
7
|
+
from starlette.responses import JSONResponse
|
|
8
|
+
from starlette.routing import BaseRoute, Route
|
|
9
|
+
|
|
10
|
+
from minder.config import MinderConfig
|
|
11
|
+
from minder.observability.metrics import record_admin_operation
|
|
12
|
+
from minder.prompts.formatter import PromptDraft, polish_prompt_draft
|
|
13
|
+
from minder.prompts import PromptRegistry
|
|
14
|
+
from .context import AdminRouteContext
|
|
15
|
+
|
|
16
|
+
logger = logging.getLogger(__name__)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class PromptCreateRequest(BaseModel):
|
|
20
|
+
name: str
|
|
21
|
+
title: str
|
|
22
|
+
description: str
|
|
23
|
+
content_template: str
|
|
24
|
+
arguments: List[str] = Field(default_factory=list)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class PromptUpdateRequest(BaseModel):
|
|
28
|
+
name: str | None = None
|
|
29
|
+
title: str | None = None
|
|
30
|
+
description: str | None = None
|
|
31
|
+
content_template: str | None = None
|
|
32
|
+
arguments: List[str] | None = None
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
class PromptPolishRequest(BaseModel):
|
|
36
|
+
name: str
|
|
37
|
+
title: str = ""
|
|
38
|
+
description: str = ""
|
|
39
|
+
content_template: str = ""
|
|
40
|
+
arguments: List[str] = Field(default_factory=list)
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def _serialize_prompt(prompt: Any) -> dict[str, Any]:
|
|
44
|
+
return {
|
|
45
|
+
"id": str(prompt.id),
|
|
46
|
+
"name": prompt.name,
|
|
47
|
+
"title": prompt.title,
|
|
48
|
+
"description": prompt.description,
|
|
49
|
+
"content_template": prompt.content_template,
|
|
50
|
+
"arguments": list(getattr(prompt, "arguments", []) or []),
|
|
51
|
+
"created_at": prompt.created_at.isoformat() if prompt.created_at else None,
|
|
52
|
+
"updated_at": prompt.updated_at.isoformat() if prompt.updated_at else None,
|
|
53
|
+
"is_builtin": bool(getattr(prompt, "is_builtin", False)),
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
async def _sync_prompts(context: AdminRouteContext) -> None:
|
|
58
|
+
if context.prompt_sync_hook is None:
|
|
59
|
+
return
|
|
60
|
+
await context.prompt_sync_hook()
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def _config_from_request(request: Request) -> MinderConfig:
|
|
64
|
+
config = getattr(request.app.state, "config", None)
|
|
65
|
+
if isinstance(config, MinderConfig):
|
|
66
|
+
return config
|
|
67
|
+
return MinderConfig()
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def build_prompts_routes(context: AdminRouteContext) -> list[BaseRoute]:
|
|
71
|
+
async def list_prompts(request: Request) -> JSONResponse:
|
|
72
|
+
del request
|
|
73
|
+
await record_admin_operation(
|
|
74
|
+
operation="list_prompts",
|
|
75
|
+
outcome="success",
|
|
76
|
+
actor_id="system",
|
|
77
|
+
store=context.store,
|
|
78
|
+
)
|
|
79
|
+
try:
|
|
80
|
+
prompt_index = {
|
|
81
|
+
prompt.name: prompt for prompt in PromptRegistry.builtin_prompt_models()
|
|
82
|
+
}
|
|
83
|
+
for prompt in await context.store.list_prompts():
|
|
84
|
+
prompt_index[prompt.name] = prompt
|
|
85
|
+
ordered_prompts = sorted(
|
|
86
|
+
prompt_index.values(),
|
|
87
|
+
key=lambda prompt: (
|
|
88
|
+
not bool(getattr(prompt, "is_builtin", False)),
|
|
89
|
+
prompt.name,
|
|
90
|
+
),
|
|
91
|
+
)
|
|
92
|
+
return JSONResponse(
|
|
93
|
+
[_serialize_prompt(prompt) for prompt in ordered_prompts]
|
|
94
|
+
)
|
|
95
|
+
except Exception as e:
|
|
96
|
+
logger.exception("Failed to list prompts", exc_info=e)
|
|
97
|
+
return JSONResponse({"error": str(e)}, status_code=500)
|
|
98
|
+
|
|
99
|
+
async def get_prompt(request: Request) -> JSONResponse:
|
|
100
|
+
prompt_id = request.path_params["prompt_id"]
|
|
101
|
+
await record_admin_operation(
|
|
102
|
+
operation="get_prompt",
|
|
103
|
+
outcome="success",
|
|
104
|
+
actor_id="system",
|
|
105
|
+
store=context.store,
|
|
106
|
+
)
|
|
107
|
+
try:
|
|
108
|
+
prompt = await context.store.get_prompt_by_id(uuid.UUID(prompt_id))
|
|
109
|
+
if not prompt:
|
|
110
|
+
return JSONResponse({"error": "Prompt not found"}, status_code=404)
|
|
111
|
+
return JSONResponse(_serialize_prompt(prompt))
|
|
112
|
+
except Exception as e:
|
|
113
|
+
logger.exception("Failed to get prompt", exc_info=e)
|
|
114
|
+
return JSONResponse({"error": str(e)}, status_code=500)
|
|
115
|
+
|
|
116
|
+
async def create_prompt(request: Request) -> JSONResponse:
|
|
117
|
+
await record_admin_operation(
|
|
118
|
+
operation="create_prompt",
|
|
119
|
+
outcome="success",
|
|
120
|
+
actor_id="system",
|
|
121
|
+
store=context.store,
|
|
122
|
+
)
|
|
123
|
+
try:
|
|
124
|
+
data = await request.json()
|
|
125
|
+
payload = PromptCreateRequest(**data)
|
|
126
|
+
prompt = await context.store.create_prompt(
|
|
127
|
+
name=payload.name,
|
|
128
|
+
title=payload.title,
|
|
129
|
+
description=payload.description,
|
|
130
|
+
content_template=payload.content_template,
|
|
131
|
+
arguments=payload.arguments,
|
|
132
|
+
)
|
|
133
|
+
await _sync_prompts(context)
|
|
134
|
+
return JSONResponse(_serialize_prompt(prompt), status_code=201)
|
|
135
|
+
except Exception as e:
|
|
136
|
+
logger.exception("Failed to create prompt", exc_info=e)
|
|
137
|
+
return JSONResponse({"error": str(e)}, status_code=400)
|
|
138
|
+
|
|
139
|
+
async def update_prompt(request: Request) -> JSONResponse:
|
|
140
|
+
prompt_id = request.path_params["prompt_id"]
|
|
141
|
+
await record_admin_operation(
|
|
142
|
+
operation="update_prompt",
|
|
143
|
+
outcome="success",
|
|
144
|
+
actor_id="system",
|
|
145
|
+
store=context.store,
|
|
146
|
+
)
|
|
147
|
+
try:
|
|
148
|
+
data = await request.json()
|
|
149
|
+
payload = PromptUpdateRequest(**data)
|
|
150
|
+
update_data = payload.model_dump(exclude_unset=True)
|
|
151
|
+
prompt = await context.store.update_prompt(
|
|
152
|
+
uuid.UUID(prompt_id), **update_data
|
|
153
|
+
)
|
|
154
|
+
if not prompt:
|
|
155
|
+
return JSONResponse({"error": "Prompt not found"}, status_code=404)
|
|
156
|
+
await _sync_prompts(context)
|
|
157
|
+
return JSONResponse(_serialize_prompt(prompt))
|
|
158
|
+
except Exception as e:
|
|
159
|
+
logger.exception("Failed to update prompt", exc_info=e)
|
|
160
|
+
return JSONResponse({"error": str(e)}, status_code=400)
|
|
161
|
+
|
|
162
|
+
async def delete_prompt(request: Request) -> JSONResponse:
|
|
163
|
+
prompt_id = request.path_params["prompt_id"]
|
|
164
|
+
await record_admin_operation(
|
|
165
|
+
operation="delete_prompt",
|
|
166
|
+
outcome="success",
|
|
167
|
+
actor_id="system",
|
|
168
|
+
store=context.store,
|
|
169
|
+
)
|
|
170
|
+
try:
|
|
171
|
+
await context.store.delete_prompt(uuid.UUID(prompt_id))
|
|
172
|
+
await _sync_prompts(context)
|
|
173
|
+
return JSONResponse({"status": "deleted"}, status_code=200)
|
|
174
|
+
except Exception as e:
|
|
175
|
+
logger.exception("Failed to delete prompt", exc_info=e)
|
|
176
|
+
return JSONResponse({"error": str(e)}, status_code=500)
|
|
177
|
+
|
|
178
|
+
async def polish_prompt(request: Request) -> JSONResponse:
|
|
179
|
+
await record_admin_operation(
|
|
180
|
+
operation="polish_prompt",
|
|
181
|
+
outcome="success",
|
|
182
|
+
actor_id="system",
|
|
183
|
+
store=context.store,
|
|
184
|
+
)
|
|
185
|
+
try:
|
|
186
|
+
data = await request.json()
|
|
187
|
+
payload = PromptPolishRequest(**data)
|
|
188
|
+
polished, metadata = polish_prompt_draft(
|
|
189
|
+
PromptDraft(
|
|
190
|
+
name=payload.name,
|
|
191
|
+
title=payload.title,
|
|
192
|
+
description=payload.description,
|
|
193
|
+
content_template=payload.content_template,
|
|
194
|
+
arguments=payload.arguments,
|
|
195
|
+
),
|
|
196
|
+
_config_from_request(request),
|
|
197
|
+
)
|
|
198
|
+
return JSONResponse(
|
|
199
|
+
{
|
|
200
|
+
"name": polished.name,
|
|
201
|
+
"title": polished.title,
|
|
202
|
+
"description": polished.description,
|
|
203
|
+
"content_template": polished.content_template,
|
|
204
|
+
"arguments": polished.arguments,
|
|
205
|
+
"llm": metadata,
|
|
206
|
+
}
|
|
207
|
+
)
|
|
208
|
+
except Exception as e:
|
|
209
|
+
logger.exception("Failed to polish prompt", exc_info=e)
|
|
210
|
+
return JSONResponse({"error": str(e)}, status_code=400)
|
|
211
|
+
|
|
212
|
+
return [
|
|
213
|
+
Route("/api/v1/prompts", list_prompts, methods=["GET"]),
|
|
214
|
+
Route("/api/v1/prompts", create_prompt, methods=["POST"]),
|
|
215
|
+
Route("/api/v1/prompts/polish", polish_prompt, methods=["POST"]),
|
|
216
|
+
Route("/api/v1/prompts/{prompt_id}", get_prompt, methods=["GET"]),
|
|
217
|
+
Route("/api/v1/prompts/{prompt_id}", update_prompt, methods=["PATCH", "PUT"]),
|
|
218
|
+
Route("/api/v1/prompts/{prompt_id}", delete_prompt, methods=["DELETE"]),
|
|
219
|
+
]
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
from urllib.parse import urlsplit
|
|
5
|
+
|
|
6
|
+
from fastapi import FastAPI
|
|
7
|
+
from starlette.middleware import Middleware
|
|
8
|
+
from starlette.middleware.cors import CORSMiddleware
|
|
9
|
+
from starlette.responses import FileResponse, PlainTextResponse, RedirectResponse
|
|
10
|
+
from starlette.routing import BaseRoute, Route
|
|
11
|
+
|
|
12
|
+
from minder.config import MinderConfig
|
|
13
|
+
from minder.observability.logging import AccessLogMiddleware, CorrelationIdMiddleware
|
|
14
|
+
from minder.observability.metrics import metrics_endpoint
|
|
15
|
+
from minder.store.interfaces import ICacheProvider, IGraphRepository, IOperationalStore
|
|
16
|
+
|
|
17
|
+
from .api import build_admin_api_routes
|
|
18
|
+
from .context import AdminRouteContext
|
|
19
|
+
from .dashboard import build_dashboard_routes
|
|
20
|
+
from .jobs import build_jobs_routes
|
|
21
|
+
from .memories import build_memories_routes
|
|
22
|
+
from .prompts import build_prompts_routes
|
|
23
|
+
from .runtime import build_runtime_routes
|
|
24
|
+
from .search import build_search_routes
|
|
25
|
+
from .skills import build_skills_routes
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
DEFAULT_DASHBOARD_DEV_ORIGIN = "http://localhost:8808"
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def _favicon_path() -> Path:
|
|
32
|
+
return Path(__file__).resolve().parents[5] / "favicon.png"
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def dashboard_dev_origin(config: MinderConfig) -> str | None:
|
|
36
|
+
dev_server_url = (config.dashboard.dev_server_url or "").strip()
|
|
37
|
+
if not dev_server_url:
|
|
38
|
+
return DEFAULT_DASHBOARD_DEV_ORIGIN
|
|
39
|
+
parts = urlsplit(dev_server_url)
|
|
40
|
+
if not parts.scheme or not parts.netloc:
|
|
41
|
+
return DEFAULT_DASHBOARD_DEV_ORIGIN
|
|
42
|
+
return f"{parts.scheme}://{parts.netloc}"
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def build_http_routes(
|
|
46
|
+
*,
|
|
47
|
+
config: MinderConfig,
|
|
48
|
+
store: IOperationalStore,
|
|
49
|
+
graph_store: IGraphRepository | None = None,
|
|
50
|
+
cache: ICacheProvider | None = None,
|
|
51
|
+
prompt_sync_hook=None,
|
|
52
|
+
) -> list[BaseRoute]:
|
|
53
|
+
context = AdminRouteContext.build(
|
|
54
|
+
config=config,
|
|
55
|
+
store=store,
|
|
56
|
+
graph_store=graph_store,
|
|
57
|
+
cache=cache,
|
|
58
|
+
prompt_sync_hook=prompt_sync_hook,
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
async def health(_request) -> PlainTextResponse:
|
|
62
|
+
return PlainTextResponse("ok", status_code=200)
|
|
63
|
+
|
|
64
|
+
async def favicon_png(_request) -> FileResponse | PlainTextResponse:
|
|
65
|
+
favicon = _favicon_path()
|
|
66
|
+
if favicon.is_file():
|
|
67
|
+
return FileResponse(favicon, media_type="image/png")
|
|
68
|
+
return PlainTextResponse("favicon not found", status_code=404)
|
|
69
|
+
|
|
70
|
+
async def favicon_ico(_request) -> RedirectResponse:
|
|
71
|
+
return RedirectResponse(url="/favicon.png", status_code=308)
|
|
72
|
+
|
|
73
|
+
return [
|
|
74
|
+
Route("/health", health, methods=["GET"]),
|
|
75
|
+
Route("/favicon.ico", favicon_ico, methods=["GET"]),
|
|
76
|
+
Route("/favicon.png", favicon_png, methods=["GET"]),
|
|
77
|
+
Route("/metrics", metrics_endpoint, methods=["GET"]),
|
|
78
|
+
*build_admin_api_routes(context),
|
|
79
|
+
*build_prompts_routes(context),
|
|
80
|
+
*build_jobs_routes(context),
|
|
81
|
+
*build_skills_routes(context),
|
|
82
|
+
*build_memories_routes(context),
|
|
83
|
+
*build_runtime_routes(context),
|
|
84
|
+
*build_search_routes(context),
|
|
85
|
+
*build_dashboard_routes(context),
|
|
86
|
+
]
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
def build_http_app(
|
|
90
|
+
*,
|
|
91
|
+
config: MinderConfig,
|
|
92
|
+
store: IOperationalStore,
|
|
93
|
+
graph_store: IGraphRepository | None = None,
|
|
94
|
+
cache: ICacheProvider | None = None,
|
|
95
|
+
) -> FastAPI:
|
|
96
|
+
from minder.application.admin.jobs import AdminJobService
|
|
97
|
+
|
|
98
|
+
middleware: list[Middleware] = []
|
|
99
|
+
|
|
100
|
+
# Observability middleware (innermost first — applied outermost-last)
|
|
101
|
+
middleware.append(Middleware(CorrelationIdMiddleware))
|
|
102
|
+
middleware.append(Middleware(AccessLogMiddleware))
|
|
103
|
+
|
|
104
|
+
dev_origin = dashboard_dev_origin(config)
|
|
105
|
+
if dev_origin:
|
|
106
|
+
middleware.append(
|
|
107
|
+
Middleware(
|
|
108
|
+
CORSMiddleware,
|
|
109
|
+
allow_origins=[dev_origin],
|
|
110
|
+
allow_credentials=True,
|
|
111
|
+
allow_methods=["*"],
|
|
112
|
+
allow_headers=["*"],
|
|
113
|
+
)
|
|
114
|
+
)
|
|
115
|
+
app = FastAPI(
|
|
116
|
+
routes=build_http_routes(
|
|
117
|
+
config=config,
|
|
118
|
+
store=store,
|
|
119
|
+
graph_store=graph_store,
|
|
120
|
+
cache=cache,
|
|
121
|
+
),
|
|
122
|
+
middleware=middleware,
|
|
123
|
+
)
|
|
124
|
+
app.state.store = store
|
|
125
|
+
app.state.config = config
|
|
126
|
+
app.state.job_service = AdminJobService(store, config)
|
|
127
|
+
return app
|