diff --git a/include/ai/core.h b/include/ai/core.h index 05fb2fc..2b8ecf8 100644 --- a/include/ai/core.h +++ b/include/ai/core.h @@ -31,9 +31,6 @@ /// #include /// /// // Use core types like ai::GenerateOptions, ai::Message, etc. -/// ai::GenerateOptions options{ -/// .model = "some-model", -/// .prompt = "Hello world" -/// }; +/// ai::GenerateOptions options("some-model", "Hello world"); /// ``` namespace ai {} \ No newline at end of file diff --git a/include/ai/logger.h b/include/ai/logger.h index 6aff531..7968a2d 100644 --- a/include/ai/logger.h +++ b/include/ai/logger.h @@ -107,21 +107,55 @@ class ConsoleLogger final : public Logger { namespace detail { +#if defined(__cpp_lib_atomic_shared_ptr) + +inline std::atomic>& logger_instance() { + static std::atomic> instance( + std::make_shared()); + return instance; +} + +inline std::shared_ptr load_logger() { + return logger_instance().load(); +} + +inline void store_logger(std::shared_ptr logger) { + logger_instance().store(std::move(logger)); +} + +#else +// Fall back to the atomic shared_ptr free functions on standard libraries +// that have not implemented std::atomic (notably libc++). + inline std::shared_ptr& logger_instance() { static std::shared_ptr instance = std::make_shared(); return instance; } +inline std::shared_ptr load_logger() { + return std::atomic_load(&logger_instance()); +} + +inline void store_logger(std::shared_ptr logger) { + std::atomic_store(&logger_instance(), std::move(logger)); +} + +#endif + } // namespace detail inline void install_logger(std::shared_ptr logger) { if (logger) { - std::atomic_store(&detail::logger_instance(), std::move(logger)); + detail::store_logger(std::move(logger)); } } +/// Returns the currently installed logger. The reference stays valid until +/// the next logger()/log_* call on the same thread; do not cache it across +/// calls that may log. inline Logger& logger() { - auto ptr = std::atomic_load(&detail::logger_instance()); + thread_local std::shared_ptr ptr; + ptr = detail::load_logger(); return *ptr; } diff --git a/src/langfuse/tracer.cpp b/src/langfuse/tracer.cpp index d0e966d..503802f 100644 --- a/src/langfuse/tracer.cpp +++ b/src/langfuse/tracer.cpp @@ -532,7 +532,7 @@ std::string Trace::to_iso8601(std::chrono::system_clock::time_point t) { #else gmtime_r(&tt, &tm); #endif - char buf[40]; + char buf[80]; std::snprintf(buf, sizeof(buf), "%04d-%02d-%02dT%02d:%02d:%02d.%03lldZ", tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec, static_cast(ms)); diff --git a/src/providers/anthropic/anthropic_factory.cpp b/src/providers/anthropic/anthropic_factory.cpp index bc6540c..9fae17b 100644 --- a/src/providers/anthropic/anthropic_factory.cpp +++ b/src/providers/anthropic/anthropic_factory.cpp @@ -2,8 +2,8 @@ #include "ai/errors.h" #include "anthropic_client.h" +#include "utils/env_utils.h" -#include #include #include @@ -18,13 +18,13 @@ std::string get_api_key_or_default(const std::string& api_key) { return api_key; } - const char* env_api_key = std::getenv("ANTHROPIC_API_KEY"); + auto env_api_key = utils::non_empty_env("ANTHROPIC_API_KEY"); if (!env_api_key) { throw ConfigurationError( "API key not provided and ANTHROPIC_API_KEY environment variable not " "set"); } - return env_api_key; + return *std::move(env_api_key); } std::string get_base_url_or_default(const std::string& base_url) { @@ -48,12 +48,12 @@ Client create_client(const std::string& api_key, const std::string& base_url) { } std::optional try_create_client() { - const char* api_key = std::getenv("ANTHROPIC_API_KEY"); + auto api_key = utils::non_empty_env("ANTHROPIC_API_KEY"); if (!api_key) { return std::nullopt; } - return Client(std::make_unique(api_key, kDefaultBaseUrl)); + return Client(std::make_unique(*api_key, kDefaultBaseUrl)); } } // namespace anthropic -} // namespace ai \ No newline at end of file +} // namespace ai diff --git a/src/providers/anthropic/anthropic_response_parser.cpp b/src/providers/anthropic/anthropic_response_parser.cpp index 4789730..a78bbe1 100644 --- a/src/providers/anthropic/anthropic_response_parser.cpp +++ b/src/providers/anthropic/anthropic_response_parser.cpp @@ -103,7 +103,7 @@ EmbeddingResult AnthropicResponseParser::parse_success_embedding_response( // Extract choices if (response.contains("data") && !response["data"].empty()) { - result.data = std::move(response["data"]); + result.data = response["data"]; } // Extract usage @@ -150,4 +150,4 @@ FinishReason AnthropicResponseParser::parse_stop_reason( } } // namespace anthropic -} // namespace ai \ No newline at end of file +} // namespace ai diff --git a/src/providers/base_provider_client.cpp b/src/providers/base_provider_client.cpp index 9484724..24c16d1 100644 --- a/src/providers/base_provider_client.cpp +++ b/src/providers/base_provider_client.cpp @@ -140,6 +140,7 @@ GenerateResult BaseProviderClient::generate_text_single_step( } StreamResult BaseProviderClient::stream_text(const StreamOptions& options) { + (void)options; // This needs to be implemented with provider-specific stream implementations // For now, return an error ai::logger::log_error("Streaming not yet implemented in BaseProviderClient"); @@ -197,4 +198,4 @@ EmbeddingResult BaseProviderClient::embeddings( } } // namespace providers -} // namespace ai \ No newline at end of file +} // namespace ai diff --git a/src/providers/openai/openai_factory.cpp b/src/providers/openai/openai_factory.cpp index 808e841..da9eb28 100644 --- a/src/providers/openai/openai_factory.cpp +++ b/src/providers/openai/openai_factory.cpp @@ -2,8 +2,8 @@ #include "ai/errors.h" #include "openai_client.h" +#include "utils/env_utils.h" -#include #include #include @@ -18,12 +18,12 @@ std::string get_api_key_or_default(const std::string& api_key) { return api_key; } - const char* env_api_key = std::getenv("OPENAI_API_KEY"); + auto env_api_key = utils::non_empty_env("OPENAI_API_KEY"); if (!env_api_key) { throw ConfigurationError( "API key not provided and OPENAI_API_KEY environment variable not set"); } - return env_api_key; + return *std::move(env_api_key); } std::string get_base_url_or_default(const std::string& base_url) { @@ -55,12 +55,12 @@ Client create_client(const std::string& api_key, } std::optional try_create_client() { - const char* api_key = std::getenv("OPENAI_API_KEY"); + auto api_key = utils::non_empty_env("OPENAI_API_KEY"); if (!api_key) { return std::nullopt; } - return Client(std::make_unique(api_key, kDefaultBaseUrl)); + return Client(std::make_unique(*api_key, kDefaultBaseUrl)); } } // namespace openai -} // namespace ai \ No newline at end of file +} // namespace ai diff --git a/src/providers/openai/openai_response_parser.cpp b/src/providers/openai/openai_response_parser.cpp index 0274956..9709706 100644 --- a/src/providers/openai/openai_response_parser.cpp +++ b/src/providers/openai/openai_response_parser.cpp @@ -145,7 +145,7 @@ EmbeddingResult OpenAIResponseParser::parse_success_embedding_response( // Extract choices if (response.contains("data") && !response["data"].empty()) { - result.data = std::move(response["data"]); + result.data = response["data"]; } // Extract usage @@ -192,4 +192,4 @@ FinishReason OpenAIResponseParser::parse_finish_reason( } } // namespace openai -} // namespace ai \ No newline at end of file +} // namespace ai diff --git a/src/tools/tool_executor.cpp b/src/tools/tool_executor.cpp index 29c1185..0d3e2cc 100644 --- a/src/tools/tool_executor.cpp +++ b/src/tools/tool_executor.cpp @@ -232,6 +232,7 @@ Tool create_simple_tool(const std::string& name, const std::string& description, const std::map& parameters, ToolExecuteFunction execute_func) { + (void)name; // The ToolSet key is the canonical tool name. JsonValue schema = create_object_schema(parameters); return create_tool(description, schema, std::move(execute_func)); } @@ -241,6 +242,7 @@ Tool create_simple_async_tool( const std::string& description, const std::map& parameters, AsyncToolExecuteFunction execute_func) { + (void)name; // The ToolSet key is the canonical tool name. JsonValue schema = create_object_schema(parameters); return create_async_tool(description, schema, std::move(execute_func)); } @@ -280,4 +282,4 @@ std::string generate_tool_call_id() { return id; } -} // namespace ai \ No newline at end of file +} // namespace ai diff --git a/src/utils/env_utils.h b/src/utils/env_utils.h new file mode 100644 index 0000000..29ce392 --- /dev/null +++ b/src/utils/env_utils.h @@ -0,0 +1,20 @@ +#pragma once + +#include +#include +#include + +namespace ai { +namespace utils { + +/// Read an environment variable, treating unset and empty values as absent. +inline std::optional non_empty_env(const char* name) { + const char* value = std::getenv(name); + if (!value || *value == '\0') { + return std::nullopt; + } + return std::string(value); +} + +} // namespace utils +} // namespace ai