mcli-framework 7.0.1__tar.gz → 7.0.5__tar.gz

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.

Files changed (266) hide show
  1. {mcli_framework-7.0.1 → mcli_framework-7.0.5}/PKG-INFO +1 -1
  2. {mcli_framework-7.0.1 → mcli_framework-7.0.5}/pyproject.toml +1 -1
  3. {mcli_framework-7.0.1 → mcli_framework-7.0.5}/src/mcli/app/completion_helpers.py +6 -2
  4. {mcli_framework-7.0.1 → mcli_framework-7.0.5}/src/mcli/app/model_cmd.py +102 -0
  5. {mcli_framework-7.0.1 → mcli_framework-7.0.5}/src/mcli/self/self_cmd.py +63 -9
  6. {mcli_framework-7.0.1 → mcli_framework-7.0.5}/src/mcli/workflow/model_service/lightweight_model_server.py +37 -0
  7. {mcli_framework-7.0.1 → mcli_framework-7.0.5}/src/mcli_framework.egg-info/PKG-INFO +1 -1
  8. {mcli_framework-7.0.1 → mcli_framework-7.0.5}/src/mcli_framework.egg-info/SOURCES.txt +1 -0
  9. mcli_framework-7.0.5/tests/test_model_cmd.py +362 -0
  10. {mcli_framework-7.0.1 → mcli_framework-7.0.5}/tests/test_self.py +32 -1
  11. {mcli_framework-7.0.1 → mcli_framework-7.0.5}/.github/dependabot.yml +0 -0
  12. {mcli_framework-7.0.1 → mcli_framework-7.0.5}/.github/workflows/build.yml +0 -0
  13. {mcli_framework-7.0.1 → mcli_framework-7.0.5}/.github/workflows/ci.yml +0 -0
  14. {mcli_framework-7.0.1 → mcli_framework-7.0.5}/.github/workflows/ml-pipeline.yml +0 -0
  15. {mcli_framework-7.0.1 → mcli_framework-7.0.5}/.github/workflows/publish-self-hosted.yml +0 -0
  16. {mcli_framework-7.0.1 → mcli_framework-7.0.5}/.github/workflows/publish.yml +0 -0
  17. {mcli_framework-7.0.1 → mcli_framework-7.0.5}/.github/workflows/release.yml +0 -0
  18. {mcli_framework-7.0.1 → mcli_framework-7.0.5}/.github/workflows/security.yml +0 -0
  19. {mcli_framework-7.0.1 → mcli_framework-7.0.5}/.github/workflows/test.yml +0 -0
  20. {mcli_framework-7.0.1 → mcli_framework-7.0.5}/LICENSE +0 -0
  21. {mcli_framework-7.0.1 → mcli_framework-7.0.5}/MANIFEST.in +0 -0
  22. {mcli_framework-7.0.1 → mcli_framework-7.0.5}/PERFORMANCE_OPTIMIZATIONS.md +0 -0
  23. {mcli_framework-7.0.1 → mcli_framework-7.0.5}/README.md +0 -0
  24. {mcli_framework-7.0.1 → mcli_framework-7.0.5}/mcli_rust/Cargo.toml +0 -0
  25. {mcli_framework-7.0.1 → mcli_framework-7.0.5}/mcli_rust/src/command_parser.rs +0 -0
  26. {mcli_framework-7.0.1 → mcli_framework-7.0.5}/mcli_rust/src/file_watcher.rs +0 -0
  27. {mcli_framework-7.0.1 → mcli_framework-7.0.5}/mcli_rust/src/lib.rs +0 -0
  28. {mcli_framework-7.0.1 → mcli_framework-7.0.5}/mcli_rust/src/process_manager.rs +0 -0
  29. {mcli_framework-7.0.1 → mcli_framework-7.0.5}/mcli_rust/src/tfidf.rs +0 -0
  30. {mcli_framework-7.0.1 → mcli_framework-7.0.5}/setup.cfg +0 -0
  31. {mcli_framework-7.0.1 → mcli_framework-7.0.5}/src/mcli/app/chat_cmd.py +0 -0
  32. {mcli_framework-7.0.1 → mcli_framework-7.0.5}/src/mcli/app/commands_cmd.py +0 -0
  33. {mcli_framework-7.0.1 → mcli_framework-7.0.5}/src/mcli/app/completion_cmd.py +0 -0
  34. {mcli_framework-7.0.1 → mcli_framework-7.0.5}/src/mcli/app/cron_test_cmd.py +0 -0
  35. {mcli_framework-7.0.1 → mcli_framework-7.0.5}/src/mcli/app/logs_cmd.py +0 -0
  36. {mcli_framework-7.0.1 → mcli_framework-7.0.5}/src/mcli/app/main.py +0 -0
  37. {mcli_framework-7.0.1 → mcli_framework-7.0.5}/src/mcli/app/model/model.py +0 -0
  38. {mcli_framework-7.0.1 → mcli_framework-7.0.5}/src/mcli/app/redis_cmd.py +0 -0
  39. {mcli_framework-7.0.1 → mcli_framework-7.0.5}/src/mcli/app/video/video.py +0 -0
  40. {mcli_framework-7.0.1 → mcli_framework-7.0.5}/src/mcli/app/visual_cmd.py +0 -0
  41. {mcli_framework-7.0.1 → mcli_framework-7.0.5}/src/mcli/chat/chat.py +0 -0
  42. {mcli_framework-7.0.1 → mcli_framework-7.0.5}/src/mcli/chat/command_rag.py +0 -0
  43. {mcli_framework-7.0.1 → mcli_framework-7.0.5}/src/mcli/chat/enhanced_chat.py +0 -0
  44. {mcli_framework-7.0.1 → mcli_framework-7.0.5}/src/mcli/chat/system_controller.py +0 -0
  45. {mcli_framework-7.0.1 → mcli_framework-7.0.5}/src/mcli/chat/system_integration.py +0 -0
  46. {mcli_framework-7.0.1 → mcli_framework-7.0.5}/src/mcli/cli.py +0 -0
  47. {mcli_framework-7.0.1 → mcli_framework-7.0.5}/src/mcli/config.toml +0 -0
  48. {mcli_framework-7.0.1 → mcli_framework-7.0.5}/src/mcli/lib/api/api.py +0 -0
  49. {mcli_framework-7.0.1 → mcli_framework-7.0.5}/src/mcli/lib/api/daemon_client.py +0 -0
  50. {mcli_framework-7.0.1 → mcli_framework-7.0.5}/src/mcli/lib/api/daemon_client_local.py +0 -0
  51. {mcli_framework-7.0.1 → mcli_framework-7.0.5}/src/mcli/lib/api/daemon_decorator.py +0 -0
  52. {mcli_framework-7.0.1 → mcli_framework-7.0.5}/src/mcli/lib/api/mcli_decorators.py +0 -0
  53. {mcli_framework-7.0.1 → mcli_framework-7.0.5}/src/mcli/lib/auth/auth.py +0 -0
  54. {mcli_framework-7.0.1 → mcli_framework-7.0.5}/src/mcli/lib/auth/aws_manager.py +0 -0
  55. {mcli_framework-7.0.1 → mcli_framework-7.0.5}/src/mcli/lib/auth/azure_manager.py +0 -0
  56. {mcli_framework-7.0.1 → mcli_framework-7.0.5}/src/mcli/lib/auth/credential_manager.py +0 -0
  57. {mcli_framework-7.0.1 → mcli_framework-7.0.5}/src/mcli/lib/auth/gcp_manager.py +0 -0
  58. {mcli_framework-7.0.1 → mcli_framework-7.0.5}/src/mcli/lib/auth/key_manager.py +0 -0
  59. {mcli_framework-7.0.1 → mcli_framework-7.0.5}/src/mcli/lib/auth/mcli_manager.py +0 -0
  60. {mcli_framework-7.0.1 → mcli_framework-7.0.5}/src/mcli/lib/auth/token_manager.py +0 -0
  61. {mcli_framework-7.0.1 → mcli_framework-7.0.5}/src/mcli/lib/auth/token_util.py +0 -0
  62. {mcli_framework-7.0.1 → mcli_framework-7.0.5}/src/mcli/lib/config/config.py +0 -0
  63. {mcli_framework-7.0.1 → mcli_framework-7.0.5}/src/mcli/lib/discovery/__init__.py +0 -0
  64. {mcli_framework-7.0.1 → mcli_framework-7.0.5}/src/mcli/lib/discovery/command_discovery.py +0 -0
  65. {mcli_framework-7.0.1 → mcli_framework-7.0.5}/src/mcli/lib/erd/erd.py +0 -0
  66. {mcli_framework-7.0.1 → mcli_framework-7.0.5}/src/mcli/lib/erd/generate_graph.py +0 -0
  67. {mcli_framework-7.0.1 → mcli_framework-7.0.5}/src/mcli/lib/files/files.py +0 -0
  68. {mcli_framework-7.0.1 → mcli_framework-7.0.5}/src/mcli/lib/fs/fs.py +0 -0
  69. {mcli_framework-7.0.1 → mcli_framework-7.0.5}/src/mcli/lib/lib.py +0 -0
  70. {mcli_framework-7.0.1 → mcli_framework-7.0.5}/src/mcli/lib/logger/logger.py +0 -0
  71. {mcli_framework-7.0.1 → mcli_framework-7.0.5}/src/mcli/lib/paths.py +0 -0
  72. {mcli_framework-7.0.1 → mcli_framework-7.0.5}/src/mcli/lib/performance/optimizer.py +0 -0
  73. {mcli_framework-7.0.1 → mcli_framework-7.0.5}/src/mcli/lib/performance/rust_bridge.py +0 -0
  74. {mcli_framework-7.0.1 → mcli_framework-7.0.5}/src/mcli/lib/performance/uvloop_config.py +0 -0
  75. {mcli_framework-7.0.1 → mcli_framework-7.0.5}/src/mcli/lib/pickles/pickles.py +0 -0
  76. {mcli_framework-7.0.1 → mcli_framework-7.0.5}/src/mcli/lib/search/cached_vectorizer.py +0 -0
  77. {mcli_framework-7.0.1 → mcli_framework-7.0.5}/src/mcli/lib/services/data_pipeline.py +0 -0
  78. {mcli_framework-7.0.1 → mcli_framework-7.0.5}/src/mcli/lib/services/lsh_client.py +0 -0
  79. {mcli_framework-7.0.1 → mcli_framework-7.0.5}/src/mcli/lib/services/redis_service.py +0 -0
  80. {mcli_framework-7.0.1 → mcli_framework-7.0.5}/src/mcli/lib/shell/shell.py +0 -0
  81. {mcli_framework-7.0.1 → mcli_framework-7.0.5}/src/mcli/lib/toml/toml.py +0 -0
  82. {mcli_framework-7.0.1 → mcli_framework-7.0.5}/src/mcli/lib/ui/styling.py +0 -0
  83. {mcli_framework-7.0.1 → mcli_framework-7.0.5}/src/mcli/lib/ui/visual_effects.py +0 -0
  84. {mcli_framework-7.0.1 → mcli_framework-7.0.5}/src/mcli/lib/watcher/watcher.py +0 -0
  85. {mcli_framework-7.0.1 → mcli_framework-7.0.5}/src/mcli/ml/api/app.py +0 -0
  86. {mcli_framework-7.0.1 → mcli_framework-7.0.5}/src/mcli/ml/api/middleware.py +0 -0
  87. {mcli_framework-7.0.1 → mcli_framework-7.0.5}/src/mcli/ml/api/routers/admin_router.py +0 -0
  88. {mcli_framework-7.0.1 → mcli_framework-7.0.5}/src/mcli/ml/api/routers/auth_router.py +0 -0
  89. {mcli_framework-7.0.1 → mcli_framework-7.0.5}/src/mcli/ml/api/routers/backtest_router.py +0 -0
  90. {mcli_framework-7.0.1 → mcli_framework-7.0.5}/src/mcli/ml/api/routers/data_router.py +0 -0
  91. {mcli_framework-7.0.1 → mcli_framework-7.0.5}/src/mcli/ml/api/routers/model_router.py +0 -0
  92. {mcli_framework-7.0.1 → mcli_framework-7.0.5}/src/mcli/ml/api/routers/monitoring_router.py +0 -0
  93. {mcli_framework-7.0.1 → mcli_framework-7.0.5}/src/mcli/ml/api/routers/portfolio_router.py +0 -0
  94. {mcli_framework-7.0.1 → mcli_framework-7.0.5}/src/mcli/ml/api/routers/prediction_router.py +0 -0
  95. {mcli_framework-7.0.1 → mcli_framework-7.0.5}/src/mcli/ml/api/routers/trade_router.py +0 -0
  96. {mcli_framework-7.0.1 → mcli_framework-7.0.5}/src/mcli/ml/api/routers/websocket_router.py +0 -0
  97. {mcli_framework-7.0.1 → mcli_framework-7.0.5}/src/mcli/ml/api/schemas.py +0 -0
  98. {mcli_framework-7.0.1 → mcli_framework-7.0.5}/src/mcli/ml/auth/auth_manager.py +0 -0
  99. {mcli_framework-7.0.1 → mcli_framework-7.0.5}/src/mcli/ml/auth/models.py +0 -0
  100. {mcli_framework-7.0.1 → mcli_framework-7.0.5}/src/mcli/ml/auth/permissions.py +0 -0
  101. {mcli_framework-7.0.1 → mcli_framework-7.0.5}/src/mcli/ml/backtesting/backtest_engine.py +0 -0
  102. {mcli_framework-7.0.1 → mcli_framework-7.0.5}/src/mcli/ml/backtesting/performance_metrics.py +0 -0
  103. {mcli_framework-7.0.1 → mcli_framework-7.0.5}/src/mcli/ml/cache.py +0 -0
  104. {mcli_framework-7.0.1 → mcli_framework-7.0.5}/src/mcli/ml/cli/main.py +0 -0
  105. {mcli_framework-7.0.1 → mcli_framework-7.0.5}/src/mcli/ml/config/settings.py +0 -0
  106. {mcli_framework-7.0.1 → mcli_framework-7.0.5}/src/mcli/ml/configs/dvc_config.py +0 -0
  107. {mcli_framework-7.0.1 → mcli_framework-7.0.5}/src/mcli/ml/configs/mlflow_config.py +0 -0
  108. {mcli_framework-7.0.1 → mcli_framework-7.0.5}/src/mcli/ml/configs/mlops_manager.py +0 -0
  109. {mcli_framework-7.0.1 → mcli_framework-7.0.5}/src/mcli/ml/dashboard/app.py +0 -0
  110. {mcli_framework-7.0.1 → mcli_framework-7.0.5}/src/mcli/ml/dashboard/app_integrated.py +0 -0
  111. {mcli_framework-7.0.1 → mcli_framework-7.0.5}/src/mcli/ml/dashboard/app_supabase.py +0 -0
  112. {mcli_framework-7.0.1 → mcli_framework-7.0.5}/src/mcli/ml/dashboard/app_training.py +0 -0
  113. {mcli_framework-7.0.1 → mcli_framework-7.0.5}/src/mcli/ml/dashboard/cli.py +0 -0
  114. {mcli_framework-7.0.1 → mcli_framework-7.0.5}/src/mcli/ml/data_ingestion/api_connectors.py +0 -0
  115. {mcli_framework-7.0.1 → mcli_framework-7.0.5}/src/mcli/ml/data_ingestion/data_pipeline.py +0 -0
  116. {mcli_framework-7.0.1 → mcli_framework-7.0.5}/src/mcli/ml/data_ingestion/stream_processor.py +0 -0
  117. {mcli_framework-7.0.1 → mcli_framework-7.0.5}/src/mcli/ml/database/migrations/env.py +0 -0
  118. {mcli_framework-7.0.1 → mcli_framework-7.0.5}/src/mcli/ml/database/models.py +0 -0
  119. {mcli_framework-7.0.1 → mcli_framework-7.0.5}/src/mcli/ml/database/session.py +0 -0
  120. {mcli_framework-7.0.1 → mcli_framework-7.0.5}/src/mcli/ml/experimentation/ab_testing.py +0 -0
  121. {mcli_framework-7.0.1 → mcli_framework-7.0.5}/src/mcli/ml/features/ensemble_features.py +0 -0
  122. {mcli_framework-7.0.1 → mcli_framework-7.0.5}/src/mcli/ml/features/political_features.py +0 -0
  123. {mcli_framework-7.0.1 → mcli_framework-7.0.5}/src/mcli/ml/features/recommendation_engine.py +0 -0
  124. {mcli_framework-7.0.1 → mcli_framework-7.0.5}/src/mcli/ml/features/stock_features.py +0 -0
  125. {mcli_framework-7.0.1 → mcli_framework-7.0.5}/src/mcli/ml/features/test_feature_engineering.py +0 -0
  126. {mcli_framework-7.0.1 → mcli_framework-7.0.5}/src/mcli/ml/logging.py +0 -0
  127. {mcli_framework-7.0.1 → mcli_framework-7.0.5}/src/mcli/ml/mlops/data_versioning.py +0 -0
  128. {mcli_framework-7.0.1 → mcli_framework-7.0.5}/src/mcli/ml/mlops/experiment_tracker.py +0 -0
  129. {mcli_framework-7.0.1 → mcli_framework-7.0.5}/src/mcli/ml/mlops/model_serving.py +0 -0
  130. {mcli_framework-7.0.1 → mcli_framework-7.0.5}/src/mcli/ml/mlops/pipeline_orchestrator.py +0 -0
  131. {mcli_framework-7.0.1 → mcli_framework-7.0.5}/src/mcli/ml/models/base_models.py +0 -0
  132. {mcli_framework-7.0.1 → mcli_framework-7.0.5}/src/mcli/ml/models/ensemble_models.py +0 -0
  133. {mcli_framework-7.0.1 → mcli_framework-7.0.5}/src/mcli/ml/models/recommendation_models.py +0 -0
  134. {mcli_framework-7.0.1 → mcli_framework-7.0.5}/src/mcli/ml/models/test_models.py +0 -0
  135. {mcli_framework-7.0.1 → mcli_framework-7.0.5}/src/mcli/ml/monitoring/drift_detection.py +0 -0
  136. {mcli_framework-7.0.1 → mcli_framework-7.0.5}/src/mcli/ml/monitoring/metrics.py +0 -0
  137. {mcli_framework-7.0.1 → mcli_framework-7.0.5}/src/mcli/ml/optimization/portfolio_optimizer.py +0 -0
  138. {mcli_framework-7.0.1 → mcli_framework-7.0.5}/src/mcli/ml/preprocessing/data_cleaners.py +0 -0
  139. {mcli_framework-7.0.1 → mcli_framework-7.0.5}/src/mcli/ml/preprocessing/feature_extractors.py +0 -0
  140. {mcli_framework-7.0.1 → mcli_framework-7.0.5}/src/mcli/ml/preprocessing/ml_pipeline.py +0 -0
  141. {mcli_framework-7.0.1 → mcli_framework-7.0.5}/src/mcli/ml/preprocessing/politician_trading_preprocessor.py +0 -0
  142. {mcli_framework-7.0.1 → mcli_framework-7.0.5}/src/mcli/ml/preprocessing/test_preprocessing.py +0 -0
  143. {mcli_framework-7.0.1 → mcli_framework-7.0.5}/src/mcli/ml/scripts/populate_sample_data.py +0 -0
  144. {mcli_framework-7.0.1 → mcli_framework-7.0.5}/src/mcli/ml/tasks.py +0 -0
  145. {mcli_framework-7.0.1 → mcli_framework-7.0.5}/src/mcli/ml/tests/test_integration.py +0 -0
  146. {mcli_framework-7.0.1 → mcli_framework-7.0.5}/src/mcli/ml/tests/test_training_dashboard.py +0 -0
  147. {mcli_framework-7.0.1 → mcli_framework-7.0.5}/src/mcli/public/oi/oi.py +0 -0
  148. {mcli_framework-7.0.1 → mcli_framework-7.0.5}/src/mcli/public/public.py +0 -0
  149. {mcli_framework-7.0.1 → mcli_framework-7.0.5}/src/mcli/workflow/daemon/api_daemon.py +0 -0
  150. {mcli_framework-7.0.1 → mcli_framework-7.0.5}/src/mcli/workflow/daemon/async_command_database.py +0 -0
  151. {mcli_framework-7.0.1 → mcli_framework-7.0.5}/src/mcli/workflow/daemon/async_process_manager.py +0 -0
  152. {mcli_framework-7.0.1 → mcli_framework-7.0.5}/src/mcli/workflow/daemon/client.py +0 -0
  153. {mcli_framework-7.0.1 → mcli_framework-7.0.5}/src/mcli/workflow/daemon/commands.py +0 -0
  154. {mcli_framework-7.0.1 → mcli_framework-7.0.5}/src/mcli/workflow/daemon/daemon.py +0 -0
  155. {mcli_framework-7.0.1 → mcli_framework-7.0.5}/src/mcli/workflow/daemon/daemon_api.py +0 -0
  156. {mcli_framework-7.0.1 → mcli_framework-7.0.5}/src/mcli/workflow/daemon/enhanced_daemon.py +0 -0
  157. {mcli_framework-7.0.1 → mcli_framework-7.0.5}/src/mcli/workflow/daemon/process_cli.py +0 -0
  158. {mcli_framework-7.0.1 → mcli_framework-7.0.5}/src/mcli/workflow/daemon/process_manager.py +0 -0
  159. {mcli_framework-7.0.1 → mcli_framework-7.0.5}/src/mcli/workflow/daemon/test_daemon.py +0 -0
  160. {mcli_framework-7.0.1 → mcli_framework-7.0.5}/src/mcli/workflow/dashboard/dashboard_cmd.py +0 -0
  161. {mcli_framework-7.0.1 → mcli_framework-7.0.5}/src/mcli/workflow/docker/docker.py +0 -0
  162. {mcli_framework-7.0.1 → mcli_framework-7.0.5}/src/mcli/workflow/file/file.py +0 -0
  163. {mcli_framework-7.0.1 → mcli_framework-7.0.5}/src/mcli/workflow/gcloud/config.toml +0 -0
  164. {mcli_framework-7.0.1 → mcli_framework-7.0.5}/src/mcli/workflow/gcloud/gcloud.py +0 -0
  165. {mcli_framework-7.0.1 → mcli_framework-7.0.5}/src/mcli/workflow/git_commit/ai_service.py +0 -0
  166. {mcli_framework-7.0.1 → mcli_framework-7.0.5}/src/mcli/workflow/git_commit/commands.py +0 -0
  167. {mcli_framework-7.0.1 → mcli_framework-7.0.5}/src/mcli/workflow/lsh_integration.py +0 -0
  168. {mcli_framework-7.0.1 → mcli_framework-7.0.5}/src/mcli/workflow/model_service/client.py +0 -0
  169. {mcli_framework-7.0.1 → mcli_framework-7.0.5}/src/mcli/workflow/model_service/download_and_run_efficient_models.py +0 -0
  170. {mcli_framework-7.0.1 → mcli_framework-7.0.5}/src/mcli/workflow/model_service/lightweight_embedder.py +0 -0
  171. {mcli_framework-7.0.1 → mcli_framework-7.0.5}/src/mcli/workflow/model_service/lightweight_test.py +0 -0
  172. {mcli_framework-7.0.1 → mcli_framework-7.0.5}/src/mcli/workflow/model_service/model_service.py +0 -0
  173. {mcli_framework-7.0.1 → mcli_framework-7.0.5}/src/mcli/workflow/model_service/ollama_efficient_runner.py +0 -0
  174. {mcli_framework-7.0.1 → mcli_framework-7.0.5}/src/mcli/workflow/model_service/pdf_processor.py +0 -0
  175. {mcli_framework-7.0.1 → mcli_framework-7.0.5}/src/mcli/workflow/model_service/test_efficient_runner.py +0 -0
  176. {mcli_framework-7.0.1 → mcli_framework-7.0.5}/src/mcli/workflow/model_service/test_example.py +0 -0
  177. {mcli_framework-7.0.1 → mcli_framework-7.0.5}/src/mcli/workflow/model_service/test_integration.py +0 -0
  178. {mcli_framework-7.0.1 → mcli_framework-7.0.5}/src/mcli/workflow/model_service/test_new_features.py +0 -0
  179. {mcli_framework-7.0.1 → mcli_framework-7.0.5}/src/mcli/workflow/openai/openai.py +0 -0
  180. {mcli_framework-7.0.1 → mcli_framework-7.0.5}/src/mcli/workflow/politician_trading/commands.py +0 -0
  181. {mcli_framework-7.0.1 → mcli_framework-7.0.5}/src/mcli/workflow/politician_trading/config.py +0 -0
  182. {mcli_framework-7.0.1 → mcli_framework-7.0.5}/src/mcli/workflow/politician_trading/connectivity.py +0 -0
  183. {mcli_framework-7.0.1 → mcli_framework-7.0.5}/src/mcli/workflow/politician_trading/data_sources.py +0 -0
  184. {mcli_framework-7.0.1 → mcli_framework-7.0.5}/src/mcli/workflow/politician_trading/database.py +0 -0
  185. {mcli_framework-7.0.1 → mcli_framework-7.0.5}/src/mcli/workflow/politician_trading/demo.py +0 -0
  186. {mcli_framework-7.0.1 → mcli_framework-7.0.5}/src/mcli/workflow/politician_trading/models.py +0 -0
  187. {mcli_framework-7.0.1 → mcli_framework-7.0.5}/src/mcli/workflow/politician_trading/monitoring.py +0 -0
  188. {mcli_framework-7.0.1 → mcli_framework-7.0.5}/src/mcli/workflow/politician_trading/scrapers.py +0 -0
  189. {mcli_framework-7.0.1 → mcli_framework-7.0.5}/src/mcli/workflow/politician_trading/scrapers_california.py +0 -0
  190. {mcli_framework-7.0.1 → mcli_framework-7.0.5}/src/mcli/workflow/politician_trading/scrapers_eu.py +0 -0
  191. {mcli_framework-7.0.1 → mcli_framework-7.0.5}/src/mcli/workflow/politician_trading/scrapers_uk.py +0 -0
  192. {mcli_framework-7.0.1 → mcli_framework-7.0.5}/src/mcli/workflow/politician_trading/scrapers_us_states.py +0 -0
  193. {mcli_framework-7.0.1 → mcli_framework-7.0.5}/src/mcli/workflow/politician_trading/supabase_functions.py +0 -0
  194. {mcli_framework-7.0.1 → mcli_framework-7.0.5}/src/mcli/workflow/politician_trading/workflow.py +0 -0
  195. {mcli_framework-7.0.1 → mcli_framework-7.0.5}/src/mcli/workflow/registry/registry.py +0 -0
  196. {mcli_framework-7.0.1 → mcli_framework-7.0.5}/src/mcli/workflow/repo/repo.py +0 -0
  197. {mcli_framework-7.0.1 → mcli_framework-7.0.5}/src/mcli/workflow/scheduler/commands.py +0 -0
  198. {mcli_framework-7.0.1 → mcli_framework-7.0.5}/src/mcli/workflow/scheduler/cron_parser.py +0 -0
  199. {mcli_framework-7.0.1 → mcli_framework-7.0.5}/src/mcli/workflow/scheduler/job.py +0 -0
  200. {mcli_framework-7.0.1 → mcli_framework-7.0.5}/src/mcli/workflow/scheduler/monitor.py +0 -0
  201. {mcli_framework-7.0.1 → mcli_framework-7.0.5}/src/mcli/workflow/scheduler/persistence.py +0 -0
  202. {mcli_framework-7.0.1 → mcli_framework-7.0.5}/src/mcli/workflow/scheduler/scheduler.py +0 -0
  203. {mcli_framework-7.0.1 → mcli_framework-7.0.5}/src/mcli/workflow/sync/sync_cmd.py +0 -0
  204. {mcli_framework-7.0.1 → mcli_framework-7.0.5}/src/mcli/workflow/sync/test_cmd.py +0 -0
  205. {mcli_framework-7.0.1 → mcli_framework-7.0.5}/src/mcli/workflow/videos/videos.py +0 -0
  206. {mcli_framework-7.0.1 → mcli_framework-7.0.5}/src/mcli/workflow/wakatime/wakatime.py +0 -0
  207. {mcli_framework-7.0.1 → mcli_framework-7.0.5}/src/mcli/workflow/workflow.py +0 -0
  208. {mcli_framework-7.0.1 → mcli_framework-7.0.5}/src/mcli_framework.egg-info/dependency_links.txt +0 -0
  209. {mcli_framework-7.0.1 → mcli_framework-7.0.5}/src/mcli_framework.egg-info/entry_points.txt +0 -0
  210. {mcli_framework-7.0.1 → mcli_framework-7.0.5}/src/mcli_framework.egg-info/requires.txt +0 -0
  211. {mcli_framework-7.0.1 → mcli_framework-7.0.5}/src/mcli_framework.egg-info/top_level.txt +0 -0
  212. {mcli_framework-7.0.1 → mcli_framework-7.0.5}/tests/conftest.py +0 -0
  213. {mcli_framework-7.0.1 → mcli_framework-7.0.5}/tests/demo_generate_graph.py +0 -0
  214. {mcli_framework-7.0.1 → mcli_framework-7.0.5}/tests/demo_hierarchical_transform.py +0 -0
  215. {mcli_framework-7.0.1 → mcli_framework-7.0.5}/tests/end_to_end_integration_test.py +0 -0
  216. {mcli_framework-7.0.1 → mcli_framework-7.0.5}/tests/pytest.ini +0 -0
  217. {mcli_framework-7.0.1 → mcli_framework-7.0.5}/tests/run_tests.py +0 -0
  218. {mcli_framework-7.0.1 → mcli_framework-7.0.5}/tests/simple_integration_test.py +0 -0
  219. {mcli_framework-7.0.1 → mcli_framework-7.0.5}/tests/test_agent_functionality.py +0 -0
  220. {mcli_framework-7.0.1 → mcli_framework-7.0.5}/tests/test_all_cli.py +0 -0
  221. {mcli_framework-7.0.1 → mcli_framework-7.0.5}/tests/test_auth.py +0 -0
  222. {mcli_framework-7.0.1 → mcli_framework-7.0.5}/tests/test_california_scraper.py +0 -0
  223. {mcli_framework-7.0.1 → mcli_framework-7.0.5}/tests/test_chat_client.py +0 -0
  224. {mcli_framework-7.0.1 → mcli_framework-7.0.5}/tests/test_chat_cmd.py +0 -0
  225. {mcli_framework-7.0.1 → mcli_framework-7.0.5}/tests/test_chat_system_control.py +0 -0
  226. {mcli_framework-7.0.1 → mcli_framework-7.0.5}/tests/test_command_discovery.py +0 -0
  227. {mcli_framework-7.0.1 → mcli_framework-7.0.5}/tests/test_congress_scraper.py +0 -0
  228. {mcli_framework-7.0.1 → mcli_framework-7.0.5}/tests/test_daemon.py +0 -0
  229. {mcli_framework-7.0.1 → mcli_framework-7.0.5}/tests/test_daemon_client.py +0 -0
  230. {mcli_framework-7.0.1 → mcli_framework-7.0.5}/tests/test_data_pipeline.py +0 -0
  231. {mcli_framework-7.0.1 → mcli_framework-7.0.5}/tests/test_enhanced_chat.py +0 -0
  232. {mcli_framework-7.0.1 → mcli_framework-7.0.5}/tests/test_erd.py +0 -0
  233. {mcli_framework-7.0.1 → mcli_framework-7.0.5}/tests/test_erd_import.py +0 -0
  234. {mcli_framework-7.0.1 → mcli_framework-7.0.5}/tests/test_file.py +0 -0
  235. {mcli_framework-7.0.1 → mcli_framework-7.0.5}/tests/test_fix.py +0 -0
  236. {mcli_framework-7.0.1 → mcli_framework-7.0.5}/tests/test_fixed_issues.py +0 -0
  237. {mcli_framework-7.0.1 → mcli_framework-7.0.5}/tests/test_gcloud.py +0 -0
  238. {mcli_framework-7.0.1 → mcli_framework-7.0.5}/tests/test_generate_graph.py +0 -0
  239. {mcli_framework-7.0.1 → mcli_framework-7.0.5}/tests/test_generic_erd.py +0 -0
  240. {mcli_framework-7.0.1 → mcli_framework-7.0.5}/tests/test_harness.py +0 -0
  241. {mcli_framework-7.0.1 → mcli_framework-7.0.5}/tests/test_lib.py +0 -0
  242. {mcli_framework-7.0.1 → mcli_framework-7.0.5}/tests/test_lsh_client.py +0 -0
  243. {mcli_framework-7.0.1 → mcli_framework-7.0.5}/tests/test_lsh_integration.py +0 -0
  244. {mcli_framework-7.0.1 → mcli_framework-7.0.5}/tests/test_main_app.py +0 -0
  245. {mcli_framework-7.0.1 → mcli_framework-7.0.5}/tests/test_main_app_functions.py +0 -0
  246. {mcli_framework-7.0.1 → mcli_framework-7.0.5}/tests/test_ml_auth.py +0 -0
  247. {mcli_framework-7.0.1 → mcli_framework-7.0.5}/tests/test_ml_models.py +0 -0
  248. {mcli_framework-7.0.1 → mcli_framework-7.0.5}/tests/test_ml_pipeline.py +0 -0
  249. {mcli_framework-7.0.1 → mcli_framework-7.0.5}/tests/test_oi.py +0 -0
  250. {mcli_framework-7.0.1 → mcli_framework-7.0.5}/tests/test_optional_dependencies.py +0 -0
  251. {mcli_framework-7.0.1 → mcli_framework-7.0.5}/tests/test_politician_trading_integration.py +0 -0
  252. {mcli_framework-7.0.1 → mcli_framework-7.0.5}/tests/test_preprocessing_simple.py +0 -0
  253. {mcli_framework-7.0.1 → mcli_framework-7.0.5}/tests/test_registry.py +0 -0
  254. {mcli_framework-7.0.1 → mcli_framework-7.0.5}/tests/test_repo.py +0 -0
  255. {mcli_framework-7.0.1 → mcli_framework-7.0.5}/tests/test_rich.py +0 -0
  256. {mcli_framework-7.0.1 → mcli_framework-7.0.5}/tests/test_uk_scraper.py +0 -0
  257. {mcli_framework-7.0.1 → mcli_framework-7.0.5}/tests/test_us_states_scraper.py +0 -0
  258. {mcli_framework-7.0.1 → mcli_framework-7.0.5}/tests/test_utility_functions.py +0 -0
  259. {mcli_framework-7.0.1 → mcli_framework-7.0.5}/tests/test_utility_functions_simple.py +0 -0
  260. {mcli_framework-7.0.1 → mcli_framework-7.0.5}/tests/test_uv_compatibility.py +0 -0
  261. {mcli_framework-7.0.1 → mcli_framework-7.0.5}/tests/test_videos.py +0 -0
  262. {mcli_framework-7.0.1 → mcli_framework-7.0.5}/tests/test_wakatime.py +0 -0
  263. {mcli_framework-7.0.1 → mcli_framework-7.0.5}/tests/test_webapp.py +0 -0
  264. {mcli_framework-7.0.1 → mcli_framework-7.0.5}/tests/test_webapp_comprehensive.py +0 -0
  265. {mcli_framework-7.0.1 → mcli_framework-7.0.5}/tests/test_workflow.py +0 -0
  266. {mcli_framework-7.0.1 → mcli_framework-7.0.5}/tests/test_workflow_integration.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mcli-framework
