Skip to content

Add C++ SDK Support#544

Open
nenad1002 wants to merge 3 commits intomainfrom
nebanfic/cpp-sdk
Open

Add C++ SDK Support#544
nenad1002 wants to merge 3 commits intomainfrom
nebanfic/cpp-sdk

Conversation

@nenad1002
Copy link

  • pipeline support missing, will come in future
  • ARM support will come soon

Copilot AI review requested due to automatic review settings March 23, 2026 20:08
@vercel
Copy link

vercel bot commented Mar 23, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
foundry-local Ready Ready Preview, Comment Mar 23, 2026 9:51pm

Request Review

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR introduces an initial C++ SDK under sdk/cpp/, including the core API surface (manager/catalog/model/client), a CMake-based build, unit tests, and sample usage.

Changes:

  • Add C++ SDK public headers and a Windows (WIL-based) implementation.
  • Add CMake project/presets plus formatting configuration for the C++ SDK.
  • Add GTest unit tests and JSON testdata fixtures covering parsing, catalog/model behavior, and client requests.

Reviewed changes

Copilot reviewed 29 out of 29 changed files in this pull request and generated 7 comments.

Show a summary per file
File Description
sdk/cpp/CMakeLists.txt Defines the C++ SDK library, sample, and unit tests; Windows-only guard and dependencies.
sdk/cpp/CMakePresets.json Adds configure/build/test presets for building the C++ SDK.
sdk/cpp/.clang-format Establishes C++ formatting rules for the SDK.
sdk/cpp/include/foundry_local.h Public C++ SDK API surface (manager, catalog, model/variant, chat/audio clients, types).
sdk/cpp/include/configuration.h Configuration types/validation for initializing Foundry Local from C++.
sdk/cpp/include/core_interop_request.h Helper for building JSON-wrapped core requests with Params.
sdk/cpp/include/flcore_native.h Native ABI struct/function-pointer definitions for calling the core DLL.
sdk/cpp/include/foundry_local_exception.h SDK exception type with optional logging.
sdk/cpp/include/foundry_local_internal_core.h Internal core interface abstraction used by SDK types/tests.
sdk/cpp/include/logger.h Logger interface + NullLogger implementation.
sdk/cpp/include/log_level.h LogLevel enum and string conversion helper.
sdk/cpp/include/parser.h JSON (de)serialization helpers for model info, chat, tool calling, etc.
sdk/cpp/src/foundry_local.cpp Main SDK implementation: DLL loading, manager/catalog/model/clients, streaming callbacks.
sdk/cpp/sample/main.cpp Example app demonstrating catalog browsing, chat (streaming/non), audio, tool calling, and variant selection.
sdk/cpp/test/mock_core.h Mock/file-backed core implementations + file reading helper for tests.
sdk/cpp/test/mock_object_factory.h Test factory enabling construction of private-constructor SDK types.
sdk/cpp/test/parser_and_types_test.cpp Unit tests for parsing/serialization utilities and small type behaviors.
sdk/cpp/test/model_variant_test.cpp Unit tests for ModelVariant and Model behaviors (load/cache/download/path/selection).
sdk/cpp/test/client_test.cpp Unit tests for ChatClient/AudioClient request formatting and streaming.
sdk/cpp/test/catalog_test.cpp Unit tests for Catalog grouping/filtering/caching and file-based fixtures.
sdk/cpp/test/testdata/empty_models_list.json Fixture: empty model list.
sdk/cpp/test/testdata/malformed_models_list.json Fixture: malformed JSON for error-path testing.
sdk/cpp/test/testdata/missing_name_field_models_list.json Fixture: missing required fields for validation/error tests.
sdk/cpp/test/testdata/mixed_openai_and_local.json Fixture: mix of OpenAI-prefixed and local models for filtering tests.
sdk/cpp/test/testdata/real_models_list.json Fixture: richer realistic model entries with optional fields/runtime/template.
sdk/cpp/test/testdata/single_cached_model.json Fixture: one cached model entry for subset tests.
sdk/cpp/test/testdata/three_variants_one_model.json Fixture: multiple variants under one alias for grouping tests.
sdk/cpp/test/testdata/valid_cached_models.json Fixture: cached model IDs list.
sdk/cpp/test/testdata/valid_loaded_models.json Fixture: loaded model IDs list.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.


