Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 17 additions & 17 deletions sdk/cpp/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -12,33 +12,31 @@ endif()
project(CppSdk LANGUAGES CXX)

# -----------------------------
# Windows-only + compiler guard
# Compiler guard
# -----------------------------
if (NOT WIN32)
message(FATAL_ERROR "CppSdk is Windows-only for now (uses Win32/WIL headers).")
endif()

# Accept MSVC OR clang-cl (Clang in MSVC compatibility mode).
# VS CMake Open-Folder often uses clang-cl by default.
if (NOT (MSVC OR (CMAKE_CXX_COMPILER_ID STREQUAL "Clang" AND CMAKE_CXX_SIMULATE_ID STREQUAL "MSVC")))
message(STATUS "CMAKE_CXX_COMPILER_ID = ${CMAKE_CXX_COMPILER_ID}")
message(STATUS "CMAKE_CXX_COMPILER = ${CMAKE_CXX_COMPILER}")
message(STATUS "CMAKE_CXX_SIMULATE_ID = ${CMAKE_CXX_SIMULATE_ID}")
message(FATAL_ERROR "Need MSVC or clang-cl (MSVC-compatible toolchain).")
if (WIN32)
# Accept MSVC OR clang-cl (Clang in MSVC compatibility mode).
if (NOT (MSVC OR (CMAKE_CXX_COMPILER_ID STREQUAL "Clang" AND CMAKE_CXX_SIMULATE_ID STREQUAL "MSVC")))
message(STATUS "CMAKE_CXX_COMPILER_ID = ${CMAKE_CXX_COMPILER_ID}")
message(STATUS "CMAKE_CXX_COMPILER = ${CMAKE_CXX_COMPILER}")
message(STATUS "CMAKE_CXX_SIMULATE_ID = ${CMAKE_CXX_SIMULATE_ID}")
message(FATAL_ERROR "On Windows, need MSVC or clang-cl (MSVC-compatible toolchain).")
endif()
endif()

set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)

# Optional: target Windows 10+ APIs (adjust if you need older)
add_compile_definitions(_WIN32_WINNT=0x0A00 WINVER=0x0A00)
if (WIN32)
# Target Windows 10+ APIs
add_compile_definitions(_WIN32_WINNT=0x0A00 WINVER=0x0A00)
endif()

# -----------------------------
# Dependencies (installed via vcpkg)
# -----------------------------
find_package(nlohmann_json CONFIG REQUIRED)
find_package(wil CONFIG REQUIRED)
find_package(Microsoft.GSL CONFIG REQUIRED)
option(BUILD_TESTING "Build unit and end-to-end tests" ON)
if (BUILD_TESTING)
Expand Down Expand Up @@ -70,9 +68,12 @@ target_link_libraries(CppSdk
PUBLIC
nlohmann_json::nlohmann_json
Microsoft.GSL::GSL
WIL::WIL
)

if (UNIX)
target_link_libraries(CppSdk PUBLIC ${CMAKE_DL_LIBS})
endif()

# -----------------------------
# Sample executable
# -----------------------------
Expand All @@ -93,7 +94,6 @@ if (BUILD_TESTING)
test/model_variant_test.cpp
test/catalog_test.cpp
test/client_test.cpp
test/live_audio_test.cpp
)

