Skip to content

Commit 95da9e4

Browse files
Merge pipeline config overlays by model ID
Reuse matching decoder and vision pipeline entries when applying config overlays, preserving filenames and existing session options. Unknown IDs continue to append in order. Reset active parser wrappers before vector growth and add a model-free regression test covering targeted updates and new IDs. Fixes #2004
1 parent b58789f commit 95da9e4

2 files changed

Lines changed: 132 additions & 12 deletions

File tree

src/config.cpp

Lines changed: 29 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -458,7 +458,9 @@ struct PipelineModel_Element : JSON::Element {
458458

459459
JSON::Element& OnObject(std::string_view name) override {
460460
if (name == "session_options") {
461-
v_.session_options = Config::SessionOptions{};
461+
if (!v_.session_options) {
462+
v_.session_options.emplace();
463+
}
462464
session_options_ = std::make_unique<SessionOptions_Element>(*v_.session_options);
463465
return *session_options_;
464466
}
@@ -491,19 +493,32 @@ struct PipelineModel_Element : JSON::Element {
491493
StringStringMap_Element output_names_forwarder_{v_.output_names_forwarder};
492494
};
493495

496+
template <typename PipelineModel>
497+
PipelineModel& GetOrCreatePipelineModel(std::vector<PipelineModel>& models, std::string_view model_id) {
498+
auto model = std::find_if(models.begin(), models.end(),
499+
[model_id](const auto& model) { return model.model_id == model_id; });
500+
if (model != models.end()) {
501+
return *model;
502+
}
503+
504+
auto& new_model = models.emplace_back();
505+
new_model.model_id = model_id;
506+
return new_model;
507+
}
508+
494509
struct PipelineModelObject_Element : JSON::Element {
495510
explicit PipelineModelObject_Element(std::vector<Config::Model::Decoder::PipelineModel>& v) : v_{v} {}
496511

497512
Element& OnObject(std::string_view name) override {
498-
auto& model = v_.emplace_back();
499-
model.model_id = name;
500-
pipeline_model_elements_.emplace_back(model);
501-
return pipeline_model_elements_.back();
513+
pipeline_model_element_.reset(); // Appending below may reallocate v_.
514+
auto& model = GetOrCreatePipelineModel(v_, name);
515+
pipeline_model_element_ = std::make_unique<PipelineModel_Element>(model);
516+
return *pipeline_model_element_;
502517
}
503518

504519
private:
505520
std::vector<Config::Model::Decoder::PipelineModel>& v_;
506-
std::vector<PipelineModel_Element> pipeline_model_elements_;
521+
std::unique_ptr<PipelineModel_Element> pipeline_model_element_;
507522
};
508523

509524
struct Pipeline_Element : JSON::Element {
@@ -729,7 +744,9 @@ struct VisionPipelineModel_Element : JSON::Element {
729744

730745
Element& OnObject(std::string_view name) override {
731746
if (name == "session_options") {
732-
v_.session_options = Config::SessionOptions{};
747+
if (!v_.session_options) {
748+
v_.session_options.emplace();
749+
}
733750
session_options_ = std::make_unique<SessionOptions_Element>(*v_.session_options);
734751
return *session_options_;
735752
}
@@ -763,15 +780,15 @@ struct VisionPipelineModelObject_Element : JSON::Element {
763780
explicit VisionPipelineModelObject_Element(std::vector<Config::Model::Vision::PipelineModel>& v) : v_{v} {}
764781

765782
Element& OnObject(std::string_view name) override {
766-
auto& model = v_.emplace_back();
767-
model.model_id = name;
768-
elements_.emplace_back(model);
769-
return elements_.back();
783+
element_.reset(); // Appending below may reallocate v_.
784+
auto& model = GetOrCreatePipelineModel(v_, name);
785+
element_ = std::make_unique<VisionPipelineModel_Element>(model);
786+
return *element_;
770787
}
771788

772789
private:
773790
std::vector<Config::Model::Vision::PipelineModel>& v_;
774-
std::vector<VisionPipelineModel_Element> elements_;
791+
std::unique_ptr<VisionPipelineModel_Element> element_;
775792
};
776793

777794
struct VisionPipeline_Element : JSON::Element {

test/config_tests.cpp

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
// Copyright (c) Microsoft Corporation. All rights reserved.
2+
// Licensed under the MIT License.
3+
4+
#include <gtest/gtest.h>
5+
6+
#include "filesystem.h"
7+
#include "config.h"
8+
9+
namespace Generators {
10+
namespace {
11+
12+
template <typename PipelineModels>
13+
void ExpectPipelineOverlayMerged(const PipelineModels& pipeline) {
14+
ASSERT_EQ(pipeline.size(), 3U);
15+
16+
const auto& untouched_model = pipeline[0];
17+
EXPECT_EQ(untouched_model.model_id, "untouched_model");
18+
EXPECT_EQ(untouched_model.filename, "untouched.onnx");
19+
ASSERT_TRUE(untouched_model.session_options);
20+
ASSERT_TRUE(untouched_model.session_options->log_id);
21+
EXPECT_EQ(*untouched_model.session_options->log_id, "untouched");
22+
EXPECT_FALSE(untouched_model.session_options->enable_profiling);
23+
24+
const auto& existing_model = pipeline[1];
25+
EXPECT_EQ(existing_model.model_id, "existing_model");
26+
EXPECT_EQ(existing_model.filename, "existing.onnx");
27+
ASSERT_TRUE(existing_model.session_options);
28+
ASSERT_TRUE(existing_model.session_options->log_id);
29+
EXPECT_EQ(*existing_model.session_options->log_id, "original");
30+
ASSERT_TRUE(existing_model.session_options->enable_profiling);
31+
EXPECT_EQ(*existing_model.session_options->enable_profiling, "profile");
32+
ASSERT_EQ(existing_model.session_options->provider_options.size(), 1U);
33+
EXPECT_EQ(existing_model.session_options->provider_options[0].name, "CPU");
34+
35+
const auto& new_model = pipeline[2];
36+
EXPECT_EQ(new_model.model_id, "new_model");
37+
EXPECT_EQ(new_model.filename, "new.onnx");
38+
}
39+
40+
TEST(ConfigTests, PipelineOverlayMergesByModelId) {
41+
Config config;
42+
OverlayConfig(config, R"({
43+
"model": {
44+
"decoder": {
45+
"pipeline": [{
46+
"untouched_model": {
47+
"filename": "untouched.onnx",
48+
"session_options": {"log_id": "untouched"}
49+
},
50+
"existing_model": {
51+
"filename": "existing.onnx",
52+
"session_options": {
53+
"log_id": "original",
54+
"provider_options": [{"CPU": {}}]
55+
}
56+
}
57+
}]
58+
},
59+
"vision": {
60+
"pipeline": [{
61+
"untouched_model": {
62+
"filename": "untouched.onnx",
63+
"session_options": {"log_id": "untouched"}
64+
},
65+
"existing_model": {
66+
"filename": "existing.onnx",
67+
"session_options": {
68+
"log_id": "original",
69+
"provider_options": [{"CPU": {}}]
70+
}
71+
}
72+
}]
73+
}
74+
}
75+
})");
76+
77+
OverlayConfig(config, R"({
78+
"model": {
79+
"decoder": {
80+
"pipeline": [{
81+
"existing_model": {
82+
"session_options": {"enable_profiling": "profile"}
83+
},
84+
"new_model": {"filename": "new.onnx"}
85+
}]
86+
},
87+
"vision": {
88+
"pipeline": [{
89+
"existing_model": {
90+
"session_options": {"enable_profiling": "profile"}
91+
},
92+
"new_model": {"filename": "new.onnx"}
93+
}]
94+
}
95+
}
96+
})");
97+
98+
ExpectPipelineOverlayMerged(config.model.decoder.pipeline);
99+
ExpectPipelineOverlayMerged(config.model.vision.pipeline);
100+
}
101+
102+
} // namespace
103+
} // namespace Generators

0 commit comments

Comments
 (0)