#include "logger.h"

namespace FoundryLocal {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: should we follow the ORT conventions for naming etc. for consistency? If so namespace would be foundry_local

Comment on lines +42 to +47
FetchContent_Declare(
nlohmann_json
GIT_REPOSITORY https://github.com/nlohmann/json.git
GIT_TAG v3.12.0
)
FetchContent_MakeAvailable(nlohmann_json)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is using FetchContent allowed by security rules or do we need to use vcpkg? I have a working vcpkg based setup in my WIP branch if you need to copy from somewhere.

# -----------------------------
FetchContent_Declare(
wil_src
GIT_REPOSITORY https://github.com/microsoft/wil.git
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is the SDK Windows only or it builds/runs on other platforms?

Comment on lines +87 to +93
add_library(CppSdk STATIC
src/foundry_local.cpp
# Add more .cpp files as you migrate:
# src/parser.cpp
# src/dllmain.cpp
# src/pch.cpp
)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we build a shared library as well or there's no requirement for that currently?

}
},
{
"name": "x86-debug",
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we care about x86? We don't have FL Core builds of that do we?

Comment on lines +672 to +674
for (const auto& v : it->second.variants_) {
modelIdToModelVariant_[v.GetInfo().name] = &v;
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need to iterate all variants here or just add the current one?
If we're adding to modelIdToModelVariant should it be using v.GetId() rather than the name? id == name:version and should be unique.

Comment on lines +613 to +617
return CollectVariantsByIds(modelIdToModelVariant_, GetLoadedModelsInternal(core_, *logger_));
}

std::vector<const ModelVariant*> Catalog::GetCachedModels() const {
return CollectVariantsByIds(modelIdToModelVariant_, GetCachedModelsInternal(core_, *logger_));
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not quite understanding this. The loaded and cached model getters should return model ids which we should be able to directly looked up in the model id to variant map.

Comment on lines +700 to +719
FoundryLocalManager::FoundryLocalManager(FoundryLocalManager&& other) noexcept
: config_(std::move(other.config_)),
core_(std::move(other.core_)),
catalog_(std::move(other.catalog_)),
logger_(other.OwnsLogger() ? &defaultLogger_ : other.logger_),
urls_(std::move(other.urls_)) {
other.logger_ = &other.defaultLogger_;
}

FoundryLocalManager& FoundryLocalManager::operator=(FoundryLocalManager&& other) noexcept {
if (this != &other) {
config_ = std::move(other.config_);
core_ = std::move(other.core_);
catalog_ = std::move(other.catalog_);
logger_ = other.OwnsLogger() ? &defaultLogger_ : other.logger_;
urls_ = std::move(other.urls_);
other.logger_ = &other.defaultLogger_;
}
return *this;
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need these?

core_->call(initReq.Command(), *logger_, &initJson);

if (config_.model_cache_dir) {
std::string current = core_->call("get_cache_directory", *logger_);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should be able to provide the cache dir in the call to initialize. I believe there's logic in Core to create the directory if it doesn't exist as well.


using Factory = MockObjectFactory;

class ChatClientTest : public ::testing::Test {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we have e2e or functional tests that exercise the public API overall (not just the chat client) with minimal mocking similar to the C# SDK?

I thought we setup some test data so that we can avoid the model download when running in the CI, although we should have a test that downloads a model if not already cached when run locally as a sanity check (C# has a 'SkipInCI' attribute so we might need something similar).

Should be fine to hit the catalog for the latest model list.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants