Skip to content

Commit 3b3815f

Browse files
committed
[GGUF FE] Add tests and typed decoder API; Q4_K integer zero-point
Add the frontend's test suite (per-op tests, weight/dequant-vs-ggml tests with real-ggml reference data) and adopt the typed GgufDecoder API the translators consume. Includes the Q4_K integer (u8) zero-point so the CPU/GPU plugin fuses the dequant into the MatMul, matching the original ggml-openvino backend's performance.
1 parent 89a0777 commit 3b3815f

76 files changed

Lines changed: 3037 additions & 279 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/debian_10_arm.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,7 @@ jobs:
117117
-DENABLE_CONFORMANCE_PGQL=ON
118118
-DENABLE_LTO=ON
119119
-DENABLE_TESTS=ON
120+
-DENABLE_OV_GGUF_FRONTEND=ON
120121
-DENABLE_PYTHON=OFF
121122
-DTHREADING=SEQ
122123
-DCMAKE_TOOLCHAIN_FILE=cmake/arm.toolchain.cmake

.github/workflows/job_cxx_unit_tests.yml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,11 @@ jobs:
128128
${{ env.SOURCE_COMMAND }} ${{ env.SETUPVARS }}
129129
${{ env.INSTALL_TEST_DIR }}/ov_ir_frontend_tests --gtest_print_time=1 --gtest_output=xml:${{ env.INSTALL_TEST_DIR }}/TEST-IRFrontend.xml
130130
131+
- name: GGUF frontend tests
132+
run: |
133+
${{ env.SOURCE_COMMAND }} ${{ env.SETUPVARS }}
134+
${{ env.INSTALL_TEST_DIR }}/ov_gguf_frontend_tests --gtest_print_time=1 --gtest_output=xml:${{ env.INSTALL_TEST_DIR }}/TEST-GGUFFrontend.xml
135+
131136
- name: PaddlePaddle frontend tests
132137
if: ${{ fromJSON(inputs.affected-components).PDPD_FE.test && runner.os != 'Windows' }} # Ticket: 149651
133138
run: |

.github/workflows/linux_arm64.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,7 @@ jobs:
114114
-G 'Ninja Multi-Config'
115115
-DENABLE_NCC_STYLE=OFF
116116
-DENABLE_TESTS=ON
117+
-DENABLE_OV_GGUF_FRONTEND=ON
117118
-DENABLE_STRICT_DEPENDENCIES=OFF
118119
-DENABLE_SYSTEM_OPENCL=ON
119120
-DCMAKE_EXPORT_COMPILE_COMMANDS=ON

.github/workflows/mac_arm64.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,7 @@ jobs:
184184
-G "${{ env.CMAKE_GENERATOR }}" \
185185
-DENABLE_NCC_STYLE=OFF \
186186
-DENABLE_TESTS=ON \
187+
-DENABLE_OV_GGUF_FRONTEND=ON \
187188
-DENABLE_WHEEL=OFF \
188189
-DCMAKE_COMPILE_WARNING_AS_ERROR=OFF \
189190
-DENABLE_STRICT_DEPENDENCIES=OFF \

.github/workflows/ubuntu_22.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,7 @@ jobs:
138138
-DENABLE_TESTS=ON
139139
-DENABLE_STRICT_DEPENDENCIES=OFF
140140
-DENABLE_SYSTEM_OPENCL=ON
141+
-DENABLE_OV_GGUF_FRONTEND=ON
141142
-DCMAKE_EXPORT_COMPILE_COMMANDS=ON
142143
-DCPACK_GENERATOR=TGZ
143144
-DENABLE_WHEEL=ON

.github/workflows/ubuntu_24.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,7 @@ jobs:
117117
-G 'Ninja Multi-Config'
118118
-DENABLE_NCC_STYLE=OFF
119119
-DENABLE_TESTS=ON
120+
-DENABLE_OV_GGUF_FRONTEND=ON
120121
-DENABLE_STRICT_DEPENDENCIES=OFF
121122
-DENABLE_SYSTEM_OPENCL=ON
122123
-DCMAKE_EXPORT_COMPILE_COMMANDS=ON

