qalita 2.3.1__py3-none-any.whl → 2.5.2__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.
- qalita/__main__.py +213 -9
- qalita/commands/{agent.py → worker.py} +89 -89
- qalita/internal/config.py +26 -19
- qalita/internal/utils.py +1 -1
- qalita/web/app.py +97 -14
- qalita/web/blueprints/context.py +13 -60
- qalita/web/blueprints/dashboard.py +35 -76
- qalita/web/blueprints/helpers.py +154 -63
- qalita/web/blueprints/sources.py +29 -61
- qalita/web/blueprints/{agents.py → workers.py} +108 -185
- qalita-2.5.2.dist-info/METADATA +66 -0
- qalita-2.5.2.dist-info/RECORD +24 -0
- {qalita-2.3.1.dist-info → qalita-2.5.2.dist-info}/WHEEL +1 -1
- qalita-2.5.2.dist-info/entry_points.txt +2 -0
- qalita/web/blueprints/studio.py +0 -1255
- qalita/web/public/chatgpt.svg +0 -3
- qalita/web/public/claude.png +0 -0
- qalita/web/public/favicon.ico +0 -0
- qalita/web/public/gemini.png +0 -0
- qalita/web/public/logo-no-slogan.png +0 -0
- qalita/web/public/logo-white-no-slogan.svg +0 -11
- qalita/web/public/mistral.svg +0 -1
- qalita/web/public/noise.webp +0 -0
- qalita/web/public/ollama.png +0 -0
- qalita/web/public/platform.png +0 -0
- qalita/web/public/sources-logos/alloy-db.png +0 -0
- qalita/web/public/sources-logos/amazon-athena.png +0 -0
- qalita/web/public/sources-logos/amazon-rds.png +0 -0
- qalita/web/public/sources-logos/api.svg +0 -2
- qalita/web/public/sources-logos/avro.svg +0 -20
- qalita/web/public/sources-logos/azure-database-mysql.png +0 -0
- qalita/web/public/sources-logos/azure-database-postgresql.png +0 -0
- qalita/web/public/sources-logos/azure-sql-database.png +0 -0
- qalita/web/public/sources-logos/azure-sql-managed-instance.png +0 -0
- qalita/web/public/sources-logos/azure-synapse-analytics.png +0 -0
- qalita/web/public/sources-logos/azure_blob.svg +0 -1
- qalita/web/public/sources-logos/bigquery.png +0 -0
- qalita/web/public/sources-logos/cassandra.svg +0 -254
- qalita/web/public/sources-logos/clickhouse.png +0 -0
- qalita/web/public/sources-logos/cloud-sql.png +0 -0
- qalita/web/public/sources-logos/cockroach-db.png +0 -0
- qalita/web/public/sources-logos/csv.svg +0 -1
- qalita/web/public/sources-logos/database.svg +0 -3
- qalita/web/public/sources-logos/databricks.png +0 -0
- qalita/web/public/sources-logos/duckdb.png +0 -0
- qalita/web/public/sources-logos/elasticsearch.svg +0 -1
- qalita/web/public/sources-logos/excel.svg +0 -1
- qalita/web/public/sources-logos/file.svg +0 -1
- qalita/web/public/sources-logos/folder.svg +0 -6
- qalita/web/public/sources-logos/gcs.png +0 -0
- qalita/web/public/sources-logos/hdfs.svg +0 -1
- qalita/web/public/sources-logos/ibm-db2.png +0 -0
- qalita/web/public/sources-logos/json.png +0 -0
- qalita/web/public/sources-logos/maria-db.png +0 -0
- qalita/web/public/sources-logos/mongodb.svg +0 -1
- qalita/web/public/sources-logos/mssql.svg +0 -1
- qalita/web/public/sources-logos/mysql.svg +0 -7
- qalita/web/public/sources-logos/oracle.svg +0 -4
- qalita/web/public/sources-logos/parquet.svg +0 -16
- qalita/web/public/sources-logos/picture.png +0 -0
- qalita/web/public/sources-logos/postgresql.svg +0 -22
- qalita/web/public/sources-logos/questdb.png +0 -0
- qalita/web/public/sources-logos/redshift.png +0 -0
- qalita/web/public/sources-logos/s3.svg +0 -34
- qalita/web/public/sources-logos/sap-hana.png +0 -0
- qalita/web/public/sources-logos/sftp.png +0 -0
- qalita/web/public/sources-logos/single-store.png +0 -0
- qalita/web/public/sources-logos/snowflake.png +0 -0
- qalita/web/public/sources-logos/sqlite.svg +0 -104
- qalita/web/public/sources-logos/sqlserver.png +0 -0
- qalita/web/public/sources-logos/starburst.png +0 -0
- qalita/web/public/sources-logos/stream.png +0 -0
- qalita/web/public/sources-logos/teradata.png +0 -0
- qalita/web/public/sources-logos/timescale.png +0 -0
- qalita/web/public/sources-logos/xls.svg +0 -1
- qalita/web/public/sources-logos/xlsx.svg +0 -1
- qalita/web/public/sources-logos/yugabyte-db.png +0 -0
- qalita/web/public/studio-logo.svg +0 -10
- qalita/web/public/studio.css +0 -304
- qalita/web/public/studio.png +0 -0
- qalita/web/public/styles.css +0 -682
- qalita/web/templates/dashboard.html +0 -373
- qalita/web/templates/navbar.html +0 -40
- qalita/web/templates/sources/added.html +0 -57
- qalita/web/templates/sources/edit.html +0 -411
- qalita/web/templates/sources/select-source.html +0 -128
- qalita/web/templates/studio/agent-panel.html +0 -769
- qalita/web/templates/studio/context-panel.html +0 -300
- qalita/web/templates/studio/index.html +0 -79
- qalita/web/templates/studio/navbar.html +0 -14
- qalita/web/templates/studio/view-panel.html +0 -529
- qalita-2.3.1.dist-info/METADATA +0 -58
- qalita-2.3.1.dist-info/RECORD +0 -101
- qalita-2.3.1.dist-info/entry_points.txt +0 -3
- {qalita-2.3.1.dist-info → qalita-2.5.2.dist-info}/licenses/LICENSE +0 -0
qalita/__main__.py
CHANGED
|
@@ -18,19 +18,19 @@ pass_config = click.make_pass_decorator(Config, ensure=True)
|
|
|
18
18
|
@click.option(
|
|
19
19
|
"--ui",
|
|
20
20
|
is_flag=True,
|
|
21
|
-
default=os.environ.get("
|
|
21
|
+
default=os.environ.get("QALITA_WORKER_UI", False),
|
|
22
22
|
help="Open the local web UI dashboard",
|
|
23
23
|
)
|
|
24
24
|
@click.option(
|
|
25
25
|
"--port",
|
|
26
|
-
default=os.environ.get("
|
|
26
|
+
default=os.environ.get("QALITA_WORKER_UI_PORT", 7070),
|
|
27
27
|
show_default=True,
|
|
28
28
|
type=int,
|
|
29
29
|
help="Port for the local web UI",
|
|
30
30
|
)
|
|
31
31
|
@click.option(
|
|
32
32
|
"--host",
|
|
33
|
-
default=os.environ.get("
|
|
33
|
+
default=os.environ.get("QALITA_WORKER_UI_HOST", "localhost"),
|
|
34
34
|
show_default=True,
|
|
35
35
|
help="Host interface to bind the local web UI",
|
|
36
36
|
)
|
|
@@ -45,14 +45,218 @@ def cli(ctx, ui=False, port=7070, host="localhost"):
|
|
|
45
45
|
----------------------------------------------------------------------------"""
|
|
46
46
|
if ui:
|
|
47
47
|
try:
|
|
48
|
+
import subprocess
|
|
49
|
+
import sys
|
|
50
|
+
import threading
|
|
51
|
+
import time
|
|
48
52
|
from qalita.web.app import run_dashboard_ui
|
|
49
|
-
|
|
50
|
-
logger.error(f"Unable to start web UI: {exc}")
|
|
51
|
-
else:
|
|
53
|
+
|
|
52
54
|
# Instantiate a Config to pass into the UI
|
|
53
55
|
cfg = Config()
|
|
54
56
|
cfg.load_source_config(verbose=False)
|
|
55
|
-
|
|
57
|
+
|
|
58
|
+
# Flask API runs on port + 1 (e.g., 7071 if UI is 7070)
|
|
59
|
+
flask_port = port + 1
|
|
60
|
+
flask_host = host
|
|
61
|
+
|
|
62
|
+
# Start Flask API in a separate thread
|
|
63
|
+
def run_flask():
|
|
64
|
+
run_dashboard_ui(cfg, host=flask_host, port=flask_port)
|
|
65
|
+
|
|
66
|
+
flask_thread = threading.Thread(target=run_flask, daemon=True)
|
|
67
|
+
flask_thread.start()
|
|
68
|
+
|
|
69
|
+
# Wait a bit for Flask to start
|
|
70
|
+
time.sleep(1)
|
|
71
|
+
|
|
72
|
+
# Start Next.js frontend
|
|
73
|
+
frontend_dir = os.path.join(os.path.dirname(__file__), "..", "frontend")
|
|
74
|
+
if not os.path.isdir(frontend_dir):
|
|
75
|
+
logger.error(f"Frontend directory not found: {frontend_dir}")
|
|
76
|
+
logger.error("Please run 'npm install' and 'npm run build' in the frontend directory first")
|
|
77
|
+
raise SystemExit(1)
|
|
78
|
+
|
|
79
|
+
# Check Node.js version (Next.js 16 requires >= 20.9.0)
|
|
80
|
+
# Try to use nvm's node if available, otherwise use system node
|
|
81
|
+
node_cmd = "node"
|
|
82
|
+
nvm_node_path = None
|
|
83
|
+
|
|
84
|
+
# Check for nvm in common locations
|
|
85
|
+
nvm_paths = [
|
|
86
|
+
os.path.expanduser("~/.nvm/versions/node"),
|
|
87
|
+
os.path.expanduser("~/.config/nvm/versions/node"),
|
|
88
|
+
]
|
|
89
|
+
|
|
90
|
+
# Try to find Node.js 20+ in nvm directories
|
|
91
|
+
for nvm_base in nvm_paths:
|
|
92
|
+
if os.path.isdir(nvm_base):
|
|
93
|
+
try:
|
|
94
|
+
# List directories and find the highest v20+ version
|
|
95
|
+
versions = []
|
|
96
|
+
for item in os.listdir(nvm_base):
|
|
97
|
+
if item.startswith("v20.") or item.startswith("v21.") or item.startswith("v22."):
|
|
98
|
+
versions.append(item)
|
|
99
|
+
if versions:
|
|
100
|
+
# Sort and get the highest version
|
|
101
|
+
versions.sort(reverse=True)
|
|
102
|
+
nvm_node_path = os.path.join(nvm_base, versions[0], "bin", "node")
|
|
103
|
+
if os.path.exists(nvm_node_path):
|
|
104
|
+
node_cmd = nvm_node_path
|
|
105
|
+
logger.info(f"Using Node.js from nvm: {nvm_node_path}")
|
|
106
|
+
break
|
|
107
|
+
except Exception:
|
|
108
|
+
pass
|
|
109
|
+
|
|
110
|
+
try:
|
|
111
|
+
node_version_result = subprocess.run(
|
|
112
|
+
[node_cmd, "--version"],
|
|
113
|
+
capture_output=True,
|
|
114
|
+
text=True,
|
|
115
|
+
check=True,
|
|
116
|
+
)
|
|
117
|
+
node_version = node_version_result.stdout.strip()
|
|
118
|
+
# Extract major and minor version numbers
|
|
119
|
+
version_parts = node_version.lstrip("v").split(".")
|
|
120
|
+
major_version = int(version_parts[0])
|
|
121
|
+
minor_version = int(version_parts[1]) if len(version_parts) > 1 else 0
|
|
122
|
+
|
|
123
|
+
if major_version < 20 or (major_version == 20 and minor_version < 9):
|
|
124
|
+
logger.error(f"Node.js version {node_version} is installed, but Next.js 16 requires Node.js >= 20.9.0")
|
|
125
|
+
logger.error("Please upgrade Node.js to version 20.9.0 or higher")
|
|
126
|
+
if not nvm_node_path:
|
|
127
|
+
logger.error("If you're using nvm, make sure to run: nvm use 20")
|
|
128
|
+
logger.error("You can install Node.js from https://nodejs.org/ or use nvm: nvm install 20")
|
|
129
|
+
raise SystemExit(1)
|
|
130
|
+
logger.info(f"Node.js version: {node_version}")
|
|
131
|
+
|
|
132
|
+
except (subprocess.CalledProcessError, FileNotFoundError, ValueError) as e:
|
|
133
|
+
logger.error("Node.js is not installed or not found in PATH")
|
|
134
|
+
logger.error("Please install Node.js >= 20.9.0 from https://nodejs.org/")
|
|
135
|
+
logger.error("Or use nvm: nvm install 20 && nvm use 20")
|
|
136
|
+
raise SystemExit(1)
|
|
137
|
+
|
|
138
|
+
# Set environment variable for Flask API URL and configure PATH for Node.js
|
|
139
|
+
env = os.environ.copy()
|
|
140
|
+
env["FLASK_API_URL"] = f"http://{flask_host}:{flask_port}"
|
|
141
|
+
env["PORT"] = str(port)
|
|
142
|
+
env["HOSTNAME"] = host
|
|
143
|
+
|
|
144
|
+
# Determine which node/npm to use and ensure they're in PATH
|
|
145
|
+
import platform
|
|
146
|
+
if nvm_node_path:
|
|
147
|
+
node_bin = nvm_node_path
|
|
148
|
+
node_bin_dir = os.path.dirname(nvm_node_path)
|
|
149
|
+
npm_cmd = os.path.join(node_bin_dir, "npm")
|
|
150
|
+
if not os.path.exists(npm_cmd):
|
|
151
|
+
npm_cmd = "npm" # Fallback
|
|
152
|
+
# Ensure nvm's bin directory is FIRST in PATH so npm uses the right node
|
|
153
|
+
current_path = env.get("PATH", "")
|
|
154
|
+
path_parts = current_path.split(":") if current_path else []
|
|
155
|
+
# Remove node_bin_dir if it's already there
|
|
156
|
+
path_parts = [p for p in path_parts if p != node_bin_dir]
|
|
157
|
+
# Put nvm's bin directory first
|
|
158
|
+
env["PATH"] = f"{node_bin_dir}:{':'.join(path_parts)}"
|
|
159
|
+
else:
|
|
160
|
+
node_bin = "node.exe" if platform.system() == "Windows" else "node"
|
|
161
|
+
npm_cmd = "npm"
|
|
162
|
+
|
|
163
|
+
# Check if node_modules exists, if not, try to install
|
|
164
|
+
node_modules_path = os.path.join(frontend_dir, "node_modules")
|
|
165
|
+
if not os.path.exists(node_modules_path):
|
|
166
|
+
logger.info("Installing Next.js dependencies...")
|
|
167
|
+
install_result = subprocess.run(
|
|
168
|
+
[npm_cmd, "install"],
|
|
169
|
+
cwd=frontend_dir,
|
|
170
|
+
capture_output=True,
|
|
171
|
+
env=env,
|
|
172
|
+
)
|
|
173
|
+
if install_result.returncode != 0:
|
|
174
|
+
logger.error("Failed to install Next.js dependencies")
|
|
175
|
+
logger.error(install_result.stderr.decode() if install_result.stderr else "Unknown error")
|
|
176
|
+
raise SystemExit(1)
|
|
177
|
+
|
|
178
|
+
logger.info(f"Starting Flask API on http://{flask_host}:{flask_port}")
|
|
179
|
+
logger.info(f"Starting Next.js frontend on http://{host}:{port}")
|
|
180
|
+
logger.info(f"QALITA CLI UI is running. Open http://{host}:{port}")
|
|
181
|
+
|
|
182
|
+
# Check for production build (standalone mode)
|
|
183
|
+
if os.path.exists(os.path.join(frontend_dir, ".next", "standalone")):
|
|
184
|
+
# Use next start from frontend directory instead of standalone
|
|
185
|
+
# This ensures static files are served correctly
|
|
186
|
+
logger.info("Running Next.js in production mode (standalone build detected)...")
|
|
187
|
+
next_bin = os.path.join(frontend_dir, "node_modules", ".bin", "next")
|
|
188
|
+
if os.path.exists(next_bin):
|
|
189
|
+
subprocess.run(
|
|
190
|
+
[node_bin, next_bin, "start", "-p", str(port), "-H", host],
|
|
191
|
+
cwd=frontend_dir,
|
|
192
|
+
env=env,
|
|
193
|
+
)
|
|
194
|
+
else:
|
|
195
|
+
subprocess.run(
|
|
196
|
+
[npm_cmd, "start"],
|
|
197
|
+
cwd=frontend_dir,
|
|
198
|
+
env=env,
|
|
199
|
+
)
|
|
200
|
+
elif os.path.exists(os.path.join(frontend_dir, ".next", "server")):
|
|
201
|
+
# Production build exists but not standalone, use next start directly with args
|
|
202
|
+
logger.info("Running Next.js in production mode...")
|
|
203
|
+
next_bin = os.path.join(frontend_dir, "node_modules", ".bin", "next")
|
|
204
|
+
if not os.path.exists(next_bin):
|
|
205
|
+
# Fallback to npm start
|
|
206
|
+
subprocess.run(
|
|
207
|
+
[npm_cmd, "start"],
|
|
208
|
+
cwd=frontend_dir,
|
|
209
|
+
env=env,
|
|
210
|
+
)
|
|
211
|
+
else:
|
|
212
|
+
subprocess.run(
|
|
213
|
+
[node_bin, next_bin, "start", "-p", str(port), "-H", host],
|
|
214
|
+
cwd=frontend_dir,
|
|
215
|
+
env=env,
|
|
216
|
+
)
|
|
217
|
+
else:
|
|
218
|
+
# Development mode - use next directly with node from nvm
|
|
219
|
+
logger.info("Running Next.js in development mode...")
|
|
220
|
+
next_cli = os.path.join(frontend_dir, "node_modules", "next", "dist", "bin", "next")
|
|
221
|
+
next_bin = os.path.join(frontend_dir, "node_modules", ".bin", "next")
|
|
222
|
+
|
|
223
|
+
# Try to use the actual Next.js CLI file directly (no shebang issues)
|
|
224
|
+
if os.path.exists(next_cli):
|
|
225
|
+
logger.info(f"Using Node.js: {node_bin} to run Next.js")
|
|
226
|
+
subprocess.run(
|
|
227
|
+
[node_bin, next_cli, "dev", "-p", str(port), "-H", host],
|
|
228
|
+
cwd=frontend_dir,
|
|
229
|
+
env=env,
|
|
230
|
+
)
|
|
231
|
+
elif os.path.exists(next_bin):
|
|
232
|
+
# Use node to execute next script (node ignores shebang when called directly)
|
|
233
|
+
logger.info(f"Using Node.js: {node_bin} to run Next.js")
|
|
234
|
+
subprocess.run(
|
|
235
|
+
[node_bin, next_bin, "dev", "-p", str(port), "-H", host],
|
|
236
|
+
cwd=frontend_dir,
|
|
237
|
+
env=env,
|
|
238
|
+
)
|
|
239
|
+
else:
|
|
240
|
+
# Fallback: use npm but ensure PATH is set correctly
|
|
241
|
+
logger.warning("next binary not found, trying npm with PATH override...")
|
|
242
|
+
# Verify node version in env before running
|
|
243
|
+
verify_result = subprocess.run(
|
|
244
|
+
[node_bin, "--version"],
|
|
245
|
+
capture_output=True,
|
|
246
|
+
text=True,
|
|
247
|
+
env=env,
|
|
248
|
+
)
|
|
249
|
+
if verify_result.returncode == 0:
|
|
250
|
+
logger.info(f"Node.js version in env: {verify_result.stdout.strip()}")
|
|
251
|
+
subprocess.run(
|
|
252
|
+
[npm_cmd, "run", "dev", "--", "-p", str(port), "-H", host],
|
|
253
|
+
cwd=frontend_dir,
|
|
254
|
+
env=env,
|
|
255
|
+
)
|
|
256
|
+
except Exception as exc:
|
|
257
|
+
logger.error(f"Unable to start web UI: {exc}")
|
|
258
|
+
import traceback
|
|
259
|
+
traceback.print_exc()
|
|
56
260
|
raise SystemExit(0)
|
|
57
261
|
# If invoked without a subcommand and without --ui, show help
|
|
58
262
|
if ctx.invoked_subcommand is None:
|
|
@@ -70,11 +274,11 @@ def version():
|
|
|
70
274
|
|
|
71
275
|
|
|
72
276
|
def add_commands_to_cli():
|
|
73
|
-
from qalita.commands import
|
|
277
|
+
from qalita.commands import worker, source, pack
|
|
74
278
|
|
|
75
279
|
# Add pack command group to cli
|
|
76
280
|
cli.add_command(pack.pack)
|
|
77
|
-
cli.add_command(
|
|
281
|
+
cli.add_command(worker.worker)
|
|
78
282
|
cli.add_command(source.source)
|
|
79
283
|
|
|
80
284
|
|