target_include_directories(CppSdkTests
Expand Down
2 changes: 2 additions & 0 deletions sdk/cpp/CMakePresets.json
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@
"generator": "Ninja",
"binaryDir": "${sourceDir}/out/build/${presetName}",
"installDir": "${sourceDir}/out/install/${presetName}",
"toolchainFile": "$env{VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake",
"cacheVariables": {
"CMAKE_BUILD_TYPE": "Debug"
},
Expand All @@ -66,6 +67,7 @@
"generator": "Ninja",
"binaryDir": "${sourceDir}/out/build/${presetName}",
"installDir": "${sourceDir}/out/install/${presetName}",
"toolchainFile": "$env{VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake",
"cacheVariables": {
"CMAKE_BUILD_TYPE": "Debug"
},
Expand Down
2 changes: 1 addition & 1 deletion sdk/cpp/include/catalog.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ class Catalog final {
}

const std::string& GetName() const { return name_; }
std::vector<IModel*> ListModels() const;
std::vector<IModel*> GetModels() const;
std::vector<IModel*> GetLoadedModels() const;
std::vector<IModel*> GetCachedModels() const;

Expand Down
16 changes: 8 additions & 8 deletions sdk/cpp/include/foundry_local_manager.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// Licensed under the MIT License.

#pragma once

#include <string>
#include <vector>
#include <memory>
Expand Down Expand Up @@ -30,7 +31,7 @@ namespace foundry_local {
/// Throws if an instance has already been created. Call Destroy() first to release the current instance.
/// @param configuration Configuration to use.
/// @param logger Optional application logger. Pass nullptr to suppress log output.
static void Create(Configuration configuration, ILogger* logger = nullptr);
static Manager& Create(Configuration configuration, ILogger* logger = nullptr);

/// Get the singleton instance.
/// Throws if Create() has not been called.
Expand All @@ -47,17 +48,16 @@ namespace foundry_local {
const Catalog& GetCatalog() const;
Catalog& GetCatalog();

/// Start the optional built-in web service.
/// Provides an OpenAI-compatible REST endpoint.
/// After startup, GetUrls() returns the actual bound URL/s.
/// Requires Configuration::Web to be set.
/// Start the embedded web service.
/// Requires Configuration::web to be set.
void StartWebService();

/// Stop the web service if started.
/// Stop the embedded web service.
/// Requires Configuration::web to be set.
void StopWebService();
Comment thread
nenad1002 marked this conversation as resolved.

/// Returns the bound URL/s after StartWebService(), or empty if not started.
gsl::span<const std::string> GetUrls() const noexcept;
/// Get the URLs the web service is bound to. Valid after StartWebService() and until StopWebService().
gsl::span<const std::string> GetWebServiceEndpoints() const noexcept;

/// Ensure execution providers are downloaded and registered.
/// Once downloaded, EPs are not re-downloaded unless a new version is available.
Expand Down
4 changes: 2 additions & 2 deletions sdk/cpp/include/model.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ namespace foundry_local {
}
#endif

using DownloadProgressCallback = std::function<void(float percentage)>;
using DownloadProgressCallback = std::function<bool(float percentage)>;
Comment thread
nenad1002 marked this conversation as resolved.
Comment thread
nenad1002 marked this conversation as resolved.

class IModel {
public:
Expand Down Expand Up @@ -153,7 +153,7 @@ namespace foundry_local {
public:
explicit Model(gsl::not_null<foundry_local::Internal::IFoundryLocalCore*> core, gsl::not_null<ILogger*> logger);

gsl::span<const ModelVariant> GetAllModelVariants() const;
gsl::span<const ModelVariant> GetVariants() const;

bool IsLoaded() const override { return SelectedVariant().IsLoaded(); }
bool IsCached() const override { return SelectedVariant().IsCached(); }
Expand Down
105 changes: 90 additions & 15 deletions sdk/cpp/sample/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@
#include <string>
#include <vector>

#ifdef _WIN32
#include <windows.h>
#endif

using namespace foundry_local;

// ---------------------------------------------------------------------------
Expand Down Expand Up @@ -34,6 +38,21 @@ class StdLogger final : public ILogger {
}
};

// ---------------------------------------------------------------------------
// Helper – Select the CPU variant for a model (if available)
// ---------------------------------------------------------------------------
void PreferCpuVariant(Model& model) {
for (const auto& variant : model.GetVariants()) {
const auto& info = variant.GetInfo();
if (info.runtime && info.runtime->device_type == DeviceType::CPU) {
model.SelectVariant(variant);
std::cout << "Selected CPU variant: " << info.name << "\n";
return;
}
}
std::cout << "No CPU variant found; using default variant.\n";
}

// ---------------------------------------------------------------------------
// Example 1 – Browse the catalog
// ---------------------------------------------------------------------------
Expand All @@ -43,7 +62,7 @@ void BrowseCatalog(Manager& manager) {
auto& catalog = manager.GetCatalog();
std::cout << "Catalog: " << catalog.GetName() << "\n";

auto models = catalog.ListModels();
auto models = catalog.GetModels();
std::cout << "Models in catalog: " << models.size() << "\n";

for (const auto* model : models) {
Expand All @@ -53,7 +72,7 @@ void BrowseCatalog(Manager& manager) {

auto* concreteModel = dynamic_cast<const Model*>(model);
if (!concreteModel) continue;
for (const auto& variant : concreteModel->GetAllModelVariants()) {
for (const auto& variant : concreteModel->GetVariants()) {
const auto& info = variant.GetInfo();
std::cout << " variant: " << info.name << " v" << info.version
<< " cached=" << (variant.IsCached() ? "yes" : "no");
Expand Down Expand Up @@ -93,7 +112,12 @@ void ChatNonStreaming(Manager& manager, const std::string& alias) {
return;
}

model->Download([](float pct) { std::cout << "\rDownloading: " << pct << "% " << std::flush; });
// Prefer CPU variant to avoid DML/GPU provider issues
if (auto* concreteModel = dynamic_cast<Model*>(model)) {
PreferCpuVariant(*concreteModel);
}

model->Download([](float pct) { std::cout << "\rDownloading: " << pct << "% " << std::flush; return true; });
std::cout << "\n";

model->Load();
Expand Down Expand Up @@ -138,6 +162,11 @@ void ChatStreaming(Manager& manager, const std::string& alias) {
return;
}

// Prefer CPU variant to avoid DML/GPU provider issues
if (auto* concreteModel = dynamic_cast<Model*>(model)) {
PreferCpuVariant(*concreteModel);
}

Comment thread
nenad1002 marked this conversation as resolved.
model->Load();

OpenAIChatClient chat(*model);
Expand Down Expand Up @@ -176,7 +205,12 @@ void TranscribeAudio(Manager& manager, const std::string& alias, const std::stri
return;
}

model->Download([](float pct) { std::cout << "\rDownloading: " << pct << "% " << std::flush; });
// Prefer CPU variant to avoid DML/GPU provider issues
if (auto* concreteModel = dynamic_cast<Model*>(model)) {
PreferCpuVariant(*concreteModel);
}

model->Download([](float pct) { std::cout << "\rDownloading: " << pct << "% " << std::flush; return true; });
std::cout << "\n";

model->Load();
Expand Down Expand Up @@ -223,7 +257,12 @@ void ChatWithToolCalling(Manager& manager, const std::string& alias) {
return;
}

model->Download([](float pct) { std::cout << "\rDownloading: " << pct << "% " << std::flush; });
// Prefer CPU variant to avoid DML/GPU provider issues
if (auto* concreteModel = dynamic_cast<Model*>(model)) {
PreferCpuVariant(*concreteModel);
}

model->Download([](float pct) { std::cout << "\rDownloading: " << pct << "% " << std::flush; return true; });
std::cout << "\n";

model->Load();
Expand Down Expand Up @@ -325,26 +364,62 @@ void ChatWithToolCalling(Manager& manager, const std::string& alias) {
// ---------------------------------------------------------------------------
// main
// ---------------------------------------------------------------------------
int main() {
int main(int argc, char* argv[]) {
#ifdef _WIN32
SetConsoleOutputCP(CP_UTF8);
#endif

// Optional command-line args: <chat-model-alias> <audio-model-alias> <audio-file-path>
const std::string chatAlias = (argc > 1) ? argv[1] : "phi-3.5-mini";
const std::string audioAlias = (argc > 2) ? argv[2] : "whisper-large-v3-turbo";
const std::string audioPath = (argc > 3) ? argv[3] : "";

try {
StdLogger logger;
Manager::Create({"SampleApp"}, &logger);
auto& manager = Manager::Instance();

// 1. Browse the full catalog
BrowseCatalog(manager);

// 2. Non-streaming chat (change alias to a model in your catalog)
ChatNonStreaming(manager, "phi-3.5-mini");
try {
BrowseCatalog(manager);
}
catch (const std::exception& ex) {
std::cerr << "Example 1 failed: " << ex.what() << "\n";
}

// 2. Non-streaming chat
try {
ChatNonStreaming(manager, chatAlias);
}
catch (const std::exception& ex) {
std::cerr << "Example 2 failed: " << ex.what() << "\n";
}

// 3. Streaming chat
ChatStreaming(manager, "phi-3.5-mini");
try {
ChatStreaming(manager, chatAlias);
}
Comment thread
nenad1002 marked this conversation as resolved.
catch (const std::exception& ex) {
std::cerr << "Example 3 failed: " << ex.what() << "\n";
}

// 4. Audio transcription (uncomment and set a valid alias + wav path)
// TranscribeAudio(manager, "whisper-small", R"(C:\path\to\your\audio.wav)");
// 4. Audio transcription (requires an audio file path)
if (!audioPath.empty()) {
try {
TranscribeAudio(manager, audioAlias, audioPath);
}
catch (const std::exception& ex) {
std::cerr << "Example 4 failed: " << ex.what() << "\n";
}
}

// 5. Tool calling (define tools, let the model call them, feed results back)
ChatWithToolCalling(manager, "phi-3.5-mini");
// 5. Tool calling
try {
ChatWithToolCalling(manager, chatAlias);
}
catch (const std::exception& ex) {
std::cerr << "Example 5 failed: " << ex.what() << "\n";
}

Manager::Destroy();
return 0;
Expand Down
4 changes: 2 additions & 2 deletions sdk/cpp/src/catalog.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ namespace foundry_local {
return nullptr;
}

std::vector<IModel*> Catalog::ListModels() const {
std::vector<IModel*> Catalog::GetModels() const {
UpdateModels();
auto state = GetState();

Expand Down Expand Up @@ -159,7 +159,7 @@ namespace foundry_local {
}

const auto& targetName = it->second->GetInfo().name;
for (auto& v : model->GetAllModelVariants()) {
for (auto& v : model->GetVariants()) {
// The variants returned by the catalog are sorted by version, so the first match should always be the
// latest version.
if (v.GetInfo().name == targetName) {
Expand Down
Loading
Loading