3
- Version: 7.0.1
3
+ Version: 7.0.5
4
4
  Summary: 🚀 High-performance CLI framework with Rust extensions, AI chat, and stunning visuals
5
5
  Author-email: Luis Fernandez de la Vara <luis@lefv.io>
6
6
  Maintainer-email: Luis Fernandez de la Vara <luis@lefv.io>
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "mcli-framework"
3
- version = "7.0.1"
3
+ version = "7.0.5"
4
4
  description = "🚀 High-performance CLI framework with Rust extensions, AI chat, and stunning visuals"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.9"
@@ -137,8 +137,12 @@ LAZY_COMMAND_COMPLETIONS = {
137
137
  "subcommands": [
138
138
  "download",
139
139
  "list",
140
- "remove",
141
- "info"
140
+ "start",
141
+ "stop",
142
+ "pull",
143
+ "delete",
144
+ "recommend",
145
+ "status"
142
146
  ]
143
147
  },
144
148
  "cron-test": {
@@ -7,6 +7,7 @@ from pathlib import Path
7
7
  from typing import Optional
8
8
 
9
9
  import click
10
+ import psutil
10
11
 
11
12
  from mcli.lib.logger.logger import get_logger
12
13
  from mcli.workflow.model_service.lightweight_model_server import (
@@ -223,5 +224,106 @@ def status(port: int):
223
224
  click.echo(f"❌ Error checking server: {e}")
224
225
 
225
226
 
227
+ @model.command()
228
+ @click.option("--port", "-p", default=8080, help="Port where server is running")
229
+ def stop(port: int):
230
+ """Stop the lightweight model server."""
231
+ import requests
232
+ import psutil
233
+
234
+ try:
235
+ # First check if server is running
236
+ try:
237
+ response = requests.get(f"http://localhost:{port}/health", timeout=2)
238
+ if response.status_code != 200:
239
+ click.echo(f"❌ No server running on port {port}")
240
+ return
241
+ except requests.exceptions.ConnectionError:
242
+ click.echo(f"❌ No server running on port {port}")
243
+ return
244
+
245
+ # Find and kill the process using the port
246
+ for proc in psutil.process_iter(["pid", "name", "connections"]):
247
+ try:
248
+ connections = proc.info.get("connections")
249
+ if connections:
250
+ for conn in connections:
251
+ if hasattr(conn, "laddr") and conn.laddr.port == port:
252
+ click.echo(f"🛑 Stopping server (PID: {proc.pid})...")
253
+ proc.terminate()
254
+ proc.wait(timeout=5)
255
+ click.echo("✅ Server stopped successfully")
256
+ return
257
+ except (psutil.NoSuchProcess, psutil.AccessDenied, psutil.TimeoutExpired):
258
+ continue
259
+
260
+ click.echo("⚠️ Could not find server process")
261
+
262
+ except Exception as e:
263
+ click.echo(f"❌ Error stopping server: {e}")
264
+
265
+
266
+ @model.command()
267
+ @click.argument("model_name")
268
+ def pull(model_name: str):
269
+ """Pull (download) a specific lightweight model."""
270
+ if model_name not in LIGHTWEIGHT_MODELS:
271
+ click.echo(f"❌ Model '{model_name}' not found.")
272
+ click.echo("Available models:")
273
+ for key in LIGHTWEIGHT_MODELS.keys():
274
+ click.echo(f" • {key}")
275
+ sys.exit(1)
276
+
277
+ server = LightweightModelServer()
278
+
279
+ click.echo(f"Pulling model: {model_name}")
280
+ success = server.download_and_load_model(model_name)
281
+
282
+ if success:
283
+ click.echo(f"✅ Successfully pulled {model_name}")
284
+ else:
285
+ click.echo(f"❌ Failed to pull {model_name}")
286
+ sys.exit(1)
287
+
288
+
289
+ @model.command()
290
+ @click.argument("model_name")
291
+ @click.option("--force", "-f", is_flag=True, help="Force deletion without confirmation")
292
+ def delete(model_name: str, force: bool):
293
+ """Delete a downloaded lightweight model."""
294
+ server = LightweightModelServer()
295
+ downloaded_models = server.downloader.get_downloaded_models()
296
+
297
+ if model_name not in downloaded_models:
298
+ click.echo(f"❌ Model '{model_name}' not found.")
299
+ click.echo("Downloaded models:")
300
+ if downloaded_models:
301
+ for model in downloaded_models:
302
+ click.echo(f" • {model}")
303
+ else:
304
+ click.echo(" (none)")
305
+ sys.exit(1)
306
+
307
+ # Confirm deletion unless --force is used
308
+ if not force:
309
+ model_info = LIGHTWEIGHT_MODELS.get(model_name, {})
310
+ name = model_info.get("name", model_name)
311
+ size = model_info.get("size_mb", "unknown")
312
+ click.echo(f"⚠️ About to delete:")
313
+ click.echo(f" Model: {name}")
314
+ click.echo(f" Size: {size} MB")
315
+ if not click.confirm("Are you sure you want to delete this model?"):
316
+ click.echo("❌ Deletion cancelled")
317
+ return
318
+
319
+ success = server.delete_model(model_name)
320
+
321
+ if success:
322
+ click.echo(f"✅ Successfully deleted {model_name}")
323
+ else:
324
+ click.echo(f"❌ Failed to delete {model_name}")
325
+ sys.exit(1)
326
+
327
+
226
328
  if __name__ == "__main__":
227
329
  model()
@@ -774,18 +774,15 @@ def logs(type: str, lines: int, follow: bool, date: str, grep: str, level: str):
774
774
  from datetime import datetime
775
775
  from pathlib import Path
776
776
 
777
- # Find the logs directory
778
- current_file = Path(__file__)
779
- # Go up 5 levels: file -> self -> mcli -> src -> repo_root
780
- project_root = current_file.parents[4]
781
- logs_dir = project_root / "logs"
777
+ # Import get_logs_dir to get the correct logs directory
778
+ from mcli.lib.paths import get_logs_dir
782
779
 
783
- # Alternative: try current working directory first
784
- if not logs_dir.exists():
785
- logs_dir = Path.cwd() / "logs"
780
+ # Get the logs directory (creates it if it doesn't exist)
781
+ logs_dir = get_logs_dir()
786
782
 
787
783
  if not logs_dir.exists():
788
784
  click.echo("❌ Logs directory not found", err=True)
785
+ click.echo(f"Expected location: {logs_dir}", err=True)
789
786
  return
790
787
 
791
788
  # Determine which log files to read
@@ -1129,11 +1126,53 @@ def dashboard(refresh: float, once: bool):
1129
1126
  console.print(f"[red]Error launching dashboard: {e}[/red]")
1130
1127
 
1131
1128
 
1129
+ def check_ci_status(version: str) -> tuple[bool, Optional[str]]:
1130
+ """
1131
+ Check GitHub Actions CI status for the main branch.
1132
+ Returns (passing, url) tuple.
1133
+ """
1134
+ try:
1135
+ import requests
1136
+
1137
+ response = requests.get(
1138
+ "https://api.github.com/repos/gwicho38/mcli/actions/runs",
1139
+ params={"per_page": 5},
1140
+ headers={
1141
+ "Accept": "application/vnd.github.v3+json",
1142
+ "User-Agent": "mcli-cli"
1143
+ },
1144
+ timeout=10
1145
+ )
1146
+
1147
+ if response.status_code == 200:
1148
+ data = response.json()
1149
+ runs = data.get("workflow_runs", [])
1150
+
1151
+ # Find the most recent completed run for main branch
1152
+ main_runs = [
1153
+ run for run in runs
1154
+ if run.get("head_branch") == "main" and run.get("status") == "completed"
1155
+ ]
1156
+
1157
+ if main_runs:
1158
+ latest_run = main_runs[0]
1159
+ passing = latest_run.get("conclusion") == "success"
1160
+ url = latest_run.get("html_url")
1161
+ return (passing, url)
1162
+
1163
+ # If we can't check CI, don't block the update
1164
+ return (True, None)
1165
+ except Exception:
1166
+ # On error, don't block the update
1167
+ return (True, None)
1168
+
1169
+
1132
1170
  @self_app.command()
1133
1171
  @click.option("--check", is_flag=True, help="Only check for updates, don't install")
1134
1172
  @click.option("--pre", is_flag=True, help="Include pre-release versions")
1135
1173
  @click.option("--yes", "-y", is_flag=True, help="Skip confirmation prompt")
1136
- def update(check: bool, pre: bool, yes: bool):
1174
+ @click.option("--skip-ci-check", is_flag=True, help="Skip CI status check and install anyway")
1175
+ def update(check: bool, pre: bool, yes: bool, skip_ci_check: bool):
1137
1176
  """🔄 Check for and install mcli updates from PyPI"""
1138
1177
  import subprocess
1139
1178
  import sys
@@ -1215,6 +1254,21 @@ def update(check: bool, pre: bool, yes: bool):
1215
1254
  console.print("[yellow]Update cancelled[/yellow]")
1216
1255
  return
1217
1256
 
1257
+ # Check CI status before installing (unless skipped)
1258
+ if not skip_ci_check:
1259
+ console.print("[cyan]🔍 Checking CI status...[/cyan]")
1260
+ ci_passing, ci_url = check_ci_status(latest_version)
1261
+
1262
+ if not ci_passing:
1263
+ console.print("[red]✗ CI build is failing for the latest version[/red]")
1264
+ if ci_url:
1265
+ console.print(f"[yellow] View CI status: {ci_url}[/yellow]")
1266
+ console.print("[yellow]⚠️ Update blocked to prevent installing a broken version[/yellow]")
1267
+ console.print("[dim] Use --skip-ci-check to install anyway (not recommended)[/dim]")
1268
+ return
1269
+ else:
1270
+ console.print("[green]✓ CI build is passing[/green]")
1271
+
1218
1272
  # Install update
1219
1273
  console.print(f"[cyan]📦 Installing mcli {latest_version}...[/cyan]")
1220
1274
 
@@ -566,6 +566,43 @@ class LightweightModelServer:
566
566
  else:
567
567
  return "distilbert-base-uncased" # Standard small model
568
568
 
569
+ def stop_server(self) -> bool:
570
+ """Stop the lightweight server"""
571
+ if not self.running:
572
+ print("⚠️ Server is not running")
573
+ return False
574
+
575
+ try:
576
+ self.running = False
577
+ print("🛑 Server stopped")
578
+ return True
579
+ except Exception as e:
580
+ print(f"❌ Error stopping server: {e}")
581
+ return False
582
+
583
+ def delete_model(self, model_key: str) -> bool:
584
+ """Delete a downloaded model"""
585
+ try:
586
+ model_dir = self.models_dir / model_key
587
+
588
+ if not model_dir.exists():
589
+ print(f"⚠️ Model '{model_key}' not found")
590
+ return False
591
+
592
+ # Remove from loaded models if present
593
+ if model_key in self.loaded_models:
594
+ del self.loaded_models[model_key]
595
+ print(f"✅ Unloaded model: {model_key}")
596
+
597
+ # Delete the model directory
598
+ shutil.rmtree(model_dir)
599
+ print(f"✅ Deleted model: {model_key}")
600
+ return True
601
+
602
+ except Exception as e:
603
+ print(f"❌ Error deleting model {model_key}: {e}")
604
+ return False
605
+
569
606
 
570
607
  def create_simple_client():
571
608
  """Create a simple client script for testing"""
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mcli-framework
3
- Version: 7.0.1
3
+ Version: 7.0.5
4
4
  Summary: 🚀 High-performance CLI framework with Rust extensions, AI chat, and stunning visuals
5
5
  Author-email: Luis Fernandez de la Vara <luis@lefv.io>
6
6
  Maintainer-email: Luis Fernandez de la Vara <luis@lefv.io>
@@ -243,6 +243,7 @@ tests/test_main_app_functions.py
243
243
  tests/test_ml_auth.py
244
244
  tests/test_ml_models.py
245
245
  tests/test_ml_pipeline.py
246
+ tests/test_model_cmd.py
246
247
  tests/test_oi.py
247
248
  tests/test_optional_dependencies.py
248
249
  tests/test_politician_trading_integration.py
@@ -0,0 +1,362 @@
1
+ """
2
+ Unit tests for mcli.app.model_cmd module
3
+ """
4
+
5
+ import pytest
6
+ from unittest.mock import Mock, patch, MagicMock
7
+ from click.testing import CliRunner
8
+
9
+ from mcli.app.model_cmd import model, list, download, start, stop, pull, delete, recommend, status
10
+
11
+
12
+ class TestModelCommands:
13
+ """Test suite for model command functionality"""
14
+
15
+ def setup_method(self):
16
+ """Setup test environment"""
17
+ self.runner = CliRunner()
18
+
19
+ def test_model_group_exists(self):
20
+ """Test that model command group is properly defined"""
21
+ assert model is not None
22
+ assert hasattr(model, 'commands')
23
+ assert model.name == 'model'
24
+
25
+ def test_list_command_exists(self):
26
+ """Test that list command is properly defined"""
27
+ assert list is not None
28
+ assert list.name == 'list'
29
+
30
+ @patch('mcli.app.model_cmd.LightweightModelServer')
31
+ def test_list_command_basic(self, mock_server_class):
32
+ """Test list command basic functionality"""
33
+ mock_server = Mock()
34
+ mock_server_class.return_value = mock_server
35
+ mock_server.downloader.get_downloaded_models.return_value = []
36
+ mock_server.get_system_info.return_value = {
37
+ 'cpu_count': 4,
38
+ 'memory_gb': 8.0,
39
+ 'disk_free_gb': 100.0
40
+ }
41
+ mock_server.recommend_model.return_value = "distilbert-base-uncased"
42
+
43
+ result = self.runner.invoke(model, ['list'])
44
+
45
+ assert result.exit_code == 0
46
+ assert 'Available Lightweight Models' in result.output
47
+
48
+ @patch('mcli.app.model_cmd.LightweightModelServer')
49
+ def test_download_command_success(self, mock_server_class):
50
+ """Test download command with successful download"""
51
+ mock_server = Mock()
52
+ mock_server_class.return_value = mock_server
53
+ mock_server.download_and_load_model.return_value = True
54
+
55
+ result = self.runner.invoke(model, ['download', 'distilbert-base-uncased'])
56
+
57
+ assert result.exit_code == 0
58
+ assert 'Successfully downloaded' in result.output
59
+ mock_server.download_and_load_model.assert_called_once_with('distilbert-base-uncased')
60
+
61
+ @patch('mcli.app.model_cmd.LightweightModelServer')
62
+ def test_download_command_failure(self, mock_server_class):
63
+ """Test download command with failed download"""
64
+ mock_server = Mock()
65
+ mock_server_class.return_value = mock_server
66
+ mock_server.download_and_load_model.return_value = False
67
+
68
+ result = self.runner.invoke(model, ['download', 'distilbert-base-uncased'])
69
+
70
+ assert result.exit_code == 1
71
+ assert 'Failed to download' in result.output
72
+
73
+ def test_download_command_invalid_model(self):
74
+ """Test download command with invalid model name"""
75
+ result = self.runner.invoke(model, ['download', 'invalid-model-name'])
76
+
77
+ assert result.exit_code == 1
78
+ assert 'not found' in result.output
79
+
80
+ @patch('mcli.app.model_cmd.LightweightModelServer')
81
+ def test_pull_command_success(self, mock_server_class):
82
+ """Test pull command with successful pull"""
83
+ mock_server = Mock()
84
+ mock_server_class.return_value = mock_server
85
+ mock_server.download_and_load_model.return_value = True
86
+
87
+ result = self.runner.invoke(model, ['pull', 'distilbert-base-uncased'])
88
+
89
+ assert result.exit_code == 0
90
+ assert 'Successfully pulled' in result.output
91
+ mock_server.download_and_load_model.assert_called_once_with('distilbert-base-uncased')
92
+
93
+ @patch('mcli.app.model_cmd.LightweightModelServer')
94
+ def test_pull_command_failure(self, mock_server_class):
95
+ """Test pull command with failed pull"""
96
+ mock_server = Mock()
97
+ mock_server_class.return_value = mock_server
98
+ mock_server.download_and_load_model.return_value = False
99
+
100
+ result = self.runner.invoke(model, ['pull', 'distilbert-base-uncased'])
101
+
102
+ assert result.exit_code == 1
103
+ assert 'Failed to pull' in result.output
104
+
105
+ def test_pull_command_invalid_model(self):
106
+ """Test pull command with invalid model name"""
107
+ result = self.runner.invoke(model, ['pull', 'invalid-model-name'])
108
+
109
+ assert result.exit_code == 1
110
+ assert 'not found' in result.output
111
+
112
+ @patch('mcli.app.model_cmd.LightweightModelServer')
113
+ def test_delete_command_success_with_force(self, mock_server_class):
114
+ """Test delete command with successful deletion using force flag"""
115
+ mock_server = Mock()
116
+ mock_server_class.return_value = mock_server
117
+ mock_server.downloader.get_downloaded_models.return_value = ['distilbert-base-uncased']
118
+ mock_server.delete_model.return_value = True
119
+
120
+ result = self.runner.invoke(model, ['delete', 'distilbert-base-uncased', '--force'])
121
+
122
+ assert result.exit_code == 0
123
+ assert 'Successfully deleted' in result.output
124
+ mock_server.delete_model.assert_called_once_with('distilbert-base-uncased')
125
+
126
+ @patch('mcli.app.model_cmd.LightweightModelServer')
127
+ def test_delete_command_failure(self, mock_server_class):
128
+ """Test delete command with failed deletion"""
129
+ mock_server = Mock()
130
+ mock_server_class.return_value = mock_server
131
+ mock_server.downloader.get_downloaded_models.return_value = ['distilbert-base-uncased']
132
+ mock_server.delete_model.return_value = False
133
+
134
+ result = self.runner.invoke(model, ['delete', 'distilbert-base-uncased', '--force'])
135
+
136
+ assert result.exit_code == 1
137
+ assert 'Failed to delete' in result.output
138
+
139
+ @patch('mcli.app.model_cmd.LightweightModelServer')
140
+ def test_delete_command_model_not_found(self, mock_server_class):
141
+ """Test delete command with model not found"""
142
+ mock_server = Mock()
143
+ mock_server_class.return_value = mock_server
144
+ mock_server.downloader.get_downloaded_models.return_value = []
145
+
146
+ result = self.runner.invoke(model, ['delete', 'distilbert-base-uncased', '--force'])
147
+
148
+ assert result.exit_code == 1
149
+ assert 'not found' in result.output
150
+
151
+ @patch('mcli.app.model_cmd.LightweightModelServer')
152
+ def test_delete_command_with_confirmation(self, mock_server_class):
153
+ """Test delete command with user confirmation"""
154
+ mock_server = Mock()
155
+ mock_server_class.return_value = mock_server
156
+ mock_server.downloader.get_downloaded_models.return_value = ['distilbert-base-uncased']
157
+ mock_server.delete_model.return_value = True
158
+
159
+ # Simulate user confirming deletion
160
+ result = self.runner.invoke(model, ['delete', 'distilbert-base-uncased'], input='y\n')
161
+
162
+ assert result.exit_code == 0
163
+ assert 'Successfully deleted' in result.output
164
+
165
+ @patch('mcli.app.model_cmd.LightweightModelServer')
166
+ def test_delete_command_cancelled(self, mock_server_class):
167
+ """Test delete command when user cancels"""
168
+ mock_server = Mock()
169
+ mock_server_class.return_value = mock_server
170
+ mock_server.downloader.get_downloaded_models.return_value = ['distilbert-base-uncased']
171
+
172
+ # Simulate user cancelling deletion
173
+ result = self.runner.invoke(model, ['delete', 'distilbert-base-uncased'], input='n\n')
174
+
175
+ assert result.exit_code == 0
176
+ assert 'cancelled' in result.output
177
+ mock_server.delete_model.assert_not_called()
178
+
179
+ def test_stop_command_success(self):
180
+ """Test stop command with successful stop"""
181
+ with patch('requests.get') as mock_get, \
182
+ patch('psutil.process_iter') as mock_proc_iter:
183
+
184
+ mock_response = Mock()
185
+ mock_response.status_code = 200
186
+ mock_get.return_value = mock_response
187
+
188
+ mock_proc = Mock()
189
+ mock_proc.pid = 1234
190
+ mock_proc.info = {'connections': [Mock(laddr=Mock(port=8080))]}
191
+ mock_proc_iter.return_value = [mock_proc]
192
+
193
+ result = self.runner.invoke(model, ['stop'])
194
+
195
+ assert result.exit_code == 0
196
+ assert 'Server stopped successfully' in result.output or 'Stopping server' in result.output
197
+
198
+ def test_stop_command_no_server(self):
199
+ """Test stop command when no server is running"""
200
+ import requests
201
+ with patch('requests.get') as mock_get:
202
+ mock_get.side_effect = requests.exceptions.ConnectionError()
203
+
204
+ result = self.runner.invoke(model, ['stop'])
205
+
206
+ assert result.exit_code == 0
207
+ assert 'No server running' in result.output
208
+
209
+ def test_status_command_server_running(self):
210
+ """Test status command when server is running"""
211
+ with patch('requests.get') as mock_get:
212
+ mock_health_response = Mock()
213
+ mock_health_response.status_code = 200
214
+
215
+ mock_models_response = Mock()
216
+ mock_models_response.status_code = 200
217
+ mock_models_response.json.return_value = {
218
+ 'models': [
219
+ {'name': 'distilbert-base-uncased', 'parameters': '66M'}
220
+ ]
221
+ }
222
+
223
+ mock_get.side_effect = [mock_health_response, mock_models_response]
224
+
225
+ result = self.runner.invoke(model, ['status'])
226
+
227
+ assert result.exit_code == 0
228
+ assert 'Server is running' in result.output
229
+
230
+ def test_status_command_server_not_running(self):
231
+ """Test status command when server is not running"""
232
+ import requests
233
+ with patch('requests.get') as mock_get:
234
+ mock_get.side_effect = requests.exceptions.ConnectionError()
235
+
236
+ result = self.runner.invoke(model, ['status'])
237
+
238
+ assert result.exit_code == 0
239
+ assert 'No server running' in result.output
240
+
241
+ @patch('mcli.app.model_cmd.LightweightModelServer')
242
+ def test_recommend_command(self, mock_server_class):
243
+ """Test recommend command"""
244
+ mock_server = Mock()
245
+ mock_server_class.return_value = mock_server
246
+ mock_server.recommend_model.return_value = "distilbert-base-uncased"
247
+ mock_server.get_system_info.return_value = {
248
+ 'cpu_count': 4,
249
+ 'memory_gb': 8.0,
250
+ 'disk_free_gb': 100.0
251
+ }
252
+ mock_server.downloader.get_downloaded_models.return_value = []
253
+
254
+ result = self.runner.invoke(model, ['recommend'])
255
+
256
+ assert result.exit_code == 0
257
+ assert 'Recommended Model' in result.output
258
+
259
+ @patch('mcli.app.model_cmd.LightweightModelServer')
260
+ def test_start_command_with_auto_download(self, mock_server_class):
261
+ """Test start command with auto-download"""
262
+ mock_server = Mock()
263
+ mock_server_class.return_value = mock_server
264
+ mock_server.recommend_model.return_value = "distilbert-base-uncased"
265
+ mock_server.downloader.get_downloaded_models.return_value = []
266
+ mock_server.download_and_load_model.return_value = True
267
+
268
+ # Use a timeout to avoid hanging on the infinite loop
269
+ import signal
270
+
271
+ def timeout_handler(signum, frame):
272
+ raise KeyboardInterrupt()
273
+
274
+ old_handler = signal.signal(signal.SIGALRM, timeout_handler)
275
+ signal.alarm(1)
276
+
277
+ try:
278
+ result = self.runner.invoke(model, ['start', '--auto-download'])
279
+ # Command will be interrupted by KeyboardInterrupt
280
+ except KeyboardInterrupt:
281
+ pass
282
+ finally:
283
+ signal.alarm(0)
284
+ signal.signal(signal.SIGALRM, old_handler)
285
+
286
+ # Verify the model was downloaded
287
+ mock_server.download_and_load_model.assert_called()
288
+
289
+
290
+ class TestModelCommandHelp:
291
+ """Test suite for model command help text"""
292
+
293
+ def setup_method(self):
294
+ """Setup test environment"""
295
+ self.runner = CliRunner()
296
+
297
+ def test_model_help(self):
298
+ """Test model command help text"""
299
+ result = self.runner.invoke(model, ['--help'])
300
+
301
+ assert result.exit_code == 0
302
+ assert 'Model management commands' in result.output
303
+
304
+ def test_list_help(self):
305
+ """Test list command help text"""
306
+ result = self.runner.invoke(model, ['list', '--help'])
307
+
308
+ assert result.exit_code == 0
309
+ assert 'List available and downloaded models' in result.output
310
+
311
+ def test_download_help(self):
312
+ """Test download command help text"""
313
+ result = self.runner.invoke(model, ['download', '--help'])
314
+
315
+ assert result.exit_code == 0
316
+ assert 'Download a specific lightweight model' in result.output
317
+
318
+ def test_pull_help(self):
319
+ """Test pull command help text"""
320
+ result = self.runner.invoke(model, ['pull', '--help'])
321
+
322
+ assert result.exit_code == 0
323
+ assert 'Pull' in result.output or 'download' in result.output
324
+
325
+ def test_delete_help(self):
326
+ """Test delete command help text"""
327
+ result = self.runner.invoke(model, ['delete', '--help'])
328
+
329
+ assert result.exit_code == 0
330
+ assert 'Delete a downloaded lightweight model' in result.output
331
+
332
+ def test_stop_help(self):
333
+ """Test stop command help text"""
334
+ result = self.runner.invoke(model, ['stop', '--help'])
335
+
336
+ assert result.exit_code == 0
337
+ assert 'Stop the lightweight model server' in result.output
338
+
339
+ def test_start_help(self):
340
+ """Test start command help text"""
341
+ result = self.runner.invoke(model, ['start', '--help'])
342
+
343
+ assert result.exit_code == 0
344
+ assert 'Start the lightweight model server' in result.output
345
+
346
+ def test_status_help(self):
347
+ """Test status command help text"""
348
+ result = self.runner.invoke(model, ['status', '--help'])
349
+
350
+ assert result.exit_code == 0
351
+ assert 'Check status of the lightweight model server' in result.output
352
+
353
+ def test_recommend_help(self):
354
+ """Test recommend command help text"""
355
+ result = self.runner.invoke(model, ['recommend', '--help'])
356
+
357
+ assert result.exit_code == 0
358
+ assert 'Get model recommendation' in result.output
359
+
360
+
361
+ if __name__ == '__main__':
362
+ pytest.main([__file__, '-v'])