diff --git a/engine/commands/engine_get_cmd.cc b/engine/commands/engine_get_cmd.cc
index a7ec8fbc1..aa4cbabf2 100644
--- a/engine/commands/engine_get_cmd.cc
+++ b/engine/commands/engine_get_cmd.cc
@@ -6,22 +6,17 @@
 
 namespace commands {
 
-void EngineGetCmd::Exec() const {
-  CTL_INF("[EngineGetCmd] engine: " << engine_);
-
-  auto engine_service = EngineService();
-  try {
-    auto status = engine_service.GetEngineInfo(engine_);
-    tabulate::Table table;
-    table.add_row({"Name", "Supported Formats", "Version", "Status"});
-    table.add_row(
-        {status.product_name, status.format, status.version, status.status});
-    std::cout << table << std::endl;
-  } catch (const std::runtime_error& e) {
-    std::cerr << "Engine " << engine_ << " is not supported!" << "\n";
-  } catch (const std::exception& e) {
-    std::cerr << "Failed to get engine info for " << engine_ << ": " << e.what()
-              << "\n";
+void EngineGetCmd::Exec(const std::string& engine_name) const {
+  auto engine = engine_service_.GetEngineInfo(engine_name);
+  if (engine == std::nullopt) {
+    CLI_LOG("Engine " + engine_name + " is not supported!");
+    return;
   }
+
+  tabulate::Table table;
+  table.add_row({"Name", "Supported Formats", "Version", "Status"});
+  table.add_row(
+      {engine->product_name, engine->format, engine->version, engine->status});
+  std::cout << table << std::endl;
 }
 };  // namespace commands
diff --git a/engine/commands/engine_get_cmd.h b/engine/commands/engine_get_cmd.h
index 505ee5120..8fbc20d08 100644
--- a/engine/commands/engine_get_cmd.h
+++ b/engine/commands/engine_get_cmd.h
@@ -1,15 +1,15 @@
 #pragma once
 
-#include <string>
+#include "services/engine_service.h"
 
 namespace commands {
 class EngineGetCmd {
  public:
-  EngineGetCmd(const std::string& engine) : engine_{engine} {};
+  explicit EngineGetCmd() : engine_service_{EngineService()} {};
 
-  void Exec() const;
+  void Exec(const std::string& engineName) const;
 
  private:
-  std::string engine_;
+  EngineService engine_service_;
 };
 }  // namespace commands
diff --git a/engine/commands/engine_init_cmd.cc b/engine/commands/engine_init_cmd.cc
deleted file mode 100644
index 350f3c6b1..000000000
--- a/engine/commands/engine_init_cmd.cc
+++ /dev/null
@@ -1,220 +0,0 @@
-#include "engine_init_cmd.h"
-#include <utility>
-#include "services/download_service.h"
-#include "trantor/utils/Logger.h"
-// clang-format off
-#include "utils/cortexso_parser.h" 
-#include "utils/archive_utils.h"   
-#include "utils/system_info_utils.h"
-// clang-format on
-#include "utils/cuda_toolkit_utils.h"
-#include "utils/engine_matcher_utils.h"
-#include "utils/file_manager_utils.h"
-
-namespace commands {
-
-EngineInitCmd::EngineInitCmd(std::string engineName, std::string version)
-    : engineName_(std::move(engineName)), version_(std::move(version)) {}
-
-bool EngineInitCmd::Exec() const {
-  auto system_info = system_info_utils::GetSystemInfo();
-  constexpr auto gitHubHost = "https://api.github.com";
-  std::string version = version_.empty() ? "latest" : version_;
-  std::ostringstream engineReleasePath;
-  engineReleasePath << "/repos/janhq/" << engineName_ << "/releases/"
-                    << version;
-  CTL_INF("Engine release path: " << gitHubHost << engineReleasePath.str());
-  using namespace nlohmann;
-
-  httplib::Client cli(gitHubHost);
-  if (auto res = cli.Get(engineReleasePath.str())) {
-    if (res->status == httplib::StatusCode::OK_200) {
-      try {
-        auto jsonResponse = json::parse(res->body);
-        auto assets = jsonResponse["assets"];
-        auto os_arch{system_info.os + "-" + system_info.arch};
-
-        std::vector<std::string> variants;
-        for (auto& asset : assets) {
-          auto asset_name = asset["name"].get<std::string>();
-          variants.push_back(asset_name);
-        }
-
-        auto cuda_driver_version = system_info_utils::GetCudaVersion();
-        CTL_INF("engineName_: " << engineName_);
-        CTL_INF("CUDA version: " << cuda_driver_version);
-        std::string matched_variant = "";
-
-        if (engineName_ == "cortex.tensorrt-llm") {
-          matched_variant = engine_matcher_utils::ValidateTensorrtLlm(
-              variants, system_info.os, cuda_driver_version);
-        } else if (engineName_ == "cortex.onnx") {
-          matched_variant = engine_matcher_utils::ValidateOnnx(
-              variants, system_info.os, system_info.arch);
-        } else if (engineName_ == "cortex.llamacpp") {
-          cortex::cpuid::CpuInfo cpu_info;
-          auto suitable_avx =
-              engine_matcher_utils::GetSuitableAvxVariant(cpu_info);
-          matched_variant = engine_matcher_utils::Validate(
-              variants, system_info.os, system_info.arch, suitable_avx,
-              cuda_driver_version);
-        }
-        CTL_INF("Matched variant: " << matched_variant);
-        if (matched_variant.empty()) {
-          CTL_ERR("No variant found for " << os_arch);
-          return false;
-        }
-
-        for (auto& asset : assets) {
-          auto assetName = asset["name"].get<std::string>();
-          if (assetName == matched_variant) {
-            auto download_url =
-                asset["browser_download_url"].get<std::string>();
-            auto file_name = asset["name"].get<std::string>();
-            CTL_INF("Download url: " << download_url);
-
-            std::filesystem::path engine_folder_path =
-                file_manager_utils::GetContainerFolderPath(
-                    file_manager_utils::DownloadTypeToString(
-                        DownloadType::Engine)) /
-                engineName_;
-
-            if (!std::filesystem::exists(engine_folder_path)) {
-              CTL_INF("Creating " << engine_folder_path.string());
-              std::filesystem::create_directories(engine_folder_path);
-            }
-
-            CTL_INF("Engine folder path: " << engine_folder_path.string()
-                                           << "\n");
-            auto local_path = engine_folder_path / file_name;
-            auto downloadTask{DownloadTask{.id = engineName_,
-                                           .type = DownloadType::Engine,
-                                           .items = {DownloadItem{
-                                               .id = engineName_,
-                                               .downloadUrl = download_url,
-                                               .localPath = local_path,
-                                           }}}};
-
-            DownloadService download_service;
-            download_service.AddDownloadTask(
-                downloadTask, [](const DownloadTask& finishedTask) {
-                  // try to unzip the downloaded file
-                  CTL_INF("Engine zip path: "
-                          << finishedTask.items[0].localPath.string());
-
-                  std::filesystem::path extract_path =
-                      finishedTask.items[0]
-                          .localPath.parent_path()
-                          .parent_path();
-
-                  archive_utils::ExtractArchive(
-                      finishedTask.items[0].localPath.string(),
-                      extract_path.string());
-
-                  // remove the downloaded file
-                  try {
-                    std::filesystem::remove(finishedTask.items[0].localPath);
-                  } catch (const std::exception& e) {
-                    CTL_WRN("Could not delete file: " << e.what());
-                  }
-                  CTL_INF("Finished!");
-                });
-            if (system_info.os == "mac" || engineName_ == "cortex.onnx") {
-              // mac and onnx engine does not require cuda toolkit
-              return true;
-            }
-
-            if (cuda_driver_version.empty()) {
-              CTL_WRN("No cuda driver, continue with CPU");
-              return true;
-            }
-
-            // download cuda toolkit
-            const std::string jan_host = "https://catalog.jan.ai";
-            const std::string cuda_toolkit_file_name = "cuda.tar.gz";
-            const std::string download_id = "cuda";
-
-            // TODO: we don't have API to retrieve list of cuda toolkit dependencies atm because we hosting it at jan
-            //  will have better logic after https://github.com/janhq/cortex/issues/1046 finished
-            // for now, assume that we have only 11.7 and 12.4
-            auto suitable_toolkit_version = "";
-            if (engineName_ == "cortex.tensorrt-llm") {
-              // for tensorrt-llm, we need to download cuda toolkit v12.4
-              suitable_toolkit_version = "12.4";
-            } else {
-              // llamacpp
-              auto cuda_driver_semver =
-                  semantic_version_utils::SplitVersion(cuda_driver_version);
-              if (cuda_driver_semver.major == 11) {
-                suitable_toolkit_version = "11.7";
-              } else if (cuda_driver_semver.major == 12) {
-                suitable_toolkit_version = "12.4";
-              }
-            }
-
-            // compare cuda driver version with cuda toolkit version
-            // cuda driver version should be greater than toolkit version to ensure compatibility
-            if (semantic_version_utils::CompareSemanticVersion(
-                    cuda_driver_version, suitable_toolkit_version) < 0) {
-              CTL_ERR("Your Cuda driver version "
-                      << cuda_driver_version
-                      << " is not compatible with cuda toolkit version "
-                      << suitable_toolkit_version);
-              return false;
-            }
-
-            std::ostringstream cuda_toolkit_url;
-            cuda_toolkit_url << jan_host << "/" << "dist/cuda-dependencies/"
-                             << cuda_driver_version << "/" << system_info.os
-                             << "/" << cuda_toolkit_file_name;
-
-            LOG_DEBUG << "Cuda toolkit download url: "
-                      << cuda_toolkit_url.str();
-            auto cuda_toolkit_local_path =
-                file_manager_utils::GetContainerFolderPath(
-                    file_manager_utils::DownloadTypeToString(
-                        DownloadType::CudaToolkit)) /
-                cuda_toolkit_file_name;
-            LOG_DEBUG << "Download to: " << cuda_toolkit_local_path.string();
-            auto downloadCudaToolkitTask{DownloadTask{
-                .id = download_id,
-                .type = DownloadType::CudaToolkit,
-                .items = {DownloadItem{.id = download_id,
-                                       .downloadUrl = cuda_toolkit_url.str(),
-                                       .localPath = cuda_toolkit_local_path}},
-            }};
-
-            download_service.AddDownloadTask(
-                downloadCudaToolkitTask, [&](const DownloadTask& finishedTask) {
-                  auto engine_path =
-                      file_manager_utils::GetEnginesContainerPath() /
-                      engineName_;
-                  archive_utils::ExtractArchive(
-                      finishedTask.items[0].localPath.string(),
-                      engine_path.string());
-
-                  try {
-                    std::filesystem::remove(finishedTask.items[0].localPath);
-                  } catch (std::exception& e) {
-                    CTL_ERR("Error removing downloaded file: " << e.what());
-                  }
-                });
-            return true;
-          }
-        }
-      } catch (const json::parse_error& e) {
-        std::cerr << "JSON parse error: " << e.what() << std::endl;
-        return false;
-      }
-    } else {
-      CTL_ERR("HTTP error: " << res->status);
-      return false;
-    }
-  } else {
-    auto err = res.error();
-    CTL_ERR("HTTP error: " << httplib::to_string(err));
-    return false;
-  }
-  return true;
-}
-};  // namespace commands
diff --git a/engine/commands/engine_init_cmd.h b/engine/commands/engine_init_cmd.h
deleted file mode 100644
index c75d76f9d..000000000
--- a/engine/commands/engine_init_cmd.h
+++ /dev/null
@@ -1,18 +0,0 @@
-#pragma once
-
-#include <array>
-#include <string>
-
-namespace commands {
-
-class EngineInitCmd {
- public:
-  EngineInitCmd(std::string engineName, std::string version);
-
-  bool Exec() const;
-
- private:
-  std::string engineName_;
-  std::string version_;
-};
-}  // namespace commands
\ No newline at end of file
diff --git a/engine/commands/engine_install_cmd.cc b/engine/commands/engine_install_cmd.cc
new file mode 100644
index 000000000..36f7a040b
--- /dev/null
+++ b/engine/commands/engine_install_cmd.cc
@@ -0,0 +1,17 @@
+#include "engine_install_cmd.h"
+// clang-format off
+#include "utils/cortexso_parser.h" 
+#include "utils/archive_utils.h"
+// clang-format on
+#include "utils/cuda_toolkit_utils.h"
+#include "utils/engine_matcher_utils.h"
+#include "utils/logging_utils.h"
+
+namespace commands {
+
+void EngineInstallCmd::Exec(const std::string& engine,
+                            const std::string& version) {
+  engine_service_.InstallEngine(engine, version);
+  CLI_LOG("Engine " << engine << " installed successfully!");
+}
+};  // namespace commands
diff --git a/engine/commands/engine_install_cmd.h b/engine/commands/engine_install_cmd.h
new file mode 100644
index 000000000..c6ba6f135
--- /dev/null
+++ b/engine/commands/engine_install_cmd.h
@@ -0,0 +1,17 @@
+#pragma once
+
+#include <string>
+#include "services/engine_service.h"
+
+namespace commands {
+
+class EngineInstallCmd {
+ public:
+  explicit EngineInstallCmd() : engine_service_{EngineService()} {};
+
+  void Exec(const std::string& engine, const std::string& version = "latest");
+
+ private:
+  EngineService engine_service_;
+};
+}  // namespace commands
diff --git a/engine/commands/engine_uninstall_cmd.cc b/engine/commands/engine_uninstall_cmd.cc
index 4ea41cac9..f74c0179f 100644
--- a/engine/commands/engine_uninstall_cmd.cc
+++ b/engine/commands/engine_uninstall_cmd.cc
@@ -4,18 +4,8 @@
 
 namespace commands {
 
-EngineUninstallCmd::EngineUninstallCmd(std::string engine)
-    : engine_{std::move(engine)} {}
-
-void EngineUninstallCmd::Exec() const {
-  CTL_INF("Uninstall engine " + engine_);
-  auto engine_service = EngineService();
-
-  try {
-    engine_service.UninstallEngine(engine_);
-    CLI_LOG("Engine " << engine_ << " uninstalled successfully!")
-  } catch (const std::exception& e) {
-    CLI_LOG("Failed to uninstall engine " << engine_ << ": " << e.what());
-  }
+void EngineUninstallCmd::Exec(const std::string& engine) {
+  engine_service_.UninstallEngine(engine);
+  CLI_LOG("Engine " << engine << " uninstalled successfully!");
 }
 };  // namespace commands
diff --git a/engine/commands/engine_uninstall_cmd.h b/engine/commands/engine_uninstall_cmd.h
index a8a760403..5d0606d3f 100644
--- a/engine/commands/engine_uninstall_cmd.h
+++ b/engine/commands/engine_uninstall_cmd.h
@@ -1,15 +1,16 @@
 #pragma once
 
 #include <string>
+#include "services/engine_service.h"
 
 namespace commands {
 class EngineUninstallCmd {
  public:
-  EngineUninstallCmd(std::string engine);
+  explicit EngineUninstallCmd() : engine_service_{EngineService()} {};
 
-  void Exec() const;
+  void Exec(const std::string& engine);
 
  private:
-  std::string engine_;
+  EngineService engine_service_;
 };
 }  // namespace commands
diff --git a/engine/commands/model_list_cmd.cc b/engine/commands/model_list_cmd.cc
index 70d1e79ea..e0ca88bd3 100644
--- a/engine/commands/model_list_cmd.cc
+++ b/engine/commands/model_list_cmd.cc
@@ -1,13 +1,9 @@
-// clang-format off
-#include "utils/cortex_utils.h"
-// clang-format on
 #include "model_list_cmd.h"
 #include <filesystem>
 #include <iostream>
 #include <tabulate/table.hpp>
 #include <vector>
 #include "config/yaml_config.h"
-#include "trantor/utils/Logger.h"
 #include "utils/file_manager_utils.h"
 #include "utils/logging_utils.h"
 
@@ -57,4 +53,4 @@ void ModelListCmd::Exec() {
     std::cout << table << std::endl;
   }
 }
-};  // namespace commands
\ No newline at end of file
+};  // namespace commands
diff --git a/engine/commands/model_list_cmd.h b/engine/commands/model_list_cmd.h
index 6a9f2aa5f..a24da8928 100644
--- a/engine/commands/model_list_cmd.h
+++ b/engine/commands/model_list_cmd.h
@@ -1,11 +1,9 @@
 #pragma once
 
