Separately addressable catalogs (C++ SDK) - #943
Open
kobby-kobbs wants to merge 14 commits into
Open
Conversation
added 8 commits
July 28, 2026 12:04
Introduce named catalog sources so multiple catalogs can be separately
addressed:
- Add CatalogSource{name,url,filter} struct and kDefaultCatalogName="public"
in configuration.h; change catalog_urls to vector<CatalogSource>.
- Validate() now rejects empty catalog name and url.
- New AddCatalog(name,url,filter) across the C ABI (foundry_local_c.h /
c_api.cc vtable) and the C++ wrapper (foundry_local_cpp.h / .inline.h);
AddCatalogUrl auto-derives the name from the URL.
- manager.cc shim converts named sources back to pairs for the existing
AzureModelCatalog.
- Tests: add reject-empty-name; update existing configuration tests to
CatalogSource.
Replace the single catalog_ member with an add-ordered list of named
catalogs. Build one AzureModelCatalog per registered source instead of
one merged catalog; when none are registered, expose the built-in Azure
Foundry catalog as the default "public" catalog.
- manager.h: NamedCatalog{name,catalog} vector; add GetCatalog(name)
overload and ListCatalogNames().
- manager.cc: construct one catalog per source; GetCatalog() returns the
first (default) catalog, GetCatalog(name) looks up by name and throws
INVALID_ARGUMENT if unknown, ListCatalogNames() enumerates in add-order.
Web service uses GetCatalog(); InvalidateCache loops all catalogs.
Default catalog = first registered ("public" when none added). No
aggregation across catalogs.
Add Manager_GetCatalogByName and Manager_ListCatalogNames to the C ABI so callers can address individual registered catalogs by name. - foundry_local_c.h: append the two functions before the V1 end marker. - c_api.cc: cache per-name flCatalog wrappers on flManager so returned pointers stay valid for the manager's lifetime; back Manager_ListCatalogNames with owned string storage. Register both in the g_api_v1 vtable. Manager_GetCatalog continues to return the default catalog.
Add Manager::GetCatalog(name) and Manager::ListCatalogNames() to the RAII C++ wrapper, layered over the C ABI added previously. - foundry_local_cpp.h: declare both methods; cache named Catalog wrappers in a mutex-guarded map so repeated lookups return the same object. - foundry_local_cpp.inline.h: implement both over Manager_GetCatalogByName and Manager_ListCatalogNames. The no-argument GetCatalog() continues to return the default catalog.
Cover the named-catalog surface across the C ABI and the C++ wrapper: - C ABI (c_api_test.cc): Manager_ListCatalogNames defaults to "public", returns registered names in add-order, AddCatalogUrl auto-derives the name from the URL; Manager_GetCatalogByName resolves registered catalogs and caches handles, fails with INVALID_ARGUMENT for unknown/null names; null-argument validation for both entry points. - C++ wrapper (cpp_api_test.cc): Configuration::AddCatalog chaining, Manager::ListCatalogNames default and ordered results, GetCatalog(name) resolution vs. the default GetCatalog(), and throwing on unknown names.
Update the C++ SDK docs and public header comments to describe the
separately addressable named catalogs:
- CppPortGuide: rewrite the catalog-architecture note to explain N named
catalogs (no aggregation/union/de-dup), the AddCatalog/AddCatalogUrl
config surface, GetCatalog(name)/ListCatalogNames, and the default
("public") catalog behavior.
- WrapperInterfacesDesign: mention the GetCatalog(name) and
ListCatalogNames overloads.
- foundry_local_c.h / foundry_local_cpp.h: clarify that the no-argument
GetCatalog returns the first-registered (or built-in "public") catalog.
A runnable example that links the SDK and exercises the multi-catalog surface without network access or model downloads: - default catalog behavior when no source is added (registered as "public") - registering several named catalogs and enumerating them with ListCatalogNames - resolving each catalog by name and the cached repeated-lookup behavior - the default catalog corresponding to the first registered name - the Error raised for an unknown catalog name Wired into the examples build as catalog_example.
Turn the catalog example into a small REPL for hands-on exploration of the named multi-catalog surface: - list enumerate registered catalog names - add <name> <url> register a named catalog (rebuilds the singleton manager) - use <name> select the current catalog (validates the name) - name show the current catalog's reported name - models live-query the current catalog's models - help / quit The manager is a process-wide singleton, so adding a catalog destroys the existing manager before constructing a new one from the updated sources.
kobby-kobbs
requested review from
baijumeswani,
Copilot,
natke and
sheetalarkadam
August 3, 2026 17:14
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
Contributor
There was a problem hiding this comment.
Pull request overview
Adds independently addressable named catalogs to the C++ SDK.
Changes:
- Adds named catalog configuration, lookup, enumeration, and C/C++ APIs.
- Refactors
Managerto own multiple catalogs and adds tests. - Adds documentation and an interactive catalog example.
Reviewed changes
Copilot reviewed 16 out of 16 changed files in this pull request and generated 8 comments.
Show a summary per file
| File | Description |
|---|---|
sdk_v2/cpp/CMakeLists.txt |
Builds the catalog example. |
sdk_v2/cpp/docs/CppPortGuide.md |
Documents multi-catalog architecture. |
sdk_v2/cpp/docs/WrapperInterfacesDesign.md |
Documents wrapper APIs. |
sdk_v2/cpp/examples/catalog/CMakeLists.txt |
Defines the example target. |
sdk_v2/cpp/examples/catalog/main.cc |
Implements the catalog REPL. |
sdk_v2/cpp/include/foundry_local/foundry_local_c.h |
Extends C API tables. |
sdk_v2/cpp/include/foundry_local/foundry_local_cpp.h |
Declares C++ APIs and caches. |
sdk_v2/cpp/include/foundry_local/foundry_local_cpp.inline.h |
Implements C++ wrappers. |
sdk_v2/cpp/src/c_api.cc |
Implements C ABI operations. |
sdk_v2/cpp/src/configuration.cc |
Validates catalog sources. |
sdk_v2/cpp/src/configuration.h |
Models named catalog sources. |
sdk_v2/cpp/src/manager.cc |
Creates and resolves multiple catalogs. |
sdk_v2/cpp/src/manager.h |
Declares multi-catalog storage. |
sdk_v2/cpp/test/internal_api/c_api_test.cc |
Tests the C ABI. |
sdk_v2/cpp/test/internal_api/configuration_test.cc |
Tests configuration validation. |
sdk_v2/cpp/test/sdk_api/cpp_api_test.cc |
Tests the public C++ API. |
Comment on lines
+45
to
+50
| for (const auto& source : catalog_urls) { | ||
| if (source.url.empty()) { | ||
| FL_THROW(FOUNDRY_LOCAL_ERROR_INVALID_ARGUMENT, "Configuration: catalog URL must not be empty"); | ||
| } | ||
| if (source.name.empty()) { | ||
| FL_THROW(FOUNDRY_LOCAL_ERROR_INVALID_ARGUMENT, "Configuration: catalog name must not be empty"); |
Comment on lines
+936
to
+937
| FL_API_STATUS(AddCatalog, _In_ flConfiguration* config, _In_ const char* name, _In_ const char* url, | ||
| _In_opt_ const char* filter_override); |
Comment on lines
+334
to
+336
| return std::make_unique<AzureModelCatalog>( | ||
| std::move(urls), | ||
| download_manager_->GetCacheDirectory(), |
Comment on lines
+214
to
+220
| inline ICatalog& Manager::GetCatalog(const std::string& name) const { | ||
| std::lock_guard<std::mutex> lock(*named_catalogs_mutex_); | ||
| auto it = named_catalogs_.find(name); | ||
| if (it == named_catalogs_.end()) { | ||
| flCatalog* cat = nullptr; | ||
| Check(detail::api()->Manager_GetCatalogByName(handle_.get(), name.c_str(), &cat)); | ||
| it = named_catalogs_.emplace(name, std::unique_ptr<Catalog>(new Catalog(*cat))).first; |
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
- configuration: reject reserved ("public") and duplicate catalog names in
Validate(), with tests covering both cases.
- C ABI: move AddCatalog to the end of flConfigurationApi (append-only rule) so
clients compiled against the previous vtable keep dispatching correctly.
- C++ wrapper: route the no-arg GetCatalog() through the named cache so the
default catalog resolves to a single canonical wrapper shared by both paths.
- catalog cache: namespace the per-catalog metadata snapshot by catalog
identity (URL/filter) while keeping the shared model-blob cache directory, so
separately addressable catalogs no longer collide on foundry.modelinfo.json.
The default ("public") catalog keeps the canonical file name.
- c_api.cc: fully initialize the flManager aggregate in Manager_CreateImpl. Three new members (catalog_by_name, catalog_names_storage, catalog_names_cache) were left out of the brace-init, which trips gcc's -Wmissing-field-initializers (-Wextra), promoted to an error by -Werror on the Linux build. MSVC (/EHsc only) ignored it, so local Windows builds passed. - Move the named-catalog tests (default-catalog, resolve-by-name, unknown-name-throws) out of cpp_api_test.cc (sdk_integration_tests binary, whose SharedTestEnv holds the process-wide Manager singleton) and into catalog_live_test.cc (cache_only_tests binary, no SharedTestEnv). Each test constructs its own Manager, which threw "Manager already created" in the old binary. Tests use a temp cache dir and are metadata-only (no network).
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds support for multiple, separately addressable named catalogs in the Foundry Local C++ SDK. Instead of a single catalog (or a merged/aggregated one), the Manager now holds N catalogs, each registered under a unique name and each backed by its own AzureModelCatalog. Results are never unioned or de-duplicated across catalogs, you address one catalog at a time.
Motivation
Consumers need to register more than one model source and query each independently (e.g. a public catalog plus a private/org catalog) without the SDK merging their contents.
What changed
Config API — Configuration::AddCatalog(name, url, filter?) registers a named catalog. AddCatalogUrl(url) remains supported and registers the catalog under an auto-derived name (its URL).
Manager — holds one AzureModelCatalog per source. GetCatalog(name) resolves a specific catalog; ListCatalogNames() enumerates registered names in add-order. The no-arg GetCatalog() returns the first-registered (default) catalog; with no source added, the built-in Azure Foundry catalog is registered under the reserved name "public" and serves as the default.
C ABI — additive, versioned: Manager_GetCatalogByName and Manager_ListCatalogNames (appended before the // End V1 marker). Unknown name → FOUNDRY_LOCAL_ERROR_INVALID_ARGUMENT.
C++ wrapper — ergonomic GetCatalog(name) / ListCatalogNames() mirroring the C ABI, with per-name wrapper caching.
Tests — C ABI and C++ wrapper coverage for registration, listing/order, name resolution + caching, default behavior, and error cases.
Docs — updated CppPortGuide.md (catalog architecture) and WrapperInterfacesDesign.md, plus header doc comments.
Example — a new interactive catalog_example REPL (list/add/use/name/models) for hands-on exploration.
Design notes
N separately addressable catalogs; no aggregation/union/de-duplication/origin-tagging.
Default catalog = first registered, or built-in "public" when none added; "public" is reserved.
AzureModelCatalog signature is unchanged; the Manager's name→catalog map is the authoritative key.
C ABI change is purely additive to preserve ABI compatibility.