Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
72 changes: 72 additions & 0 deletions sdk/plugins/qairt/include/qnn_runtime_utils.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,16 @@
#include <windows.h>
#endif

#include <algorithm>
#include <array>
#include <cstdlib>
#include <filesystem>
#include <fstream>
#include <optional>
#include <string>
#include <vector>

#include "external/json.hpp"
#include "types.h"

namespace geniex::qairt::runtime {
Expand All @@ -29,6 +33,74 @@ inline std::optional<std::string> 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<const char*, 6> 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<std::string> 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<std::string> unrecognized;
std::vector<const nlohmann::json*> 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<std::string>();
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.
//
Expand Down
11 changes: 11 additions & 0 deletions sdk/plugins/qairt/src/llm.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
}
Expand Down
11 changes: 11 additions & 0 deletions sdk/plugins/qairt/src/vlm.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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;
}
Expand Down