Skip to content

Commit 2545ca1

Browse files
jambaykCopilot
andcommitted
Detect model packages by manifest.json without a root genai_config.json
A flat model directory can carry its own unrelated manifest.json, which the previous check misclassified as a model package. A flat directory always keeps genai_config.json at its root while a package keeps it inside each variant, so treat a directory as a package only when it has a top-level manifest.json and no top-level genai_config.json. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent b31eea1 commit 2545ca1

4 files changed

Lines changed: 35 additions & 3 deletions

File tree

docs/model_package.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,11 @@ When passed a package they auto-detect the execution provider; an ambiguous pack
168168
returns an error pointing at `OgaCreateConfigFromPackageEp`. The `FromPackageEp` entry
169169
point rejects flat directories.
170170
171+
A directory is treated as a package when it has a top-level `manifest.json` and no
172+
top-level `genai_config.json`. A flat model directory always keeps `genai_config.json` at
173+
its root, so one that happens to carry an unrelated `manifest.json` is still loaded as a
174+
flat directory.
175+
171176
Combining a model package with `OgaRuntimeSettings` is not supported and returns an
172177
error. Runtime settings work as before with flat directories via
173178
`OgaCreateModelWithRuntimeSettings`.

src/models/model_package.cpp

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -59,12 +59,19 @@ std::unique_ptr<OrtSessionOptions> BuildSelectionSessionOptions(const std::strin
5959

6060
bool IsModelPackage(const fs::path& path) {
6161
constexpr const char* kManifestFilename = "manifest.json";
62+
constexpr const char* kGenaiConfigFilename = "genai_config.json";
6263
std::error_code ec;
6364
const std::filesystem::path root = AsStdPath(path);
64-
if (!std::filesystem::is_directory(root, ec) || ec) {
65+
if (!std::filesystem::is_directory(root, ec)) {
6566
return false;
6667
}
67-
return std::filesystem::is_regular_file(root / kManifestFilename, ec) && !ec;
68+
if (!std::filesystem::is_regular_file(root / kManifestFilename, ec)) {
69+
return false;
70+
}
71+
// A model package keeps genai_config.json inside each variant directory, never at the root.
72+
// A flat model directory keeps it at the root and may carry an unrelated manifest.json, so a
73+
// root genai_config.json means this is a flat directory, not a package.
74+
return !std::filesystem::is_regular_file(root / kGenaiConfigFilename, ec);
6875
}
6976

7077
#if ORT_GENAI_HAS_MODEL_PACKAGE

src/models/model_package.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@
1010

1111
namespace Generators {
1212

13-
// True when `path` is a directory containing a top-level manifest.json.
13+
// True when `path` is a model package directory: it has a top-level manifest.json and no
14+
// top-level genai_config.json (a flat model directory keeps genai_config.json at its root).
1415
bool IsModelPackage(const fs::path& path);
1516

1617
#if ORT_GENAI_HAS_MODEL_PACKAGE

test/model_package_test.cpp

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,25 @@ TEST(ModelPackage, RejectsFlatDirectory) {
133133
EXPECT_NE(message.find("is not a model package"), std::string::npos) << message;
134134
}
135135

136+
TEST(ModelPackage, FlatDirectoryWithManifestIsNotAPackage) {
137+
// A flat model directory may carry its own unrelated manifest.json. The root genai_config.json
138+
// marks it as flat, so it must not be treated as a model package.
139+
const auto root = MakeTempDir("flat_with_manifest");
140+
WriteFile(root / "genai_config.json",
141+
"{ \"model\": { \"type\": \"tiny-test-model\","
142+
" \"vocab_size\": 16, \"context_length\": 32 }, \"search\": {} }");
143+
WriteFile(root / "model.onnx", "placeholder");
144+
WriteFile(root / "manifest.json", "{ \"unrelated\": true }");
145+
146+
// Passing an ep treats the path as a package; it must be rejected as a flat directory instead.
147+
const std::string message = CaptureThrowMessage(
148+
[&] { OgaConfig::CreateFromPackageEp(root.string().c_str(), "cpu"); });
149+
EXPECT_NE(message.find("is not a model package"), std::string::npos) << message;
150+
151+
// Loading it as a flat directory (no ep) succeeds despite the manifest.json.
152+
EXPECT_NO_THROW(OgaConfig::Create(root.string().c_str()));
153+
}
154+
136155
TEST(ModelPackage, RejectsMissingPath) {
137156
EXPECT_THROW(OgaConfig::CreateFromPackageEp("/this/path/does/not/exist/12345", "cpu"),
138157
std::runtime_error);

0 commit comments

Comments
 (0)