HTTP clients
Every LLM client in Koog expects a KoogHttpClient — the abstract HTTP contract the framework uses to talk to providers. You hand one in at construction.
You can build that KoogHttpClient yourself, but it's real work: each provider has its own base URL, auth header shape, content-type, and SSE conventions. Getting all of that right per provider is exactly what KoogHttpClient.Factory exists to spare you. You pass in a Factory and the provider client calls Factory.create(...) with the parameters that fit its API.
Four backend factories ship out of the box — Ktor, the JDK HttpClient, OkHttp, and Spring's WebClient — and you can implement your own.
How it works
One factory works for any provider: pick a backend once and use it across clients.
fun main() {
val factory = KtorKoogHttpClient.Factory()
val openai = OpenAILLMClient(
apiKey = System.getenv("OPENAI_API_KEY"),
settings = OpenAIClientSettings(),
httpClientFactory = factory,
)
val anthropic = AnthropicLLMClient(
apiKey = System.getenv("ANTHROPIC_API_KEY"),
settings = AnthropicClientSettings(),
httpClientFactory = factory,
)
}
import ai.koog.http.client.ktor.KtorKoogHttpClient;
import ai.koog.prompt.executor.clients.anthropic.AnthropicClientSettings;
import ai.koog.prompt.executor.clients.anthropic.AnthropicLLMClient;
import ai.koog.prompt.executor.clients.openai.OpenAIClientSettings;
import ai.koog.prompt.executor.clients.openai.OpenAILLMClient;
KtorKoogHttpClient.Factory factory = new KtorKoogHttpClient.Factory();
OpenAILLMClient openai = new OpenAILLMClient(
System.getenv("OPENAI_API_KEY"),
new OpenAIClientSettings(),
factory
);
AnthropicLLMClient anthropic = new AnthropicLLMClient(
System.getenv("ANTHROPIC_API_KEY"),
new AnthropicClientSettings(),
factory
);
Supported HTTP client flavors
| Module | Notes |
|---|---|
http-client-ktor |
The only backend usable from non-JVM targets. |
http-client-java |
Wraps the JDK 11+ java.net.http.HttpClient. |
http-client-okhttp |
Backed by OkHttp. Android-friendly. |
http-client-spring-webclient |
Backed by Spring WebClient. |
Convenience APIs and factory auto-discovery
On JVM and Android, you can construct each LLM client without passing a factory explicitly.
Behind the scenes, HttpClientFactoryResolver uses java.util.ServiceLoader to resolve KoogHttpClient.Factory from the runtime classpath:
- Every backend module provides a
ServiceLoaderregistration. - Resolution succeeds only when exactly one factory is visible on the runtime classpath.
prompt-executor-llms-alldeclareshttp-client-ktoras aruntimeOnlydependency, so you get Ktor by default without compile-time exposure to that module.simple<Provider>Executor(apiKey)andPromptExecutorBuilder.<provider>(apiKey)use the same resolution path.
import static ai.koog.prompt.executor.clients.openai.OpenAIClientFactory.openAIClient;
import static ai.koog.prompt.executor.llms.all.SimplePromptExecutors.simpleOpenAIExecutor;
String apiKey = System.getenv("OPENAI_API_KEY");
OpenAILLMClient client = openAIClient(apiKey);
PromptExecutor executor = simpleOpenAIExecutor(apiKey);
Auto-discovery is not supported on KMP at the moment, so the convenience methods are not available outside the JVM either. From commonMain, pass a Factory explicitly.
Auto-discovery gotchas
- Zero backends on the runtime classpath →
IllegalStateExceptionon first resolution. Add a backend module to the runtime classpath, or pass aFactoryexplicitly. - Two or more backends → same exception; the message names the providers it found. Exclude all but one with Gradle (
exclude(module = "http-client-ktor")on the offending dependency) or pass aFactoryexplicitly at the call site.
Custom backends
Any class implementing KoogHttpClient.Factory works. To make it auto-discoverable on the JVM, register it as a ServiceLoader provider:
The file contains a single line: the fully qualified name of your factory class. The literal $ (separator for the nested Factory class) is correct — the file is KoogHttpClient$Factory, not KoogHttpClient.Factory.
If you don't want auto-discovery, skip the registration and pass your factory explicitly everywhere.