qalita 2.3.2__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.
Files changed (95) hide show
  1. qalita/__main__.py +213 -9
  2. qalita/commands/{agent.py → worker.py} +89 -89
  3. qalita/internal/config.py +26 -19
  4. qalita/internal/utils.py +1 -1
  5. qalita/web/app.py +97 -14
  6. qalita/web/blueprints/context.py +13 -60
  7. qalita/web/blueprints/dashboard.py +35 -76
  8. qalita/web/blueprints/helpers.py +154 -63
  9. qalita/web/blueprints/sources.py +29 -61
  10. qalita/web/blueprints/{agents.py → workers.py} +108 -185
  11. qalita-2.5.2.dist-info/METADATA +66 -0
  12. qalita-2.5.2.dist-info/RECORD +24 -0
  13. {qalita-2.3.2.dist-info → qalita-2.5.2.dist-info}/WHEEL +1 -1
  14. qalita-2.5.2.dist-info/entry_points.txt +2 -0
  15. qalita/web/blueprints/studio.py +0 -1294
  16. qalita/web/public/chatgpt.svg +0 -3
  17. qalita/web/public/claude.png +0 -0
  18. qalita/web/public/favicon.ico +0 -0
  19. qalita/web/public/gemini.png +0 -0
  20. qalita/web/public/logo-no-slogan.png +0 -0
  21. qalita/web/public/logo-white-no-slogan.svg +0 -11
  22. qalita/web/public/mistral.svg +0 -1
  23. qalita/web/public/noise.webp +0 -0
  24. qalita/web/public/ollama.png +0 -0
  25. qalita/web/public/platform.png +0 -0
  26. qalita/web/public/sources-logos/alloy-db.png +0 -0
  27. qalita/web/public/sources-logos/amazon-athena.png +0 -0
  28. qalita/web/public/sources-logos/amazon-rds.png +0 -0
  29. qalita/web/public/sources-logos/api.svg +0 -2
  30. qalita/web/public/sources-logos/avro.svg +0 -20
  31. qalita/web/public/sources-logos/azure-database-mysql.png +0 -0
  32. qalita/web/public/sources-logos/azure-database-postgresql.png +0 -0
  33. qalita/web/public/sources-logos/azure-sql-database.png +0 -0
  34. qalita/web/public/sources-logos/azure-sql-managed-instance.png +0 -0
  35. qalita/web/public/sources-logos/azure-synapse-analytics.png +0 -0
  36. qalita/web/public/sources-logos/azure_blob.svg +0 -1
  37. qalita/web/public/sources-logos/bigquery.png +0 -0
  38. qalita/web/public/sources-logos/cassandra.svg +0 -254
  39. qalita/web/public/sources-logos/clickhouse.png +0 -0
  40. qalita/web/public/sources-logos/cloud-sql.png +0 -0
  41. qalita/web/public/sources-logos/cockroach-db.png +0 -0
  42. qalita/web/public/sources-logos/csv.svg +0 -1
  43. qalita/web/public/sources-logos/database.svg +0 -3
  44. qalita/web/public/sources-logos/databricks.png +0 -0
  45. qalita/web/public/sources-logos/duckdb.png +0 -0
  46. qalita/web/public/sources-logos/elasticsearch.svg +0 -1
  47. qalita/web/public/sources-logos/excel.svg +0 -1
  48. qalita/web/public/sources-logos/file.svg +0 -1
  49. qalita/web/public/sources-logos/folder.svg +0 -6
  50. qalita/web/public/sources-logos/gcs.png +0 -0
  51. qalita/web/public/sources-logos/hdfs.svg +0 -1
  52. qalita/web/public/sources-logos/ibm-db2.png +0 -0
  53. qalita/web/public/sources-logos/json.png +0 -0
  54. qalita/web/public/sources-logos/maria-db.png +0 -0
  55. qalita/web/public/sources-logos/mongodb.svg +0 -1
  56. qalita/web/public/sources-logos/mssql.svg +0 -1
  57. qalita/web/public/sources-logos/mysql.svg +0 -7
  58. qalita/web/public/sources-logos/oracle.svg +0 -4
  59. qalita/web/public/sources-logos/parquet.svg +0 -16
  60. qalita/web/public/sources-logos/picture.png +0 -0
  61. qalita/web/public/sources-logos/postgresql.svg +0 -22
  62. qalita/web/public/sources-logos/questdb.png +0 -0
  63. qalita/web/public/sources-logos/redshift.png +0 -0
  64. qalita/web/public/sources-logos/s3.svg +0 -34
  65. qalita/web/public/sources-logos/sap-hana.png +0 -0
  66. qalita/web/public/sources-logos/sftp.png +0 -0
  67. qalita/web/public/sources-logos/single-store.png +0 -0
  68. qalita/web/public/sources-logos/snowflake.png +0 -0
  69. qalita/web/public/sources-logos/sqlite.svg +0 -104
  70. qalita/web/public/sources-logos/sqlserver.png +0 -0
  71. qalita/web/public/sources-logos/starburst.png +0 -0
  72. qalita/web/public/sources-logos/stream.png +0 -0
  73. qalita/web/public/sources-logos/teradata.png +0 -0
  74. qalita/web/public/sources-logos/timescale.png +0 -0
  75. qalita/web/public/sources-logos/xls.svg +0 -1
  76. qalita/web/public/sources-logos/xlsx.svg +0 -1
  77. qalita/web/public/sources-logos/yugabyte-db.png +0 -0
  78. qalita/web/public/studio-logo.svg +0 -10
  79. qalita/web/public/studio.css +0 -304
  80. qalita/web/public/studio.png +0 -0
  81. qalita/web/public/styles.css +0 -682
  82. qalita/web/templates/dashboard.html +0 -373
  83. qalita/web/templates/navbar.html +0 -40
  84. qalita/web/templates/sources/added.html +0 -57
  85. qalita/web/templates/sources/edit.html +0 -411
  86. qalita/web/templates/sources/select-source.html +0 -128
  87. qalita/web/templates/studio/agent-panel.html +0 -828
  88. qalita/web/templates/studio/context-panel.html +0 -300
  89. qalita/web/templates/studio/index.html +0 -79
  90. qalita/web/templates/studio/navbar.html +0 -14
  91. qalita/web/templates/studio/view-panel.html +0 -529
  92. qalita-2.3.2.dist-info/METADATA +0 -58
  93. qalita-2.3.2.dist-info/RECORD +0 -101
  94. qalita-2.3.2.dist-info/entry_points.txt +0 -3
  95. {qalita-2.3.2.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("QALITA_AGENT_UI", False),
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("QALITA_AGENT_UI_PORT", 7070),
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("QALITA_AGENT_UI_HOST", "localhost"),
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
- except Exception as exc:
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
- run_dashboard_ui(cfg, host=host, port=port)
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 agent, source, pack
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(agent.agent)
281
+ cli.add_command(worker.worker)
78
282
  cli.add_command(source.source)
79
283
 
80
284