-#include <string>
-
 namespace commands {
 
 class ModelListCmd {
  public:
   void Exec();
 };
-}  // namespace commands
\ No newline at end of file
+}  // namespace commands
diff --git a/engine/commands/model_pull_cmd.cc b/engine/commands/model_pull_cmd.cc
index 1ae637817..e46d78981 100644
--- a/engine/commands/model_pull_cmd.cc
+++ b/engine/commands/model_pull_cmd.cc
@@ -1,25 +1,8 @@
 #include "model_pull_cmd.h"
-#include <utility>
-#include "services/download_service.h"
 #include "utils/cortexso_parser.h"
-#include "utils/logging_utils.h"
-#include "utils/model_callback_utils.h"
 
 namespace commands {
-ModelPullCmd::ModelPullCmd(std::string model_handle, std::string branch)
-    : model_handle_(std::move(model_handle)), branch_(std::move(branch)) {}
-
-bool ModelPullCmd::Exec() {
-  auto downloadTask = cortexso_parser::getDownloadTask(model_handle_, branch_);
-  if (downloadTask.has_value()) {
-    DownloadService().AddDownloadTask(downloadTask.value(),
-                                      model_callback_utils::DownloadModelCb);
-    CTL_INF("Download finished");
-    return true;
-  } else {
-    CTL_ERR("Model not found");
-    return false;
-  }
+void ModelPullCmd::Exec(const std::string& input) {
+  model_service_.DownloadModel(input);
 }
-
 };  // namespace commands
diff --git a/engine/commands/model_pull_cmd.h b/engine/commands/model_pull_cmd.h
index da5713bdf..9a90e1950 100644
--- a/engine/commands/model_pull_cmd.h
+++ b/engine/commands/model_pull_cmd.h
@@ -1,16 +1,15 @@
 #pragma once
 
-#include <string>
+#include "services/model_service.h"
 
 namespace commands {
 
 class ModelPullCmd {
  public:
-explicit  ModelPullCmd(std::string model_handle, std::string branch);
-  bool Exec();
+  explicit ModelPullCmd() : model_service_{ModelService()} {};
+  void Exec(const std::string& input);
 
  private:
-  std::string model_handle_;
-  std::string branch_;
+  ModelService model_service_;
 };
-}  // namespace commands
\ No newline at end of file
+}  // namespace commands
diff --git a/engine/commands/run_cmd.cc b/engine/commands/run_cmd.cc
index c8bde1b14..64bc50d6f 100644
--- a/engine/commands/run_cmd.cc
+++ b/engine/commands/run_cmd.cc
@@ -2,18 +2,11 @@
 #include "chat_cmd.h"
 #include "cmd_info.h"
 #include "config/yaml_config.h"
