Module openai
Summary
The openai module implements the OpenAI-specific protocol layer within the networking framework. It owns the public async calling functions (call_completion_async, call_llm_async, and the templated call_structured_async) that initiate requests to OpenAI-compatible LLM endpoints, returning integer handles for tracking or cancellation. Internally, the module provides the detail::Protocol struct responsible for reading environment credentials, building request URLs, headers, and JSON payloads, as well as parsing responses—including tool calls, content parts, and structured output formats.
On the protocol side, the module exposes serialization and validation helpers under clore::net::openai::protocol::detail, such as serialize_message, serialize_tool_choice, serialize_tool_definition, serialize_response_format, validate_request, and parsing functions like parse_content_parts and parse_tool_calls. These functions form the public-facing implementation scope for constructing and interpreting OpenAI API requests and responses, ensuring type-safe and correct communication with the service.
Imports
Dependency Diagram
Types
clore::net::openai::detail::Protocol
Declaration: network/openai.cppm:692
Definition: network/openai.cppm:692
Declaration: Namespace clore::net::openai::detail
The struct clore::net::openai::detail::Protocol is a stateless protocol adapter that encapsulates all OpenAI-specific HTTP interactions. All member functions are static, and the struct holds no data; it simply composes lower-level utilities from clore::net::detail and clore::net::protocol. The central invariant is that read_environment must succeed before any request-building functions are called, as they depend on the returned clore::net::detail::EnvironmentConfig. build_url appends "chat/completions" to the configured API base, while build_headers sets "Content-Type" and "Authorization" using the API key. build_request_json delegates to clore::net::protocol::build_request_json, and parse_response validates the raw HTTP response: it rejects empty bodies or status codes >=400 with descriptive LLMError values, then delegates successful responses to clore::net::protocol::parse_response. The provider_name returns "LLM" as a fixed string view.
Invariants
- All member functions are static and constexpr-compatible on compilers supporting constexpr
std::string? - No mutable state is held by the struct.
- Environment variables
OPENAI_BASE_URLandOPENAI_API_KEYmust be set forread_environmentto succeed. build_request_jsonandparse_responserely on external protocol utilities.
Key Members
read_environmentbuild_urlbuild_headersbuild_request_jsonparse_responseprovider_name
Usage Patterns
- Called by a client to obtain environment configuration for constructing HTTP requests.
build_urlandbuild_headersare used to prepare the HTTP request.build_request_jsonserializes aCompletionRequestto JSON.parse_responsedeserializes the HTTP response body intoCompletionResponse.provider_nameis used for logging or identification.
Member Functions
clore::net::openai::detail::Protocol::build_headers
Declaration: network/openai.cppm:705
Definition: network/openai.cppm:705
Declaration: Namespace clore::net::openai::detail
Implementation
static auto build_headers(const clore::net::detail::EnvironmentConfig& environment)
-> std::vector<kota::http::header> {
return std::vector<kota::http::header>{
kota::http::header{
.name = "Content-Type",
.value = "application/json; charset=utf-8",
},
kota::http::header{
.name = "Authorization",
.value = std::format("Bearer {}", environment.api_key),
},
};
}clore::net::openai::detail::Protocol::build_request_json
Declaration: network/openai.cppm:719
Definition: network/openai.cppm:719
Declaration: Namespace clore::net::openai::detail
Implementation
static auto build_request_json(const CompletionRequest& request)
-> std::expected<std::string, LLMError> {
return clore::net::protocol::build_request_json(request);
}clore::net::openai::detail::Protocol::build_url
Declaration: network/openai.cppm:701
Definition: network/openai.cppm:701
Declaration: Namespace clore::net::openai::detail
Implementation
static auto build_url(const clore::net::detail::EnvironmentConfig& environment) -> std::string {
return clore::net::detail::append_url_path(environment.api_base, "chat/completions");
}clore::net::openai::detail::Protocol::parse_response
Declaration: network/openai.cppm:724
Definition: network/openai.cppm:724
Declaration: Namespace clore::net::openai::detail
Implementation
static auto parse_response(const clore::net::detail::RawHttpResponse& raw_response)
-> std::expected<CompletionResponse, LLMError> {
if(raw_response.body.empty()) {
return std::unexpected(LLMError("empty response from LLM"));
}
if(raw_response.http_status >= 400) {
return std::unexpected(
LLMError(std::format("LLM request failed with HTTP {}: {}",
raw_response.http_status,
clore::net::detail::excerpt_for_error(raw_response.body))));
}
return clore::net::protocol::parse_response(raw_response.body);
}clore::net::openai::detail::Protocol::provider_name
Declaration: network/openai.cppm:739
Definition: network/openai.cppm:739
Declaration: Namespace clore::net::openai::detail
Implementation
static auto provider_name() -> std::string_view {
return "LLM";
}clore::net::openai::detail::Protocol::read_environment
Declaration: network/openai.cppm:693
Definition: network/openai.cppm:693
Declaration: Namespace clore::net::openai::detail
Implementation
static auto read_environment()
-> std::expected<clore::net::detail::EnvironmentConfig, LLMError> {
return clore::net::detail::read_credentials(clore::net::detail::CredentialEnv{
.base_url_env = "OPENAI_BASE_URL",
.api_key_env = "OPENAI_API_KEY",
});
}Functions
clore::net::openai::call_completion_async
Declaration: network/openai.cppm:748
Definition: network/openai.cppm:775
Declaration: Namespace clore::net::openai
The implementation of clore::net::openai::call_completion_async serves as a thin delegation layer that invokes the generic clore::net::call_completion_async template, explicitly instantiating it with clore::net::openai::detail::Protocol. The function moves the incoming CompletionRequest and passes a pointer to the provided kota::event_loop, then applies .or_fail() on the returned coroutine task to convert any failure into a kota::task<CompletionResponse, LLMError>. No additional logic, validation, or transformation is performed at this level; all protocol‑specific behavior (URL construction, header building, JSON serialization, and response parsing) is delegated to the detail::Protocol class and its associated free functions in clore::net::openai::protocol and clore::net::openai::protocol::detail.
Side Effects
- performs an asynchronous network request to a completion API
Reads From
requestparameter of typeCompletionRequestloopparameter of typekota::event_loop&- network state via
clore::net::call_completion_async
Usage Patterns
- called with a
CompletionRequestand an event loop reference - typically
co_awaited within another coroutine
clore::net::openai::call_llm_async
Declaration: network/openai.cppm:752
Definition: network/openai.cppm:782
Declaration: Namespace clore::net::openai
The implementation is a thin coroutine wrapper that delegates to the generic template clore::net::call_llm_async instantiated with clore::net::openai::detail::Protocol. After awaiting the generic call, it invokes .or_fail() to convert the outcome into a kota::task<std::string, LLMError>. All request construction, HTTP transport, and response parsing are handled by the generic pipeline, which uses detail::Protocol for OpenAI-specific URL building, header creation, JSON serialization, and response deserialization.
Side Effects
- Initiates an asynchronous LLM request (network I/O)
- Schedules a coroutine or callback on the event loop
Reads From
modelparametersystem_promptparameterrequest(int) parameterloop(event loop) parameter
Usage Patterns
- Called with a model name, system prompt, integer parameter, and event loop to start an async LLM call
- Used to submit a request to an LLM endpoint asynchronously
clore::net::openai::call_llm_async
Declaration: network/openai.cppm:758
Definition: network/openai.cppm:793
Declaration: Namespace clore::net::openai
The function clore::net::openai::call_llm_async is a thin async adapter that delegates to the generic templated clore::net::call_llm_async<detail::Protocol>. It passes the model, system_prompt, and prompt string arguments directly, and provides a pointer to the kota::event_loop obtained from the reference. The inner call returns a kota::task<std::string, LLMError>; the .or_fail() member is called to transform the outcome into a coroutine that resumes with the result string or throws the LLMError on failure. The underlying implementation relies on the detail::Protocol class, which encapsulates OpenAI‑specific request building (via Protocol::build_url, Protocol::build_request_json, and Protocol::build_headers), response parsing (Protocol::parse_response), and environment reading (Protocol::read_environment). The protocol‑specific logic further uses helpers from clore::net::openai::protocol::detail to serialize messages, tool definitions, and tool choices, and to parse tool calls and content parts from the JSON response.
Side Effects
- Initiates an asynchronous HTTP request to an LLM API endpoint, sending the provided prompts and model identifier, and receiving a response. This involves observable I/O as a side effect.
Reads From
- model
system_prompt- prompt
- loop
Writes To
- network socket
- response buffer (internal)
Usage Patterns
- Used to invoke large language models asynchronously in a coroutine context
- Commonly called from other async functions that compose LLM calls
clore::net::openai::call_structured_async
Declaration: network/openai.cppm:765
Definition: network/openai.cppm:805
Declaration: Namespace clore::net::openai
This implementation is a thin coroutine wrapper that delegates all protocol-specific logic to the generic function template clore::net::call_structured_async<detail::Protocol, T>, passing through the model, system_prompt, prompt, and a pointer to the loop. Control flow begins by entering a co_await on the generic call, which internally uses detail::Protocol to build the request URL (via Protocol::build_url), construct the JSON body (via Protocol::build_request_json), and assemble HTTP headers (via Protocol::build_headers). After the network round trip, the generic function invokes Protocol::parse_response to deserialize the JSON response and extract the structured type T, handling error objects and tool-call parsing through helpers like clore::net::openai::protocol::detail::parse_tool_calls and validate_request. The .or_fail() call converts any expected failure (e.g., LLMError) into a thrown exception or immediate error, so the caller receives either a valid T or an error. Dependencies include the protocol infrastructure (detail::Protocol, its nested types and free functions), the JSON utilities in clore::net::openai::protocol::detail, and the generic call_structured_async template that orchestrates the execution lifecycle.
Side Effects
- Initiates an asynchronous network request to an
OpenAI-compatible API via the underlyingclore::net::call_structured_asyncfunction - Allocates a coroutine frame for the async operation
Reads From
std::string_view modelstd::string_view system_promptstd::string_view promptkota::event_loop& loop
Writes To
- Returns a
kota::task<T, LLMError>that will eventually contain the structured result or error
Usage Patterns
- Used to obtain structured outputs from an
OpenAIlanguage model in an asynchronous context - Called from other coroutines that require structured data from LLM completions
clore::net::openai::protocol::detail::parse_content_parts
Declaration: network/openai.cppm:288
Definition: network/openai.cppm:288
Declaration: Namespace clore::net::openai::protocol::detail
The function clore::net::openai::protocol::detail::parse_content_parts iterates over a json::Array of content parts, extracting plain text and refusal content into a single AssistantOutput. For each element, it validates the structure using clore::net::detail::expect_object and reads the "type" field (defaulting to "text"). Parts with type "refusal" require a "refusal" string field, which is appended to a local refusal accumulator; a saw_refusal flag is set. Parts with type "text" or "output_text" look for a "text" payload—either a direct string or an object containing a "value" string—and append it to a text accumulator, setting saw_text. Other types are silently skipped. After processing all elements, the function assigns the accumulated strings to output.text and output.refusal only if their respective flags were set, then returns the assembled AssistantOutput. Dependencies include clore::net::detail::expect_object and clore::net::detail::expect_string for JSON validation, and the function returns std::expected<AssistantOutput, LLMError>.
Side Effects
No observable side effects are evident from the extracted code.
Reads From
- the
const json::Array& partsparameter - nested JSON objects and strings accessed via
part->get()andtext_object->get()
Writes To
- the returned
std::expected<AssistantOutput, LLMError>object
Usage Patterns
- Called to parse the
contentarray of an assistant message in theOpenAIprotocol - Used within message deserialization to convert raw JSON into domain types
clore::net::openai::protocol::detail::parse_tool_calls
Declaration: network/openai.cppm:369
Definition: network/openai.cppm:369
Declaration: Namespace clore::net::openai::protocol::detail
The function iterates over each element of the input calls JSON array. For each element, it first validates that the element is a JSON object using clore::net::detail::expect_object. It then extracts the id field, ensures it is a non-empty string via clore::net::detail::expect_string, and checks for duplicate ids using a local std::unordered_set<std::string>; any duplicate causes an immediate std::unexpected error. Next, the type field is extracted and must equal the string "function"; otherwise an unsupported-type error is returned. From the function object the name and arguments fields are retrieved: name is taken as a plain string, while arguments must be a string that is then parsed into a json::Value via json::parse. If any required field is missing or fails its type check, the function returns std::unexpected with an LLMError describing the problem. On success, a ToolCall struct is populated with id, name, the raw arguments_json string, and the parsed arguments JSON value, and appended to the result vector. After processing all elements, the vector is returned as a success value. The implementation relies on clore::net::detail::expect_object and expect_string for safe typed field access, and on json::parse for converting the arguments string into a structured JSON value.
Side Effects
No observable side effects are evident from the extracted code.
Reads From
- input
const json::Array& callsparameter
Writes To
- local
std::vector<ToolCall> parsed_calls - return value in
std::expected
Usage Patterns
- parse tool calls from
OpenAIAPI response - deserialize tool call array in protocol layer
clore::net::openai::protocol::detail::serialize_message
Declaration: network/openai.cppm:27
Definition: network/openai.cppm:27
Declaration: Namespace clore::net::openai::protocol::detail
The function clore::net::openai::protocol::detail::serialize_message uses std::visit to dispatch over the variant-based Message type. For each concrete message variant (SystemMessage, UserMessage, AssistantMessage, AssistantToolCallMessage, or ToolResultMessage), it constructs a JSON object by setting the "role" field, inserting UTF‑8‑normalized content via clore::net::detail::insert_string_field, and, for AssistantToolCallMessage, iterating over tool_calls to build nested "function" objects with "name" and "arguments". The completed object is appended to the output json::Array. All JSON creation and field insertion is guarded by clore::net::detail::make_empty_object and clore::net::detail::insert_string_field; any failure causes an early return of std::unexpected with an LLMError. Error propagation is uniform across all branches through the std::expected<void, LLMError> return type.
Side Effects
- mutates output array
out - allocates memory for JSON objects and strings
Reads From
messageparameter- message fields:
content,tool_calls,tool_call_id,name,arguments_json
Writes To
- output array
out - temporary JSON objects that are moved into
out
Usage Patterns
- called during request serialization to convert a
Messagevariant to JSON - used in constructing the messages array for
OpenAIchat completions API
clore::net::openai::protocol::detail::serialize_response_format
Declaration: network/openai.cppm:209
Definition: network/openai.cppm:209
Declaration: Namespace clore::net::openai::protocol::detail
The function begins by allocating two empty JSON objects through clore::net::detail::make_empty_object—one for the response format and one for its optional schema. Allocation failure is immediately propagated as an std::unexpected error. It then inspects format.schema: if absent, a simple "json_object" type is assigned; if present, the type is set to "json_schema" and the function fills the schema object by inserting the format.name (using clore::net::detail::insert_string_field), the format.strict flag, and a cloned copy of the schema content via clore::net::detail::clone_object. Every intermediate operation that may fail returns the error via std::unexpected. Finally, the constructed response format object is moved into the provided root under the key "response_format", and a success value is returned.
Side Effects
- Modifies the provided
json::Object& rootby inserting aresponse_formatfield. - Allocates memory for JSON objects and clones schema via
make_empty_objectandclone_object.
Reads From
formatparameter (of typeconst ResponseFormat&): readsformat.schema,format.name,format.strict.
Writes To
root(json::Object&): inserts"response_format"with the serialized object.- Local
objectandschema_objectjson::Objectinstances, which are then moved intoroot.
Usage Patterns
- Used in request serialization for
OpenAIAPI calls. - Called alongside
serialize_message,serialize_tool_choice, etc. to build a full request body.
clore::net::openai::protocol::detail::serialize_tool_choice
Declaration: network/openai.cppm:167
Definition: network/openai.cppm:167
Declaration: Namespace clore::net::openai::protocol::detail
The function uses std::visit to pattern-match on the ToolChoice variant. For the trivial alternatives (ToolChoiceAuto, ToolChoiceRequired, ToolChoiceNone), it directly inserts the corresponding string literal ("auto", "required", or "none") into the output json::Object under the key "tool_choice" and returns success. The default branch handles a forced tool choice that carries a name. It allocates two temporary JSON objects via clore::net::detail::make_empty_object, sets "type": "function" on the outer object, uses clore::net::detail::insert_string_field to write the name into a nested function object, and then embeds that function object into the outer object before inserting the complete structure into root. Each allocation or insertion can fail; failures are propagated as std::unexpected<LLMError> through the returned std::expected<void, LLMError>.
Side Effects
- Modifies
rootby inserting the"tool_choice"key and its corresponding value. - Creates temporary
json::Objectinstances viaclore::net::detail::make_empty_object.
Reads From
choiceparameter (a variant)current.namefor forced tool choice
Writes To
root(thejson::Object¶meter)- Error state in the return value
Usage Patterns
- Called during
OpenAIAPI request serialization to set thetool_choicefield. - Part of the
clore::net::openai::protocol::detailnamespace for protocol implementation.
clore::net::openai::protocol::detail::serialize_tool_definition
Declaration: network/openai.cppm:248
Definition: network/openai.cppm:248
Declaration: Namespace clore::net::openai::protocol::detail
The function constructs a JSON tool definition object and appends it to the provided json::Array. It begins by creating two empty JSON objects using clore::net::detail::make_empty_object; if either allocation fails, the function immediately returns std::unexpected with the propagated error. The outer object receives a "type" field set to "function". Then, using clore::net::detail::insert_string_field, the tool’s name and description are inserted into the inner function_object; each insertion is checked and any failure causes an early error return. The tool’s parameters are cloned via clore::net::detail::clone_object, and both the cloned parameters and the strict boolean are placed into the function_object. The completed function_object is moved into the outer object under the "function" key, and the outer object is appended to the array via push_back. On success, the function returns an empty std::expected.
The implementation depends on several helper utilities from clore::net::detail: make_empty_object for error-checked object creation, insert_string_field for string insertion with error reporting, and clone_object to deep‑copy the parameters schema. All error paths propagate through std::expected as LLMError, ensuring no partial modifications persist in the output array when a step fails.
Side Effects
- Appends a new JSON object representing the tool definition to the
toolsarray.
Reads From
toolparameter:tool.name,tool.description,tool.parameters,tool.strict
Writes To
- Output parameter
tools(json::Array)
Usage Patterns
- Serializes a single tool definition for inclusion in an
OpenAIAPI request - Called by higher-level serialization functions that build the tools array
clore::net::openai::protocol::detail::validate_request
Declaration: network/openai.cppm:23
Definition: network/openai.cppm:23
Declaration: Namespace clore::net::openai::protocol::detail
The function clore::net::openai::protocol::detail::validate_request validates a CompletionRequest for the OpenAI protocol. Its implementation forwards directly to clore::net::detail::validate_completion_request(request, true, true), enabling both standard request validation and tool-call validation. This design centralizes core validation logic within the clore::net::detail namespace, allowing the OpenAI-specific layer to reuse it without duplication. The function returns std::expected<void, LLMError>, where success indicates the request is well-formed, and failure provides an error description.
The control flow is trivial: no additional checks or processing occur beyond the delegate call. The primary dependency is the shared validation function that inspects field completeness, type correctness, and semantic constraints such as tool call consistency. This ensures that downstream code in the OpenAI protocol—such as build_request_json or serialize_message—receives a valid request before constructing the API payload.
Side Effects
No observable side effects are evident from the extracted code.
Reads From
- const
CompletionRequest& request
Usage Patterns
- Used to validate a
CompletionRequestbefore processing
clore::net::protocol::build_request_json
Declaration: network/openai.cppm:457
Definition: network/openai.cppm:465
Declaration: Namespace clore::net::protocol
The function first validates the incoming request via openai::protocol::detail::validate_request, returning an error immediately if validation fails. It then constructs the top-level JSON object and a messages array using utility helpers from clore::net::detail. The request’s model is inserted directly into the root, and each message in request.messages is serialized by openai::protocol::detail::serialize_message and appended to the array. After inserting the messages array, optional fields are handled: if request.response_format is present, it is serialized via openai::protocol::detail::serialize_response_format; if request.tools is non‑empty, a tools array is built using openai::protocol::detail::serialize_tool_definition for each tool; similarly, request.tool_choice (if set) is serialized via openai::protocol::detail::serialize_tool_choice, and request.parallel_tool_calls (if set) is inserted directly. Finally, the complete JSON object is converted to a string by kota::codec::json::to_string, and any serialization error is wrapped in an LLMError. The implementation relies entirely on the OpenAI‑protocol detail functions for type‑specific serialization and on kota’s JSON library for the final string encoding.
Side Effects
No observable side effects are evident from the extracted code.
Reads From
- request
Writes To
- a JSON string inside the returned expected
Usage Patterns
- serializing a completion request to JSON
- preparing data for HTTP request
- converting
CompletionRequestto JSON string
clore::net::protocol::parse_response
Declaration: network/openai.cppm:459
Definition: network/openai.cppm:532
Declaration: Namespace clore::net::protocol
The implementation of clore::net::protocol::parse_response first parses the incoming json_text as a JSON object using kota::codec::json::parse. If parsing fails, it returns an LLMError describing the parse failure. It then checks for a top-level "error" field; if present, it extracts the "message" sub-field and returns an LLMError with that message, or a generic error if the message is missing. After confirming no error, the function extracts the required "id" and "model" strings from the root object, failing with descriptive LLMError values if either is missing or not a string. It then retrieves the "choices" array, ensuring it is non-empty, and focuses on choices[0], from which it obtains the "finish_reason" string. The function validates the finish_reason against known values: "length" and "content_filter" are treated as errors, "stop" and "tool_calls" are accepted, and any other value triggers an unsupported error.
Next, the function extracts the "message" object from the first choice. It handles an optional "refusal" field (non-null) and the "content" field, which may be a plain string, a JSON array of content parts, or null. If content is an array, it delegates to openai::protocol::detail::parse_content_parts to separate text and refusal segments. An optional "tool_calls" array, if present, is parsed by openai::protocol::detail::parse_tool_calls. After extracting these fields, the function enforces consistency: if finish_reason is "tool_calls" but no tool calls were returned, it returns an error; conversely, if finish_reason is "stop" but tool calls exist, it also returns an error. Finally, it verifies that at least one of text, refusal, or tool_calls is present before constructing and returning a CompletionResponse containing the extracted id, model, the assembled message (AssistantOutput), and the raw JSON string for downstream consumers.
Side Effects
No observable side effects are evident from the extracted code.
Reads From
json_textparameter- global JSON parsing library (
kota::codec::json::parse)
Usage Patterns
- Called to parse a raw JSON response from an LLM API endpoint
- Used in the protocol layer to convert HTTP response body to domain object
- Typically invoked by higher-level functions that handle network responses
Internal Structure
The module openai implements the OpenAI provider within clore::net and is organized into three internal layers: a protocol::detail namespace with low-level serialization and parsing functions (e.g., serialize_message, serialize_tool_choice, parse_content_parts, parse_tool_calls, validate_request, build_request_json, and parse_response); a detail namespace containing the Protocol struct, which encapsulates provider-specific concerns such as environment reading, URL/header construction, request JSON building, and response parsing; and a public API layer exposing call_llm_async, call_completion_async, and the template call_structured_async. The module depends on client, http, protocol, provider, schema, std, and support modules, leveraging the core networking layer for HTTP dispatch, the protocol types for request/response models, the provider module for credential management, schema generation for structured outputs, and support utilities for JSON and string handling. Internally, the Protocol struct orchestrates HTTP interactions through the imported http module, while the protocol::detail functions handle the conversion between internal representation and OpenAI-specific JSON formats, ensuring a clean separation of concerns that facilitates independent testing and future provider additions.