87 public AiChatResponse
generate(
final AiChatRequest request)
throws IOException, InterruptedException {
88 final byte[] payload = jsonCodec.toJsonBytes(toPayload(request));
89 final var builder = HttpRequest.newBuilder(config.chatCompletionsUri()).timeout(config.timeout())
90 .header(
"Authorization",
"Bearer " + config.apiKey()).header(
"Content-Type",
"application/json")
91 .POST(HttpRequest.BodyPublishers.ofByteArray(payload));
93 if (!config.organization().isBlank()) {
94 builder.header(
"OpenAI-Organization", config.organization());
96 if (!config.project().isBlank()) {
97 builder.header(
"OpenAI-Project", config.project());
99 config.defaultHeaders().forEach(builder::header);
101 final var response = httpClient.send(builder.build(), HttpResponse.BodyHandlers.ofByteArray());
102 if (response.statusCode() < 200 || response.statusCode() >= 300) {
103 throw new AiHttpException(
"OpenAI request failed with HTTP " + response.statusCode(), response.statusCode(),
104 new String(response.body(), StandardCharsets.UTF_8));
107 final JsonNode root = jsonCodec.readTree(response.body());
108 final JsonNode choice = root.path(
"choices").path(0);
109 final JsonNode messageNode = choice.path(
"message");
111 text(messageNode,
"content"));
112 return new AiChatResponse(text(root,
"id"), text(root,
"model"), message, text(choice,
"finish_reason"),
113 usage(root.path(
"usage")));
120 .map(message -> Map.of(
"role", message.role().wireValue(),
"content", message.content())).toList());