openai/openai-java
GitHub: openai/openai-java
OpenAI 官方 Java SDK,封装了完整的 OpenAI REST API 接口,让 Java 应用能便捷地调用 GPT 等模型。
Stars: 1478 | Forks: 231
# OpenAI Java API Library
[](https://central.sonatype.com/artifact/com.openai/openai-java/4.39.1)
[](https://javadoc.io/doc/com.openai/openai-java/4.39.1)
The OpenAI Java SDK provides convenient access to the [OpenAI REST API](https://platform.openai.com/docs) from applications written in Java.
The REST API documentation can be found on [platform.openai.com](https://platform.openai.com/docs). Javadocs are available on [javadoc.io](https://javadoc.io/doc/com.openai/openai-java/4.39.1).
## Installation
[_Try `openai-java-spring-boot-starter` if you're using Spring Boot!_](#spring-boot)
### Gradle
implementation("com.openai:openai-java:4.39.1")
### Maven
com.openai
openai-java
4.39.1
## Requirements
This library requires Java 8 or later.
## Usage
The primary API for interacting with OpenAI models is the [Responses API](https://platform.openai.com/docs/api-reference/responses). You can generate text from the model with the code below.
import com.openai.client.OpenAIClient;
import com.openai.client.okhttp.OpenAIOkHttpClient;
import com.openai.models.ChatModel;
import com.openai.models.responses.Response;
import com.openai.models.responses.ResponseCreateParams;
// Configures using the `OPENAI_API_KEY`, `OPENAI_ORG_ID` and `OPENAI_PROJECT_ID` environment variables
OpenAIClient client = OpenAIOkHttpClient.fromEnv();
ResponseCreateParams params = ResponseCreateParams.builder()
.input("Say this is a test")
.model(ChatModel.GPT_5_2)
.build();
Response response = client.responses().create(params);
The previous standard (supported indefinitely) for generating text is the [Chat Completions API](https://platform.openai.com/docs/api-reference/chat). You can use that API to generate text from the model with the code below.
import com.openai.client.OpenAIClient;
import com.openai.client.okhttp.OpenAIOkHttpClient;
import com.openai.models.ChatModel;
import com.openai.models.chat.completions.ChatCompletion;
import com.openai.models.chat.completions.ChatCompletionCreateParams;
// Configures using the `openai.apiKey`, `openai.adminKey`, `openai.orgId`, `openai.projectId`, `openai.webhookSecret` and `openai.baseUrl` system properties
// Or configures using the `OPENAI_API_KEY`, `OPENAI_ADMIN_KEY`, `OPENAI_ORG_ID`, `OPENAI_PROJECT_ID`, `OPENAI_WEBHOOK_SECRET` and `OPENAI_BASE_URL` environment variables
OpenAIClient client = OpenAIOkHttpClient.fromEnv();
ChatCompletionCreateParams params = ChatCompletionCreateParams.builder()
.addUserMessage("Say this is a test")
.model(ChatModel.GPT_5_2)
.build();
ChatCompletion chatCompletion = client.chat().completions().create(params);
## Client configuration
Configure the client using system properties or environment variables:
import com.openai.client.OpenAIClient;
import com.openai.client.okhttp.OpenAIOkHttpClient;
// Configures using the `openai.apiKey`, `openai.adminKey`, `openai.orgId`, `openai.projectId`, `openai.webhookSecret` and `openai.baseUrl` system properties
// Or configures using the `OPENAI_API_KEY`, `OPENAI_ADMIN_KEY`, `OPENAI_ORG_ID`, `OPENAI_PROJECT_ID`, `OPENAI_WEBHOOK_SECRET` and `OPENAI_BASE_URL` environment variables
OpenAIClient client = OpenAIOkHttpClient.fromEnv();
Or manually:
import com.openai.client.OpenAIClient;
import com.openai.client.okhttp.OpenAIOkHttpClient;
OpenAIClient client = OpenAIOkHttpClient.builder()
.apiKey("My API Key")
.adminApiKey("My Admin API Key")
.build();
Or using a combination of the two approaches:
import com.openai.client.OpenAIClient;
import com.openai.client.okhttp.OpenAIOkHttpClient;
OpenAIClient client = OpenAIOkHttpClient.builder()
// Configures using the `openai.apiKey`, `openai.adminKey`, `openai.orgId`, `openai.projectId`, `openai.webhookSecret` and `openai.baseUrl` system properties
// Or configures using the `OPENAI_API_KEY`, `OPENAI_ADMIN_KEY`, `OPENAI_ORG_ID`, `OPENAI_PROJECT_ID`, `OPENAI_WEBHOOK_SECRET` and `OPENAI_BASE_URL` environment variables
.fromEnv()
.apiKey("My API Key")
.build();
See this table for the available options:
| Setter | System property | Environment variable | Required | Default value |
| --------------- | ---------------------- | ----------------------- | -------- | ----------------------------- |
| `apiKey` | `openai.apiKey` | `OPENAI_API_KEY` | false | - |
| `adminApiKey` | `openai.adminKey` | `OPENAI_ADMIN_KEY` | false | - |
| `organization` | `openai.orgId` | `OPENAI_ORG_ID` | false | - |
| `project` | `openai.projectId` | `OPENAI_PROJECT_ID` | false | - |
| `webhookSecret` | `openai.webhookSecret` | `OPENAI_WEBHOOK_SECRET` | false | - |
| `baseUrl` | `openai.baseUrl` | `OPENAI_BASE_URL` | true | `"https://api.openai.com/v1"` |
System properties take precedence over environment variables.
### Modifying configuration
To temporarily use a modified client configuration, while reusing the same connection and thread pools, call `withOptions()` on any client or service:
import com.openai.client.OpenAIClient;
OpenAIClient clientWithOptions = client.withOptions(optionsBuilder -> {
optionsBuilder.baseUrl("https://example.com");
optionsBuilder.maxRetries(42);
});
The `withOptions()` method does not affect the original client or service.
### Workload identity authentication
Workload identity authentication allows applications running in cloud environments (Kubernetes, Azure, GCP) to authenticate using short-lived tokens issued by the cloud provider, instead of long-lived API keys.
#### Basic setup
import com.openai.auth.*;
import com.openai.client.OpenAIClient;
import com.openai.client.okhttp.OpenAIOkHttpClient;
SubjectTokenProvider provider = K8sServiceAccountTokenProvider.builder().build();
WorkloadIdentity workloadIdentity = WorkloadIdentity.builder()
.identityProviderId("your-identity-provider-id")
.serviceAccountId("your-service-account-id")
.provider(provider)
.build();
OpenAIClient client = OpenAIOkHttpClient.builder()
.workloadIdentity(workloadIdentity)
.build();
#### Kubernetes service account token provider
// Use default token path (/var/run/secrets/kubernetes.io/serviceaccount/token)
SubjectTokenProvider provider = K8sServiceAccountTokenProvider.builder().build();
// Or specify a custom token path
SubjectTokenProvider provider = K8sServiceAccountTokenProvider.builder()
.tokenPath("/custom/path/to/token")
.build();
#### Azure Managed Identity provider
import com.openai.auth.*;
// Use defaults (resource: https://management.azure.com/, api-version: 2018-02-01)
SubjectTokenProvider provider = AzureManagedIdentityTokenProvider.builder()
.build();
import com.openai.auth.*;
// Or customize
SubjectTokenProvider provider = AzureManagedIdentityTokenProvider.builder()
.resource("https://management.azure.com/")
.apiVersion("2018-02-01")
.build();
#### GCP ID token provider
import com.openai.auth.*;
SubjectTokenProvider provider = GcpIdTokenProvider.builder()
.build();
import com.openai.auth.*;
// Or customize the audience
SubjectTokenProvider provider = GcpIdTokenProvider.builder()
.audience("https://api.openai.com/v1")
.build();
## Requests and responses
To send a request to the OpenAI API, build an instance of some `Params` class and pass it to the corresponding client method. When the response is received, it will be deserialized into an instance of a Java class.
For example, `client.chat().completions().create(...)` should be called with an instance of `ChatCompletionCreateParams`, and it will return an instance of `ChatCompletion`.
## Immutability
Each class in the SDK has an associated [builder](https://blogs.oracle.com/javamagazine/post/exploring-joshua-blochs-builder-design-pattern-in-java) or factory method for constructing it.
Each class is [immutable](https://docs.oracle.com/javase/tutorial/essential/concurrency/immutable.html) once constructed. If the class has an associated builder, then it has a `toBuilder()` method, which can be used to convert it back to a builder for making a modified copy.
Because each class is immutable, builder modification will _never_ affect already built class instances.
## Asynchronous execution
The default client is synchronous. To switch to asynchronous execution, call the `async()` method:
import com.openai.client.OpenAIClient;
import com.openai.client.okhttp.OpenAIOkHttpClient;
import com.openai.models.ChatModel;
import com.openai.models.chat.completions.ChatCompletion;
import com.openai.models.chat.completions.ChatCompletionCreateParams;
import java.util.concurrent.CompletableFuture;
// Configures using the `openai.apiKey`, `openai.adminKey`, `openai.orgId`, `openai.projectId`, `openai.webhookSecret` and `openai.baseUrl` system properties
// Or configures using the `OPENAI_API_KEY`, `OPENAI_ADMIN_KEY`, `OPENAI_ORG_ID`, `OPENAI_PROJECT_ID`, `OPENAI_WEBHOOK_SECRET` and `OPENAI_BASE_URL` environment variables
OpenAIClient client = OpenAIOkHttpClient.fromEnv();
ChatCompletionCreateParams params = ChatCompletionCreateParams.builder()
.addUserMessage("Say this is a test")
.model(ChatModel.GPT_5_2)
.build();
CompletableFuture chatCompletion = client.async().chat().completions().create(params);
Or create an asynchronous client from the beginning:
import com.openai.client.OpenAIClientAsync;
import com.openai.client.okhttp.OpenAIOkHttpClientAsync;
import com.openai.models.ChatModel;
import com.openai.models.chat.completions.ChatCompletion;
import com.openai.models.chat.completions.ChatCompletionCreateParams;
import java.util.concurrent.CompletableFuture;
// Configures using the `openai.apiKey`, `openai.adminKey`, `openai.orgId`, `openai.projectId`, `openai.webhookSecret` and `openai.baseUrl` system properties
// Or configures using the `OPENAI_API_KEY`, `OPENAI_ADMIN_KEY`, `OPENAI_ORG_ID`, `OPENAI_PROJECT_ID`, `OPENAI_WEBHOOK_SECRET` and `OPENAI_BASE_URL` environment variables
OpenAIClientAsync client = OpenAIOkHttpClientAsync.fromEnv();
ChatCompletionCreateParams params = ChatCompletionCreateParams.builder()
.addUserMessage("Say this is a test")
.model(ChatModel.GPT_5_2)
.build();
CompletableFuture chatCompletion = client.chat().completions().create(params);
The asynchronous client supports the same options as the synchronous one, except most methods return `CompletableFuture`s.
## Streaming
The SDK defines methods that return response "chunk" streams, where each chunk can be individually processed as soon as it arrives instead of waiting on the full response. Streaming methods generally correspond to [SSE](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events) or [JSONL](https://jsonlines.org) responses.
Some of these methods may have streaming and non-streaming variants, but a streaming method will always have a `Streaming` suffix in its name, even if it doesn't have a non-streaming variant.
These streaming methods return [`StreamResponse`](openai-java-core/src/main/kotlin/com/openai/core/http/StreamResponse.kt) for synchronous clients:
import com.openai.core.http.StreamResponse;
import com.openai.models.chat.completions.ChatCompletionChunk;
try (StreamResponse streamResponse = client.chat().completions().createStreaming(params)) {
streamResponse.stream().forEach(chunk -> {
System.out.println(chunk);
});
System.out.println("No more chunks!");
}
Or [`AsyncStreamResponse`](openai-java-core/src/main/kotlin/com/openai/core/http/AsyncStreamResponse.kt) for asynchronous clients:
import com.openai.core.http.AsyncStreamResponse;
import com.openai.models.chat.completions.ChatCompletionChunk;
import java.util.Optional;
client.async().chat().completions().createStreaming(params).subscribe(chunk -> {
System.out.println(chunk);
});
// If you need to handle errors or completion of the stream
client.async().chat().completions().createStreaming(params).subscribe(new AsyncStreamResponse.Handler<>() {
@Override
public void onNext(ChatCompletionChunk chunk) {
System.out.println(chunk);
}
@Override
public void onComplete(Optional error) {
if (error.isPresent()) {
System.out.println("Something went wrong!");
throw new RuntimeException(error.get());
} else {
System.out.println("No more chunks!");
}
}
});
// Or use futures
client.async().chat().completions().createStreaming(params)
.subscribe(chunk -> {
System.out.println(chunk);
})
.onCompleteFuture()
.whenComplete((unused, error) -> {
if (error != null) {
System.out.println("Something went wrong!");
throw new RuntimeException(error);
} else {
System.out.println("No more chunks!");
}
});
Async streaming uses a dedicated per-client cached thread pool [`Executor`](https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/Executor.html) to stream without blocking the current thread. This default is suitable for most purposes.
To use a different `Executor`, configure the subscription using the `executor` parameter:
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
Executor executor = Executors.newFixedThreadPool(4);
client.async().chat().completions().createStreaming(params).subscribe(
chunk -> System.out.println(chunk), executor
);
Or configure the client globally using the `streamHandlerExecutor` method:
import com.openai.client.OpenAIClient;
import com.openai.client.okhttp.OpenAIOkHttpClient;
import java.util.concurrent.Executors;
OpenAIClient client = OpenAIOkHttpClient.builder()
.fromEnv()
.streamHandlerExecutor(Executors.newFixedThreadPool(4))
.build();
### Streaming helpers
The SDK provides conveniences for streamed chat completions. A
[`ChatCompletionAccumulator`](openai-java-core/src/main/kotlin/com/openai/helpers/ChatCompletionAccumulator.kt)
can record the stream of chat completion chunks in the response as they are processed and accumulate
a [`ChatCompletion`](openai-java-core/src/main/kotlin/com/openai/models/chat/completions/ChatCompletion.kt)
object similar to that which would have been returned by the non-streaming API.
For a synchronous response add a
[`Stream.peek()`](https://docs.oracle.com/javase/8/docs/api/java/util/stream/Stream.html#peek-java.util.function.Consumer-)
call to the stream pipeline to accumulate each chunk:
import com.openai.core.http.StreamResponse;
import com.openai.helpers.ChatCompletionAccumulator;
import com.openai.models.chat.completions.ChatCompletion;
import com.openai.models.chat.completions.ChatCompletionChunk;
ChatCompletionAccumulator chatCompletionAccumulator = ChatCompletionAccumulator.create();
try (StreamResponse streamResponse =
client.chat().completions().createStreaming(createParams)) {
streamResponse.stream()
.peek(chatCompletionAccumulator::accumulate)
.flatMap(completion -> completion.choices().stream())
.flatMap(choice -> choice.delta().content().stream())
.forEach(System.out::print);
}
ChatCompletion chatCompletion = chatCompletionAccumulator.chatCompletion();
For an asynchronous response, add the `ChatCompletionAccumulator` to the `subscribe()` call:
import com.openai.helpers.ChatCompletionAccumulator;
import com.openai.models.chat.completions.ChatCompletion;
ChatCompletionAccumulator chatCompletionAccumulator = ChatCompletionAccumulator.create();
client.chat()
.completions()
.createStreaming(createParams)
.subscribe(chunk -> chatCompletionAccumulator.accumulate(chunk).choices().stream()
.flatMap(choice -> choice.delta().content().stream())
.forEach(System.out::print))
.onCompleteFuture()
.join();
ChatCompletion chatCompletion = chatCompletionAccumulator.chatCompletion();
The SDK provides conveniences for streamed responses. A
[`ResponseAccumulator`](openai-java-core/src/main/kotlin/com/openai/helpers/ResponseAccumulator.kt)
can record the stream of response events as they are processed and accumulate a
[`Response`](openai-java-core/src/main/kotlin/com/openai/models/responses/Response.kt)
object similar to that which would have been returned by the non-streaming API.
For a synchronous response add a
[`Stream.peek()`](https://docs.oracle.com/javase/8/docs/api/java/util/stream/Stream.html#peek-java.util.function.Consumer-)
call to the stream pipeline to accumulate each event:
import com.openai.core.http.StreamResponse;
import com.openai.helpers.ResponseAccumulator;
import com.openai.models.responses.Response;
import com.openai.models.responses.ResponseStreamEvent;
ResponseAccumulator responseAccumulator = ResponseAccumulator.create();
try (StreamResponse streamResponse =
client.responses().createStreaming(createParams)) {
streamResponse.stream()
.peek(responseAccumulator::accumulate)
.flatMap(event -> event.outputTextDelta().stream())
.forEach(textEvent -> System.out.print(textEvent.delta()));
}
Response response = responseAccumulator.response();
For an asynchronous response, add the `ResponseAccumulator` to the `subscribe()` call:
import com.openai.helpers.ResponseAccumulator;
import com.openai.models.responses.Response;
ResponseAccumulator responseAccumulator = ResponseAccumulator.create();
client.responses()
.createStreaming(createParams)
.subscribe(event -> responseAccumulator.accumulate(event)
.outputTextDelta().ifPresent(textEvent -> System.out.print(textEvent.delta())))
.onCompleteFuture()
.join();
Response response = responseAccumulator.response();
## Structured outputs with JSON schemas
Open AI [Structured Outputs](https://platform.openai.com/docs/guides/structured-outputs?api-mode=chat)
is a feature that ensures that the model will always generate responses that adhere to a supplied
[JSON schema](https://json-schema.org/overview/what-is-jsonschema).
A JSON schema can be defined by creating a
[`ResponseFormatJsonSchema`](openai-java-core/src/main/kotlin/com/openai/models/ResponseFormatJsonSchema.kt)
and setting it on the input parameters. However, for greater convenience, a JSON schema can instead
be derived automatically from the structure of an arbitrary Java class. The JSON content from the
response will then be converted automatically to an instance of that Java class. A full, working
example of the use of Structured Outputs with arbitrary Java classes can be seen in
[`StructuredOutputsExample`](openai-java-example/src/main/java/com/openai/example/StructuredOutputsExample.java).
Java classes can contain fields declared to be instances of other classes and can use collections
(see [Defining JSON schema properties](#defining-json-schema-properties) for more details):
class Person {
public String name;
public int birthYear;
}
class Book {
public String title;
public Person author;
public int publicationYear;
}
class BookList {
public List books;
}
Pass the top-level class—`BookList` in this example—to `responseFormat(Class)` when building the
parameters and then access an instance of `BookList` from the generated message content in the
response:
import com.openai.models.ChatModel;
import com.openai.models.chat.completions.ChatCompletionCreateParams;
import com.openai.models.chat.completions.StructuredChatCompletionCreateParams;
StructuredChatCompletionCreateParams params = ChatCompletionCreateParams.builder()
.addUserMessage("List some famous late twentieth century novels.")
.model(ChatModel.GPT_5_2)
.responseFormat(BookList.class)
.build();
client.chat().completions().create(params).choices().stream()
.flatMap(choice -> choice.message().content().stream())
.flatMap(bookList -> bookList.books.stream())
.forEach(book -> System.out.println(book.title + " by " + book.author.name));
You can start building the parameters with an instance of
[`ChatCompletionCreateParams.Builder`](openai-java-core/src/main/kotlin/com/openai/models/chat/completions/ChatCompletionCreateParams.kt)
or
[`StructuredChatCompletionCreateParams.Builder`](openai-java-core/src/main/kotlin/com/openai/models/chat/completions/StructuredChatCompletionCreateParams.kt).
If you start with the former (which allows for more compact code) the builder type will change to
the latter when `ChatCompletionCreateParams.Builder.responseFormat(Class)` is called.
If a field in a class is optional and does not require a defined value, you can represent this using
the [`java.util.Optional`](https://docs.oracle.com/javase/8/docs/api/java/util/Optional.html) class.
It is up to the AI model to decide whether to provide a value for that field or leave it empty.
import java.util.Optional;
class Book {
public String title;
public Person author;
public int publicationYear;
public Optional isbn;
}
Generic type information for fields is retained in the class's metadata, but _generic type erasure_
applies in other scopes. While, for example, a JSON schema defining an array of books can be derived
from the `BookList.books` field with type `List`, a valid JSON schema cannot be derived from a
local variable of that same type, so the following will _not_ work:
List books = new ArrayList<>();
StructuredChatCompletionCreateParams
- > params = ChatCompletionCreateParams.builder()
.responseFormat(books.getClass())
// ...
.build();
If an error occurs while converting a JSON response to an instance of a Java class, the error
message will include the JSON response to assist in diagnosis. For instance, if the response is
truncated, the JSON data will be incomplete and cannot be converted to a class instance. If your
JSON response may contain sensitive information, avoid logging it directly, or ensure that you
redact any sensitive details from the error message.
### Local JSON schema validation
Structured Outputs supports a
[subset](https://platform.openai.com/docs/guides/structured-outputs#supported-schemas) of the JSON
Schema language. Schemas are generated automatically from classes to align with this subset.
However, due to the inherent structure of the classes, the generated schema may still violate
certain OpenAI schema restrictions, such as exceeding the maximum nesting depth or utilizing
unsupported data types.
To facilitate compliance, the method `responseFormat(Class
- > messages = client.chat().completions().create(params)._messages();
if (messages.isMissing()) {
// The property is absent from the JSON response
} else if (messages.isNull()) {
// The property was set to literal null
} else {
// Check if value was provided as a string
// Other methods include `asNumber()`, `asBoolean()`, etc.
Optional
标签:AI, DLL 劫持, JS文件枚举, OpenAI, SOC Prime, 内存规避, 后台面板检测, 大语言模型, 开发工具, 自动化代码审查