Skip to content
Merged
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
4 changes: 3 additions & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,8 @@ foreach(provider openai anthropic)
)
endforeach()

# Langfuse tracing component links to core and uses httplib + OpenSSL.
# Langfuse tracing component links to core and uses httplib + OpenSSL +
# stduuid (header-only, vendored under third_party/stduuid-header-only).
target_link_libraries(ai-sdk-cpp-langfuse
PUBLIC
ai::core
Expand All @@ -153,6 +154,7 @@ target_link_libraries(ai-sdk-cpp-langfuse
$<BUILD_INTERFACE:httplib::httplib>
$<BUILD_INTERFACE:OpenSSL::SSL>
$<BUILD_INTERFACE:OpenSSL::Crypto>
$<BUILD_INTERFACE:stduuid::stduuid>
)
target_compile_definitions(ai-sdk-cpp-langfuse
PUBLIC
Expand Down
4 changes: 2 additions & 2 deletions examples/langfuse_tracing.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,9 @@

namespace {

const char* env_or(const char* name, const char* fallback) {
const char* getenv_nonempty(const char* name) {
const char* v = std::getenv(name);
return (v && *v) ? v : fallback;
return (v && *v) ? v : nullptr;
}

ai::JsonValue lookup_user(const ai::JsonValue& args,
Expand Down
50 changes: 35 additions & 15 deletions include/ai/langfuse.h
Original file line number Diff line number Diff line change
Expand Up @@ -68,19 +68,34 @@ struct Config {
int connection_timeout_sec = 10;
int read_timeout_sec = 30;

/// If true, suppress HTTP/JSON errors from `Trace::end()` (best-effort
/// tracing). Failures are still logged via the SDK logger.
bool best_effort = true;
/// Error policy for `Trace::end()`. Strict (the default) reports HTTP/JSON
/// failures via the return value so misconfigurations surface immediately;
/// kBestEffort swallows them (failures are still logged).
enum class ErrorPolicy { kStrict, kBestEffort };
ErrorPolicy error_policy = ErrorPolicy::kStrict;
};

class Trace;

/// Optional fields settable at trace-creation time. Mirrors the
/// struct-of-options style used elsewhere in the SDK (GenerateOptions, Config).
struct TraceOptions {
std::optional<JsonValue> input;
std::optional<JsonValue> output;
std::optional<std::string> user_id;
std::optional<std::string> session_id;
std::optional<JsonValue> metadata;
std::vector<std::string> tags;
};

/// Tracer holds the Langfuse credentials and is the entry point for creating
/// traces. Construct one per process; it is safe to create multiple traces
/// concurrently from a single Tracer.
class Tracer {
public:
explicit Tracer(Config config);
~Tracer(); // Out-of-line: HttpState is forward-declared; its destructor
// needs the complete type, which lives in tracer.cpp.

/// True iff host, public_key and secret_key are set.
bool is_valid() const;
Expand All @@ -89,21 +104,23 @@ class Tracer {

/// Start a new trace. Returns a shared handle so callbacks attached via
/// `Trace::instrument()` can keep it alive until generate_text returns.
std::shared_ptr<Trace> start_trace(
const std::string& name,
std::optional<JsonValue> input = std::nullopt,
std::optional<std::string> user_id = std::nullopt,
std::optional<std::string> session_id = std::nullopt,
std::optional<JsonValue> metadata = std::nullopt,
std::vector<std::string> tags = {});
std::shared_ptr<Trace> start_trace(const std::string& name,
TraceOptions opts = {});

/// POST a batch of ingestion events to Langfuse. Returns true on 2xx.
/// Public so advanced callers can build their own event batches; most users
/// should rely on `Trace::end()`.
bool send_batch(const JsonValue& events);

private:
// Lazily constructed httplib client + base path + Basic-auth header. Cached
// to avoid TLS handshake per send_batch call. Guarded by mu_.
struct HttpState;
HttpState& http_state();

Config config_;
std::mutex mu_;
std::unique_ptr<HttpState> http_;
};

/// A Trace owns a list of pending ingestion events and writes them all to
Expand Down Expand Up @@ -141,14 +158,20 @@ class Trace : public std::enable_shared_from_this<Trace> {
void finish_generation(const GenerateResult& result);

/// Flush all accumulated events to Langfuse. Idempotent; subsequent calls
/// are no-ops. Returns true on success (or when best_effort is enabled and
/// the request failed).
/// are no-ops. After end(), further set_* / instrument / finish_generation
/// calls become no-ops to prevent silently dropping events. Returns true on
/// success (or when Config::error_policy is kBestEffort and the request
/// failed).
bool end();

/// Generate a new UUID v4. Exposed so callers (and `Tracer::start_trace`)
/// can mint trace ids without re-implementing UUID generation.
static std::string new_uuid();

/// Format a system_clock time_point as RFC 3339 / ISO 8601 with millisecond
/// precision and trailing 'Z'. Public so callers can stamp custom events.
static std::string to_iso8601(std::chrono::system_clock::time_point t);

private:
struct PendingGeneration {
std::string id;
Expand All @@ -169,9 +192,6 @@ class Trace : public std::enable_shared_from_this<Trace> {
// input/output/metadata/tags.
JsonValue build_trace_event() const;

// Helpers
static std::string now_iso8601();

Tracer& tracer_;
std::string id_;
std::string name_;
Expand Down
6 changes: 0 additions & 6 deletions include/ai/tools.h
Original file line number Diff line number Diff line change
Expand Up @@ -84,12 +84,6 @@ class MultiStepCoordinator {
generate_func);

private:
/// Create the next generation options based on previous step
static GenerateOptions create_next_step_options(
const GenerateOptions& base_options,
const GenerateResult& previous_result,
const std::vector<ToolResult>& tool_results);

/// Convert tool results to messages for the next step
static Messages tool_results_to_messages(
const std::vector<ToolCall>& tool_calls,
Expand Down
Loading
Loading