Skip to content

Commit 3dafa1b

Browse files
mvafinclaude
andcommitted
[GGUF FE] Fix dequant/op correctness bugs and Q2_K/Q3_K packing
Correctness fixes found in code review: - Q4_0 requant read u4 nibbles as unsigned, ignoring the 0x88 XOR sign encoding; token_embd/output Q4_0 weights were corrupted. Read as signed i4 when there is no zero-point. - GELU/GEGLU used OpenVINO's default (ERF) Gelu; ggml uses the tanh approximation. Pass GeluApproximationMode::TANH. - MUL_MAT type-mismatch Convert re-read the original input(0), discarding the op_case 2/3 reprocessing of B and targeting the wrong element type. - SCALE aborted when the "bias" attribute was absent; give scale/bias defaults like SOFT_MAX. - IMROPE read data_node->get_shape()[3], throwing on a dynamic dim; use the decoder output_shape like the NORMAL/NEOX branches. - VIEW op_case 3 could leave slice_dim == -1 and index the shape at SIZE_MAX; return the input unchanged for an identity view. - SetRows stateless lowering only guarded dim 2 against dynamic; emit -1 for any dynamic batch/n_seq/emb dim. - FLASH_ATTN_EXT did not convert k/v to f16, so f32 K/V hit SDPA's mixed-type check; convert explicitly. - Q4_K integer zero-point (u8) was unclamped and could wrap mod 256; clamp to [0,255] via a shared helper. Q2_K / Q3_K dequant (previously DISABLED as known-failing): - Q2_K: block layout is scales-first then qs (offsets were swapped), and qs is not in element order, so replace the memcpy with a per-element unpack into LSB-packed u2 matching ggml's dequantize_row_q2_K. - Q3_K: rebuild the 16 signed 6-bit sub-scales with ggml's kmask1/kmask2 interleave (-32 bias) and unpack qs/hmask in ggml's strided element order. - dequant_extracted_to_f32 (Q8_0_C requant path) gains a u2 branch for Q2_K and reads no-zp 4-bit weights as signed (Q3_K centered i4 / Q4_0). - Re-enable the Q2_K/Q3_K dequant and weight tests; both now match the ggml reference within f16 tolerance. Infra / hygiene: - Drop the duplicate ov::WeightlessCacheAttribute definition and include the canonical core dev_api header (matches the ONNX frontend), fixing an ODR hazard. - Rename public headers frontend.h/decoder.h to .hpp so ov_add_frontend (which installs *.hpp only) ships them for direct linkers. - Parallelize fill_q6_k (the only serial fill) and reuse the requant flag instead of recomputing needs_q8_0_c_requant. - Correct the stale features.cmake / src CMakeLists comments (the FE is installed and exports entry points; it is hidden via m_hidden), and remove leftover commented-out debug code. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
1 parent b18361b commit 3dafa1b

22 files changed

Lines changed: 187 additions & 106 deletions

cmake/features.cmake

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -155,8 +155,10 @@ ov_option(ENABLE_OV_PYTORCH_FRONTEND "Enable PyTorch FrontEnd" ON)
155155
ov_option(ENABLE_OV_JAX_FRONTEND "Enable JAX FrontEnd" ON)
156156
ov_option(ENABLE_OV_TF_FRONTEND "Enable TensorFlow FrontEnd" ON)
157157
ov_option(ENABLE_OV_TF_LITE_FRONTEND "Enable TensorFlow Lite FrontEnd" ON)
158-
# GGUF frontend is an MVP development/testing component; disabled by default and never
159-
# installed into the release package (see SKIP_INSTALL in its CMakeLists.txt).
158+
# GGUF frontend is an MVP development/testing component, disabled by default. When enabled it is
159+
# built and installed as a linkable frontend (LINKABLE_FRONTEND), but it registers itself as
160+
# hidden (FrontEndPluginInfo::m_hidden) so FrontEndManager never lists or auto-selects it -- it is
161+
# usable only by direct linkers. See the discoverability note in frontend.cpp.
160162
ov_option(ENABLE_OV_GGUF_FRONTEND "Enable GGUF FrontEnd" OFF)
161163

162164
if(WIN32 AND AARCH64 AND CMAKE_CL_64)

src/frontends/gguf/include/openvino/frontend/gguf/decoder.h renamed to src/frontends/gguf/include/openvino/frontend/gguf/decoder.hpp

File renamed without changes.

src/frontends/gguf/include/openvino/frontend/gguf/frontend.h renamed to src/frontends/gguf/include/openvino/frontend/gguf/frontend.hpp

File renamed without changes.

