diff --git a/sdk/plugins/qairt/include/qnn_runtime_utils.h b/sdk/plugins/qairt/include/qnn_runtime_utils.h index 10685f8cb..da4177b79 100644 --- a/sdk/plugins/qairt/include/qnn_runtime_utils.h +++ b/sdk/plugins/qairt/include/qnn_runtime_utils.h @@ -7,12 +7,16 @@ #include #endif +#include +#include #include #include +#include #include #include #include +#include "external/json.hpp" #include "types.h" namespace geniex::qairt::runtime { @@ -29,6 +33,74 @@ inline std::optional find_optional_file(const std::filesystem::path return std::nullopt; } +// dsp_arch values the pinned QAIRT release recognizes; keep in step when the +// QAIRT dependency is bumped. +inline constexpr std::array kRecognizedDspArchs = {"v68", "v69", "v73", "v75", "v79", "v81"}; + +// Returns a loggable diagnostic when `htp_backend_ext_config.json` contains a +// dsp_arch outside kRecognizedDspArchs; nullopt otherwise (including a missing +// or unparseable file — QNN owns those failure modes). +inline std::optional dsp_arch_diagnostic(const std::string& htp_config_path) { + if (htp_config_path.empty()) { + return std::nullopt; + } + std::ifstream file(htp_config_path); + if (!file.is_open()) { + return std::nullopt; + } + const nlohmann::json root = nlohmann::json::parse(file, nullptr, /*allow_exceptions=*/false); + if (root.is_discarded()) { + return std::nullopt; + } + + // Exporters nest dsp_arch differently, so walk the whole document. + std::vector unrecognized; + std::vector stack{&root}; + while (!stack.empty()) { + const nlohmann::json* node = stack.back(); + stack.pop_back(); + if (node->is_object()) { + for (auto it = node->begin(); it != node->end(); ++it) { + if (it.key() == "dsp_arch" && it.value().is_string()) { + const auto arch = it.value().get(); + const bool known = std::find(kRecognizedDspArchs.begin(), kRecognizedDspArchs.end(), arch) != + kRecognizedDspArchs.end(); + if (!known && std::find(unrecognized.begin(), unrecognized.end(), arch) == unrecognized.end()) { + unrecognized.push_back(arch); + } + } else { + stack.push_back(&it.value()); + } + } + } else if (node->is_array()) { + for (const auto& child : *node) { + stack.push_back(&child); + } + } + } + if (unrecognized.empty()) { + return std::nullopt; + } + + std::string offending; + for (const auto& arch : unrecognized) { + if (!offending.empty()) { + offending += ", "; + } + offending += "'" + arch + "'"; + } + std::string recognized; + for (const char* arch : kRecognizedDspArchs) { + if (!recognized.empty()) { + recognized += ", "; + } + recognized += arch; + } + return "htp_backend_ext_config.json specifies dsp_arch " + offending + + ", which this QAIRT release does not recognize (recognized values: " + recognized + + "). The bundle was likely exported for a newer QAIRT; re-export it for this runtime or update QAIRT."; +} + // Returns a QnnRuntimeConfig for the given model directory and optional user-supplied // QNN lib folder path. // diff --git a/sdk/plugins/qairt/src/llm.cpp b/sdk/plugins/qairt/src/llm.cpp index db2dbac6c..bb87a0a2f 100644 --- a/sdk/plugins/qairt/src/llm.cpp +++ b/sdk/plugins/qairt/src/llm.cpp @@ -78,6 +78,12 @@ int32_t QairtLlm::create(const geniex_LlmCreateInput* input) { GENIEX_LOG_DEBUG("Found {} model shards in {}", model_cfg.model_paths.size(), model_dir.string()); + // An unrecognized dsp_arch fails inside QNN with a bare error 1008 (#1254). + const auto dsp_arch_diag = qairt::runtime::dsp_arch_diagnostic(model_cfg.htp_config_path); + if (dsp_arch_diag) { + GENIEX_LOG_WARN("{}", *dsp_arch_diag); + } + // Tokenizer path: an explicit caller override wins over the bundle's own. if (input->tokenizer_path && input->tokenizer_path[0] != '\0') { model_cfg.tokenizer_path = input->tokenizer_path; @@ -100,6 +106,11 @@ int32_t QairtLlm::create(const geniex_LlmCreateInput* input) { // Create LLMPipeline via the model_id-driven dispatcher auto pipe = makeLLMPipeline(runtime_cfg, model_cfg); if (!pipe) { + if (dsp_arch_diag) { + GENIEX_LOG_ERROR( + "Failed to create QAIRT LLM pipeline from bundle: {}. {}", model_dir.string(), *dsp_arch_diag); + return GENIEX_ERROR_COMMON_PARAM_NOT_SUPPORTED; + } GENIEX_LOG_ERROR("Failed to create QAIRT LLM pipeline from bundle: {}", model_dir.string()); return GENIEX_ERROR_COMMON_MODEL_LOAD; } diff --git a/sdk/plugins/qairt/src/vlm.cpp b/sdk/plugins/qairt/src/vlm.cpp index 881c75389..aff42b220 100644 --- a/sdk/plugins/qairt/src/vlm.cpp +++ b/sdk/plugins/qairt/src/vlm.cpp @@ -129,6 +129,12 @@ int32_t QairtVlm::create(const geniex_VlmCreateInput* input) { has_vision_encoder_ = !vision_cfg.model_paths.empty(); vision_cfg.htp_config_path = llm_cfg.htp_config_path; + // An unrecognized dsp_arch fails inside QNN with a bare error 1008 (#1254). + const auto dsp_arch_diag = qairt::runtime::dsp_arch_diagnostic(llm_cfg.htp_config_path); + if (dsp_arch_diag) { + GENIEX_LOG_WARN("{}", *dsp_arch_diag); + } + // ── Build VLMConfig and create pipeline ─────────────────────────────────── VLMConfig vlm_cfg{}; vlm_cfg.llm_config = std::move(llm_cfg); @@ -138,6 +144,11 @@ int32_t QairtVlm::create(const geniex_VlmCreateInput* input) { // the matching VLM family factory (currently qwen2_5_vl_*). auto pipe = makeVLMPipeline(runtime_cfg, vlm_cfg); if (!pipe) { + if (dsp_arch_diag) { + GENIEX_LOG_ERROR( + "Failed to create QAIRT VLM pipeline from bundle: {}. {}", model_dir.string(), *dsp_arch_diag); + return GENIEX_ERROR_COMMON_PARAM_NOT_SUPPORTED; + } GENIEX_LOG_ERROR("Failed to create QAIRT VLM pipeline from bundle: {}", model_dir.string()); return GENIEX_ERROR_COMMON_MODEL_LOAD; }