Skip to content
Closed
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
1 change: 1 addition & 0 deletions .env.local.example
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

# --- LLM provider (required by the example) ---
OPENAI_API_KEY=
ANTHROPIC_API_KEY=

# --- Langfuse (required) ---
# Project keys from your Langfuse instance -> Settings -> API keys
Expand Down
3 changes: 0 additions & 3 deletions .gitmodules
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
[submodule "third_party/zlib"]
path = third_party/zlib
url = https://github.com/madler/zlib.git
[submodule "third_party/brotli"]
path = third_party/brotli
url = https://github.com/google/brotli.git
[submodule "third_party/googletest"]
path = third_party/googletest
url = https://github.com/google/googletest.git
Expand Down
25 changes: 23 additions & 2 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ endfunction()
# Common compile definitions for HTTP support
set(HTTPLIB_COMPILE_DEFS
CPPHTTPLIB_OPENSSL_SUPPORT=1
CPPHTTPLIB_BROTLI_SUPPORT=1
CPPHTTPLIB_ZLIB_SUPPORT=1
CPPHTTPLIB_THREAD_POOL_COUNT=8
)

Expand All @@ -48,6 +48,13 @@ add_library(ai-sdk-cpp-anthropic)
add_library(ai-sdk-cpp-langfuse)
add_library(ai-sdk-cpp INTERFACE)

# Keep installed target names consistent with the build-tree aliases.
set_target_properties(ai-sdk-cpp-core PROPERTIES EXPORT_NAME core)
set_target_properties(ai-sdk-cpp-openai PROPERTIES EXPORT_NAME openai)
set_target_properties(ai-sdk-cpp-anthropic PROPERTIES EXPORT_NAME anthropic)
set_target_properties(ai-sdk-cpp-langfuse PROPERTIES EXPORT_NAME langfuse)
set_target_properties(ai-sdk-cpp PROPERTIES EXPORT_NAME sdk)

# Set library aliases
add_library(ai::sdk ALIAS ai-sdk-cpp)
add_library(ai::core ALIAS ai-sdk-cpp-core)
Expand Down Expand Up @@ -80,6 +87,7 @@ target_sources(ai-sdk-cpp-core
# HTTP and base provider infrastructure
src/http/http_request_handler.cpp
src/providers/base_provider_client.cpp
src/providers/http_sse_stream.cpp

# Tool calling implementations
src/tools/tool_executor.cpp
Expand Down Expand Up @@ -121,6 +129,10 @@ target_link_libraries(ai-sdk-cpp-core
$<BUILD_INTERFACE:OpenSSL::SSL>
$<BUILD_INTERFACE:OpenSSL::Crypto>
$<BUILD_INTERFACE:concurrentqueue>
$<INSTALL_INTERFACE:OpenSSL::SSL>
$<INSTALL_INTERFACE:OpenSSL::Crypto>
$<INSTALL_INTERFACE:ZLIB::ZLIB>
$<INSTALL_INTERFACE:Threads::Threads>
)

# Provider components link to core and share common dependencies
Expand All @@ -132,6 +144,10 @@ foreach(provider openai anthropic)
PRIVATE
$<BUILD_INTERFACE:httplib::httplib>
$<BUILD_INTERFACE:concurrentqueue>
$<INSTALL_INTERFACE:OpenSSL::SSL>
$<INSTALL_INTERFACE:OpenSSL::Crypto>
$<INSTALL_INTERFACE:ZLIB::ZLIB>
$<INSTALL_INTERFACE:Threads::Threads>
)

