Skip to content

Commit 0adec4a

Browse files
authored
Validate Structured Output Config before execution (#3584)
1 parent c1a41f5 commit 0adec4a

15 files changed

+178
-6
lines changed

Makefile

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -161,11 +161,11 @@ ifeq ($(findstring ubuntu,$(BASE_OS)),ubuntu)
161161
ifeq ($(BASE_OS_TAG),24.04)
162162
OS=ubuntu24
163163
INSTALL_DRIVER_VERSION ?= "24.52.32224"
164-
DLDT_PACKAGE_URL ?= https://storage.openvinotoolkit.org/repositories/openvino/packages/nightly/2025.3.0-19757-b3c74fae04a/openvino_toolkit_ubuntu24_2025.3.0.dev20250812_x86_64.tgz
164+
DLDT_PACKAGE_URL ?= https://storage.openvinotoolkit.org/repositories/openvino/packages/nightly/2025.3.0-19785-689ab8b0685/openvino_toolkit_ubuntu24_2025.3.0.dev20250814_x86_64.tgz
165165
else ifeq ($(BASE_OS_TAG),22.04)
166166
OS=ubuntu22
167167
INSTALL_DRIVER_VERSION ?= "24.39.31294"
168-
DLDT_PACKAGE_URL ?= https://storage.openvinotoolkit.org/repositories/openvino/packages/nightly/2025.3.0-19757-b3c74fae04a/openvino_toolkit_ubuntu22_2025.3.0.dev20250812_x86_64.tgz
168+
DLDT_PACKAGE_URL ?= https://storage.openvinotoolkit.org/repositories/openvino/packages/nightly/2025.3.0-19785-689ab8b0685/openvino_toolkit_ubuntu22_2025.3.0.dev20250814_x86_64.tgz
169169
endif
170170
endif
171171
ifeq ($(BASE_OS),redhat)
@@ -174,7 +174,7 @@ ifeq ($(BASE_OS),redhat)
174174
BASE_IMAGE ?= registry.access.redhat.com/ubi9/ubi:$(BASE_OS_TAG_REDHAT)
175175
BASE_IMAGE_RELEASE=registry.access.redhat.com/ubi9/ubi-minimal:$(BASE_OS_TAG_REDHAT)
176176
DIST_OS=redhat
177-
DLDT_PACKAGE_URL ?= https://storage.openvinotoolkit.org/repositories/openvino/packages/nightly/2025.3.0-19757-b3c74fae04a/openvino_toolkit_rhel8_2025.3.0.dev20250812_x86_64.tgz
177+
DLDT_PACKAGE_URL ?= https://storage.openvinotoolkit.org/repositories/openvino/packages/nightly/2025.3.0-19785-689ab8b0685/openvino_toolkit_rhel8_2025.3.0.dev20250814_x86_64.tgz
178178
INSTALL_DRIVER_VERSION ?= "24.52.32224"
179179
endif
180180

demos/common/export_models/requirements.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
--pre
55
optimum-intel
66
openvino-tokenizers<=2025.3.0.0.dev20250812
7-
openvino<=2025.3.0.dev20250812
7+
openvino<=2025.3.0.dev20250814
88
nncf>=2.11.0
99
sentence_transformers
1010
sentencepiece==0.2.0

src/BUILD

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2541,6 +2541,7 @@ cc_test(
25412541
"test/llm/config.json",
25422542
"test/llm/assisted_decoding_config.json",
25432543
"test/llm/lm_cb_regular.pbtxt",
2544+
"test/llm/lm_cb_with_tool_parser.pbtxt",
25442545
"test/llm/lm_legacy_regular.pbtxt",
25452546
"test/llm/lm_cb_speculative.pbtxt",
25462547
"test/llm/lm_cb_prompt_lookup.pbtxt",

src/llm/io_processing/base_generation_config_builder.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,12 @@ void BaseGenerationConfigBuilder::setStructuralTagsConfig(const ov::genai::Struc
2929
}
3030
}
3131

32+
void BaseGenerationConfigBuilder::validateStructuredOutputConfig(ov::genai::Tokenizer& tokenizer) {
33+
if (config.structured_output_config.has_value()) {
34+
config.structured_output_config.value().validate(tokenizer);
35+
}
36+
}
37+
3238
void BaseGenerationConfigBuilder::parseConfigFromRequest(const OpenAIChatCompletionsRequest& request) {
3339
// Generic
3440
config.apply_chat_template = false; // template is applied on the serving side

src/llm/io_processing/base_generation_config_builder.hpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
//*****************************************************************************
1616
#pragma once
1717
#include <openvino/genai/generation_config.hpp>
18+
#include <openvino/genai/tokenizer.hpp>
1819
#include "../apis/openai_request.hpp"
1920

2021
namespace ovms {
@@ -39,6 +40,10 @@ class BaseGenerationConfigBuilder {
3940

4041
ov::genai::GenerationConfig& getConfig() { return config; }
4142

43+
// Validates the structured output configuration, if exists.
44+
// Throws exception if validation fails.
45+
void validateStructuredOutputConfig(ov::genai::Tokenizer& tokenizer);
46+
4247
/*
4348
* Fills generation config with values read from OpenAI request.
4449
* If extended, model specific implementation should call base class method first to fill in common configuration

src/llm/io_processing/generation_config_builder.hpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
#include <iostream>
2020
#include <string>
2121
#include <openvino/genai/generation_config.hpp>
22+
#include <openvino/genai/tokenizer.hpp>
2223
#include "base_generation_config_builder.hpp"
2324
#include "phi4/generation_config_builder.hpp"
2425
#include "llama3/generation_config_builder.hpp"
@@ -58,6 +59,12 @@ class GenerationConfigBuilder {
5859
return builder_impl->getConfig();
5960
}
6061

62+
// Validates the structured output configuration, if exists.
63+
// Throws exception if validation fails.
64+
void validateStructuredOutputConfig(ov::genai::Tokenizer& tokenizer) {
65+
builder_impl->validateStructuredOutputConfig(tokenizer);
66+
}
67+
6168
// Fills generation config with values read from OpenAI request
6269
void parseConfigFromRequest(const OpenAIChatCompletionsRequest& request) {
6370
builder_impl->parseConfigFromRequest(request);

src/llm/io_processing/hermes3/generation_config_builder.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,12 @@ void Hermes3GenerationConfigBuilder::parseConfigFromRequest(const OpenAIChatComp
2626
// Call the base class method to fill in common configuration
2727
BaseGenerationConfigBuilder::parseConfigFromRequest(request);
2828

29+
// For now the only specific part is related to tools, so if there are no tools provided in the request
30+
// we can exit early
31+
if (request.toolNameSchemaMap.empty()) {
32+
return;
33+
}
34+
2935
// Set tool guided generation config specific to Hermes3 and Qwen3 models
3036
ov::genai::StructuralTagsConfig structuralTagsConfig;
3137
static const std::string toolCallTrigger = "<tool_call>";

src/llm/io_processing/llama3/generation_config_builder.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,12 @@ void Llama3GenerationConfigBuilder::parseConfigFromRequest(const OpenAIChatCompl
2626
// Call the base class method to fill in common configuration
2727
BaseGenerationConfigBuilder::parseConfigFromRequest(request);
2828

29+
// For now the only specific part is related to tools, so if there are no tools provided in the request
30+
// we can exit early
31+
if (request.toolNameSchemaMap.empty()) {
32+
return;
33+
}
34+
2935
// Set tool guided generation config specific to Llama-3 model
3036
ov::genai::StructuralTagsConfig structuralTagsConfig;
3137
static const std::string beginOfToolsString = "<|python_tag|>";

src/llm/io_processing/phi4/generation_config_builder.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,12 @@ void Phi4GenerationConfigBuilder::parseConfigFromRequest(const OpenAIChatComplet
2626
// Call the base class method to fill in common configuration
2727
BaseGenerationConfigBuilder::parseConfigFromRequest(request);
2828

29+
// For now the only specific part is related to tools, so if there are no tools provided in the request
30+
// we can exit early
31+
if (request.toolNameSchemaMap.empty()) {
32+
return;
33+
}
34+
2935
// Set tool guided generation config specific to Phi-4 model as described in template from:
3036
// https://github.com/vllm-project/vllm/blob/v0.9.2/examples/tool_chat_template_phi4_mini.jinja
3137
ov::genai::StructuralTagsConfig structuralTagsConfig;

src/llm/servable.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,12 @@ absl::Status GenAiServable::parseRequest(std::shared_ptr<GenAiServableExecutionC
8181
}
8282
executionContext->generationConfigBuilder = std::make_shared<GenerationConfigBuilder>(getProperties()->baseGenerationConfig, getProperties()->toolParserName, getProperties()->enableToolGuidedGeneration);
8383
executionContext->generationConfigBuilder->parseConfigFromRequest(executionContext->apiHandler->getRequest());
84+
try {
85+
executionContext->generationConfigBuilder->validateStructuredOutputConfig(getProperties()->tokenizer);
86+
} catch (const std::exception& e) {
87+
SPDLOG_LOGGER_DEBUG(llm_calculator_logger, "Failed to validate structured output config: {}", e.what());
88+
return absl::Status(absl::StatusCode::kInvalidArgument, "Invalid structured output config. Check if JSON schemas in your request are correct.");
89+
}
8490
return absl::OkStatus();
8591
}
8692

0 commit comments

Comments
 (0)