mcli-framework 7.8.3__py3-none-any.whl → 7.8.5__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.
Potentially problematic release.
This version of mcli-framework might be problematic. Click here for more details.
- mcli/__init__.py +160 -0
- mcli/__main__.py +14 -0
- mcli/app/__init__.py +23 -0
- mcli/app/commands_cmd.py +942 -199
- mcli/app/main.py +5 -21
- mcli/app/model/__init__.py +0 -0
- mcli/app/model_cmd.py +57 -472
- mcli/app/video/__init__.py +5 -0
- mcli/chat/__init__.py +34 -0
- mcli/lib/__init__.py +0 -0
- mcli/lib/api/__init__.py +0 -0
- mcli/lib/auth/__init__.py +1 -0
- mcli/lib/config/__init__.py +1 -0
- mcli/lib/erd/__init__.py +25 -0
- mcli/lib/files/__init__.py +0 -0
- mcli/lib/fs/__init__.py +1 -0
- mcli/lib/logger/__init__.py +3 -0
- mcli/lib/performance/__init__.py +17 -0
- mcli/lib/pickles/__init__.py +1 -0
- mcli/lib/shell/__init__.py +0 -0
- mcli/lib/toml/__init__.py +1 -0
- mcli/lib/watcher/__init__.py +0 -0
- mcli/ml/__init__.py +16 -0
- mcli/ml/api/__init__.py +30 -0
- mcli/ml/api/routers/__init__.py +27 -0
- mcli/ml/auth/__init__.py +41 -0
- mcli/ml/backtesting/__init__.py +33 -0
- mcli/ml/cli/__init__.py +5 -0
- mcli/ml/config/__init__.py +33 -0
- mcli/ml/configs/__init__.py +16 -0
- mcli/ml/dashboard/__init__.py +12 -0
- mcli/ml/dashboard/app_supabase.py +57 -12
- mcli/ml/dashboard/components/__init__.py +7 -0
- mcli/ml/dashboard/pages/__init__.py +6 -0
- mcli/ml/dashboard/pages/predictions_enhanced.py +82 -38
- mcli/ml/dashboard/utils.py +39 -11
- mcli/ml/data_ingestion/__init__.py +29 -0
- mcli/ml/database/__init__.py +40 -0
- mcli/ml/experimentation/__init__.py +29 -0
- mcli/ml/features/__init__.py +39 -0
- mcli/ml/mlops/__init__.py +19 -0
- mcli/ml/models/__init__.py +90 -0
- mcli/ml/monitoring/__init__.py +25 -0
- mcli/ml/optimization/__init__.py +27 -0
- mcli/ml/predictions/__init__.py +5 -0
- mcli/ml/preprocessing/__init__.py +24 -0
- mcli/ml/scripts/__init__.py +1 -0
- mcli/ml/trading/__init__.py +63 -0
- mcli/ml/training/__init__.py +7 -0
- mcli/mygroup/__init__.py +3 -0
- mcli/public/__init__.py +1 -0
- mcli/public/commands/__init__.py +2 -0
- mcli/self/__init__.py +3 -0
- mcli/self/self_cmd.py +4 -253
- mcli/self/store_cmd.py +5 -3
- mcli/workflow/__init__.py +0 -0
- mcli/workflow/daemon/__init__.py +15 -0
- mcli/workflow/dashboard/__init__.py +5 -0
- mcli/workflow/dashboard/dashboard_cmd.py +1 -0
- mcli/workflow/docker/__init__.py +0 -0
- mcli/workflow/file/__init__.py +0 -0
- mcli/workflow/gcloud/__init__.py +1 -0
- mcli/workflow/git_commit/__init__.py +0 -0
- mcli/workflow/interview/__init__.py +0 -0
- mcli/workflow/politician_trading/__init__.py +4 -0
- mcli/workflow/registry/__init__.py +0 -0
- mcli/workflow/repo/__init__.py +0 -0
- mcli/workflow/scheduler/__init__.py +25 -0
- mcli/workflow/search/__init__.py +0 -0
- mcli/workflow/sync/__init__.py +5 -0
- mcli/workflow/videos/__init__.py +1 -0
- mcli/workflow/wakatime/__init__.py +80 -0
- {mcli_framework-7.8.3.dist-info → mcli_framework-7.8.5.dist-info}/METADATA +1 -1
- {mcli_framework-7.8.3.dist-info → mcli_framework-7.8.5.dist-info}/RECORD +78 -18
- mcli/app/chat_cmd.py +0 -42
- mcli/test/cron_test_cmd.py +0 -697
- mcli/test/test_cmd.py +0 -30
- {mcli_framework-7.8.3.dist-info → mcli_framework-7.8.5.dist-info}/WHEEL +0 -0
- {mcli_framework-7.8.3.dist-info → mcli_framework-7.8.5.dist-info}/entry_points.txt +0 -0
- {mcli_framework-7.8.3.dist-info → mcli_framework-7.8.5.dist-info}/licenses/LICENSE +0 -0
- {mcli_framework-7.8.3.dist-info → mcli_framework-7.8.5.dist-info}/top_level.txt +0 -0
mcli/app/main.py
CHANGED
|
@@ -12,8 +12,6 @@ from typing import List, Optional
|
|
|
12
12
|
import click
|
|
13
13
|
import tomli
|
|
14
14
|
|
|
15
|
-
# Import chat command group
|
|
16
|
-
from mcli.app.chat_cmd import chat
|
|
17
15
|
from mcli.lib.logger.logger import disable_runtime_tracing, enable_runtime_tracing, get_logger
|
|
18
16
|
from mcli.lib.ui.styling import info, success
|
|
19
17
|
|
|
@@ -346,15 +344,6 @@ def _add_lazy_commands(app: click.Group):
|
|
|
346
344
|
except Exception as e:
|
|
347
345
|
logger.debug(f"Could not load self commands: {e}")
|
|
348
346
|
|
|
349
|
-
# Test group - load immediately for testing commands
|
|
350
|
-
try:
|
|
351
|
-
from mcli.test.test_cmd import test_group
|
|
352
|
-
|
|
353
|
-
app.add_command(test_group, name="test")
|
|
354
|
-
logger.debug("Added test group commands")
|
|
355
|
-
except Exception as e:
|
|
356
|
-
logger.debug(f"Could not load test commands: {e}")
|
|
357
|
-
|
|
358
347
|
# Add workflow with completion-aware lazy loading
|
|
359
348
|
try:
|
|
360
349
|
from mcli.app.completion_helpers import create_completion_aware_lazy_group
|
|
@@ -377,16 +366,11 @@ def _add_lazy_commands(app: click.Group):
|
|
|
377
366
|
app.add_command(workflow_group, name="workflow")
|
|
378
367
|
|
|
379
368
|
# Lazy load other heavy commands that are used less frequently
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
"model": {
|
|
386
|
-
"import_path": "mcli.app.model_cmd.model",
|
|
387
|
-
"help": "Model management commands for offline and online model usage",
|
|
388
|
-
},
|
|
389
|
-
}
|
|
369
|
+
# NOTE: chat and model commands have been removed
|
|
370
|
+
# - chat: removed from core commands
|
|
371
|
+
# - model: moved to ~/.mcli/commands workflow
|
|
372
|
+
# - test: removed from core commands
|
|
373
|
+
lazy_commands = {}
|
|
390
374
|
|
|
391
375
|
for cmd_name, cmd_info in lazy_commands.items():
|
|
392
376
|
# Skip workflow since we already added it with completion support
|
|
File without changes
|
mcli/app/model_cmd.py
CHANGED
|
@@ -1,481 +1,66 @@
|
|
|
1
|
-
"""
|
|
1
|
+
"""
|
|
2
|
+
Model command stub - redirects to workflow command in ~/.mcli/commands/model.json
|
|
3
|
+
|
|
4
|
+
This stub exists for backwards compatibility with tests and imports.
|
|
5
|
+
The actual model command implementation is now in ~/.mcli/commands/model.json
|
|
6
|
+
"""
|
|
2
7
|
|
|
3
|
-
import os
|
|
4
|
-
import subprocess
|
|
5
8
|
import sys
|
|
6
9
|
from pathlib import Path
|
|
7
|
-
from typing import Optional
|
|
8
|
-
|
|
9
|
-
import click
|
|
10
|
-
import psutil
|
|
11
|
-
|
|
12
|
-
from mcli.lib.logger.logger import get_logger
|
|
13
|
-
from mcli.workflow.model_service.lightweight_model_server import (
|
|
14
|
-
LIGHTWEIGHT_MODELS,
|
|
15
|
-
LightweightModelServer,
|
|
16
|
-
)
|
|
17
|
-
|
|
18
|
-
logger = get_logger(__name__)
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
def _start_openai_server(server, host: str, port: int, api_key: Optional[str], model: str):
|
|
22
|
-
"""Start FastAPI server with OpenAI compatibility"""
|
|
23
|
-
try:
|
|
24
|
-
import uvicorn
|
|
25
|
-
from fastapi import FastAPI
|
|
26
|
-
from fastapi.middleware.cors import CORSMiddleware
|
|
27
|
-
|
|
28
|
-
from mcli.workflow.model_service.openai_adapter import create_openai_adapter
|
|
29
|
-
|
|
30
|
-
# Create FastAPI app
|
|
31
|
-
app = FastAPI(
|
|
32
|
-
title="MCLI Model Service (OpenAI Compatible)",
|
|
33
|
-
description="OpenAI-compatible API for MCLI lightweight models",
|
|
34
|
-
version="1.0.0",
|
|
35
|
-
)
|
|
36
|
-
|
|
37
|
-
# Add CORS middleware
|
|
38
|
-
app.add_middleware(
|
|
39
|
-
CORSMiddleware,
|
|
40
|
-
allow_origins=["*"],
|
|
41
|
-
allow_credentials=True,
|
|
42
|
-
allow_methods=["*"],
|
|
43
|
-
allow_headers=["*"],
|
|
44
|
-
)
|
|
45
|
-
|
|
46
|
-
# Create OpenAI adapter
|
|
47
|
-
require_auth = api_key is not None
|
|
48
|
-
adapter = create_openai_adapter(server, require_auth=require_auth)
|
|
49
|
-
|
|
50
|
-
# Add API key if provided
|
|
51
|
-
if api_key:
|
|
52
|
-
adapter.api_key_manager.add_key(api_key, name="default")
|
|
53
|
-
click.echo(f"🔐 API key authentication enabled")
|
|
54
|
-
|
|
55
|
-
# Include OpenAI routes
|
|
56
|
-
app.include_router(adapter.router)
|
|
57
|
-
|
|
58
|
-
# Add health check endpoint
|
|
59
|
-
@app.get("/health")
|
|
60
|
-
async def health():
|
|
61
|
-
return {"status": "healthy", "model": model}
|
|
62
|
-
|
|
63
|
-
# Display server info
|
|
64
|
-
click.echo(f"\n📝 Server running at:")
|
|
65
|
-
click.echo(f" - Base URL: http://{host}:{port}")
|
|
66
|
-
click.echo(f" - OpenAI API: http://{host}:{port}/v1")
|
|
67
|
-
click.echo(f" - Models: http://{host}:{port}/v1/models")
|
|
68
|
-
click.echo(f" - Chat: http://{host}:{port}/v1/chat/completions")
|
|
69
|
-
click.echo(f" - Health: http://{host}:{port}/health")
|
|
70
|
-
|
|
71
|
-
if require_auth:
|
|
72
|
-
click.echo(f"\n🔐 Authentication: Required")
|
|
73
|
-
click.echo(f" Use: Authorization: Bearer {api_key}")
|
|
74
|
-
else:
|
|
75
|
-
click.echo(f"\n⚠️ Authentication: Disabled (not recommended for public access)")
|
|
76
|
-
|
|
77
|
-
if host == "0.0.0.0":
|
|
78
|
-
click.echo(f"\n⚠️ Server is publicly accessible on all interfaces!")
|
|
79
|
-
|
|
80
|
-
click.echo(f"\n📚 For aider, use:")
|
|
81
|
-
if require_auth:
|
|
82
|
-
click.echo(f" export OPENAI_API_KEY={api_key}")
|
|
83
|
-
click.echo(f" export OPENAI_API_BASE=http://{host}:{port}/v1")
|
|
84
|
-
click.echo(f" aider --model {model}")
|
|
85
|
-
|
|
86
|
-
click.echo(f"\n Press Ctrl+C to stop the server")
|
|
87
|
-
|
|
88
|
-
# Start server
|
|
89
|
-
uvicorn.run(app, host=host, port=port, log_level="info")
|
|
90
|
-
|
|
91
|
-
except ImportError as e:
|
|
92
|
-
click.echo(f"❌ Missing dependencies for OpenAI-compatible server: {e}")
|
|
93
|
-
click.echo(f" Install with: pip install fastapi uvicorn")
|
|
94
|
-
sys.exit(1)
|
|
95
|
-
except Exception as e:
|
|
96
|
-
click.echo(f"❌ Failed to start OpenAI-compatible server: {e}")
|
|
97
|
-
logger.error(f"Server error: {e}", exc_info=True)
|
|
98
|
-
sys.exit(1)
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
@click.group()
|
|
102
|
-
def model():
|
|
103
|
-
"""Model management commands for offline and online model usage."""
|
|
104
|
-
pass
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
@model.command()
|
|
108
|
-
@click.option("--list-available", "-l", is_flag=True, help="List all available lightweight models")
|
|
109
|
-
@click.option("--list-downloaded", "-d", is_flag=True, help="List downloaded models")
|
|
110
|
-
@click.option(
|
|
111
|
-
"--system-info", "-s", is_flag=True, help="Show system information and recommendations"
|
|
112
|
-
)
|
|
113
|
-
def list(list_available: bool, list_downloaded: bool, system_info: bool):
|
|
114
|
-
"""List available and downloaded models."""
|
|
115
|
-
server = LightweightModelServer()
|
|
116
|
-
|
|
117
|
-
if system_info:
|
|
118
|
-
info = server.get_system_info()
|
|
119
|
-
click.echo("🖥️ System Information:")
|
|
120
|
-
click.echo(f" CPU Cores: {info['cpu_count']}")
|
|
121
|
-
click.echo(f" RAM: {info['memory_gb']:.1f} GB")
|
|
122
|
-
click.echo(f" Free Disk: {info['disk_free_gb']:.1f} GB")
|
|
123
|
-
recommended = server.recommend_model()
|
|
124
|
-
click.echo(f" Recommended Model: {recommended}")
|
|
125
|
-
click.echo("")
|
|
126
|
-
|
|
127
|
-
if list_available or (not list_downloaded and not system_info):
|
|
128
|
-
click.echo("📋 Available Lightweight Models:")
|
|
129
|
-
click.echo("=" * 50)
|
|
130
|
-
|
|
131
|
-
downloaded_models = server.downloader.get_downloaded_models()
|
|
132
|
-
|
|
133
|
-
for key, info in LIGHTWEIGHT_MODELS.items():
|
|
134
|
-
status = "✅ Downloaded" if key in downloaded_models else "⏳ Available"
|
|
135
|
-
click.echo(f"{status} - {info['name']} ({info['parameters']})")
|
|
136
|
-
click.echo(
|
|
137
|
-
f" Size: {info['size_mb']} MB | Efficiency: {info['efficiency_score']}/10"
|
|
138
|
-
)
|
|
139
|
-
click.echo(f" Type: {info['model_type']} | Tags: {', '.join(info['tags'])}")
|
|
140
|
-
click.echo()
|
|
141
|
-
|
|
142
|
-
if list_downloaded:
|
|
143
|
-
downloaded_models = server.downloader.get_downloaded_models()
|
|
144
|
-
if downloaded_models:
|
|
145
|
-
click.echo("📦 Downloaded Models:")
|
|
146
|
-
click.echo("=" * 30)
|
|
147
|
-
for model in downloaded_models:
|
|
148
|
-
info = LIGHTWEIGHT_MODELS.get(model, {})
|
|
149
|
-
name = info.get("name", model)
|
|
150
|
-
params = info.get("parameters", "Unknown")
|
|
151
|
-
click.echo(f"✅ {name} ({params})")
|
|
152
|
-
else:
|
|
153
|
-
click.echo(
|
|
154
|
-
"No models downloaded yet. Use 'mcli model download <model>' to download a model."
|
|
155
|
-
)
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
@model.command()
|
|
159
|
-
@click.argument("model_name")
|
|
160
|
-
def download(model_name: str):
|
|
161
|
-
"""Download a specific lightweight model."""
|
|
162
|
-
if model_name not in LIGHTWEIGHT_MODELS:
|
|
163
|
-
click.echo(f"❌ Model '{model_name}' not found.")
|
|
164
|
-
click.echo("Available models:")
|
|
165
|
-
for key in LIGHTWEIGHT_MODELS.keys():
|
|
166
|
-
click.echo(f" • {key}")
|
|
167
|
-
sys.exit(1)
|
|
168
|
-
|
|
169
|
-
server = LightweightModelServer()
|
|
170
|
-
|
|
171
|
-
click.echo(f"Downloading model: {model_name}")
|
|
172
|
-
success = server.download_and_load_model(model_name)
|
|
173
|
-
|
|
174
|
-
if success:
|
|
175
|
-
click.echo(f"✅ Successfully downloaded {model_name}")
|
|
176
|
-
else:
|
|
177
|
-
click.echo(f"❌ Failed to download {model_name}")
|
|
178
|
-
sys.exit(1)
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
@model.command()
|
|
182
|
-
@click.option("--model", "-m", help="Specific model to use")
|
|
183
|
-
@click.option(
|
|
184
|
-
"--port", "-p", default=None, help="Port to run server on (default: from config or 51234)"
|
|
185
|
-
)
|
|
186
|
-
@click.option(
|
|
187
|
-
"--host", "-h", default="localhost", help="Host to bind to (use 0.0.0.0 for public access)"
|
|
188
|
-
)
|
|
189
|
-
@click.option(
|
|
190
|
-
"--auto-download",
|
|
191
|
-
is_flag=True,
|
|
192
|
-
default=True,
|
|
193
|
-
help="Automatically download model if not available",
|
|
194
|
-
)
|
|
195
|
-
@click.option(
|
|
196
|
-
"--openai-compatible",
|
|
197
|
-
is_flag=True,
|
|
198
|
-
default=False,
|
|
199
|
-
help="Enable OpenAI-compatible API endpoints",
|
|
200
|
-
)
|
|
201
|
-
@click.option(
|
|
202
|
-
"--api-key",
|
|
203
|
-
default=None,
|
|
204
|
-
help="API key for authentication (if not set, auth is disabled)",
|
|
205
|
-
)
|
|
206
|
-
def start(
|
|
207
|
-
model: Optional[str],
|
|
208
|
-
port: Optional[int],
|
|
209
|
-
host: str,
|
|
210
|
-
auto_download: bool,
|
|
211
|
-
openai_compatible: bool,
|
|
212
|
-
api_key: Optional[str],
|
|
213
|
-
):
|
|
214
|
-
"""Start the lightweight model server."""
|
|
215
|
-
# Load port from config if not specified
|
|
216
|
-
if port is None:
|
|
217
|
-
try:
|
|
218
|
-
from mcli.lib.config.config import load_config
|
|
219
|
-
|
|
220
|
-
config = load_config()
|
|
221
|
-
port = config.get("model", {}).get("server_port", 51234)
|
|
222
|
-
except Exception:
|
|
223
|
-
port = 51234 # Default ephemeral port
|
|
224
|
-
|
|
225
|
-
server = LightweightModelServer(port=port)
|
|
226
|
-
|
|
227
|
-
# Determine which model to use
|
|
228
|
-
if not model:
|
|
229
|
-
model = server.recommend_model()
|
|
230
|
-
click.echo(f"🎯 Using recommended model: {model}")
|
|
231
|
-
elif model not in LIGHTWEIGHT_MODELS:
|
|
232
|
-
click.echo(f"❌ Model '{model}' not found.")
|
|
233
|
-
click.echo("Available models:")
|
|
234
|
-
for key in LIGHTWEIGHT_MODELS.keys():
|
|
235
|
-
click.echo(f" • {key}")
|
|
236
|
-
sys.exit(1)
|
|
237
10
|
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
11
|
+
# Try to load the model command from the workflow system
|
|
12
|
+
try:
|
|
13
|
+
from mcli.lib.custom_commands import load_command_from_file
|
|
14
|
+
|
|
15
|
+
# Load model command from ~/.mcli/commands/model.json
|
|
16
|
+
model_json_path = Path.home() / ".mcli" / "commands" / "model.json"
|
|
17
|
+
|
|
18
|
+
if model_json_path.exists():
|
|
19
|
+
# Load the command from the JSON file
|
|
20
|
+
import json
|
|
21
|
+
|
|
22
|
+
with open(model_json_path, "r") as f:
|
|
23
|
+
command_data = json.load(f)
|
|
24
|
+
|
|
25
|
+
# Execute the code to get the app (model command group)
|
|
26
|
+
code = command_data.get("code", "")
|
|
27
|
+
namespace = {}
|
|
28
|
+
exec(code, namespace)
|
|
29
|
+
|
|
30
|
+
# Extract the app (model command group) and individual commands
|
|
31
|
+
app = namespace.get("app")
|
|
32
|
+
if app:
|
|
33
|
+
# The model group command
|
|
34
|
+
model = app
|
|
35
|
+
|
|
36
|
+
# Extract individual subcommands from the model group
|
|
37
|
+
if hasattr(model, "commands"):
|
|
38
|
+
commands = model.commands
|
|
39
|
+
list = commands.get("list")
|
|
40
|
+
download = commands.get("download")
|
|
41
|
+
start = commands.get("start")
|
|
42
|
+
recommend = commands.get("recommend")
|
|
43
|
+
status = commands.get("status")
|
|
44
|
+
stop = commands.get("stop")
|
|
45
|
+
pull = commands.get("pull")
|
|
46
|
+
delete = commands.get("delete")
|
|
47
|
+
else:
|
|
48
|
+
# Fallback if commands aren't available
|
|
49
|
+
list = download = start = recommend = status = stop = pull = delete = None
|
|
247
50
|
else:
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
)
|
|
251
|
-
sys.exit(1)
|
|
51
|
+
# If app is not found, create empty placeholders
|
|
52
|
+
model = list = download = start = recommend = status = stop = pull = delete = None
|
|
252
53
|
else:
|
|
253
|
-
#
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
click.echo(f"❌ Failed to load {model}")
|
|
257
|
-
sys.exit(1)
|
|
54
|
+
# If the JSON file doesn't exist, create empty placeholders
|
|
55
|
+
print(f"Warning: {model_json_path} not found", file=sys.stderr)
|
|
56
|
+
model = list = download = start = recommend = status = stop = pull = delete = None
|
|
258
57
|
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
_start_openai_server(server, host, port, api_key, model)
|
|
263
|
-
else:
|
|
264
|
-
click.echo(f"🚀 Starting lightweight server on {host}:{port}...")
|
|
265
|
-
server.start_server()
|
|
266
|
-
|
|
267
|
-
click.echo(f"\n📝 Server running at:")
|
|
268
|
-
click.echo(f" - API: http://{host}:{port}")
|
|
269
|
-
click.echo(f" - Health: http://{host}:{port}/health")
|
|
270
|
-
click.echo(f" - Models: http://{host}:{port}/models")
|
|
271
|
-
|
|
272
|
-
if host == "0.0.0.0":
|
|
273
|
-
click.echo(f"\n⚠️ Server is publicly accessible!")
|
|
274
|
-
click.echo(f" Consider using --openai-compatible with --api-key for security")
|
|
275
|
-
|
|
276
|
-
click.echo(f"\n Press Ctrl+C to stop the server")
|
|
277
|
-
|
|
278
|
-
try:
|
|
279
|
-
# Keep server running
|
|
280
|
-
import time
|
|
281
|
-
|
|
282
|
-
while True:
|
|
283
|
-
time.sleep(1)
|
|
284
|
-
except KeyboardInterrupt:
|
|
285
|
-
click.echo("\n🛑 Server stopped")
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
@model.command()
|
|
289
|
-
def recommend():
|
|
290
|
-
"""Get model recommendation based on system capabilities."""
|
|
291
|
-
server = LightweightModelServer()
|
|
292
|
-
recommended = server.recommend_model()
|
|
293
|
-
|
|
294
|
-
info = server.get_system_info()
|
|
295
|
-
click.echo("🔍 System Analysis:")
|
|
296
|
-
click.echo(f" CPU Cores: {info['cpu_count']}")
|
|
297
|
-
click.echo(f" RAM: {info['memory_gb']:.1f} GB")
|
|
298
|
-
click.echo(f" Free Disk: {info['disk_free_gb']:.1f} GB")
|
|
299
|
-
click.echo("")
|
|
300
|
-
|
|
301
|
-
model_info = LIGHTWEIGHT_MODELS[recommended]
|
|
302
|
-
click.echo(f"🎯 Recommended Model: {recommended}")
|
|
303
|
-
click.echo(f" Name: {model_info['name']}")
|
|
304
|
-
click.echo(f" Description: {model_info['description']}")
|
|
305
|
-
click.echo(f" Parameters: {model_info['parameters']}")
|
|
306
|
-
click.echo(f" Size: {model_info['size_mb']} MB")
|
|
307
|
-
click.echo(f" Efficiency Score: {model_info['efficiency_score']}/10")
|
|
308
|
-
|
|
309
|
-
downloaded_models = server.downloader.get_downloaded_models()
|
|
310
|
-
if recommended not in downloaded_models:
|
|
311
|
-
click.echo(f"\n💡 To download: mcli model download {recommended}")
|
|
312
|
-
else:
|
|
313
|
-
click.echo(f"\n✅ Model already downloaded")
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
@model.command()
|
|
317
|
-
@click.option(
|
|
318
|
-
"--port",
|
|
319
|
-
"-p",
|
|
320
|
-
default=None,
|
|
321
|
-
help="Port where server is running (default: from config or 51234)",
|
|
322
|
-
)
|
|
323
|
-
def status(port: Optional[int]):
|
|
324
|
-
"""Check status of the lightweight model server."""
|
|
325
|
-
# Load port from config if not specified
|
|
326
|
-
if port is None:
|
|
327
|
-
try:
|
|
328
|
-
from mcli.lib.config.config import load_config
|
|
329
|
-
|
|
330
|
-
config = load_config()
|
|
331
|
-
port = config.get("model", {}).get("server_port", 51234)
|
|
332
|
-
except Exception:
|
|
333
|
-
port = 51234 # Default ephemeral port
|
|
334
|
-
|
|
335
|
-
import requests
|
|
336
|
-
|
|
337
|
-
try:
|
|
338
|
-
response = requests.get(f"http://localhost:{port}/health", timeout=5)
|
|
339
|
-
if response.status_code == 200:
|
|
340
|
-
click.echo(f"✅ Server is running on port {port}")
|
|
341
|
-
|
|
342
|
-
# Get loaded models
|
|
343
|
-
models_response = requests.get(f"http://localhost:{port}/models", timeout=5)
|
|
344
|
-
if models_response.status_code == 200:
|
|
345
|
-
models_data = models_response.json()
|
|
346
|
-
models = models_data.get("models", [])
|
|
347
|
-
if models:
|
|
348
|
-
click.echo(f"🤖 Loaded models ({len(models)}):")
|
|
349
|
-
for model in models:
|
|
350
|
-
click.echo(f" - {model['name']} ({model['parameters']})")
|
|
351
|
-
else:
|
|
352
|
-
click.echo("⚠️ No models currently loaded")
|
|
353
|
-
else:
|
|
354
|
-
click.echo(f"❌ Server responded with status {response.status_code}")
|
|
355
|
-
|
|
356
|
-
except requests.exceptions.ConnectionError:
|
|
357
|
-
click.echo(f"❌ No server running on port {port}")
|
|
358
|
-
except requests.exceptions.Timeout:
|
|
359
|
-
click.echo(f"⏰ Server on port {port} is not responding")
|
|
360
|
-
except Exception as e:
|
|
361
|
-
click.echo(f"❌ Error checking server: {e}")
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
@model.command()
|
|
365
|
-
@click.option(
|
|
366
|
-
"--port",
|
|
367
|
-
"-p",
|
|
368
|
-
default=None,
|
|
369
|
-
help="Port where server is running (default: from config or 51234)",
|
|
370
|
-
)
|
|
371
|
-
def stop(port: Optional[int]):
|
|
372
|
-
"""Stop the lightweight model server."""
|
|
373
|
-
# Load port from config if not specified
|
|
374
|
-
if port is None:
|
|
375
|
-
try:
|
|
376
|
-
from mcli.lib.config.config import load_config
|
|
377
|
-
|
|
378
|
-
config = load_config()
|
|
379
|
-
port = config.get("model", {}).get("server_port", 51234)
|
|
380
|
-
except Exception:
|
|
381
|
-
port = 51234 # Default ephemeral port
|
|
382
|
-
|
|
383
|
-
import psutil
|
|
384
|
-
import requests
|
|
385
|
-
|
|
386
|
-
try:
|
|
387
|
-
# First check if server is running
|
|
388
|
-
try:
|
|
389
|
-
response = requests.get(f"http://localhost:{port}/health", timeout=2)
|
|
390
|
-
if response.status_code != 200:
|
|
391
|
-
click.echo(f"❌ No server running on port {port}")
|
|
392
|
-
return
|
|
393
|
-
except requests.exceptions.ConnectionError:
|
|
394
|
-
click.echo(f"❌ No server running on port {port}")
|
|
395
|
-
return
|
|
396
|
-
|
|
397
|
-
# Find and kill the process using the port
|
|
398
|
-
for proc in psutil.process_iter(["pid", "name", "connections"]):
|
|
399
|
-
try:
|
|
400
|
-
connections = proc.info.get("connections")
|
|
401
|
-
if connections:
|
|
402
|
-
for conn in connections:
|
|
403
|
-
if hasattr(conn, "laddr") and conn.laddr.port == port:
|
|
404
|
-
click.echo(f"🛑 Stopping server (PID: {proc.pid})...")
|
|
405
|
-
proc.terminate()
|
|
406
|
-
proc.wait(timeout=5)
|
|
407
|
-
click.echo("✅ Server stopped successfully")
|
|
408
|
-
return
|
|
409
|
-
except (psutil.NoSuchProcess, psutil.AccessDenied, psutil.TimeoutExpired):
|
|
410
|
-
continue
|
|
411
|
-
|
|
412
|
-
click.echo("⚠️ Could not find server process")
|
|
413
|
-
|
|
414
|
-
except Exception as e:
|
|
415
|
-
click.echo(f"❌ Error stopping server: {e}")
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
@model.command()
|
|
419
|
-
@click.argument("model_name")
|
|
420
|
-
def pull(model_name: str):
|
|
421
|
-
"""Pull (download) a specific lightweight model."""
|
|
422
|
-
if model_name not in LIGHTWEIGHT_MODELS:
|
|
423
|
-
click.echo(f"❌ Model '{model_name}' not found.")
|
|
424
|
-
click.echo("Available models:")
|
|
425
|
-
for key in LIGHTWEIGHT_MODELS.keys():
|
|
426
|
-
click.echo(f" • {key}")
|
|
427
|
-
sys.exit(1)
|
|
428
|
-
|
|
429
|
-
server = LightweightModelServer()
|
|
430
|
-
|
|
431
|
-
click.echo(f"Pulling model: {model_name}")
|
|
432
|
-
success = server.download_and_load_model(model_name)
|
|
433
|
-
|
|
434
|
-
if success:
|
|
435
|
-
click.echo(f"✅ Successfully pulled {model_name}")
|
|
436
|
-
else:
|
|
437
|
-
click.echo(f"❌ Failed to pull {model_name}")
|
|
438
|
-
sys.exit(1)
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
@model.command()
|
|
442
|
-
@click.argument("model_name")
|
|
443
|
-
@click.option("--force", "-f", is_flag=True, help="Force deletion without confirmation")
|
|
444
|
-
def delete(model_name: str, force: bool):
|
|
445
|
-
"""Delete a downloaded lightweight model."""
|
|
446
|
-
server = LightweightModelServer()
|
|
447
|
-
downloaded_models = server.downloader.get_downloaded_models()
|
|
448
|
-
|
|
449
|
-
if model_name not in downloaded_models:
|
|
450
|
-
click.echo(f"❌ Model '{model_name}' not found.")
|
|
451
|
-
click.echo("Downloaded models:")
|
|
452
|
-
if downloaded_models:
|
|
453
|
-
for model in downloaded_models:
|
|
454
|
-
click.echo(f" • {model}")
|
|
455
|
-
else:
|
|
456
|
-
click.echo(" (none)")
|
|
457
|
-
sys.exit(1)
|
|
458
|
-
|
|
459
|
-
# Confirm deletion unless --force is used
|
|
460
|
-
if not force:
|
|
461
|
-
model_info = LIGHTWEIGHT_MODELS.get(model_name, {})
|
|
462
|
-
name = model_info.get("name", model_name)
|
|
463
|
-
size = model_info.get("size_mb", "unknown")
|
|
464
|
-
click.echo(f"⚠️ About to delete:")
|
|
465
|
-
click.echo(f" Model: {name}")
|
|
466
|
-
click.echo(f" Size: {size} MB")
|
|
467
|
-
if not click.confirm("Are you sure you want to delete this model?"):
|
|
468
|
-
click.echo("❌ Deletion cancelled")
|
|
469
|
-
return
|
|
470
|
-
|
|
471
|
-
success = server.delete_model(model_name)
|
|
472
|
-
|
|
473
|
-
if success:
|
|
474
|
-
click.echo(f"✅ Successfully deleted {model_name}")
|
|
475
|
-
else:
|
|
476
|
-
click.echo(f"❌ Failed to delete {model_name}")
|
|
477
|
-
sys.exit(1)
|
|
58
|
+
except Exception as e:
|
|
59
|
+
print(f"Error loading model command from workflow: {e}", file=sys.stderr)
|
|
60
|
+
import traceback
|
|
478
61
|
|
|
62
|
+
traceback.print_exc()
|
|
63
|
+
model = list = download = start = recommend = status = stop = pull = delete = None
|
|
479
64
|
|
|
480
|
-
|
|
481
|
-
|
|
65
|
+
# Export the commands for backwards compatibility
|
|
66
|
+
__all__ = ["model", "list", "download", "start", "recommend", "status", "stop", "pull", "delete"]
|
mcli/chat/__init__.py
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
"""
|
|
2
|
+
MCLI Chat System
|
|
3
|
+
Real-time system control and interaction capabilities for MCLI chat
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from .system_controller import (
|
|
7
|
+
SystemController,
|
|
8
|
+
control_app,
|
|
9
|
+
execute_system_command,
|
|
10
|
+
open_file_or_url,
|
|
11
|
+
open_textedit_and_write,
|
|
12
|
+
system_controller,
|
|
13
|
+
take_screenshot,
|
|
14
|
+
)
|
|
15
|
+
from .system_integration import (
|
|
16
|
+
ChatSystemIntegration,
|
|
17
|
+
chat_system_integration,
|
|
18
|
+
get_system_capabilities,
|
|
19
|
+
handle_system_request,
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
__all__ = [
|
|
23
|
+
"SystemController",
|
|
24
|
+
"system_controller",
|
|
25
|
+
"ChatSystemIntegration",
|
|
26
|
+
"chat_system_integration",
|
|
27
|
+
"handle_system_request",
|
|
28
|
+
"get_system_capabilities",
|
|
29
|
+
"open_textedit_and_write",
|
|
30
|
+
"control_app",
|
|
31
|
+
"execute_system_command",
|
|
32
|
+
"take_screenshot",
|
|
33
|
+
"open_file_or_url",
|
|
34
|
+
]
|
mcli/lib/__init__.py
ADDED
|
File without changes
|
mcli/lib/api/__init__.py
ADDED
|
File without changes
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
from .auth import *
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
from .config import *
|
mcli/lib/erd/__init__.py
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Entity Relationship Diagram (ERD) package.
|
|
3
|
+
|
|
4
|
+
This package provides utilities for generating Entity Relationship Diagrams from MCLI type metadata.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
# Import and export all public functions from erd.py
|
|
8
|
+
from .erd import (
|
|
9
|
+
analyze_graph_for_hierarchical_exports,
|
|
10
|
+
create_merged_erd,
|
|
11
|
+
do_erd,
|
|
12
|
+
find_top_nodes_in_graph,
|
|
13
|
+
generate_erd_for_top_nodes,
|
|
14
|
+
generate_merged_erd_for_types,
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
# Define __all__ to control exports
|
|
18
|
+
__all__ = [
|
|
19
|
+
"do_erd",
|
|
20
|
+
"create_merged_erd",
|
|
21
|
+
"generate_merged_erd_for_types",
|
|
22
|
+
"find_top_nodes_in_graph",
|
|
23
|
+
"generate_erd_for_top_nodes",
|
|
24
|
+
"analyze_graph_for_hierarchical_exports",
|
|
25
|
+
]
|
|
File without changes
|
mcli/lib/fs/__init__.py
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
from .fs import *
|