# Set component availability and HTTP definitions
Expand All @@ -155,6 +171,10 @@ target_link_libraries(ai-sdk-cpp-langfuse
$<BUILD_INTERFACE:OpenSSL::SSL>
$<BUILD_INTERFACE:OpenSSL::Crypto>
$<BUILD_INTERFACE:stduuid::stduuid>
$<INSTALL_INTERFACE:OpenSSL::SSL>
$<INSTALL_INTERFACE:OpenSSL::Crypto>
$<INSTALL_INTERFACE:ZLIB::ZLIB>
$<INSTALL_INTERFACE:Threads::Threads>
)
target_compile_definitions(ai-sdk-cpp-langfuse
PUBLIC
Expand Down Expand Up @@ -268,6 +288,7 @@ configure_package_config_file(
"${CMAKE_CURRENT_SOURCE_DIR}/cmake/ai-sdk-cpp-config.cmake.in"
"${CMAKE_CURRENT_BINARY_DIR}/ai-sdk-cpp-config.cmake"
INSTALL_DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/ai-sdk-cpp
PATH_VARS CMAKE_INSTALL_INCLUDEDIR
)

write_basic_package_version_file(
Expand All @@ -290,4 +311,4 @@ message(STATUS " C++ standard: ${CMAKE_CXX_STANDARD}")
message(STATUS " Build examples: ${BUILD_EXAMPLES}")
message(STATUS " Build tests: ${BUILD_TESTS}")
message(STATUS " Install prefix: ${CMAKE_INSTALL_PREFIX}")
message(STATUS "")
message(STATUS "")
10 changes: 5 additions & 5 deletions DEVELOPMENT.md
Original file line number Diff line number Diff line change
Expand Up @@ -325,18 +325,18 @@ With **debug** level enabled:
```
[2024-01-01 12:00:00.123] [debug] Initializing OpenAI client with base_url: https://api.openai.com
[2024-01-01 12:00:00.124] [debug] OpenAI client configured - host: api.openai.com, use_ssl: true
[2024-01-01 12:00:00.125] [debug] Starting text generation - model: gpt-4o, prompt length: 42
[2024-01-01 12:00:00.126] [debug] Request JSON built: {"model":"gpt-4o","messages":[...]}
[2024-01-01 12:00:00.125] [debug] Starting text generation - model: gpt-5.6, prompt length: 42
[2024-01-01 12:00:00.126] [debug] Request JSON built: {"model":"gpt-5.6","messages":[...]}
[2024-01-01 12:00:00.127] [debug] Creating SSL client for host: api.openai.com
[2024-01-01 12:00:01.234] [debug] Received response - status: 200, body length: 1234
[2024-01-01 12:00:01.235] [info] Text generation successful - model: gpt-4o, response_id: chatcmpl-abc123
[2024-01-01 12:00:01.235] [info] Text generation successful - model: gpt-5.6, response_id: chatcmpl-abc123
```

With **info** level enabled:

```
[2024-01-01 12:00:01.235] [info] Text generation successful - model: gpt-4o, response_id: chatcmpl-abc123
[2024-01-01 12:00:02.456] [info] Text streaming started - model: gpt-4o-mini
[2024-01-01 12:00:01.235] [info] Text generation successful - model: gpt-5.6, response_id: chatcmpl-abc123
[2024-01-01 12:00:02.456] [info] Text streaming started - model: gpt-5.6-luna
[2024-01-01 12:00:03.789] [info] Stream completed - tokens used: 150 prompt, 350 completion, 500 total
```

Expand Down
99 changes: 46 additions & 53 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,18 +24,17 @@ The AI SDK CPP Core module provides a unified API to interact with model provide

```cpp
#include <ai/openai.h>
#include <ai/generate.h>
#include <ai/core.h>
#include <iostream>

int main() {
// Ensure OPENAI_API_KEY environment variable is set
auto client = ai::openai::create_client();

auto result = client.generate_text({
.model = ai::openai::models::kGpt54, // this can also be a string like "gpt-5.4"
.system = "You are a friendly assistant!",
.prompt = "Why is the sky blue?"
});
ai::GenerateOptions options(ai::openai::models::kGpt56,
"Why is the sky blue?");
options.system = "You are a friendly assistant!";
auto result = client.generate_text(options);

if (result) {
std::cout << result->text << std::endl;
Expand All @@ -49,17 +48,16 @@ int main() {

```cpp
#include <ai/anthropic.h>
#include <ai/generate.h>
#include <ai/core.h>
#include <iostream>

int main() {
// Ensure ANTHROPIC_API_KEY environment variable is set
auto client = ai::anthropic::create_client();
auto result = client.generate_text({
.model = ai::anthropic::models::kClaudeSonnet46,
.system = "You are a helpful assistant.",
.prompt = "Explain quantum computing in simple terms."
});
ai::GenerateOptions options(ai::anthropic::models::kClaudeSonnet5,
"Explain quantum computing in simple terms.");
options.system = "You are a helpful assistant.";
auto result = client.generate_text(options);

if (result) {
std::cout << result->text << std::endl;
Expand All @@ -73,21 +71,20 @@ int main() {

```cpp
#include <ai/openai.h>
#include <ai/stream.h>
#include <ai/core.h>
#include <iostream>

int main() {
auto client = ai::openai::create_client();

auto stream = client.stream_text({
.model = ai::openai::models::kGpt54, // this can also be a string like "gpt-5.4"
.system = "You are a helpful assistant.",
.prompt = "Write a short story about a robot."
});
ai::GenerateOptions generate_options(ai::openai::models::kGpt56,
"Write a short story about a robot.");
generate_options.system = "You are a helpful assistant.";
auto stream = client.stream_text(ai::StreamOptions(generate_options));

for (const auto& chunk : stream) {
if (chunk.text) {
std::cout << chunk.text.value() << std::flush;
for (const auto& event : stream) {
if (event.is_text_delta()) {
std::cout << event.text_delta << std::flush;
}
}

Expand All @@ -99,23 +96,21 @@ int main() {

```cpp
#include <ai/openai.h>
#include <ai/generate.h>
#include <ai/core.h>
#include <iostream>

int main() {
auto client = ai::openai::create_client();

ai::Messages messages = {
{"system", "You are a helpful math tutor."},
{"user", "What is 2 + 2?"},
{"assistant", "2 + 2 equals 4."},
{"user", "Now what is 4 + 4?"}
ai::Message::system("You are a helpful math tutor."),
ai::Message::user("What is 2 + 2?"),
ai::Message::assistant("2 + 2 equals 4."),
ai::Message::user("Now what is 4 + 4?")
};

auto result = client.generate_text({
.model = ai::openai::models::kGpt54, // this can also be a string like "gpt-5.4"
.messages = messages
});
auto result = client.generate_text(
ai::GenerateOptions(ai::openai::models::kGpt56, messages));

if (result) {
std::cout << result->text << std::endl;
Expand All @@ -131,7 +126,7 @@ The AI SDK CPP supports function calling, allowing models to interact with exter

```cpp
#include <ai/openai.h>
#include <ai/generate.h>
#include <ai/core.h>
#include <ai/tools.h>
#include <iostream>

Expand Down Expand Up @@ -160,12 +155,11 @@ int main() {
)}
};

auto result = client.generate_text({
.model = ai::openai::models::kGpt54,
.prompt = "What's the weather like in San Francisco?",
.tools = tools,
.max_steps = 3 // Enable multi-step tool calling
});
ai::GenerateOptions options(ai::openai::models::kGpt56,
"What's the weather like in San Francisco?");
options.tools = tools;
options.max_steps = 3;
auto result = client.generate_text(options);

if (result) {
std::cout << result->text << std::endl;
Expand Down Expand Up @@ -216,11 +210,10 @@ int main() {
};

// Multiple async tools will execute in parallel
auto result = client.generate_text({
.model = ai::openai::models::kGpt54,
.prompt = "Fetch data from the user and product APIs",
.tools = tools
});
ai::GenerateOptions options(ai::openai::models::kGpt56,
"Fetch data from the user and product APIs");
options.tools = tools;
auto result = client.generate_text(options);

return 0;
}
Expand Down Expand Up @@ -251,22 +244,23 @@ int main() {
// The client will automatically retry on transient failures:
// - Network errors
// - HTTP 408, 409, 429 (rate limits), and 5xx errors
auto result = client.generate_text({
.model = ai::openai::models::kGpt54,
.prompt = "Hello, world!"
});
auto result = client.generate_text(
ai::GenerateOptions(ai::openai::models::kGpt56, "Hello, world!"));

return 0;
}
```

The same retry-config overload is available from
`ai::anthropic::create_client` for Anthropic requests.

#### Using OpenAI-Compatible APIs (OpenRouter, etc.)

The OpenAI client can be used with any OpenAI-compatible API by specifying a custom base URL. This allows you to use alternative providers like OpenRouter, which offers access to multiple models through a unified API.

```cpp
#include <ai/openai.h>
#include <ai/generate.h>
#include <ai/core.h>
#include <iostream>
#include <cstdlib>

Expand All @@ -285,11 +279,10 @@ int main() {
);

// Use any model available on OpenRouter
auto result = client.generate_text({
.model = "anthropic/claude-sonnet-4-6", // or "meta-llama/llama-3.1-8b-instruct", etc.
.system = "You are a helpful assistant.",
.prompt = "What are the benefits of using OpenRouter?"
});
ai::GenerateOptions options("anthropic/claude-sonnet-5",
"What are the benefits of using OpenRouter?");
options.system = "You are a helpful assistant.";
auto result = client.generate_text(options);

if (result) {
std::cout << result->text << std::endl;
Expand All @@ -314,6 +307,7 @@ See the [OpenRouter example](examples/openrouter_example.cpp) for a complete dem
- ✅ **Streaming**: Real-time streaming of generated content
- ✅ **Multi-turn Conversations**: Support for conversation history
- ✅ **Error Handling**: Comprehensive error handling with optional types
- ✅ **Embeddings**: OpenAI text embedding support

### Recently Added

Expand All @@ -324,7 +318,6 @@ See the [OpenRouter example](examples/openrouter_example.cpp) for a complete dem
### Coming Soon

- 🚧 **Additional Providers**: Google, Cohere, and other providers
- 🚧 **Embeddings**: Text embedding support
- 🚧 **Image Generation**: Support for image generation models

## Examples
Expand Down
17 changes: 11 additions & 6 deletions cmake/ai-sdk-cpp-config.cmake.in
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,11 @@

# AI SDK C++ Package Configuration

include(CMakeFindDependencyMacro)
find_dependency(OpenSSL REQUIRED)
find_dependency(Threads REQUIRED)
find_dependency(ZLIB REQUIRED)

set(AI_SDK_VERSION "@PROJECT_VERSION@")

# Find PUBLIC dependencies only
Expand All @@ -11,14 +16,16 @@ find_dependency(nlohmann_json CONFIG REQUIRED)
include("${CMAKE_CURRENT_LIST_DIR}/ai-sdk-cpp-targets.cmake")

# Available components
set(AI_SDK_AVAILABLE_COMPONENTS core openai anthropic)
set(AI_SDK_AVAILABLE_COMPONENTS core openai anthropic langfuse)

# Check requested components
set(AI_SDK_REQUESTED_COMPONENTS ${ai-sdk-cpp_FIND_COMPONENTS})

# If no components requested, use all available
set(AI_SDK_USE_UMBRELLA_TARGET FALSE)
if(NOT AI_SDK_REQUESTED_COMPONENTS)
set(AI_SDK_REQUESTED_COMPONENTS ${AI_SDK_AVAILABLE_COMPONENTS})
set(AI_SDK_USE_UMBRELLA_TARGET TRUE)
endif()

# Check that all requested components are available
Expand Down Expand Up @@ -54,10 +61,8 @@ foreach(component ${AI_SDK_REQUESTED_COMPONENTS})
list(APPEND AI_SDK_LIBRARIES ai::${component})
endforeach()

# Always add main library if no specific components requested
if("core" IN_LIST AI_SDK_REQUESTED_COMPONENTS AND
"openai" IN_LIST AI_SDK_REQUESTED_COMPONENTS AND
"anthropic" IN_LIST AI_SDK_REQUESTED_COMPONENTS)
# Add the umbrella library only when no specific components were requested.
if(AI_SDK_USE_UMBRELLA_TARGET)
list(APPEND AI_SDK_LIBRARIES ai::sdk)
endif()

Expand All @@ -66,4 +71,4 @@ if(NOT TARGET ai::core)
message(FATAL_ERROR "ai::core target not found. Make sure ai-sdk-cpp was properly installed.")
endif()

message(STATUS "Found AI SDK C++: ${CMAKE_CURRENT_LIST_DIR} (found version \"${AI_SDK_VERSION}\")")
message(STATUS "Found AI SDK C++: ${CMAKE_CURRENT_LIST_DIR} (found version \"${AI_SDK_VERSION}\")")
Loading
Loading