.github/workflows/windows_vs2022_debug.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ jobs:
7373
-G 'Ninja'
7474
-DENABLE_PYTHON=OFF
7575
-DENABLE_TESTS=ON
76+
-DENABLE_OV_GGUF_FRONTEND=ON
7677
-DCMAKE_COMPILE_WARNING_AS_ERROR=ON
7778
-DENABLE_STRICT_DEPENDENCIES=OFF
7879
-DCMAKE_DISABLE_FIND_PACKAGE_PkgConfig=ON

.github/workflows/windows_vs2022_release.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@ jobs:
7777
-DENABLE_PYTHON=ON
7878
-DENABLE_WHEEL=OFF
7979
-DENABLE_TESTS=ON
80+
-DENABLE_OV_GGUF_FRONTEND=ON
8081
-DCMAKE_COMPILE_WARNING_AS_ERROR=ON
8182
-DENABLE_STRICT_DEPENDENCIES=OFF
8283
-DCMAKE_DISABLE_FIND_PACKAGE_PkgConfig=ON

src/frontends/gguf/CMakeLists.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,7 @@
33
#
44

55
add_subdirectory(src)
6+
7+
if(ENABLE_TESTS)
8+
add_subdirectory(tests)
9+
endif()

src/frontends/gguf/include/openvino/frontend/gguf/decoder.h

Lines changed: 65 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
// Copyright (C) 2018-2026 Intel Corporation
2+
// SPDX-License-Identifier: Apache-2.0
3+
//
4+
15
#pragma once
26