-#include "engine_init_cmd.h"
-#include "model_pull_cmd.h"
 #include "model_start_cmd.h"
-#include "trantor/utils/Logger.h"
-#include "utils/cortex_utils.h"
 #include "utils/file_manager_utils.h"
 
 namespace commands {
 
-RunCmd::RunCmd(std::string host, int port, std::string model_id)
-    : host_(std::move(host)), port_(port), model_id_(std::move(model_id)) {}
-
 void RunCmd::Exec() {
   auto address = host_ + ":" + std::to_string(port_);
   CmdInfo ci(model_id_);
@@ -22,22 +15,23 @@ void RunCmd::Exec() {
   // TODO should we clean all resource if something fails?
   // Check if model existed. If not, download it
   {
-    if (!IsModelExisted(model_file)) {
-      ModelPullCmd model_pull_cmd(ci.model_name, ci.branch);
-      if (!model_pull_cmd.Exec()) {
-        return;
-      }
+    auto model_conf = model_service_.GetDownloadedModel(model_id_);
+    if (!model_conf.has_value()) {
+      model_service_.DownloadModel(model_id_);
     }
   }
 
   // Check if engine existed. If not, download it
   {
-    if (!IsEngineExisted(ci.engine_name)) {
-      EngineInitCmd eic(ci.engine_name, "");
-      if (!eic.Exec()) {
-        LOG_INFO << "Failed to install engine";
-        return;
-      }
+    auto required_engine = engine_service_.GetEngineInfo(ci.engine_name);
+    if (!required_engine.has_value()) {
+      throw std::runtime_error("Engine not found: " + ci.engine_name);
+    }
+    if (required_engine.value().status == EngineService::kIncompatible) {
+      throw std::runtime_error("Engine " + ci.engine_name + " is incompatible");
+    }
+    if (required_engine.value().status == EngineService::kNotInstalled) {
+      engine_service_.InstallEngine(ci.engine_name);
     }
   }
 
@@ -59,37 +53,4 @@ void RunCmd::Exec() {
     cc.Exec("");
   }
 }
-
-bool RunCmd::IsModelExisted(const std::string& model_id) {
-  auto models_path = file_manager_utils::GetModelsContainerPath();
-  if (std::filesystem::exists(models_path) &&
-      std::filesystem::is_directory(models_path)) {
-    // Iterate through directory
-    for (const auto& entry : std::filesystem::directory_iterator(models_path)) {
-      if (entry.is_regular_file() && entry.path().extension() == ".yaml") {
-        try {
-          config::YamlHandler handler;
-          handler.ModelConfigFromFile(entry.path().string());
-          if (entry.path().stem().string() == model_id) {
-            return true;
-          }
-        } catch (const std::exception& e) {
-          LOG_ERROR << "Error reading yaml file '" << entry.path().string()
-                    << "': " << e.what();
-        }
-      }
-    }
-  }
-  return false;
-}
-
-bool RunCmd::IsEngineExisted(const std::string& e) {
-  auto engines_path = file_manager_utils::GetEnginesContainerPath();
-  if (std::filesystem::exists(engines_path) &&
-      std::filesystem::exists(engines_path.string() + "/" + e)) {
-    return true;
-  }
-  return false;
-}
-
 };  // namespace commands
diff --git a/engine/commands/run_cmd.h b/engine/commands/run_cmd.h
index ca44b9d24..c862926a6 100644
--- a/engine/commands/run_cmd.h
+++ b/engine/commands/run_cmd.h
@@ -1,22 +1,25 @@
 #pragma once
 #include <string>
-#include <vector>
-#include "config/model_config.h"
-#include "nlohmann/json.hpp"
+#include "services/engine_service.h"
+#include "services/model_service.h"
 
 namespace commands {
 class RunCmd {
  public:
-  explicit RunCmd(std::string host, int port, std::string model_id);
-  void Exec();
+  explicit RunCmd(std::string host, int port, std::string model_id)
+      : host_{std::move(host)},
+        port_{port},
+        model_id_{std::move(model_id)},
+        model_service_{ModelService()} {};
 
- private:
-  bool IsModelExisted(const std::string& model_id);
-  bool IsEngineExisted(const std::string& e);
+  void Exec();
 
  private:
   std::string host_;
   int port_;
   std::string model_id_;
+
+  ModelService model_service_;
+  EngineService engine_service_;
 };
-}  // namespace commands
\ No newline at end of file
+}  // namespace commands
diff --git a/engine/config/gguf_parser.cc b/engine/config/gguf_parser.cc
index 00b461719..f2f058336 100644
--- a/engine/config/gguf_parser.cc
+++ b/engine/config/gguf_parser.cc
@@ -321,7 +321,11 @@ void GGUFHandler::Parse(const std::string& file_path) {
     offset += value_byte_length;
     LOG_INFO << "-------------------------------------------- " << "\n";
   }
-  PrintMetadata();
+  try {
+    PrintMetadata();
+  } catch (const std::exception& e) {
+    LOG_ERROR << "Error parsing metadata: " << e.what() << "\n";
+  }
   ModelConfigFromMetadata();
   CloseFile();
 }
@@ -579,4 +583,4 @@ void GGUFHandler::ModelConfigFromMetadata() {
 const ModelConfig& GGUFHandler::GetModelConfig() const {
   return model_config_;
 }
-}  // namespace config
\ No newline at end of file
+}  // namespace config
diff --git a/engine/config/model_config.h b/engine/config/model_config.h
index b7cd15810..a8d7f4fda 100644
--- a/engine/config/model_config.h
+++ b/engine/config/model_config.h
@@ -1,5 +1,5 @@
 #pragma once
