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
minder/llm/openai.py
ADDED
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from collections.abc import Generator
|
|
4
|
+
from typing import Any
|
|
5
|
+
|
|
6
|
+
from minder.graph.state import GraphState
|
|
7
|
+
from minder.runtime import load_attr, module_available
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class OpenAIFallbackLLM:
|
|
11
|
+
def __init__(self, api_key: str | None, model: str, runtime: str = "mock") -> None:
|
|
12
|
+
self._api_key = api_key
|
|
13
|
+
self._model = model
|
|
14
|
+
self._runtime = runtime
|
|
15
|
+
self._completion_fn: Any | None = None
|
|
16
|
+
|
|
17
|
+
def available(self) -> bool:
|
|
18
|
+
return bool(self._api_key)
|
|
19
|
+
|
|
20
|
+
@property
|
|
21
|
+
def runtime(self) -> str:
|
|
22
|
+
if self._runtime == "auto":
|
|
23
|
+
return "litellm" if module_available("litellm") else "mock"
|
|
24
|
+
return self._runtime
|
|
25
|
+
|
|
26
|
+
def generate(self, state: GraphState) -> dict[str, object]:
|
|
27
|
+
if not self.available():
|
|
28
|
+
raise RuntimeError("OpenAI fallback is not configured")
|
|
29
|
+
text = (
|
|
30
|
+
f"{state.workflow_context.get('guidance', '')}\n"
|
|
31
|
+
f"Fallback answer for '{state.query}' using {self._model}."
|
|
32
|
+
)
|
|
33
|
+
if self.runtime == "litellm":
|
|
34
|
+
text = self._generate_with_litellm(state, fallback=text)
|
|
35
|
+
return {
|
|
36
|
+
"text": text,
|
|
37
|
+
"sources": [doc["path"] for doc in state.reranked_docs[:3]],
|
|
38
|
+
"provider": "openai_fallback",
|
|
39
|
+
"model": self._model,
|
|
40
|
+
"runtime": self.runtime,
|
|
41
|
+
"stream": [text],
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
def stream_generate(
|
|
45
|
+
self, state: GraphState
|
|
46
|
+
) -> Generator[dict[str, object], None, None]:
|
|
47
|
+
result = self.generate(state)
|
|
48
|
+
text = str(result.get("text", ""))
|
|
49
|
+
if text:
|
|
50
|
+
yield {"type": "chunk", "delta": text}
|
|
51
|
+
yield {"type": "result", "result": result}
|
|
52
|
+
|
|
53
|
+
def _generate_with_litellm(self, state: GraphState, *, fallback: str) -> str:
|
|
54
|
+
completion = self._litellm_completion()
|
|
55
|
+
if completion is None:
|
|
56
|
+
return fallback
|
|
57
|
+
reasoning_output = getattr(state, "reasoning_output", {}) or {}
|
|
58
|
+
try:
|
|
59
|
+
response = completion(
|
|
60
|
+
model=self._model,
|
|
61
|
+
api_key=self._api_key,
|
|
62
|
+
messages=[
|
|
63
|
+
{
|
|
64
|
+
"role": "user",
|
|
65
|
+
"content": str(reasoning_output.get("prompt") or state.query),
|
|
66
|
+
}
|
|
67
|
+
],
|
|
68
|
+
)
|
|
69
|
+
except Exception:
|
|
70
|
+
return fallback
|
|
71
|
+
choices = getattr(response, "choices", None)
|
|
72
|
+
if choices is None and isinstance(response, dict):
|
|
73
|
+
choices = response.get("choices", [])
|
|
74
|
+
if not choices:
|
|
75
|
+
return fallback
|
|
76
|
+
first = choices[0]
|
|
77
|
+
message = getattr(first, "message", None)
|
|
78
|
+
if message is None and isinstance(first, dict):
|
|
79
|
+
message = first.get("message", {})
|
|
80
|
+
content = getattr(message, "content", None)
|
|
81
|
+
if content is None and isinstance(message, dict):
|
|
82
|
+
content = message.get("content")
|
|
83
|
+
return str(content or fallback).strip() or fallback
|
|
84
|
+
|
|
85
|
+
def _litellm_completion(self) -> Any | None:
|
|
86
|
+
if self._completion_fn is not None:
|
|
87
|
+
return self._completion_fn
|
|
88
|
+
self._completion_fn = load_attr("litellm", "completion")
|
|
89
|
+
return self._completion_fn
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
# SQLAlchemy Base
|
|
2
|
+
from .base import Base as Base
|
|
3
|
+
|
|
4
|
+
# User
|
|
5
|
+
from .user import User as User, UserSchema as UserSchema
|
|
6
|
+
|
|
7
|
+
# Client/Auth Gateway
|
|
8
|
+
from .client import (
|
|
9
|
+
AuditLog as AuditLog,
|
|
10
|
+
AuditLogSchema as AuditLogSchema,
|
|
11
|
+
Client as Client,
|
|
12
|
+
ClientApiKey as ClientApiKey,
|
|
13
|
+
ClientApiKeySchema as ClientApiKeySchema,
|
|
14
|
+
ClientSchema as ClientSchema,
|
|
15
|
+
ClientSession as ClientSession,
|
|
16
|
+
ClientSessionSchema as ClientSessionSchema,
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
# Skill
|
|
20
|
+
from .skill import Skill as Skill, SkillSchema as SkillSchema
|
|
21
|
+
|
|
22
|
+
# Admin Jobs
|
|
23
|
+
from .job import AdminJob as AdminJob, AdminJobSchema as AdminJobSchema
|
|
24
|
+
|
|
25
|
+
# Session
|
|
26
|
+
from .session import Session as Session, SessionSchema as SessionSchema
|
|
27
|
+
|
|
28
|
+
# Workflow
|
|
29
|
+
from .workflow import Workflow as Workflow, WorkflowSchema as WorkflowSchema
|
|
30
|
+
|
|
31
|
+
# Repository
|
|
32
|
+
from .repository import (
|
|
33
|
+
Repository as Repository,
|
|
34
|
+
RepositorySchema as RepositorySchema,
|
|
35
|
+
RepositoryWorkflowState as RepositoryWorkflowState,
|
|
36
|
+
RepositoryWorkflowStateSchema as RepositoryWorkflowStateSchema,
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
# History
|
|
40
|
+
from .history import History as History, HistorySchema as HistorySchema
|
|
41
|
+
|
|
42
|
+
# Error
|
|
43
|
+
from .error import Error as Error, ErrorSchema as ErrorSchema
|
|
44
|
+
|
|
45
|
+
# Document
|
|
46
|
+
from .document import Document as Document, DocumentSchema as DocumentSchema
|
|
47
|
+
|
|
48
|
+
# Rules, Feedback & Misc
|
|
49
|
+
from .rule import (
|
|
50
|
+
Feedback as Feedback,
|
|
51
|
+
FeedbackSchema as FeedbackSchema,
|
|
52
|
+
MetadataSchema as MetadataSchema,
|
|
53
|
+
Rule as Rule,
|
|
54
|
+
RuleSchema as RuleSchema,
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
# Knowledge Graph
|
|
58
|
+
from .graph import (
|
|
59
|
+
GraphEdge as GraphEdge,
|
|
60
|
+
GraphEdgeSchema as GraphEdgeSchema,
|
|
61
|
+
GraphNode as GraphNode,
|
|
62
|
+
GraphNodeSchema as GraphNodeSchema,
|
|
63
|
+
)
|
|
64
|
+
|
|
65
|
+
__all__ = [
|
|
66
|
+
"Base",
|
|
67
|
+
"AuditLog",
|
|
68
|
+
"AuditLogSchema",
|
|
69
|
+
"AdminJob",
|
|
70
|
+
"AdminJobSchema",
|
|
71
|
+
"Client",
|
|
72
|
+
"ClientApiKey",
|
|
73
|
+
"ClientApiKeySchema",
|
|
74
|
+
"ClientSchema",
|
|
75
|
+
"ClientSession",
|
|
76
|
+
"ClientSessionSchema",
|
|
77
|
+
"Document",
|
|
78
|
+
"DocumentSchema",
|
|
79
|
+
"Error",
|
|
80
|
+
"ErrorSchema",
|
|
81
|
+
"Feedback",
|
|
82
|
+
"FeedbackSchema",
|
|
83
|
+
"GraphEdge",
|
|
84
|
+
"GraphEdgeSchema",
|
|
85
|
+
"GraphNode",
|
|
86
|
+
"GraphNodeSchema",
|
|
87
|
+
"History",
|
|
88
|
+
"HistorySchema",
|
|
89
|
+
"MetadataSchema",
|
|
90
|
+
"Repository",
|
|
91
|
+
"RepositorySchema",
|
|
92
|
+
"RepositoryWorkflowState",
|
|
93
|
+
"RepositoryWorkflowStateSchema",
|
|
94
|
+
"Rule",
|
|
95
|
+
"RuleSchema",
|
|
96
|
+
"Prompt",
|
|
97
|
+
"PromptSchema",
|
|
98
|
+
"Session",
|
|
99
|
+
"SessionSchema",
|
|
100
|
+
"Skill",
|
|
101
|
+
"SkillSchema",
|
|
102
|
+
"User",
|
|
103
|
+
"UserSchema",
|
|
104
|
+
"Workflow",
|
|
105
|
+
"WorkflowSchema",
|
|
106
|
+
]
|
|
107
|
+
|
|
108
|
+
# Prompts
|
|
109
|
+
from .prompt import Prompt as Prompt, PromptSchema as PromptSchema
|
minder/models/base.py
ADDED
minder/models/client.py
ADDED
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
import uuid
|
|
2
|
+
from datetime import UTC, datetime
|
|
3
|
+
from typing import Any
|
|
4
|
+
|
|
5
|
+
from pydantic import Field
|
|
6
|
+
from sqlalchemy import DateTime, JSON, String, UUID, func
|
|
7
|
+
from sqlalchemy.orm import Mapped, mapped_column
|
|
8
|
+
|
|
9
|
+
from .base import Base, BaseModelMeta
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class ClientSchema(BaseModelMeta):
|
|
13
|
+
id: uuid.UUID = Field(default_factory=uuid.uuid4)
|
|
14
|
+
name: str
|
|
15
|
+
slug: str
|
|
16
|
+
description: str = ""
|
|
17
|
+
status: str = "active"
|
|
18
|
+
created_by_user_id: uuid.UUID
|
|
19
|
+
owner_team: str | None = None
|
|
20
|
+
transport_modes: list[str] = Field(default_factory=lambda: ["sse", "stdio"])
|
|
21
|
+
tool_scopes: list[str] = Field(default_factory=list)
|
|
22
|
+
repo_scopes: list[str] = Field(default_factory=list)
|
|
23
|
+
workflow_scopes: list[str] = Field(default_factory=list)
|
|
24
|
+
rate_limit_policy: dict[str, Any] = Field(default_factory=dict)
|
|
25
|
+
created_at: datetime = Field(default_factory=lambda: datetime.now(UTC))
|
|
26
|
+
updated_at: datetime = Field(default_factory=lambda: datetime.now(UTC))
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class ClientApiKeySchema(BaseModelMeta):
|
|
30
|
+
id: uuid.UUID = Field(default_factory=uuid.uuid4)
|
|
31
|
+
client_id: uuid.UUID
|
|
32
|
+
key_prefix: str
|
|
33
|
+
secret_hash: str
|
|
34
|
+
status: str = "active"
|
|
35
|
+
last_used_at: datetime | None = None
|
|
36
|
+
created_by_user_id: uuid.UUID
|
|
37
|
+
created_at: datetime = Field(default_factory=lambda: datetime.now(UTC))
|
|
38
|
+
expires_at: datetime | None = None
|
|
39
|
+
revoked_at: datetime | None = None
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
class ClientSessionSchema(BaseModelMeta):
|
|
43
|
+
id: uuid.UUID = Field(default_factory=uuid.uuid4)
|
|
44
|
+
client_id: uuid.UUID
|
|
45
|
+
access_token_id: str
|
|
46
|
+
status: str = "active"
|
|
47
|
+
scopes: list[str] = Field(default_factory=list)
|
|
48
|
+
issued_at: datetime = Field(default_factory=lambda: datetime.now(UTC))
|
|
49
|
+
expires_at: datetime
|
|
50
|
+
last_seen_at: datetime | None = None
|
|
51
|
+
session_metadata: dict[str, Any] = Field(default_factory=dict)
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
class AuditLogSchema(BaseModelMeta):
|
|
55
|
+
id: uuid.UUID = Field(default_factory=uuid.uuid4)
|
|
56
|
+
actor_type: str
|
|
57
|
+
actor_id: str
|
|
58
|
+
event_type: str
|
|
59
|
+
resource_type: str
|
|
60
|
+
resource_id: str | None = None
|
|
61
|
+
request_id: str | None = None
|
|
62
|
+
tool_name: str | None = None
|
|
63
|
+
outcome: str = "success"
|
|
64
|
+
ip: str | None = None
|
|
65
|
+
user_agent: str | None = None
|
|
66
|
+
audit_metadata: dict[str, Any] = Field(default_factory=dict)
|
|
67
|
+
created_at: datetime = Field(default_factory=lambda: datetime.now(UTC))
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
class Client(Base):
|
|
71
|
+
__tablename__ = "clients"
|
|
72
|
+
|
|
73
|
+
id: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
|
|
74
|
+
name: Mapped[str] = mapped_column(String, nullable=False)
|
|
75
|
+
slug: Mapped[str] = mapped_column(String, unique=True, index=True, nullable=False)
|
|
76
|
+
description: Mapped[str] = mapped_column(String, default="")
|
|
77
|
+
status: Mapped[str] = mapped_column(String, default="active")
|
|
78
|
+
created_by_user_id: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True), index=True)
|
|
79
|
+
owner_team: Mapped[str | None] = mapped_column(String, nullable=True)
|
|
80
|
+
transport_modes: Mapped[list[str]] = mapped_column(JSON, default=list)
|
|
81
|
+
tool_scopes: Mapped[list[str]] = mapped_column(JSON, default=list)
|
|
82
|
+
repo_scopes: Mapped[list[str]] = mapped_column(JSON, default=list)
|
|
83
|
+
workflow_scopes: Mapped[list[str]] = mapped_column(JSON, default=list)
|
|
84
|
+
rate_limit_policy: Mapped[dict[str, Any]] = mapped_column(JSON, default=dict)
|
|
85
|
+
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now())
|
|
86
|
+
updated_at: Mapped[datetime] = mapped_column(
|
|
87
|
+
DateTime(timezone=True),
|
|
88
|
+
server_default=func.now(),
|
|
89
|
+
onupdate=func.now(),
|
|
90
|
+
)
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
class ClientApiKey(Base):
|
|
94
|
+
__tablename__ = "client_api_keys"
|
|
95
|
+
|
|
96
|
+
id: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
|
|
97
|
+
client_id: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True), index=True)
|
|
98
|
+
key_prefix: Mapped[str] = mapped_column(String, index=True)
|
|
99
|
+
secret_hash: Mapped[str] = mapped_column(String, nullable=False)
|
|
100
|
+
status: Mapped[str] = mapped_column(String, default="active")
|
|
101
|
+
last_used_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True)
|
|
102
|
+
created_by_user_id: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True), index=True)
|
|
103
|
+
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now())
|
|
104
|
+
expires_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True)
|
|
105
|
+
revoked_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True)
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
class ClientSession(Base):
|
|
109
|
+
__tablename__ = "client_sessions"
|
|
110
|
+
|
|
111
|
+
id: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
|
|
112
|
+
client_id: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True), index=True)
|
|
113
|
+
access_token_id: Mapped[str] = mapped_column(String, unique=True, index=True)
|
|
114
|
+
status: Mapped[str] = mapped_column(String, default="active")
|
|
115
|
+
scopes: Mapped[list[str]] = mapped_column(JSON, default=list)
|
|
116
|
+
issued_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now())
|
|
117
|
+
expires_at: Mapped[datetime] = mapped_column(DateTime(timezone=True))
|
|
118
|
+
last_seen_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True)
|
|
119
|
+
session_metadata: Mapped[dict[str, Any]] = mapped_column(JSON, default=dict)
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
class AuditLog(Base):
|
|
123
|
+
__tablename__ = "audit_logs"
|
|
124
|
+
|
|
125
|
+
id: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
|
|
126
|
+
actor_type: Mapped[str] = mapped_column(String, index=True)
|
|
127
|
+
actor_id: Mapped[str] = mapped_column(String, index=True)
|
|
128
|
+
event_type: Mapped[str] = mapped_column(String, index=True)
|
|
129
|
+
resource_type: Mapped[str] = mapped_column(String, index=True)
|
|
130
|
+
resource_id: Mapped[str | None] = mapped_column(String, nullable=True)
|
|
131
|
+
request_id: Mapped[str | None] = mapped_column(String, nullable=True)
|
|
132
|
+
tool_name: Mapped[str | None] = mapped_column(String, nullable=True)
|
|
133
|
+
outcome: Mapped[str] = mapped_column(String, default="success")
|
|
134
|
+
ip: Mapped[str | None] = mapped_column(String, nullable=True)
|
|
135
|
+
user_agent: Mapped[str | None] = mapped_column(String, nullable=True)
|
|
136
|
+
audit_metadata: Mapped[dict[str, Any]] = mapped_column(JSON, default=dict)
|
|
137
|
+
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now())
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import uuid
|
|
2
|
+
from datetime import datetime, UTC
|
|
3
|
+
from typing import Dict, Any, List, Optional
|
|
4
|
+
from sqlalchemy.orm import Mapped, mapped_column
|
|
5
|
+
from sqlalchemy import String, DateTime, UUID, JSON, func
|
|
6
|
+
from pydantic import Field
|
|
7
|
+
|
|
8
|
+
from .base import Base, BaseModelMeta
|
|
9
|
+
|
|
10
|
+
class DocumentSchema(BaseModelMeta):
|
|
11
|
+
id: uuid.UUID = Field(default_factory=uuid.uuid4)
|
|
12
|
+
title: str
|
|
13
|
+
content: str
|
|
14
|
+
doc_type: str # enum: markdown, code, api_spec, config
|
|
15
|
+
source_path: str
|
|
16
|
+
chunks: Dict[str, Any] = Field(default_factory=dict) # JSON list of chunks
|
|
17
|
+
embedding: Optional[List[float]] = None # vector(default 768) stored as JSON list
|
|
18
|
+
project: str
|
|
19
|
+
created_at: datetime = Field(default_factory=lambda: datetime.now(UTC))
|
|
20
|
+
updated_at: datetime = Field(default_factory=lambda: datetime.now(UTC))
|
|
21
|
+
|
|
22
|
+
class Document(Base):
|
|
23
|
+
__tablename__ = "documents"
|
|
24
|
+
|
|
25
|
+
id: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
|
|
26
|
+
title: Mapped[str] = mapped_column(String)
|
|
27
|
+
content: Mapped[str] = mapped_column(String)
|
|
28
|
+
doc_type: Mapped[str] = mapped_column(String, index=True)
|
|
29
|
+
source_path: Mapped[str] = mapped_column(String)
|
|
30
|
+
chunks: Mapped[Dict[str, Any]] = mapped_column(JSON, default=dict)
|
|
31
|
+
embedding: Mapped[Optional[Dict[str, Any]]] = mapped_column(JSON, nullable=True)
|
|
32
|
+
project: Mapped[str] = mapped_column(String, index=True)
|
|
33
|
+
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now())
|
|
34
|
+
updated_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now(), onupdate=func.now())
|
minder/models/error.py
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import uuid
|
|
2
|
+
from datetime import datetime, UTC
|
|
3
|
+
from typing import Dict, Any, List, Optional
|
|
4
|
+
from sqlalchemy.orm import Mapped, mapped_column
|
|
5
|
+
from sqlalchemy import String, Boolean, DateTime, UUID, JSON, func
|
|
6
|
+
from pydantic import Field
|
|
7
|
+
|
|
8
|
+
from .base import Base, BaseModelMeta
|
|
9
|
+
|
|
10
|
+
class ErrorSchema(BaseModelMeta):
|
|
11
|
+
id: uuid.UUID = Field(default_factory=uuid.uuid4)
|
|
12
|
+
error_code: str
|
|
13
|
+
error_message: str
|
|
14
|
+
stack_trace: Optional[str] = None
|
|
15
|
+
context: Dict[str, Any] = Field(default_factory=dict)
|
|
16
|
+
resolution: Optional[str] = None
|
|
17
|
+
embedding: Optional[List[float]] = None # vector(default 768) stored as JSON list
|
|
18
|
+
resolved: bool = False
|
|
19
|
+
created_at: datetime = Field(default_factory=lambda: datetime.now(UTC))
|
|
20
|
+
|
|
21
|
+
class Error(Base):
|
|
22
|
+
__tablename__ = "errors"
|
|
23
|
+
|
|
24
|
+
id: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
|
|
25
|
+
error_code: Mapped[str] = mapped_column(String, index=True)
|
|
26
|
+
error_message: Mapped[str] = mapped_column(String)
|
|
27
|
+
stack_trace: Mapped[Optional[str]] = mapped_column(String, nullable=True)
|
|
28
|
+
context: Mapped[Dict[str, Any]] = mapped_column(JSON, default=dict)
|
|
29
|
+
resolution: Mapped[Optional[str]] = mapped_column(String, nullable=True)
|
|
30
|
+
embedding: Mapped[Optional[Dict[str, Any]]] = mapped_column(JSON, nullable=True) # vector as JSON fallback
|
|
31
|
+
resolved: Mapped[bool] = mapped_column(Boolean, default=False)
|
|
32
|
+
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now())
|
minder/models/graph.py
ADDED
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Knowledge Graph SQLAlchemy models.
|
|
3
|
+
|
|
4
|
+
GraphNode: any entity in the knowledge graph (module, file, service, owner).
|
|
5
|
+
GraphEdge: directed relationship between two nodes (depends_on, imports, calls, owns).
|
|
6
|
+
|
|
7
|
+
v2 schema adds repo_id + branch columns so nodes from different repositories
|
|
8
|
+
and branches are stored independently. UniqueConstraint is now
|
|
9
|
+
(repo_id, branch, node_type, name).
|
|
10
|
+
|
|
11
|
+
Migration from v1 is handled by KnowledgeGraphStore.init_db() which calls
|
|
12
|
+
_migrate_graph_v2() on first boot when the columns are absent.
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
from __future__ import annotations
|
|
16
|
+
|
|
17
|
+
import uuid
|
|
18
|
+
from datetime import datetime, UTC
|
|
19
|
+
from typing import Any
|
|
20
|
+
|
|
21
|
+
from pydantic import Field
|
|
22
|
+
from sqlalchemy import DateTime, Float, JSON, String, UUID, UniqueConstraint, func
|
|
23
|
+
from sqlalchemy.orm import Mapped, mapped_column
|
|
24
|
+
|
|
25
|
+
from .base import Base, BaseModelMeta
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
# ---------------------------------------------------------------------------
|
|
29
|
+
# Pydantic schemas
|
|
30
|
+
# ---------------------------------------------------------------------------
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class GraphNodeSchema(BaseModelMeta):
|
|
34
|
+
id: uuid.UUID = Field(default_factory=uuid.uuid4)
|
|
35
|
+
repo_id: str = ""
|
|
36
|
+
branch: str = ""
|
|
37
|
+
node_type: str # module | file | service | owner | route | api_endpoint | websocket_endpoint | mq_topic | …
|
|
38
|
+
name: str
|
|
39
|
+
metadata: dict[str, Any] = Field(default_factory=dict)
|
|
40
|
+
created_at: datetime = Field(default_factory=lambda: datetime.now(UTC))
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
class GraphEdgeSchema(BaseModelMeta):
|
|
44
|
+
id: uuid.UUID = Field(default_factory=uuid.uuid4)
|
|
45
|
+
repo_id: str = ""
|
|
46
|
+
source_id: uuid.UUID
|
|
47
|
+
target_id: uuid.UUID
|
|
48
|
+
relation: str # depends_on | owns | imports | calls | exposes_route | publishes | consumes | cross_repo_calls
|
|
49
|
+
weight: float = 1.0
|
|
50
|
+
created_at: datetime = Field(default_factory=lambda: datetime.now(UTC))
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
# ---------------------------------------------------------------------------
|
|
54
|
+
# SQLAlchemy ORM models
|
|
55
|
+
# ---------------------------------------------------------------------------
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
class GraphNode(Base):
|
|
59
|
+
"""A node in the knowledge graph — module, file, service, or owner.
|
|
60
|
+
|
|
61
|
+
v2: scoped by (repo_id, branch) so nodes from different repositories or
|
|
62
|
+
branches never collide. repo_id="" and branch="" denotes global/shared
|
|
63
|
+
nodes (e.g. external packages used by many repos).
|
|
64
|
+
"""
|
|
65
|
+
|
|
66
|
+
__tablename__ = "graph_nodes"
|
|
67
|
+
__table_args__ = (
|
|
68
|
+
# v2 constraint: repo_id + branch + type + name must be unique
|
|
69
|
+
UniqueConstraint(
|
|
70
|
+
"repo_id", "branch", "node_type", "name",
|
|
71
|
+
name="uq_graph_node_repo_branch_type_name",
|
|
72
|
+
),
|
|
73
|
+
)
|
|
74
|
+
|
|
75
|
+
id: Mapped[uuid.UUID] = mapped_column(
|
|
76
|
+
UUID(as_uuid=True), primary_key=True, default=uuid.uuid4
|
|
77
|
+
)
|
|
78
|
+
# Scope columns (v2) — empty string = "global / no specific repo"
|
|
79
|
+
repo_id: Mapped[str] = mapped_column(String, index=True, default="", server_default="")
|
|
80
|
+
branch: Mapped[str] = mapped_column(String, index=True, default="", server_default="")
|
|
81
|
+
node_type: Mapped[str] = mapped_column(String, index=True)
|
|
82
|
+
name: Mapped[str] = mapped_column(String, index=True)
|
|
83
|
+
node_metadata: Mapped[dict] = mapped_column("metadata", JSON, default=dict)
|
|
84
|
+
created_at: Mapped[datetime] = mapped_column(
|
|
85
|
+
DateTime(timezone=True), server_default=func.now()
|
|
86
|
+
)
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
class GraphEdge(Base):
|
|
90
|
+
"""A directed edge between two graph nodes.
|
|
91
|
+
|
|
92
|
+
v2: repo_id added for efficient repo-scoped edge queries.
|
|
93
|
+
"""
|
|
94
|
+
|
|
95
|
+
__tablename__ = "graph_edges"
|
|
96
|
+
__table_args__ = (
|
|
97
|
+
UniqueConstraint(
|
|
98
|
+
"repo_id", "source_id", "target_id", "relation",
|
|
99
|
+
name="uq_graph_edge_repo_src_tgt_rel",
|
|
100
|
+
),
|
|
101
|
+
)
|
|
102
|
+
|
|
103
|
+
id: Mapped[uuid.UUID] = mapped_column(
|
|
104
|
+
UUID(as_uuid=True), primary_key=True, default=uuid.uuid4
|
|
105
|
+
)
|
|
106
|
+
# Scope column (v2)
|
|
107
|
+
repo_id: Mapped[str] = mapped_column(String, index=True, default="", server_default="")
|
|
108
|
+
source_id: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True), index=True)
|
|
109
|
+
target_id: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True), index=True)
|
|
110
|
+
relation: Mapped[str] = mapped_column(String, index=True)
|
|
111
|
+
weight: Mapped[float] = mapped_column(Float, default=1.0)
|
|
112
|
+
created_at: Mapped[datetime] = mapped_column(
|
|
113
|
+
DateTime(timezone=True), server_default=func.now()
|
|
114
|
+
)
|
minder/models/history.py
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import uuid
|
|
2
|
+
from datetime import datetime, UTC
|
|
3
|
+
from typing import Dict, Any, Optional
|
|
4
|
+
from sqlalchemy.orm import Mapped, mapped_column
|
|
5
|
+
from sqlalchemy import String, Integer, DateTime, UUID, JSON, func
|
|
6
|
+
from pydantic import Field
|
|
7
|
+
|
|
8
|
+
from .base import Base, BaseModelMeta
|
|
9
|
+
|
|
10
|
+
class HistorySchema(BaseModelMeta):
|
|
11
|
+
id: uuid.UUID = Field(default_factory=uuid.uuid4)
|
|
12
|
+
session_id: uuid.UUID
|
|
13
|
+
role: str # enum: user, assistant, system, tool
|
|
14
|
+
content: str
|
|
15
|
+
reasoning_trace: Optional[str] = None
|
|
16
|
+
tool_calls: Dict[str, Any] = Field(default_factory=dict) # JSON list of tool calls
|
|
17
|
+
tokens_used: int = 0
|
|
18
|
+
latency_ms: int = 0
|
|
19
|
+
created_at: datetime = Field(default_factory=lambda: datetime.now(UTC))
|
|
20
|
+
|
|
21
|
+
class History(Base):
|
|
22
|
+
__tablename__ = "history"
|
|
23
|
+
|
|
24
|
+
id: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
|
|
25
|
+
session_id: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True), index=True)
|
|
26
|
+
role: Mapped[str] = mapped_column(String)
|
|
27
|
+
content: Mapped[str] = mapped_column(String)
|
|
28
|
+
reasoning_trace: Mapped[Optional[str]] = mapped_column(String, nullable=True)
|
|
29
|
+
tool_calls: Mapped[Dict[str, Any]] = mapped_column(JSON, default=dict)
|
|
30
|
+
tokens_used: Mapped[int] = mapped_column(Integer, default=0)
|
|
31
|
+
latency_ms: Mapped[int] = mapped_column(Integer, default=0)
|
|
32
|
+
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now())
|
minder/models/job.py
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import uuid
|
|
2
|
+
from datetime import UTC, datetime
|
|
3
|
+
from typing import Any
|
|
4
|
+
|
|
5
|
+
from pydantic import Field
|
|
6
|
+
from sqlalchemy import DateTime, JSON, String, UUID, func
|
|
7
|
+
from sqlalchemy.orm import Mapped, mapped_column
|
|
8
|
+
|
|
9
|
+
from .base import Base, BaseModelMeta
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class AdminJobSchema(BaseModelMeta):
|
|
13
|
+
id: uuid.UUID = Field(default_factory=uuid.uuid4)
|
|
14
|
+
job_type: str
|
|
15
|
+
title: str
|
|
16
|
+
status: str = "queued"
|
|
17
|
+
requested_by_user_id: uuid.UUID | None = None
|
|
18
|
+
payload: dict[str, Any] = Field(default_factory=dict)
|
|
19
|
+
result_payload: dict[str, Any] | None = None
|
|
20
|
+
error_message: str | None = None
|
|
21
|
+
progress_current: int = 0
|
|
22
|
+
progress_total: int = 0
|
|
23
|
+
message: str | None = None
|
|
24
|
+
events: list[dict[str, Any]] = Field(default_factory=list)
|
|
25
|
+
created_at: datetime = Field(default_factory=lambda: datetime.now(UTC))
|
|
26
|
+
updated_at: datetime = Field(default_factory=lambda: datetime.now(UTC))
|
|
27
|
+
started_at: datetime | None = None
|
|
28
|
+
finished_at: datetime | None = None
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class AdminJob(Base):
|
|
32
|
+
__tablename__ = "admin_jobs"
|
|
33
|
+
|
|
34
|
+
id: Mapped[uuid.UUID] = mapped_column(
|
|
35
|
+
UUID(as_uuid=True), primary_key=True, default=uuid.uuid4
|
|
36
|
+
)
|
|
37
|
+
company_id: Mapped[str] = mapped_column(String, index=True, default="default")
|
|
38
|
+
job_type: Mapped[str] = mapped_column(String, index=True)
|
|
39
|
+
title: Mapped[str] = mapped_column(String)
|
|
40
|
+
status: Mapped[str] = mapped_column(String, index=True, default="queued")
|
|
41
|
+
requested_by_user_id: Mapped[uuid.UUID | None] = mapped_column(
|
|
42
|
+
UUID(as_uuid=True), index=True, nullable=True
|
|
43
|
+
)
|
|
44
|
+
payload: Mapped[dict[str, Any]] = mapped_column(JSON, default=dict)
|
|
45
|
+
result_payload: Mapped[dict[str, Any] | None] = mapped_column(JSON, nullable=True)
|
|
46
|
+
error_message: Mapped[str | None] = mapped_column(String, nullable=True)
|
|
47
|
+
progress_current: Mapped[int] = mapped_column(default=0)
|
|
48
|
+
progress_total: Mapped[int] = mapped_column(default=0)
|
|
49
|
+
message: Mapped[str | None] = mapped_column(String, nullable=True)
|
|
50
|
+
events: Mapped[list[dict[str, Any]]] = mapped_column(JSON, default=list)
|
|
51
|
+
created_at: Mapped[datetime] = mapped_column(
|
|
52
|
+
DateTime(timezone=True), server_default=func.now()
|
|
53
|
+
)
|
|
54
|
+
updated_at: Mapped[datetime] = mapped_column(
|
|
55
|
+
DateTime(timezone=True), server_default=func.now(), onupdate=func.now()
|
|
56
|
+
)
|
|
57
|
+
started_at: Mapped[datetime | None] = mapped_column(
|
|
58
|
+
DateTime(timezone=True), nullable=True
|
|
59
|
+
)
|
|
60
|
+
finished_at: Mapped[datetime | None] = mapped_column(
|
|
61
|
+
DateTime(timezone=True), nullable=True
|
|
62
|
+
)
|
minder/models/prompt.py
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import uuid
|
|
2
|
+
from datetime import datetime, UTC
|
|
3
|
+
from typing import List
|
|
4
|
+
from sqlalchemy.orm import Mapped, mapped_column
|
|
5
|
+
from sqlalchemy import String, DateTime, UUID, JSON, Text, func
|
|
6
|
+
from pydantic import Field
|
|
7
|
+
|
|
8
|
+
from .base import Base, BaseModelMeta
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
# Pydantic Schema
|
|
12
|
+
class PromptSchema(BaseModelMeta):
|
|
13
|
+
id: uuid.UUID = Field(default_factory=uuid.uuid4)
|
|
14
|
+
name: str
|
|
15
|
+
title: str
|
|
16
|
+
description: str
|
|
17
|
+
content_template: str
|
|
18
|
+
arguments: List[str] = Field(default_factory=list)
|
|
19
|
+
created_at: datetime = Field(default_factory=lambda: datetime.now(UTC))
|
|
20
|
+
updated_at: datetime = Field(default_factory=lambda: datetime.now(UTC))
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
# SQLAlchemy Model
|
|
24
|
+
class Prompt(Base):
|
|
25
|
+
__tablename__ = "prompts"
|
|
26
|
+
|
|
27
|
+
id: Mapped[uuid.UUID] = mapped_column(
|
|
28
|
+
UUID(as_uuid=True), primary_key=True, default=uuid.uuid4
|
|
29
|
+
)
|
|
30
|
+
company_id: Mapped[str] = mapped_column(String, index=True, default="default")
|
|
31
|
+
name: Mapped[str] = mapped_column(String, unique=True, index=True)
|
|
32
|
+
title: Mapped[str] = mapped_column(String)
|
|
33
|
+
description: Mapped[str] = mapped_column(String)
|
|
34
|
+
content_template: Mapped[str] = mapped_column(Text)
|
|
35
|
+
arguments: Mapped[list[str]] = mapped_column(JSON, default=list)
|
|
36
|
+
created_at: Mapped[datetime] = mapped_column(
|
|
37
|
+
DateTime(timezone=True), server_default=func.now()
|
|
38
|
+
)
|
|
39
|
+
updated_at: Mapped[datetime] = mapped_column(
|
|
40
|
+
DateTime(timezone=True), server_default=func.now(), onupdate=func.now()
|
|
41
|
+
)
|