slothquery 1.0.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.
- app/__init__.py +1 -0
- app/cli.py +26 -0
- app/database.py +24 -0
- app/dist/assets/index-BDTPNUTQ.css +1 -0
- app/dist/assets/index-DKvMVJoY.js +269 -0
- app/dist/assets/logo-RBZv3XvP.png +0 -0
- app/dist/index.html +14 -0
- app/main.py +79 -0
- app/models.py +90 -0
- app/schemas.py +119 -0
- slothquery-1.0.0.dist-info/METADATA +11 -0
- slothquery-1.0.0.dist-info/RECORD +15 -0
- slothquery-1.0.0.dist-info/WHEEL +5 -0
- slothquery-1.0.0.dist-info/entry_points.txt +2 -0
- slothquery-1.0.0.dist-info/top_level.txt +1 -0
|
Binary file
|
app/dist/index.html
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8" />
|
|
5
|
+
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
|
6
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
7
|
+
<title>SlothQuery</title>
|
|
8
|
+
<script type="module" crossorigin src="/assets/index-DKvMVJoY.js"></script>
|
|
9
|
+
<link rel="stylesheet" crossorigin href="/assets/index-BDTPNUTQ.css">
|
|
10
|
+
</head>
|
|
11
|
+
<body>
|
|
12
|
+
<div id="root"></div>
|
|
13
|
+
</body>
|
|
14
|
+
</html>
|
app/main.py
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
from fastapi import FastAPI
|
|
2
|
+
from fastapi.middleware.cors import CORSMiddleware
|
|
3
|
+
from fastapi.staticfiles import StaticFiles
|
|
4
|
+
from fastapi.responses import FileResponse
|
|
5
|
+
import os
|
|
6
|
+
from .database import engine, Base
|
|
7
|
+
from . import models
|
|
8
|
+
from .routers import core
|
|
9
|
+
|
|
10
|
+
# Create tables if they don't exist
|
|
11
|
+
Base.metadata.create_all(bind=engine)
|
|
12
|
+
|
|
13
|
+
# Ensure vault_ids column exists in chats table
|
|
14
|
+
from sqlalchemy import text
|
|
15
|
+
with engine.connect() as conn:
|
|
16
|
+
try:
|
|
17
|
+
conn.execute(text("ALTER TABLE chats ADD COLUMN vault_ids JSON"))
|
|
18
|
+
conn.commit()
|
|
19
|
+
except Exception:
|
|
20
|
+
pass
|
|
21
|
+
|
|
22
|
+
try:
|
|
23
|
+
conn.execute(text("ALTER TABLE playbooks ADD COLUMN always_include BOOLEAN DEFAULT 0"))
|
|
24
|
+
conn.commit()
|
|
25
|
+
except Exception:
|
|
26
|
+
pass
|
|
27
|
+
|
|
28
|
+
app = FastAPI(title="SlothQuery API", description="Local-first organizational intelligence platform")
|
|
29
|
+
|
|
30
|
+
app.add_middleware(
|
|
31
|
+
CORSMiddleware,
|
|
32
|
+
allow_origins=["*"], # Allow all origins for local ease-of-use
|
|
33
|
+
allow_credentials=True,
|
|
34
|
+
allow_methods=["*"],
|
|
35
|
+
allow_headers=["*"],
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
app.include_router(core.router)
|
|
39
|
+
|
|
40
|
+
@app.get("/health")
|
|
41
|
+
def health_check():
|
|
42
|
+
return {"status": "healthy"}
|
|
43
|
+
|
|
44
|
+
# Mount frontend build static files if built
|
|
45
|
+
current_dir = os.path.dirname(os.path.abspath(__file__))
|
|
46
|
+
# 1. First check if dist is inside package directory (installed mode)
|
|
47
|
+
dist_path = os.path.join(current_dir, "dist")
|
|
48
|
+
if not os.path.exists(dist_path):
|
|
49
|
+
# 2. Fallback to development directory structure
|
|
50
|
+
root_dir = os.path.dirname(os.path.dirname(current_dir))
|
|
51
|
+
dist_path = os.path.join(root_dir, "frontend", "dist")
|
|
52
|
+
|
|
53
|
+
if os.path.exists(dist_path):
|
|
54
|
+
assets_path = os.path.join(dist_path, "assets")
|
|
55
|
+
if os.path.exists(assets_path):
|
|
56
|
+
app.mount("/assets", StaticFiles(directory=assets_path), name="assets")
|
|
57
|
+
|
|
58
|
+
@app.get("/")
|
|
59
|
+
def serve_index():
|
|
60
|
+
return FileResponse(os.path.join(dist_path, "index.html"))
|
|
61
|
+
|
|
62
|
+
@app.get("/{catchall:path}")
|
|
63
|
+
def serve_catchall(catchall: str):
|
|
64
|
+
# Avoid intercepting API routes or health check
|
|
65
|
+
if catchall.startswith("api") or catchall.startswith("health"):
|
|
66
|
+
return {"detail": "Not Found"}
|
|
67
|
+
|
|
68
|
+
# If it matches an actual file in dist (like logo.png or favicon.ico), serve it
|
|
69
|
+
local_file = os.path.join(dist_path, catchall)
|
|
70
|
+
if os.path.exists(local_file) and os.path.isfile(local_file):
|
|
71
|
+
return FileResponse(local_file)
|
|
72
|
+
|
|
73
|
+
# Fallback to index.html for React Router
|
|
74
|
+
return FileResponse(os.path.join(dist_path, "index.html"))
|
|
75
|
+
else:
|
|
76
|
+
@app.get("/")
|
|
77
|
+
def read_root():
|
|
78
|
+
return {"message": "Welcome to SlothQuery API (Frontend not built)"}
|
|
79
|
+
|
app/models.py
ADDED
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import uuid
|
|
2
|
+
from datetime import datetime
|
|
3
|
+
from sqlalchemy import Column, String, Text, DateTime, ForeignKey, Boolean, JSON
|
|
4
|
+
from sqlalchemy.orm import relationship
|
|
5
|
+
from .database import Base
|
|
6
|
+
|
|
7
|
+
def generate_uuid():
|
|
8
|
+
return str(uuid.uuid4())
|
|
9
|
+
|
|
10
|
+
class Vault(Base):
|
|
11
|
+
__tablename__ = "vaults"
|
|
12
|
+
id = Column(String, primary_key=True, default=generate_uuid)
|
|
13
|
+
name = Column(String, nullable=False)
|
|
14
|
+
description = Column(String, nullable=True)
|
|
15
|
+
created_at = Column(DateTime, default=datetime.utcnow)
|
|
16
|
+
|
|
17
|
+
queries = relationship("Query", back_populates="vault")
|
|
18
|
+
playbooks = relationship("Playbook", back_populates="vault")
|
|
19
|
+
|
|
20
|
+
class Query(Base):
|
|
21
|
+
__tablename__ = "queries"
|
|
22
|
+
id = Column(String, primary_key=True, default=generate_uuid)
|
|
23
|
+
vault_id = Column(String, ForeignKey("vaults.id"))
|
|
24
|
+
title = Column(String, nullable=False)
|
|
25
|
+
description = Column(String, nullable=True)
|
|
26
|
+
sql_query = Column(Text, nullable=False)
|
|
27
|
+
sql_comments = Column(Text, nullable=True)
|
|
28
|
+
tags = Column(String, nullable=True)
|
|
29
|
+
dialect = Column(String, nullable=False) # snowflake | bigquery | postgresql etc.
|
|
30
|
+
created_at = Column(DateTime, default=datetime.utcnow)
|
|
31
|
+
updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
|
|
32
|
+
|
|
33
|
+
vault = relationship("Vault", back_populates="queries")
|
|
34
|
+
context = relationship("QueryContext", back_populates="query", uselist=False)
|
|
35
|
+
|
|
36
|
+
class QueryContext(Base):
|
|
37
|
+
__tablename__ = "query_contexts"
|
|
38
|
+
id = Column(String, primary_key=True, default=generate_uuid)
|
|
39
|
+
query_id = Column(String, ForeignKey("queries.id"), unique=True)
|
|
40
|
+
context_json = Column(JSON, nullable=False)
|
|
41
|
+
approval_status = Column(String, default="draft") # draft | approved | archived
|
|
42
|
+
version_hash = Column(String, nullable=True)
|
|
43
|
+
created_at = Column(DateTime, default=datetime.utcnow)
|
|
44
|
+
updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
|
|
45
|
+
|
|
46
|
+
query = relationship("Query", back_populates="context")
|
|
47
|
+
|
|
48
|
+
class Playbook(Base):
|
|
49
|
+
__tablename__ = "playbooks"
|
|
50
|
+
id = Column(String, primary_key=True, default=generate_uuid)
|
|
51
|
+
vault_id = Column(String, ForeignKey("vaults.id"))
|
|
52
|
+
playbook_type = Column(String, nullable=False)
|
|
53
|
+
name = Column(String, nullable=False)
|
|
54
|
+
content = Column(Text, nullable=False)
|
|
55
|
+
always_include = Column(Boolean, default=False)
|
|
56
|
+
created_at = Column(DateTime, default=datetime.utcnow)
|
|
57
|
+
updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
|
|
58
|
+
|
|
59
|
+
vault = relationship("Vault", back_populates="playbooks")
|
|
60
|
+
|
|
61
|
+
class Chat(Base):
|
|
62
|
+
__tablename__ = "chats"
|
|
63
|
+
id = Column(String, primary_key=True, default=generate_uuid)
|
|
64
|
+
title = Column(String, nullable=False)
|
|
65
|
+
vault_ids = Column(JSON, nullable=True) # List of vault ID strings
|
|
66
|
+
created_at = Column(DateTime, default=datetime.utcnow)
|
|
67
|
+
last_interacted_at = Column(DateTime, default=datetime.utcnow)
|
|
68
|
+
|
|
69
|
+
messages = relationship("ChatMessage", back_populates="chat", order_by="ChatMessage.created_at")
|
|
70
|
+
|
|
71
|
+
class ChatMessage(Base):
|
|
72
|
+
__tablename__ = "chat_messages"
|
|
73
|
+
id = Column(String, primary_key=True, default=generate_uuid)
|
|
74
|
+
chat_id = Column(String, ForeignKey("chats.id"))
|
|
75
|
+
role = Column(String, nullable=False) # user | assistant
|
|
76
|
+
content = Column(Text, nullable=False)
|
|
77
|
+
metadata_json = Column(JSON, nullable=True) # avoiding metadata as name collision with Base.metadata
|
|
78
|
+
created_at = Column(DateTime, default=datetime.utcnow)
|
|
79
|
+
|
|
80
|
+
chat = relationship("Chat", back_populates="messages")
|
|
81
|
+
|
|
82
|
+
class Provider(Base):
|
|
83
|
+
__tablename__ = "providers"
|
|
84
|
+
id = Column(String, primary_key=True, default=generate_uuid)
|
|
85
|
+
provider_type = Column(String, nullable=False) # openai | anthropic | google | groq | openrouter | deepseek
|
|
86
|
+
profile_name = Column(String, nullable=False)
|
|
87
|
+
model_name = Column(String, nullable=False)
|
|
88
|
+
encrypted_api_key = Column(String, nullable=False)
|
|
89
|
+
is_active = Column(Boolean, default=False)
|
|
90
|
+
created_at = Column(DateTime, default=datetime.utcnow)
|
app/schemas.py
ADDED
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
from pydantic import BaseModel, Field
|
|
2
|
+
from typing import Optional, List, Dict, Any
|
|
3
|
+
from datetime import datetime
|
|
4
|
+
|
|
5
|
+
class ProviderBase(BaseModel):
|
|
6
|
+
provider_type: str
|
|
7
|
+
profile_name: str
|
|
8
|
+
model_name: str
|
|
9
|
+
|
|
10
|
+
class ProviderCreate(ProviderBase):
|
|
11
|
+
api_key: str
|
|
12
|
+
|
|
13
|
+
class ProviderResponse(ProviderBase):
|
|
14
|
+
id: str
|
|
15
|
+
is_active: bool
|
|
16
|
+
created_at: datetime
|
|
17
|
+
|
|
18
|
+
class Config:
|
|
19
|
+
from_attributes = True
|
|
20
|
+
|
|
21
|
+
class VaultBase(BaseModel):
|
|
22
|
+
name: str
|
|
23
|
+
description: Optional[str] = None
|
|
24
|
+
|
|
25
|
+
class VaultCreate(VaultBase):
|
|
26
|
+
pass
|
|
27
|
+
|
|
28
|
+
class VaultResponse(VaultBase):
|
|
29
|
+
id: str
|
|
30
|
+
created_at: datetime
|
|
31
|
+
|
|
32
|
+
class Config:
|
|
33
|
+
from_attributes = True
|
|
34
|
+
|
|
35
|
+
class QueryBase(BaseModel):
|
|
36
|
+
title: str
|
|
37
|
+
description: Optional[str] = None
|
|
38
|
+
sql_query: str
|
|
39
|
+
sql_comments: Optional[str] = None
|
|
40
|
+
tags: Optional[str] = None
|
|
41
|
+
dialect: str
|
|
42
|
+
|
|
43
|
+
class QueryCreate(QueryBase):
|
|
44
|
+
vault_id: str
|
|
45
|
+
|
|
46
|
+
class QueryResponse(QueryBase):
|
|
47
|
+
id: str
|
|
48
|
+
vault_id: str
|
|
49
|
+
created_at: datetime
|
|
50
|
+
updated_at: datetime
|
|
51
|
+
|
|
52
|
+
class Config:
|
|
53
|
+
from_attributes = True
|
|
54
|
+
|
|
55
|
+
class ChatMessageBase(BaseModel):
|
|
56
|
+
role: str
|
|
57
|
+
content: str
|
|
58
|
+
metadata_json: Optional[Dict[str, Any]] = None
|
|
59
|
+
|
|
60
|
+
class ChatMessageCreate(ChatMessageBase):
|
|
61
|
+
chat_id: str
|
|
62
|
+
|
|
63
|
+
class ChatMessageResponse(ChatMessageBase):
|
|
64
|
+
id: str
|
|
65
|
+
chat_id: str
|
|
66
|
+
created_at: datetime
|
|
67
|
+
|
|
68
|
+
class Config:
|
|
69
|
+
from_attributes = True
|
|
70
|
+
|
|
71
|
+
class ChatBase(BaseModel):
|
|
72
|
+
title: str
|
|
73
|
+
|
|
74
|
+
class ChatCreate(ChatBase):
|
|
75
|
+
pass
|
|
76
|
+
|
|
77
|
+
class ChatResponse(ChatBase):
|
|
78
|
+
id: str
|
|
79
|
+
vault_ids: Optional[List[str]] = []
|
|
80
|
+
created_at: datetime
|
|
81
|
+
last_interacted_at: Optional[datetime] = None
|
|
82
|
+
messages: List[ChatMessageResponse] = []
|
|
83
|
+
|
|
84
|
+
class Config:
|
|
85
|
+
from_attributes = True
|
|
86
|
+
|
|
87
|
+
class PlaybookBase(BaseModel):
|
|
88
|
+
name: str
|
|
89
|
+
playbook_type: str
|
|
90
|
+
content: str
|
|
91
|
+
always_include: Optional[bool] = False
|
|
92
|
+
|
|
93
|
+
class PlaybookCreate(PlaybookBase):
|
|
94
|
+
vault_id: str
|
|
95
|
+
|
|
96
|
+
class PlaybookResponse(PlaybookBase):
|
|
97
|
+
id: str
|
|
98
|
+
vault_id: str
|
|
99
|
+
created_at: datetime
|
|
100
|
+
updated_at: datetime
|
|
101
|
+
|
|
102
|
+
class Config:
|
|
103
|
+
from_attributes = True
|
|
104
|
+
|
|
105
|
+
class DraftExtractionRequest(BaseModel):
|
|
106
|
+
vault_id: str
|
|
107
|
+
title: str
|
|
108
|
+
description: Optional[str] = None
|
|
109
|
+
sql_query: str
|
|
110
|
+
sql_comments: Optional[str] = None
|
|
111
|
+
tags: Optional[str] = None
|
|
112
|
+
dialect: str
|
|
113
|
+
|
|
114
|
+
class QueryCreateWithContext(QueryCreate):
|
|
115
|
+
context_json: Dict[str, Any]
|
|
116
|
+
|
|
117
|
+
class PlaybookPushPreviewRequest(BaseModel):
|
|
118
|
+
vault_id: str
|
|
119
|
+
context_json: Dict[str, Any]
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: slothquery
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: Local-first organizational intelligence platform
|
|
5
|
+
Author: Ayush Thakur
|
|
6
|
+
Author-email: ayush01thakur@gmail.com
|
|
7
|
+
Requires-Python: >=3.8
|
|
8
|
+
Dynamic: author
|
|
9
|
+
Dynamic: author-email
|
|
10
|
+
Dynamic: requires-python
|
|
11
|
+
Dynamic: summary
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
app/__init__.py,sha256=yh-SB_A42QQ4lYkFF4N3npKmcEHpG17QJc9wzRYthww,25
|
|
2
|
+
app/cli.py,sha256=MaWk7l-ZXNHgqdXeH0iSoazXE8zSo3lRoIqiUWK_T6A,860
|
|
3
|
+
app/database.py,sha256=ANYP1K76wd1dCluVkRMpk3Dj8tUCau5p97Im1_Xg0xc,687
|
|
4
|
+
app/main.py,sha256=TM6-faB4MCKrBOcEY0SwIQOJV5XlJF7tZDxlYcPhD3M,2726
|
|
5
|
+
app/models.py,sha256=xPVCQ5S0xQpHqySHNatQbCE_DxAgmi0nBx8KhDdcEbI,4118
|
|
6
|
+
app/schemas.py,sha256=4_KReSLloGu32Ga284ea4_s4BmNXLH9Kdj_tlpVOPNU,2600
|
|
7
|
+
app/dist/index.html,sha256=PXn452Bhf3jIdMEtEpYH0Gk3UkhkHOIfJI1MOoYGjWQ,457
|
|
8
|
+
app/dist/assets/index-BDTPNUTQ.css,sha256=0xj9MPToNENXCuWcSK15NWkE2leui6XD7zU4mVrYRNo,26628
|
|
9
|
+
app/dist/assets/index-DKvMVJoY.js,sha256=m9kYc_2ALBTYkrc_OZWBm8CiuYIlB5QTs0IfDzh-cZA,444634
|
|
10
|
+
app/dist/assets/logo-RBZv3XvP.png,sha256=HCQOHxDDiNmwsrvP_9Yg3myRRVLerobU7nh1WMhcthI,464247
|
|
11
|
+
slothquery-1.0.0.dist-info/METADATA,sha256=C6spHFLvi5ksTB4Bi4AjPSSaexpktOBb4mYwWT7QXx4,285
|
|
12
|
+
slothquery-1.0.0.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
|
|
13
|
+
slothquery-1.0.0.dist-info/entry_points.txt,sha256=AawlO-VZOgiZFjLyIXG8lZ4_eqCDqjBoNj9CvrsF_1w,44
|
|
14
|
+
slothquery-1.0.0.dist-info/top_level.txt,sha256=io9g7LCbfmTG1SFKgEOGXmCFB9uMP2H5lerm0HiHWQE,4
|
|
15
|
+
slothquery-1.0.0.dist-info/RECORD,,
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
app
|