-#include <cmath>
+
 #include <limits>
 #include <string>
 #include <vector>
@@ -37,4 +37,4 @@ struct ModelConfig {
   std::string object;
   std::string owned_by = "";
 };
-}  // namespace config
\ No newline at end of file
+}  // namespace config
diff --git a/engine/config/yaml_config.cc b/engine/config/yaml_config.cc
index fe3e57370..81f62df9d 100644
--- a/engine/config/yaml_config.cc
+++ b/engine/config/yaml_config.cc
@@ -2,11 +2,9 @@
 #include <ctime>
 #include <fstream>
 #include <iostream>
-#include <limits>
 #include <string>
 using namespace std;
 
-#include "yaml-cpp/yaml.h"
 #include "yaml_config.h"
 
 namespace config {
@@ -209,4 +207,4 @@ void YamlHandler::WriteYamlFile(const std::string& file_path) const {
     throw;
   }
 }
-}  // namespace config
\ No newline at end of file
+}  // namespace config
diff --git a/engine/config/yaml_config.h b/engine/config/yaml_config.h
index 3f8af5400..87b9083a1 100644
--- a/engine/config/yaml_config.h
+++ b/engine/config/yaml_config.h
@@ -1,13 +1,11 @@
 #pragma once
-#include <cmath>
+
 #include <ctime>
-#include <fstream>
-#include <iostream>
-#include <limits>
 #include <string>
 
-#include "yaml-cpp/yaml.h"
 #include "model_config.h"
+#include "yaml-cpp/yaml.h"
+
 namespace config {
 class YamlHandler {
  private:
@@ -29,4 +27,4 @@ class YamlHandler {
   // Method to write all attributes to a YAML file
   void WriteYamlFile(const std::string& file_path) const;
 };
-}
\ No newline at end of file
+}  // namespace config
diff --git a/engine/controllers/command_line_parser.cc b/engine/controllers/command_line_parser.cc
index b8baf3466..8c0378abf 100644
--- a/engine/controllers/command_line_parser.cc
+++ b/engine/controllers/command_line_parser.cc
@@ -3,9 +3,10 @@
 #include "commands/cmd_info.h"
 #include "commands/cortex_upd_cmd.h"
 #include "commands/engine_get_cmd.h"
-#include "commands/engine_init_cmd.h"
+#include "commands/engine_install_cmd.h"
 #include "commands/engine_list_cmd.h"
 #include "commands/engine_uninstall_cmd.h"
+#include "commands/model_del_cmd.h"
 #include "commands/model_get_cmd.h"
 #include "commands/model_list_cmd.h"
 #include "commands/model_pull_cmd.h"
@@ -13,7 +14,6 @@
 #include "commands/model_stop_cmd.h"
 #include "commands/run_cmd.h"
 #include "commands/server_stop_cmd.h"
-#include "commands/model_del_cmd.h"
 #include "config/yaml_config.h"
 #include "services/engine_service.h"
 #include "utils/file_manager_utils.h"
@@ -66,10 +66,7 @@ bool CommandLineParser::SetupCommand(int argc, char** argv) {
 
     auto list_models_cmd =
         models_cmd->add_subcommand("list", "List all models locally");
-    list_models_cmd->callback([]() {
-      commands::ModelListCmd command;
-      command.Exec();
-    });
+    list_models_cmd->callback([]() { commands::ModelListCmd().Exec(); });
 
     auto get_models_cmd =
         models_cmd->add_subcommand("get", "Get info of {model_id} locally");
@@ -79,18 +76,21 @@ bool CommandLineParser::SetupCommand(int argc, char** argv) {
       command.Exec();
     });
 
-    auto model_pull_cmd =
-        app_.add_subcommand("pull",
-                            "Download a model from a registry. Working with "
-                            "HuggingFace repositories. For available models, "
-                            "please visit https://huggingface.co/cortexso");
-    model_pull_cmd->add_option("model_id", model_id, "");
-
-    model_pull_cmd->callback([&model_id]() {
-      commands::CmdInfo ci(model_id);
-      commands::ModelPullCmd command(ci.model_name, ci.branch);
-      command.Exec();
-    });
+    {
+      auto model_pull_cmd =
+          app_.add_subcommand("pull",
+                              "Download a model from a registry. Working with "
+                              "HuggingFace repositories. For available models, "
+                              "please visit https://huggingface.co/cortexso");
+      model_pull_cmd->add_option("model_id", model_id, "");
+      model_pull_cmd->callback([&model_id]() {
+        try {
+          commands::ModelPullCmd().Exec(model_id);
+        } catch (const std::exception& e) {
+          CLI_LOG(e.what());
+        }
+      });
+    }
 
     auto model_del_cmd =
         models_cmd->add_subcommand("delete", "Delete a model by ID locally");