src/frontends/gguf/src/CMakeLists.txt

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,13 @@
77
# frontends (LINKABLE_FRONTEND, no SKIP_INSTALL) so callers -- the llama.cpp ggml-openvino
88
# backend and OpenVINO GenAI -- can link openvino::frontend::gguf directly.
99
#
10-
# It is intentionally NOT discoverable via FrontEndManager: it exports no plugin-registration
11-
# entry points (get_api_version / GetFrontEndData), so it never appears in available_front_ends()
12-
# and is never auto-selected by load_by_model / load_by_framework, even though it is installed in
13-
# the frontend directory. See the discoverability note in frontend.cpp. This keeps the
14-
# not-yet-production frontend hidden from the generic model-loading API while remaining usable by
15-
# direct linkers.
10+
# It is intentionally NOT discoverable via FrontEndManager: it DOES export the plugin-registration
11+
# entry points (get_api_version / get_front_end_data, required so FrontEndManager can load it
12+
# without error while scanning the frontend directory), but it registers itself as hidden
13+
# (FrontEndPluginInfo::m_hidden = true), so it never appears in available_front_ends() and is never
14+
# auto-selected by load_by_model / load_by_framework. See the discoverability note in frontend.cpp.
15+
# This keeps the not-yet-production frontend hidden from the generic model-loading API while
16+
# remaining usable by direct linkers.
1617
ov_add_frontend(NAME gguf
1718
LINKABLE_FRONTEND
1819
FILEDESCRIPTION "FrontEnd to convert GGUF models"

src/frontends/gguf/src/frontend.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,13 @@
22
// SPDX-License-Identifier: Apache-2.0
33
//
44

5-
#include "openvino/frontend/gguf/frontend.h"
5+
#include "openvino/frontend/gguf/frontend.hpp"
66

77
#include "input_model.h"
88
#include "op_table.h"
99
#include "openvino/core/so_extension.hpp"
1010
#include "openvino/frontend/extension/decoder_transformation.hpp"
11-
#include "openvino/frontend/gguf/decoder.h"
11+
#include "openvino/frontend/gguf/decoder.hpp"
1212
#include "openvino/frontend/manager.hpp"
1313
#include "translate_session.h"
1414

src/frontends/gguf/src/input_model.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
#include "input_model.h"
66

7-
#include "openvino/frontend/gguf/decoder.h"
7+
#include "openvino/frontend/gguf/decoder.hpp"
88

99
namespace ov {
1010
namespace frontend {

src/frontends/gguf/src/input_model.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
#include <openvino/frontend/input_model.hpp>
88

9-
#include "openvino/frontend/gguf/decoder.h"
9+
#include "openvino/frontend/gguf/decoder.hpp"
1010
#include "openvino/frontend/gguf/visibility.hpp"
1111

1212
namespace ov {

src/frontends/gguf/src/node_context.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
#include <openvino/frontend/node_context.hpp>
99
#include <string>
1010

11-
#include "openvino/frontend/gguf/decoder.h"
11+
#include "openvino/frontend/gguf/decoder.hpp"
1212

1313
namespace ov {
1414
namespace frontend {

src/frontends/gguf/src/op/flash_attn_ext.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,16 @@ OutputVector translate_flash_attn_ext(const NodeContext& context) {
8080
k = tile_kv(q_shape[1], k_shape[1], q_shape[3], k);
8181
v = tile_kv(q_shape[1], k_shape[1], q_shape[3], v);
8282

83+
// q/mask/scale are f16; SDPA requires all real inputs share an element type. k/v usually come
84+
// from an f16 KV-cache, but a current-token K/V that did not pass through the cache can be f32,
85+
// so convert explicitly to avoid a "Mixed input types are not supported" validation failure.
86+
if (k.get_element_type() != ov::element::f16) {
87+
k = std::make_shared<ov::op::v0::Convert>(k, ov::element::f16);
88+
}
89+
if (v.get_element_type() != ov::element::f16) {
90+
v = std::make_shared<ov::op::v0::Convert>(v, ov::element::f16);
91+
}
92+
8393
auto sdpa = std::make_shared<ov::op::v13::ScaledDotProductAttention>(q, k, v, mask_sliced, scale_node, false);
8494
res = std::make_shared<ov::op::v1::Transpose>(sdpa,
8595
ov::op::v0::Constant::create(ov::element::i64, {4}, {0, 2, 1, 3}));

src/frontends/gguf/src/op/glu_geglu.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,8 @@ OutputVector translate_glu_geglu(const NodeContext& context) {
5151
std::swap(src0, src1);
5252
}
5353

54-
auto gelu = std::make_shared<ov::op::v7::Gelu>(src0);
54+
// ggml's GEGLU uses the tanh GELU approximation (ggml_gelu_f32); v7::Gelu defaults to ERF.
55+
auto gelu = std::make_shared<ov::op::v7::Gelu>(src0, ov::op::GeluApproximationMode::TANH);
5556
auto res = std::make_shared<ov::op::v1::Multiply>(gelu, src1);
5657

5758
return rename_outputs_with_suffix({res}, context.get_name());

0 commit comments

Comments
 (0)