37
#include <cstdint>
@@ -10,42 +14,73 @@ namespace ov {
1014
namespace frontend {
1115
namespace gguf {
1216

17+
// Typed RoPE configuration, replacing the raw gguf `op_params`/`rope_params` int32 array
18+
// the translators used to dereference by byte offset. A decoder is responsible for
19+
// producing this (the llama.cpp cgraph decoder by parsing ggml's layout, a future
20+
// gguf-file decoder by construction), so the op translators never need to know ggml's
21+
// memory layout.
22+
struct RopeConfig {
23+
int n_dims = 0;
24+
int n_ctx_orig = 0;
25+
float freq_base = 0.0f;
26+
float freq_scale = 0.0f;
27+
float ext_factor = 0.0f;
28+
float attn_factor = 0.0f;
29+
float beta_fast = 0.0f;
30+
float beta_slow = 0.0f;
31+
};
32+
33+
// Decoder interface consumed by the gguf frontend translators.
34+
//
35+
// This is a typed, ggml-free interface: operation parameters are exposed through
36+
// get_attribute(node_idx, name) / get_input_view_offset / RopeConfig rather than as raw
37+
// ggml `op_params` int32 arrays. A concrete decoder (e.g. the llama.cpp cgraph decoder)
38+
// only has to translate ggml's layout into these typed accessors -- the op translators
39+
// here never touch ggml memory directly.
1340
class GgufDecoder : public DecoderBase {
1441
public:
15-
virtual ov::Any get_attribute(const std::string& name) const = 0;
42+
ov::Any get_attribute(const std::string& name) const override = 0;
43+
44+
// Per-node typed attribute access. This is the mechanism the op translators use to
45+
// read scalar operation parameters (e.g. "eps", "scale", "bias", "swapped",
46+
// "softmax_axis", "rope_config") without dereferencing ggml's raw op_params layout.
47+
virtual ov::Any get_attribute(int node_idx, const std::string& name) const = 0;
1648

1749
virtual PartialShape get_input_shape(int node_idx, const std::string& name) const = 0;
1850

1951
virtual std::vector<size_t> get_input_stride(int node_idx, const std::string& name) const = 0;
2052

53+
// Byte offset of an input that is a gguf VIEW into a larger tensor (0 when the input
54+
// is not a view). Replaces the raw `get_input_op_params(...)[0]` read: a view's start
55+
// offset is a single semantic scalar the decoder knows (by parsing ggml, or by
56+
// construction), so translators never touch ggml's op_params layout. Translators
57+
// convert bytes -> elements using get_input_stride as needed.
58+
virtual int64_t get_input_view_offset(int node_idx, const std::string& name) const = 0;
59+
2160
virtual element::Type get_input_type(int node_idx, const std::string& name) const = 0;
2261

23-
virtual size_t get_input_size() const = 0;
62+
size_t get_input_size() const override = 0;
2463

2564
virtual size_t get_input_size(int node_idx) const = 0;
2665

27-
virtual void get_input_node(size_t input_port_idx,
28-
std::string& producer_name,
29-
std::string& producer_output_port_name,
30-
size_t& producer_output_port_index) const = 0;
66+
void get_input_node(size_t input_port_idx,
67+
std::string& producer_name,
68+
std::string& producer_output_port_name,
69+
size_t& producer_output_port_index) const override = 0;
3170

3271
virtual std::vector<std::string> get_input_names(int node_idx) const = 0;
3372

3473
virtual PartialShape get_output_shape(int node_idx) const = 0;
3574

3675
virtual element::Type get_output_type(const int node_idx) const = 0;
3776

38-
virtual int32_t* get_input_op_params(int node_idx, const std::string& name) const = 0;
39-
40-
virtual int32_t * get_output_op_params(int node_idx) const = 0;
41-
4277
virtual std::vector<std::string> get_output_names(int node_idx) const = 0;
4378

44-
virtual const std::string& get_op_type() const = 0;
79+
const std::string& get_op_type() const override = 0;
4580

4681
virtual const std::string& get_op_type(int node_idx) const = 0;
4782

48-
virtual const std::string& get_op_name() const = 0;
83+
const std::string& get_op_name() const override = 0;
4984

5085
virtual const std::string& get_op_name(int node_idx) const = 0;
5186

@@ -55,18 +90,33 @@ class GgufDecoder : public DecoderBase {
5590

5691
virtual const std::map<std::string, std::shared_ptr<ov::Node>>& get_model_inputs() const = 0;
5792
virtual const std::map<std::string, std::shared_ptr<ov::Node>>& get_model_extra_inputs() const = 0;
58-
virtual const std::map<std::string, std::shared_ptr<ov::Node>>& get_model_weights() const = 0;
5993
virtual std::vector<std::string> get_model_output_names() const = 0;
6094

61-
virtual int32_t* get_rope_params() const = 0;
95+
// NOTE: there is no get_model_weights(). A GGUF weight is surfaced as a regular node in
96+
// visit_subgraph with op type "GGML_OP_WEIGHT": the decoder exposes the raw weight bytes
97+
// via get_attribute<ov::Tensor>(node_idx, "data"), the ggml quant type name via
98+
// get_attribute<std::string>(node_idx, "quant_type") (e.g. "Q4_K", "F16") and the logical
99+
// [rows, cols] shape via get_output_shape(node_idx). The frontend's translate_weight does
100+
// the dequant / repacking / requantization, so the decoder never builds OV nodes itself.
101+
102+
// Model-level RoPE configuration, used by TranslateSession::preprocess to pre-build
103+
// the shared rope sin/cos. has_rope() == false means the model uses no RoPE.
104+
virtual bool has_rope() const = 0;
105+
106+
// When true, each ROPE op generates its own sin/cos from its per-op rope_config
107+
// (e.g. gemma4 where SWA and global layers have different n_dims). The shared
108+
// rope_cos/rope_sin tensor is NOT added to the tensor map.
109+
virtual bool use_per_op_rope() const = 0;
110+
111+
virtual RopeConfig get_rope_config() const = 0;
62112

63113
virtual std::map<std::string, std::string> get_kv_param_res_names() const = 0;
64114

65115
virtual bool is_static() const = 0;
66116

67117
virtual bool is_stateful() const = 0;
68118

69-
virtual int is_swa_layer(int layer) const = 0;
119+
virtual bool is_swa_layer(int layer) const = 0;
70120
};
71121

72122
} // namespace gguf

0 commit comments

Comments
 (0)