@@ -224,9 +224,12 @@ void CommandLineParser::EngineInstall(CLI::App* parent,
                                       std::string& version) {
   auto install_engine_cmd = parent->add_subcommand(engine_name, "");
 
-  install_engine_cmd->callback([=] {
-    commands::EngineInitCmd eic(engine_name, version);
-    eic.Exec();
+  install_engine_cmd->callback([engine_name, version] {
+    try {
+      commands::EngineInstallCmd().Exec(engine_name, version);
+    } catch (const std::exception& e) {
+      CTL_ERR(e.what());
+    }
   });
 }
 
@@ -234,9 +237,12 @@ void CommandLineParser::EngineUninstall(CLI::App* parent,
                                         const std::string& engine_name) {
   auto uninstall_engine_cmd = parent->add_subcommand(engine_name, "");
 
-  uninstall_engine_cmd->callback([=] {
-    commands::EngineUninstallCmd cmd(engine_name);
-    cmd.Exec();
+  uninstall_engine_cmd->callback([engine_name] {
+    try {
+      commands::EngineUninstallCmd().Exec(engine_name);
+    } catch (const std::exception& e) {
+      CTL_ERR(e.what());
+    }
   });
 }
 
@@ -248,9 +254,7 @@ void CommandLineParser::EngineGet(CLI::App* parent) {
     std::string desc = "Get " + engine_name + " status";
 
     auto engine_get_cmd = get_cmd->add_subcommand(engine_name, desc);
-    engine_get_cmd->callback([engine_name] {
-      commands::EngineGetCmd cmd(engine_name);
-      cmd.Exec();
-    });
+    engine_get_cmd->callback(
+        [engine_name] { commands::EngineGetCmd().Exec(engine_name); });
   }
 }
diff --git a/engine/controllers/engines.cc b/engine/controllers/engines.cc
index c9e2ba1b4..e35002e1f 100644
--- a/engine/controllers/engines.cc
+++ b/engine/controllers/engines.cc
@@ -144,11 +144,11 @@ void Engines::GetEngine(const HttpRequestPtr& req,
   try {
     auto status = engine_service.GetEngineInfo(engine);
     Json::Value ret;
-    ret["name"] = status.name;
-    ret["description"] = status.description;
-    ret["version"] = status.version;
-    ret["productName"] = status.product_name;
-    ret["status"] = status.status;
+    ret["name"] = status->name;
+    ret["description"] = status->description;
+    ret["version"] = status->version;
+    ret["productName"] = status->product_name;
+    ret["status"] = status->status;
 
     auto resp = cortex_utils::CreateCortexHttpJsonResponse(ret);
     resp->setStatusCode(k200OK);
diff --git a/engine/controllers/server.cc b/engine/controllers/server.cc
index ee9951968..8aa4a7e86 100644
--- a/engine/controllers/server.cc
+++ b/engine/controllers/server.cc
@@ -1,14 +1,9 @@
 #include "server.h"
 
-#include <chrono>
-#include <fstream>
-#include <iostream>
-
 #include "trantor/utils/Logger.h"
 #include "utils/cortex_utils.h"
 #include "utils/cpuid/cpu_info.h"
 #include "utils/file_manager_utils.h"
-#include "utils/logging_utils.h"
 
 using namespace inferences;
 using json = nlohmann::json;
@@ -462,4 +457,4 @@ bool server::HasFieldInReq(
   return true;
 }
 
-}  // namespace inferences
\ No newline at end of file
+}  // namespace inferences
diff --git a/engine/e2e-test/main.py b/engine/e2e-test/main.py
index da1197c33..1df424e65 100644
--- a/engine/e2e-test/main.py
+++ b/engine/e2e-test/main.py
@@ -4,10 +4,11 @@
 from test_cli_engine_install import TestCliEngineInstall
 from test_cli_engine_list import TestCliEngineList
 from test_cli_engine_uninstall import TestCliEngineUninstall
-from test_cortex_update import TestCortexUpdate
+from test_cli_model_delete import TestCliModelDelete
+from test_cli_model_pull_direct_url import TestCliModelPullDirectUrl
 from test_cli_server_start import TestCliServerStart
+from test_cortex_update import TestCortexUpdate
 from test_create_log_folder import TestCreateLogFolder
-from test_cli_model_delete import TestCliModelDelete
 
 if __name__ == "__main__":
     pytest.main([__file__, "-v"])
diff --git a/engine/e2e-test/test_cli_engine_install.py b/engine/e2e-test/test_cli_engine_install.py
index 84af8b777..be23167be 100644
--- a/engine/e2e-test/test_cli_engine_install.py
+++ b/engine/e2e-test/test_cli_engine_install.py
@@ -8,7 +8,7 @@ class TestCliEngineInstall:
 
     def test_engines_install_llamacpp_should_be_successfully(self):
         exit_code, output, error = run(
-            "Install Engine", ["engines", "install", "cortex.llamacpp"]
+            "Install Engine", ["engines", "install", "cortex.llamacpp"], timeout=60
         )
         assert "Start downloading" in output, "Should display downloading message"
         assert exit_code == 0, f"Install engine failed with error: {error}"
diff --git a/engine/e2e-test/test_cli_model_pull_direct_url.py b/engine/e2e-test/test_cli_model_pull_direct_url.py
new file mode 100644
index 000000000..7d6fa677b
--- /dev/null
+++ b/engine/e2e-test/test_cli_model_pull_direct_url.py
@@ -0,0 +1,17 @@
+import platform
+
+import pytest
+from test_runner import run
+
+
+class TestCliModelPullDirectUrl:
+
+    @pytest.mark.skipif(True, reason="Expensive test. Only test when needed.")
+    def test_model_pull_with_direct_url_should_be_success(self):
+        exit_code, output, error = run(
+            "Pull model", ["pull", "https://huggingface.co/TheBloke/TinyLlama-1.1B-Chat-v0.3-GGUF/blob/main/tinyllama-1.1b-chat-v0.3.Q2_K.gguf"],
+            timeout=None
+        )
+        assert exit_code == 0, f"Model pull failed with error: {error}"
+        # TODO: verify that the model has been pull successfully
+        # TODO: skip this test. since download model is taking too long
\ No newline at end of file
diff --git a/engine/e2e-test/test_cli_model_pull_from_cortexso.py b/engine/e2e-test/test_cli_model_pull_from_cortexso.py
new file mode 100644
index 000000000..e5651ce2f
--- /dev/null
+++ b/engine/e2e-test/test_cli_model_pull_from_cortexso.py
@@ -0,0 +1,17 @@
+import platform
+
+import pytest
+from test_runner import run
+
+
+class TestCliModelPullCortexso:
+
+    @pytest.mark.skipif(True, reason="Expensive test. Only test when needed.")
+    def test_model_pull_with_direct_url_should_be_success(self):
+        exit_code, output, error = run(
+            "Pull model", ["pull", "tinyllama"],
+            timeout=None
+        )
+        assert exit_code == 0, f"Model pull failed with error: {error}"
+        # TODO: verify that the model has been pull successfully
+        # TODO: skip this test. since download model is taking too long
\ No newline at end of file
diff --git a/engine/e2e-test/test_runner.py b/engine/e2e-test/test_runner.py
index bc89fb836..bedf8d39d 100644
--- a/engine/e2e-test/test_runner.py
+++ b/engine/e2e-test/test_runner.py
@@ -24,13 +24,16 @@ def getExecutablePath() -> str:
 
 
 # Execute a command
-def run(test_name: str, arguments: List[str], timeout_sec = 5):
+def run(test_name: str, arguments: List[str], timeout=timeout) -> (int, str, str):
     executable_path = getExecutablePath()
     print("Running:", test_name)
     print("Command:", [executable_path] + arguments)
 
     result = subprocess.run(
-        [executable_path] + arguments, capture_output=True, text=True, timeout=timeout_sec
+        [executable_path] + arguments,
+        capture_output=True,
+        text=True,
+        timeout=timeout,
     )
     return result.returncode, result.stdout, result.stderr
 
diff --git a/engine/services/download_service.h b/engine/services/download_service.h
index 2583f629f..5015ec29c 100644
--- a/engine/services/download_service.h
+++ b/engine/services/download_service.h
@@ -62,7 +62,7 @@ class DownloadService {
       std::optional<OnDownloadTaskSuccessfully> callback = std::nullopt);
 
   /**
-   * Getting file size for a provided url.
+   * Getting file size for a provided url. Can be used to validating the download url.
    *
    * @param url - url to get file size
    */
diff --git a/engine/services/engine_service.cc b/engine/services/engine_service.cc
index 99e946b56..5691d728c 100644
--- a/engine/services/engine_service.cc
+++ b/engine/services/engine_service.cc
@@ -1,20 +1,23 @@
 #include "engine_service.h"
+#include <httplib.h>
 #include <stdexcept>
 #include "algorithm"
+#include "utils/archive_utils.h"
+#include "utils/engine_matcher_utils.h"
 #include "utils/file_manager_utils.h"
+#include "utils/json.hpp"
+#include "utils/semantic_version_utils.h"
+#include "utils/system_info_utils.h"
+#include "utils/url_parser.h"
 
-namespace {
-constexpr static auto kIncompatible = "Incompatible";
-constexpr static auto kReady = "Ready";
-constexpr static auto kNotInstalled = "Not Installed";
-}  // namespace
+using json = nlohmann::json;
 
-EngineInfo EngineService::GetEngineInfo(const std::string& engine) const {
+std::optional<EngineInfo> EngineService::GetEngineInfo(
+    const std::string& engine) const {
   // if engine is not found in kSupportEngine, throw runtime error
   if (std::find(kSupportEngines.begin(), kSupportEngines.end(), engine) ==
       kSupportEngines.end()) {
-    // TODO: create a custom exception class
-    throw std::runtime_error("Engine " + engine + " is not supported!");
+    return std::nullopt;
   }
 
   auto engine_status_list = GetEngineInfoList();
@@ -70,6 +73,189 @@ std::vector<EngineInfo> EngineService::GetEngineInfoList() const {
   return engines;
 }
 
+void EngineService::InstallEngine(const std::string& engine,
+                                  const std::string& version) {
+  auto system_info = system_info_utils::GetSystemInfo();
+  auto url_obj = url_parser::Url{
+      .protocol = "https",
+      .host = "api.github.com",
+      .pathParams = {"repos", "janhq", engine, "releases", version},
+  };
+
+  httplib::Client cli(url_obj.GetProtocolAndHost());
+  if (auto res = cli.Get(url_obj.GetPathAndQuery());
+      res->status == httplib::StatusCode::OK_200) {
+    auto body = json::parse(res->body);
+    auto assets = body["assets"];
+    auto os_arch{system_info.os + "-" + system_info.arch};
+
+    std::vector<std::string> variants;
+    for (auto& asset : assets) {
+      auto asset_name = asset["name"].get<std::string>();
+      variants.push_back(asset_name);
+    }
+
+    auto cuda_driver_version = system_info_utils::GetCudaVersion();
+    CTL_INF("engine: " << engine);
+    CTL_INF("CUDA version: " << cuda_driver_version);
+    std::string matched_variant = "";
+
+    if (engine == "cortex.tensorrt-llm") {
+      matched_variant = engine_matcher_utils::ValidateTensorrtLlm(
+          variants, system_info.os, cuda_driver_version);
+    } else if (engine == "cortex.onnx") {
+      matched_variant = engine_matcher_utils::ValidateOnnx(
+          variants, system_info.os, system_info.arch);
+    } else if (engine == "cortex.llamacpp") {
+      cortex::cpuid::CpuInfo cpu_info;
+      auto suitable_avx = engine_matcher_utils::GetSuitableAvxVariant(cpu_info);
+      matched_variant = engine_matcher_utils::Validate(
+          variants, system_info.os, system_info.arch, suitable_avx,
+          cuda_driver_version);
+    }
+    CTL_INF("Matched variant: " << matched_variant);
+    if (matched_variant.empty()) {
+      CTL_ERR("No variant found for " << os_arch);
+      throw std::runtime_error("No variant found for " + os_arch);
+    }
+
+    for (auto& asset : assets) {
+      auto assetName = asset["name"].get<std::string>();
+      if (assetName == matched_variant) {
+        auto download_url = asset["browser_download_url"].get<std::string>();
+        auto file_name = asset["name"].get<std::string>();
+        CTL_INF("Download url: " << download_url);
+
+        std::filesystem::path engine_folder_path =
+            file_manager_utils::GetContainerFolderPath(
+                file_manager_utils::DownloadTypeToString(
+                    DownloadType::Engine)) /
+            engine;
+
+        if (!std::filesystem::exists(engine_folder_path)) {
+          CTL_INF("Creating " << engine_folder_path.string());
+          std::filesystem::create_directories(engine_folder_path);
+        }
+
+        CTL_INF("Engine folder path: " << engine_folder_path.string() << "\n");
+        auto local_path = engine_folder_path / file_name;
+        auto downloadTask{DownloadTask{.id = engine,
+                                       .type = DownloadType::Engine,
+                                       .items = {DownloadItem{
+                                           .id = engine,
+                                           .downloadUrl = download_url,
+                                           .localPath = local_path,
+                                       }}}};
+
+        DownloadService download_service;
+        download_service.AddDownloadTask(
+            downloadTask, [](const DownloadTask& finishedTask) {
+              // try to unzip the downloaded file
+              CTL_INF("Engine zip path: "
+                      << finishedTask.items[0].localPath.string());
+
+              std::filesystem::path extract_path =
+                  finishedTask.items[0].localPath.parent_path().parent_path();
+
+              archive_utils::ExtractArchive(
+                  finishedTask.items[0].localPath.string(),
+                  extract_path.string());
+
+              // remove the downloaded file
+              try {
+                std::filesystem::remove(finishedTask.items[0].localPath);
+              } catch (const std::exception& e) {
+                CTL_WRN("Could not delete file: " << e.what());
+              }
+              CTL_INF("Finished!");
+            });
+        if (system_info.os == "mac" || engine == "cortex.onnx") {
+          // mac and onnx engine does not require cuda toolkit
+          return;
+        }
+
+        if (cuda_driver_version.empty()) {
+          CTL_WRN("No cuda driver, continue with CPU");
+          return;
+        }
+
+        // download cuda toolkit
+        const std::string jan_host = "https://catalog.jan.ai";
+        const std::string cuda_toolkit_file_name = "cuda.tar.gz";
+        const std::string download_id = "cuda";
+
+        // TODO: we don't have API to retrieve list of cuda toolkit dependencies atm because we hosting it at jan
+        //  will have better logic after https://github.com/janhq/cortex/issues/1046 finished
+        // for now, assume that we have only 11.7 and 12.4
+        auto suitable_toolkit_version = "";
+        if (engine == "cortex.tensorrt-llm") {
+          // for tensorrt-llm, we need to download cuda toolkit v12.4
+          suitable_toolkit_version = "12.4";
+        } else {
+          // llamacpp
+          auto cuda_driver_semver =
+              semantic_version_utils::SplitVersion(cuda_driver_version);
+          if (cuda_driver_semver.major == 11) {
+            suitable_toolkit_version = "11.7";
+          } else if (cuda_driver_semver.major == 12) {
+            suitable_toolkit_version = "12.4";
+          }
+        }
+
+        // compare cuda driver version with cuda toolkit version
+        // cuda driver version should be greater than toolkit version to ensure compatibility
+        if (semantic_version_utils::CompareSemanticVersion(
+                cuda_driver_version, suitable_toolkit_version) < 0) {
+          CTL_ERR("Your Cuda driver version "
+                  << cuda_driver_version
+                  << " is not compatible with cuda toolkit version "
+                  << suitable_toolkit_version);
+          throw std::runtime_error(
+              "Cuda driver is not compatible with cuda toolkit");
+        }
+
+        std::ostringstream cuda_toolkit_url;
+        cuda_toolkit_url << jan_host << "/" << "dist/cuda-dependencies/"
+                         << cuda_driver_version << "/" << system_info.os << "/"
+                         << cuda_toolkit_file_name;
+
+        LOG_DEBUG << "Cuda toolkit download url: " << cuda_toolkit_url.str();
+        auto cuda_toolkit_local_path =
+            file_manager_utils::GetContainerFolderPath(
+                file_manager_utils::DownloadTypeToString(
+                    DownloadType::CudaToolkit)) /
+            cuda_toolkit_file_name;
+        LOG_DEBUG << "Download to: " << cuda_toolkit_local_path.string();
+        auto downloadCudaToolkitTask{DownloadTask{
+            .id = download_id,
+            .type = DownloadType::CudaToolkit,
+            .items = {DownloadItem{.id = download_id,
+                                   .downloadUrl = cuda_toolkit_url.str(),
+                                   .localPath = cuda_toolkit_local_path}},
+        }};
+
+        download_service.AddDownloadTask(
+            downloadCudaToolkitTask, [&](const DownloadTask& finishedTask) {
+              auto engine_path =
+                  file_manager_utils::GetEnginesContainerPath() / engine;
+              archive_utils::ExtractArchive(
+                  finishedTask.items[0].localPath.string(),
+                  engine_path.string());
+
+              try {
+                std::filesystem::remove(finishedTask.items[0].localPath);
+              } catch (std::exception& e) {
+                CTL_ERR("Error removing downloaded file: " << e.what());
+              }
+            });
+        return;
+      }
+    }
+  } else {
+    throw std::runtime_error("Failed to fetch engine release: " + engine);
+  }
+}
+
 void EngineService::UninstallEngine(const std::string& engine) {
   // TODO: Unload the model which is currently running on engine_
 
diff --git a/engine/services/engine_service.h b/engine/services/engine_service.h
index c6bd4f83a..442923356 100644
--- a/engine/services/engine_service.h
+++ b/engine/services/engine_service.h
@@ -1,5 +1,6 @@
 #pragma once
 
+#include <optional>
 #include <string>
 #include <string_view>
 #include <vector>
@@ -15,14 +16,19 @@ struct EngineInfo {
 
 class EngineService {
  public:
+  constexpr static auto kIncompatible = "Incompatible";
+  constexpr static auto kReady = "Ready";
+  constexpr static auto kNotInstalled = "Not Installed";
+
   const std::vector<std::string_view> kSupportEngines = {
       "cortex.llamacpp", "cortex.onnx", "cortex.tensorrt-llm"};
 
-  EngineInfo GetEngineInfo(const std::string& engine) const;
+  std::optional<EngineInfo> GetEngineInfo(const std::string& engine) const;
 
   std::vector<EngineInfo> GetEngineInfoList() const;
 
-  void InstallEngine(const std::string& engine);
+  void InstallEngine(const std::string& engine,
+                     const std::string& version = "latest");
 
   void UninstallEngine(const std::string& engine);
 };
diff --git a/engine/services/model_service.cc b/engine/services/model_service.cc
new file mode 100644
index 000000000..7943cace4
--- /dev/null
+++ b/engine/services/model_service.cc
@@ -0,0 +1,116 @@
+#include "model_service.h"
+#include <filesystem>
+#include <iostream>
+#include "commands/cmd_info.h"
+#include "utils/cortexso_parser.h"
+#include "utils/file_manager_utils.h"
+#include "utils/logging_utils.h"
+#include "utils/model_callback_utils.h"
+#include "utils/url_parser.h"
+
+void ModelService::DownloadModel(const std::string& input) {
+  if (input.empty()) {
+    throw std::runtime_error(
+        "Input must be Cortex Model Hub handle or HuggingFace url!");
+  }
+
+  // case input is a direct url
+  auto url_obj = url_parser::FromUrlString(input);
+  // TODO: handle case user paste url from cortexso
+  if (url_obj.protocol == "https") {
+    if (url_obj.host != kHuggingFaceHost) {
+      CLI_LOG("Only huggingface.co is supported for now");
+      return;
+    }
+    return DownloadModelByDirectUrl(input);
+  } else {
+    commands::CmdInfo ci(input);
+    return DownloadModelFromCortexso(ci.model_name, ci.branch);
+  }
+}
+
+std::optional<config::ModelConfig> ModelService::GetDownloadedModel(
+    const std::string& modelId) const {
+  auto models_path = file_manager_utils::GetModelsContainerPath();
+  if (!std::filesystem::exists(models_path) ||
+      !std::filesystem::is_directory(models_path)) {
+    return std::nullopt;
+  }
+
+  for (const auto& entry : std::filesystem::directory_iterator(models_path)) {
+    if (entry.is_regular_file() &&
+        entry.path().filename().string() == modelId &&
+        entry.path().extension() == ".yaml") {
+      try {
+        config::YamlHandler handler;
+        handler.ModelConfigFromFile(entry.path().string());
+        auto model_conf = handler.GetModelConfig();
+        return model_conf;
+      } catch (const std::exception& e) {
+        LOG_ERROR << "Error reading yaml file '" << entry.path().string()
+                  << "': " << e.what();
+      }
+    }
+  }
+  return std::nullopt;
+}
+
+void ModelService::DownloadModelByDirectUrl(const std::string& url) {
+  // check for malformed url
+  // question: What if the url is from cortexso itself
+  // answer: then route to download from cortexso
+  auto url_obj = url_parser::FromUrlString(url);
+
+  if (url_obj.host == kHuggingFaceHost) {
+    // goto hugging face parser to normalize the url
+    // loop through path params, replace blob to resolve if any
+    if (url_obj.pathParams[2] == "blob") {
+      url_obj.pathParams[2] = "resolve";
+    }
+  }
+
+  // should separate this function out
+  auto model_id{url_obj.pathParams[1]};
+  auto file_name{url_obj.pathParams.back()};
+
+  auto local_path =
+      file_manager_utils::GetModelsContainerPath() / model_id / model_id;
+
+  try {
+    std::filesystem::create_directories(local_path.parent_path());
+  } catch (const std::filesystem::filesystem_error& e) {
+    // if file exist, remove it
+    std::filesystem::remove(local_path.parent_path());
+    std::filesystem::create_directories(local_path.parent_path());
+  }
+
+  auto download_url = url_parser::FromUrl(url_obj);
+  // this assume that the model being downloaded is a single gguf file
+  auto downloadTask{DownloadTask{.id = url_obj.pathParams.back(),
+                                 .type = DownloadType::Model,
+                                 .items = {DownloadItem{
+                                     .id = url_obj.pathParams.back(),
+                                     .downloadUrl = download_url,
+                                     .localPath = local_path,
+                                 }}}};
+
+  auto on_finished = [](const DownloadTask& finishedTask) {
+    std::cout << "Download success" << std::endl;
+    auto gguf_download_item = finishedTask.items[0];
+    model_callback_utils::ParseGguf(gguf_download_item);
+  };
+
+  download_service_.AddDownloadTask(downloadTask, on_finished);
+}
+
+void ModelService::DownloadModelFromCortexso(const std::string& name,
+                                             const std::string& branch) {
+  auto downloadTask = cortexso_parser::getDownloadTask(name, branch);
+  if (downloadTask.has_value()) {
+    DownloadService().AddDownloadTask(downloadTask.value(),
+                                      model_callback_utils::DownloadModelCb);
+    CTL_INF("Download finished");
+  } else {
+    CTL_ERR("Model not found");
+  }
+}
diff --git a/engine/services/model_service.h b/engine/services/model_service.h
new file mode 100644
index 000000000..81ec4e4b3
--- /dev/null
+++ b/engine/services/model_service.h
@@ -0,0 +1,25 @@
+#pragma once
+
+#include <string>
+#include "config/model_config.h"
+#include "services/download_service.h"
+
+class ModelService {
+ public:
+  ModelService() : download_service_{DownloadService()} {};
+
+  void DownloadModel(const std::string& input);
+
+  std::optional<config::ModelConfig> GetDownloadedModel(
+      const std::string& modelId) const;
+
+ private:
+  void DownloadModelByDirectUrl(const std::string& url);
+
+  void DownloadModelFromCortexso(const std::string& name,
+                                 const std::string& branch);
+
+  DownloadService download_service_;
+
+  constexpr auto static kHuggingFaceHost = "huggingface.co";
+};
diff --git a/engine/test/components/test_url_parser.cc b/engine/test/components/test_url_parser.cc
index 375ce385d..decffcbf8 100644
--- a/engine/test/components/test_url_parser.cc
+++ b/engine/test/components/test_url_parser.cc
@@ -1,3 +1,4 @@
+#include <iostream>
 #include "gtest/gtest.h"
 #include "utils/url_parser.h"
 
@@ -24,3 +25,48 @@ TEST_F(UrlParserTestSuite, ConstructUrlCorrectly) {
 
   EXPECT_EQ(url_str, kValidUrlWithOnlyPaths);
 }
+
+TEST_F(UrlParserTestSuite, ConstructUrlWithQueryCorrectly) {
+  auto url = url_parser::Url{
+      .protocol = "https",
+      .host = "jan.ai",
+      .pathParams = {"path1", "path2"},
+      .queries = {{"key1", "value1"}, {"key2", 2}, {"key3", true}},
+  };
+  auto url_str = url_parser::FromUrl(url);
+
+  auto contains_key1 = url_str.find("key1=value1") != std::string::npos;
+  auto contains_key2 = url_str.find("key2=2") != std::string::npos;
+  auto contains_key3 = url_str.find("key3=true") != std::string::npos;
+
+  EXPECT_TRUE(contains_key1);
+  EXPECT_TRUE(contains_key2);
+  EXPECT_TRUE(contains_key3);
+}
+
+TEST_F(UrlParserTestSuite, ConstructUrlWithEmptyPathCorrectly) {
+  auto url = url_parser::Url{
+      .protocol = "https",
+      .host = "jan.ai",
+      .pathParams = {},
+  };
+  auto url_str = url_parser::FromUrl(url);
+
+  EXPECT_EQ(url_str, "https://jan.ai");
+}
+
+TEST_F(UrlParserTestSuite, GetProtocolAndHostCorrectly) {
+  auto url = url_parser::Url{.protocol = "https", .host = "jan.ai"};
+  auto protocol_and_host = url.GetProtocolAndHost();
+  EXPECT_EQ(protocol_and_host, "https://jan.ai");
+}
+
+TEST_F(UrlParserTestSuite, GetPathAndQueryCorrectly) {
+  auto url = url_parser::Url{
+      .protocol = "https",
+      .host = "jan.ai",
+      .pathParams = {"path1", "path2"},
+  };
+  auto path_and_query = url.GetPathAndQuery();
+  EXPECT_EQ(path_and_query, "/path1/path2");
+}
diff --git a/engine/utils/cortexso_parser.h b/engine/utils/cortexso_parser.h
index a9592d98d..d4e85bee9 100644
--- a/engine/utils/cortexso_parser.h
+++ b/engine/utils/cortexso_parser.h
@@ -7,6 +7,7 @@
 #include <nlohmann/json.hpp>
 #include "httplib.h"
 #include "utils/file_manager_utils.h"
+#include "utils/logging_utils.h"
 
 namespace cortexso_parser {
 constexpr static auto kHuggingFaceHost = "https://huggingface.co";
@@ -34,7 +35,7 @@ inline std::optional<DownloadTask> getDownloadTask(
         file_manager_utils::CreateDirectoryRecursively(
             model_container_path.string());
 
-        for (auto& [key, value] : jsonResponse.items()) {
+        for (const auto& [key, value] : jsonResponse.items()) {
           std::ostringstream downloadUrlOutput;
           auto path = value["path"].get<std::string>();
           if (path == ".gitattributes" || path == ".gitignore" ||
@@ -58,7 +59,7 @@ inline std::optional<DownloadTask> getDownloadTask(
 
         return downloadTask;
       } catch (const json::parse_error& e) {
-        std::cerr << "JSON parse error: " << e.what() << std::endl;
+        CTL_ERR("JSON parse error: {}" << e.what());
       }
     }
   } else {
diff --git a/engine/utils/file_manager_utils.h b/engine/utils/file_manager_utils.h
index 8e449b3d2..7b12fab43 100644
--- a/engine/utils/file_manager_utils.h
+++ b/engine/utils/file_manager_utils.h
@@ -208,7 +208,7 @@ inline std::filesystem::path GetModelsContainerPath() {
   if (!std::filesystem::exists(models_container_path)) {
     CTL_INF("Model container folder not found. Create one: "
             << models_container_path.string());
-    std::filesystem::create_directory(models_container_path);
+    std::filesystem::create_directories(models_container_path);
   }
 
   return models_container_path;
diff --git a/engine/utils/url_parser.h b/engine/utils/url_parser.h
index dba3ec0a5..55dd557b8 100644
--- a/engine/utils/url_parser.h
+++ b/engine/utils/url_parser.h
@@ -7,14 +7,29 @@
 #include "exceptions/malformed_url_exception.h"
 
 namespace url_parser {
-// TODO: add an unordered map to store the query
-// TODO: add a function to construct a string from Url
-
 struct Url {
   std::string protocol;
   std::string host;
   std::vector<std::string> pathParams;
   std::unordered_map<std::string, std::variant<std::string, int, bool>> queries;
+
+  std::string GetProtocolAndHost() const { return protocol + "://" + host; }
+
+  std::string GetPathAndQuery() const {
+    std::string path;
+    for (const auto& path_param : pathParams) {
+      path += "/" + path_param;
+    }
+    std::string query;
+    for (const auto& [key, value] : queries) {
+      query += key + "=" + std::get<std::string>(value) + "&";
+    }
+    if (!query.empty()) {
+      query.pop_back();
+      return path + "?" + query;
+    }
+    return path;
+  };
 };
 
 const std::regex url_regex(
@@ -81,7 +96,31 @@ inline std::string FromUrl(const Url& url) {
     url_string << "/" << path;
   }
 
-  // TODO: handle queries
+  std::string query_string;
+  for (const auto& [key, value] : url.queries) {
+    try {
+      std::string value_str;
+      if (std::holds_alternative<std::string>(value)) {
+        value_str = std::get<std::string>(value);
+      } else if (std::holds_alternative<int>(value)) {
+        value_str = std::to_string(std::get<int>(value));
+      } else if (std::holds_alternative<bool>(value)) {
+        value_str = std::get<bool>(value) ? "true" : "false";
+      }
+      if (!query_string.empty()) {
+        query_string += "&";
+      }
+      query_string += key + "=" + value_str;
+    } catch (const std::bad_variant_access& e) {
+      // Handle the case where the variant does not match any of the expected types
+      // This should not happen if the map was created correctly
+      throw std::runtime_error("Invalid variant type in queries map");
+    }
+  }
+
+  if (!query_string.empty()) {
+    url_string << "?" << query_string;
+  }
 
   return url_string.str();
 }