{"id":15069222,"url":"https://github.com/stefanbratanov/jvm-openai","last_synced_at":"2026-02-27T09:43:16.122Z","repository":{"id":213877943,"uuid":"735147993","full_name":"StefanBratanov/jvm-openai","owner":"StefanBratanov","description":"A minimalistic OpenAI API client for the JVM, written in Java","archived":false,"fork":false,"pushed_at":"2024-04-11T09:15:39.000Z","size":1002,"stargazers_count":15,"open_issues_count":2,"forks_count":4,"subscribers_count":3,"default_branch":"master","last_synced_at":"2024-04-12T16:31:31.856Z","etag":null,"topics":["ai","api","chatgpt","dall-e","gpt-3","gpt-4","java","jvm","library","machine-learning","microsoft","openai","openai-api"],"latest_commit_sha":null,"homepage":"","language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/StefanBratanov.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null}},"created_at":"2023-12-23T20:48:33.000Z","updated_at":"2024-06-15T09:20:13.285Z","dependencies_parsed_at":"2024-04-15T10:49:20.205Z","dependency_job_id":"217c0f8e-56a2-419d-a37b-fe91fe35c4c5","html_url":"https://github.com/StefanBratanov/jvm-openai","commit_stats":null,"previous_names":["stefanbratanov/chatjpt","stefanbratanov/jvm-openai"],"tags_count":16,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/StefanBratanov%2Fjvm-openai","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/StefanBratanov%2Fjvm-openai/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/StefanBratanov%2Fjvm-openai/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/StefanBratanov%2Fjvm-openai/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/StefanBratanov","download_url":"https://codeload.github.com/StefanBratanov/jvm-openai/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248256302,"owners_count":21073502,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["ai","api","chatgpt","dall-e","gpt-3","gpt-4","java","jvm","library","machine-learning","microsoft","openai","openai-api"],"created_at":"2024-09-25T01:41:08.532Z","updated_at":"2026-02-27T09:43:16.110Z","avatar_url":"https://github.com/StefanBratanov.png","language":"Java","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003e ⚠️ **Project Status Update:**\nUnfortunately, I’m no longer able to actively maintain this project due to time constraints. Thank you to everyone who has contributed and supported this project.\nIf you'd like to continue using or adapting the code, feel free to fork the repository and make any modifications that suit your needs. For those seeking a maintained alternative, [openai-java](https://github.com/openai/openai-java) is a solid option worth exploring.\n\n\n# jvm-openai\n\n[![build](https://github.com/StefanBratanov/jvm-openai/actions/workflows/build.yml/badge.svg)](https://github.com/StefanBratanov/jvm-openai/actions/workflows/build.yml)\n[![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=StefanBratanov_jvm-openai\u0026metric=alert_status)](https://sonarcloud.io/summary/new_code?id=StefanBratanov_jvm-openai)\n[![Maven Central](https://img.shields.io/maven-central/v/io.github.stefanbratanov/jvm-openai)](https://central.sonatype.com/artifact/io.github.stefanbratanov/jvm-openai)\n[![javadoc](https://javadoc.io/badge2/io.github.stefanbratanov/jvm-openai/javadoc.svg)](https://javadoc.io/doc/io.github.stefanbratanov/jvm-openai)\n\nA minimalistic unofficial [OpenAI API](https://platform.openai.com/docs/api-reference) client for the JVM, written in\nJava. The only dependency used is [Jackson](https://github.com/FasterXML/jackson) for JSON parsing.\n\n## Requirements\n\njvm-openai works on Java 17+. Android support is not yet available.\n\n## Add dependency\n\n### Gradle\n\n```groovy\nimplementation(\"io.github.stefanbratanov:jvm-openai:${version}\")\n```\n\n### Maven\n\n```xml\n\u003cdependency\u003e\n    \u003cgroupId\u003eio.github.stefanbratanov\u003c/groupId\u003e\n    \u003cartifactId\u003ejvm-openai\u003c/artifactId\u003e\n    \u003cversion\u003e${version}\u003c/version\u003e\n\u003c/dependency\u003e\n```\n\n## Minimal example\n\n```java\nOpenAI openAI = OpenAI.newBuilder(System.getenv(\"OPENAI_API_KEY\")).build();\n\nChatClient chatClient = openAI.chatClient();\nCreateChatCompletionRequest createChatCompletionRequest = CreateChatCompletionRequest.newBuilder()\n    .model(OpenAIModel.GPT_3_5_TURBO)\n    .message(ChatMessage.userMessage(\"Who won the world series in 2020?\"))\n    .build();\nChatCompletion chatCompletion = chatClient.createChatCompletion(createChatCompletionRequest);\n```\n\n## Supported APIs\n\n### Endpoints\n\n| API                                                                       | Status |\n|---------------------------------------------------------------------------|:------:|\n| [Audio](https://platform.openai.com/docs/api-reference/audio)             |   ✔️   |\n| [Chat](https://platform.openai.com/docs/api-reference/chat)               |   ✔️   |\n| [Embeddings](https://platform.openai.com/docs/api-reference/embeddings)   |   ✔️   |\n| [Fine-tuning](https://platform.openai.com/docs/api-reference/fine-tuning) |   ✔️   |\n| [Batch](https://platform.openai.com/docs/api-reference/batch)             |   ✔️   |\n| [Files](https://platform.openai.com/docs/api-reference/files)             |   ✔️   |\n| [Uploads](https://platform.openai.com/docs/api-reference/uploads)         |   ✔️   |\n| [Images](https://platform.openai.com/docs/api-reference/images)           |   ✔️   |\n| [Models](https://platform.openai.com/docs/api-reference/models)           |   ✔️   |\n| [Moderations](https://platform.openai.com/docs/api-reference/moderations) |   ✔️   |\n\n### Assistants (Beta)\n\n| API                                                                                                    | Status |\n|--------------------------------------------------------------------------------------------------------|:------:|\n| [Assistants](https://platform.openai.com/docs/api-reference/assistants)                                |   ✔️   |\n| [Threads](https://platform.openai.com/docs/api-reference/threads)                                      |   ✔️   |\n| [Messages](https://platform.openai.com/docs/api-reference/messages)                                    |   ✔️   |\n| [Runs](https://platform.openai.com/docs/api-reference/runs)                                            |   ✔️   |\n| [Run Steps](https://platform.openai.com/docs/api-reference/run-steps)                                  |   ✔️   |\n| [Vector Stores](https://platform.openai.com/docs/api-reference/vector-stores)                          |   ✔️     |\n| [Vector Store Files](https://platform.openai.com/docs/api-reference/vector-stores-files)               |   ✔️     |\n| [Vector Store File Batches](https://platform.openai.com/docs/api-reference/vector-stores-file-batches) |   ✔️     |\n\n### Administration\n\n| API                                                                                                 | Status |\n|-----------------------------------------------------------------------------------------------------|:------:|\n| [Admin API Keys](https://platform.openai.com/docs/api-reference/admin-api-keys)                     |        |\n| [Invites](https://platform.openai.com/docs/api-reference/invite)                                    |   ✔️   |\n| [Users](https://platform.openai.com/docs/api-reference/users)                                       |   ✔️   |\n| [Projects](https://platform.openai.com/docs/api-reference/projects)                                 |   ✔️   |\n| [Project Users](https://platform.openai.com/docs/api-reference/project-users)                       |   ✔️   |\n| [Project Service Accounts](https://platform.openai.com/docs/api-reference/project-service-accounts) |   ✔️   |\n| [Project API Keys](https://platform.openai.com/docs/api-reference/project-api-keys)                 |    ✔️    |\n| [Project rate limits](https://platform.openai.com/docs/api-reference/project-rate-limits)           |          |\n| [Audit Logs](https://platform.openai.com/docs/api-reference/audit-logs)                             |   ✔️     |\n| [Usage](https://platform.openai.com/docs/api-reference/usage)                                                                                           |          |\n\n\u003e **_NOTE:_** Realtime and Legacy APIs are not supported\n\n## More examples\n\n- Configure an organization and project\n```java\nOpenAI openAI = OpenAI.newBuilder(System.getenv(\"OPENAI_API_KEY\"))\n    .organization(\"org-zweLLamVlP6c5n66zY334ivs\")\n    .project(System.getenv(\"PROJECT_ID\"))        \n    .build();\n```\n- Configure a custom base url\n```java\nOpenAI openAI = OpenAI.newBuilder(System.getenv(\"OPENAI_API_KEY\"))\n    .baseUrl(\"https://api.foobar.com/v1/\")     \n    .build();\n```\n- Configure a custom Java's `HttpClient`\n```java\nHttpClient httpClient = HttpClient.newBuilder()\n    .connectTimeout(Duration.ofSeconds(20))\n    .executor(Executors.newFixedThreadPool(3))\n    .proxy(ProxySelector.of(new InetSocketAddress(\"proxy.example.com\", 80)))\n    .build();\nOpenAI openAI = OpenAI.newBuilder(System.getenv(\"OPENAI_API_KEY\"))\n    .httpClient(httpClient)\n    .build();\n```\n- Configure a timeout for all requests\n```java\nOpenAI openAI = OpenAI.newBuilder(System.getenv(\"OPENAI_API_KEY\"))\n    .requestTimeout(Duration.ofSeconds(10))\n    .build();\n```\n- Create chat completion async\n```java\nChatClient chatClient = openAI.chatClient();\nCreateChatCompletionRequest request = CreateChatCompletionRequest.newBuilder()\n    .model(OpenAIModel.GPT_3_5_TURBO)\n    .message(ChatMessage.userMessage(\"Who won the world series in 2020?\"))\n    .build();\nCompletableFuture\u003cChatCompletion\u003e chatCompletion = chatClient.createChatCompletionAsync(request);\nchatCompletion.thenAccept(System.out::println);\n```\n- Streaming\n```java\nChatClient chatClient = openAI.chatClient();\nCreateChatCompletionRequest request = CreateChatCompletionRequest.newBuilder()\n    .message(ChatMessage.userMessage(\"Who won the world series in 2020?\"))\n    .stream(true)\n    .build();\n// with java.util.stream.Stream\nchatClient.streamChatCompletion(request).forEach(System.out::println);\n// with subscriber\nchatClient.streamChatCompletion(request, new ChatCompletionStreamSubscriber() {\n    @Override\n    public void onChunk(ChatCompletionChunk chunk) {\n        System.out.println(chunk);\n    }\n\n    @Override\n    public void onException(Throwable ex) {\n        // ...\n    }\n    \n    @Override\n    public void onComplete() {\n        // ...\n    }\n});\n```\n- Create image\n```java\nImagesClient imagesClient = openAI.imagesClient();\nCreateImageRequest createImageRequest = CreateImageRequest.newBuilder()\n    .model(OpenAIModel.DALL_E_3)\n    .prompt(\"A cute baby sea otter\")\n    .build();\nImages images = imagesClient.createImage(createImageRequest);\n```\n- Create speech\n```java\nAudioClient audioClient = openAI.audioClient();\nSpeechRequest request = SpeechRequest.newBuilder()\n    .model(OpenAIModel.TTS_1)\n    .input(\"The quick brown fox jumped over the lazy dog.\")\n    .voice(Voice.ALLOY)\n    .build();\nPath output = Paths.get(\"/tmp/speech.mp3\");\naudioClient.createSpeech(request, output);\n```\n- Create translation\n```java\nAudioClient audioClient = openAI.audioClient();\nTranslationRequest request = TranslationRequest.newBuilder()\n    .model(OpenAIModel.WHISPER_1)\n    .file(Paths.get(\"/tmp/german.m4a\"))\n    .build();\nString translatedText = audioClient.createTranslation(request);\n```\n- List models\n```java\nModelsClient modelsClient = openAI.modelsClient();\nList\u003cModel\u003e models = modelsClient.listModels();\n```\n- Classify if text violates OpenAI's Content Policy\n```java\nModerationsClient moderationsClient = openAI.moderationsClient();\nModerationRequest request = ModerationRequest.newBuilder()\n    .input(\"I want to bake a cake.\")\n    .build();\nModeration moderation = moderationsClient.createModeration(request);\nboolean violence = moderation.results().get(0).categories().violence();\n```\n- Create and execute a batch\n```java\n// Upload JSONL file containing requests for the batch\nFilesClient filesClient = openAI.filesClient();\nUploadFileRequest uploadInputFileRequest = UploadFileRequest.newBuilder()\n    .file(Paths.get(\"/tmp/batch-requests.jsonl\"))\n    .purpose(Purpose.BATCH)\n    .build();\nFile inputFile = filesClient.uploadFile(uploadInputFileRequest);\n\nBatchClient batchClient = openAI.batchClient();\nCreateBatchRequest request = CreateBatchRequest.newBuilder()\n    .inputFileId(inputFile.id())\n    .endpoint(\"/v1/chat/completions\")\n    .completionWindow(\"24h\")\n    .build();\nBatch batch = batchClient.createBatch(request);\n// check status of the batch\nBatch retrievedBatch = batchClient.retrieveBatch(batch.id());\nSystem.out.println(retrievedBatch.status());      \n```\n- Upload large file in multiple parts\n```java\nUploadsClient uploadsClient = openAI.uploadsClient();\nCreateUploadRequest createUploadRequest = CreateUploadRequest.newBuilder()\n    .filename(\"training_examples.jsonl\")\n    .purpose(Purpose.FINE_TUNE)\n    .bytes(2147483648)\n    .mimeType(\"text/jsonl\")\n    .build();\nUpload upload = uploadsClient.createUpload(createUploadRequest);\n\nUploadPart part1 = uploadsClient.addUploadPart(upload.id(), Paths.get(\"/tmp/part1.jsonl\"));\nUploadPart part2 = uploadsClient.addUploadPart(upload.id(), Paths.get(\"/tmp/part2.jsonl\"));\n\nCompleteUploadRequest completeUploadRequest = CompleteUploadRequest.newBuilder()\n    .partIds(List.of(part1.id(), part2.id()))\n    .build();\n\nUpload completedUpload = uploadsClient.completeUpload(upload.id(), completeUploadRequest);\n// the created usable File object\nFile file = completedUpload.file();\n```\n- Build AI Assistant\n```java\nAssistantsClient assistantsClient = openAI.assistantsClient();\nThreadsClient threadsClient = openAI.threadsClient();\nMessagesClient messagesClient = openAI.messagesClient();\nRunsClient runsClient = openAI.runsClient();\n\n// Step 1: Create an Assistant\nCreateAssistantRequest createAssistantRequest = CreateAssistantRequest.newBuilder()\n    .name(\"Math Tutor\")\n    .model(OpenAIModel.GPT_3_5_TURBO_1106)\n    .instructions(\"You are a personal math tutor. Write and run code to answer math questions.\")\n    .tool(Tool.codeInterpreterTool())\n    .build();\nAssistant assistant = assistantsClient.createAssistant(createAssistantRequest);\n\n// Step 2: Create a Thread\nCreateThreadRequest createThreadRequest = CreateThreadRequest.newBuilder().build();\nThread thread = threadsClient.createThread(createThreadRequest);\n\n// Step 3: Add a Message to a Thread\nCreateMessageRequest createMessageRequest = CreateMessageRequest.newBuilder()\n    .role(Role.USER)\n    .content(\"I need to solve the equation `3x + 11 = 14`. Can you help me?\")\n    .build();\nThreadMessage message = messagesClient.createMessage(thread.id(), createMessageRequest);\n\n// Step 4: Run the Assistant\nCreateRunRequest createRunRequest = CreateRunRequest.newBuilder()\n    .assistantId(assistant.id())\n    .instructions(\"Please address the user as Jane Doe. The user has a premium account.\")\n    .build();\nThreadRun run = runsClient.createRun(thread.id(), createRunRequest);\n\n// Step 5: Check the Run status\nThreadRun retrievedRun = runsClient.retrieveRun(thread.id(), run.id());\nString status = retrievedRun.status();\n\n// Step 6: Display the Assistant's Response\nMessagesClient.PaginatedThreadMessages paginatedMessages = messagesClient.listMessages(thread.id(), PaginationQueryParameters.none(), Optional.empty());\nList\u003cThreadMessage\u003e messages = paginatedMessages.data();\n```\n- Build AI Assistant with File Search Enabled\n```java\nAssistantsClient assistantsClient = openAI.assistantsClient();\nThreadsClient threadsClient = openAI.threadsClient();\nMessagesClient messagesClient = openAI.messagesClient();\nRunsClient runsClient = openAI.runsClient();\nVectorStoresClient vectorStoresClient = openAI.vectorStoresClient();\nFilesClient filesClient = openAI.filesClient();\nVectorStoreFileBatchesClient vectorStoreFileBatchesClient = openAI.vectorStoreFileBatchesClient();\n\n// Step 1: Create a new Assistant with File Search Enabled\nCreateAssistantRequest createAssistantRequest = CreateAssistantRequest.newBuilder()\n    .name(\"Financial Analyst Assistant\")\n    .model(OpenAIModel.GPT_4_TURBO)\n    .instructions(\"You are an expert financial analyst. Use you knowledge base to answer questions about audited financial statements.\")\n    .tool(Tool.fileSearchTool())\n    .build();\nAssistant assistant = assistantsClient.createAssistant(createAssistantRequest);\n\n// Step 2: Upload files and add them to a Vector Store\nCreateVectorStoreRequest createVectorStoreRequest = CreateVectorStoreRequest.newBuilder()\n    .name(\"Financial Statements\")\n    .build();\nVectorStore vectorStore = vectorStoresClient.createVectorStore(createVectorStoreRequest);\nUploadFileRequest uploadFileRequest1 = UploadFileRequest.newBuilder()\n    .file(Paths.get(\"edgar/goog-10k.pdf\"))\n    .purpose(Purpose.ASSISTANTS)\n    .build();\nFile file1 = filesClient.uploadFile(uploadFileRequest1);\nUploadFileRequest uploadFileRequest2 = UploadFileRequest.newBuilder()\n    .file(Paths.get(\"edgar/brka-10k.txt\"))\n    .purpose(Purpose.ASSISTANTS)\n    .build();\nFile file2 = filesClient.uploadFile(uploadFileRequest2);\nCreateVectorStoreFileBatchRequest createVectorStoreFileBatchRequest = CreateVectorStoreFileBatchRequest.newBuilder()\n    .fileIds(List.of(file1.id(), file2.id()))\n    .build();\nVectorStoreFileBatch batch = vectorStoreFileBatchesClient.createVectorStoreFileBatch(vectorStore.id(), createVectorStoreFileBatchRequest);\n// need to query the status of the file batch for completion\nvectorStoreFileBatchesClient.retrieveVectorStoreFileBatch(vectorStore.id(), batch.id());\n\n// Step 3: Update the assistant to use the new Vector Store\nModifyAssistantRequest modifyAssistantRequest = ModifyAssistantRequest.newBuilder()\n    .toolResources(ToolResources.fileSearchToolResources(vectorStore.id()))\n    .build();\nassistantsClient.modifyAssistant(assistant.id(), modifyAssistantRequest);\n\n// Step 4: Create a thread\nCreateThreadRequest.Message message = CreateThreadRequest.Message.newBuilder()\n    .role(Role.USER)\n    .content(\"How many shares of AAPL were outstanding at the end of of October 2023?\")\n    .build();\nCreateThreadRequest createThreadRequest = CreateThreadRequest.newBuilder()\n    .message(message)\n    .build();\nThread thread = threadsClient.createThread(createThreadRequest);\n\n// Step 5: Create a run and check the output\nCreateRunRequest createRunRequest = CreateRunRequest.newBuilder()\n    .assistantId(assistant.id())\n    .instructions(\"Please address the user as Jane Doe. The user has a premium account.\")\n    .build();\nThreadRun run = runsClient.createRun(thread.id(), createRunRequest);\n// check the run status\nThreadRun retrievedRun = runsClient.retrieveRun(thread.id(), run.id());\nString status = retrievedRun.status();\n// display the Assistant's Response\nMessagesClient.PaginatedThreadMessages paginatedMessages = messagesClient.listMessages(thread.id(), PaginationQueryParameters.none(), Optional.empty());\nList\u003cThreadMessage\u003e messages = paginatedMessages.data();\n```\n- Create a run and stream the result of executing the run ([Assistants Streaming](https://platform.openai.com/docs/api-reference/assistants-streaming))\n```java\nRunsClient runsClient = openAI.runsClient();\nCreateRunRequest createRunRequest = CreateRunRequest.newBuilder()\n    .assistantId(assistant.id())\n    .instructions(\"Please address the user as Jane Doe. The user has a premium account.\")\n    .stream(true)   \n    .build();\n// with java.util.stream.Stream\nrunsClient.createRunAndStream(thread.id(), createRunRequest).forEach(assistantStreamEvent -\u003e {\n    System.out.println(assistantStreamEvent.event());\n    System.out.println(assistantStreamEvent.data());\n});\n// with subscriber\nrunsClient.createRunAndStream(thread.id(), createRunRequest, new AssistantStreamEventSubscriber() {\n    @Override\n    public void onThread(String event, Thread thread) {\n        // ...\n    }\n\n    @Override\n    public void onThreadRun(String event, ThreadRun threadRun) {\n        // ...\n    }\n\n    @Override\n    public void onThreadRunStep(String event, ThreadRunStep threadRunStep) {\n        // ...\n    }\n\n    @Override\n    public void onThreadRunStepDelta(String event, ThreadRunStepDelta threadRunStepDelta) {\n        // ...\n    }\n\n    @Override\n    public void onThreadMessage(String event, ThreadMessage threadMessage) {\n        // ...\n    }\n\n    @Override\n    public void onThreadMessageDelta(String event, ThreadMessageDelta threadMessageDelta) {\n        // ...\n    }\n\n    @Override\n    public void onUnknownEvent(String event, String data) {\n        // ...\n    }\n\n    @Override\n    public void onException(Throwable ex) {\n        // ...\n    }\n\n    @Override\n    public void onComplete() {\n        // ...\n    }    \n});\n// \"createThreadAndRunAndStream\" and \"submitToolOutputsAndStream\" methods are also available\n```\n- List all the users in an organization.\n```java\nOpenAI openAI = OpenAI.newBuilder()\n    .adminKey(System.getenv(\"OPENAI_ADMIN_KEY\"))     \n    .build();\n\nUsersClient usersClient = openAI.usersClient();\nList\u003cUser\u003e users = usersClient.listUsers(Optional.empty(), Optional.empty()).data();\n``` \n- List user actions and configuration changes within an organization\n```java\nOpenAI openAI = OpenAI.newBuilder()\n    .adminKey(System.getenv(\"OPENAI_ADMIN_KEY\"))     \n    .build();\n\nAuditLogsClient auditLogsClient = openAI.auditLogsClient();\nListAuditLogsQueryParameters queryParameters = ListAuditLogsQueryParameters.newBuilder()\n    .eventTypes(List.of(\"invite.sent\", \"invite.deleted\"))\n    .build();\nList\u003cAuditLog\u003e auditLogs = auditLogsClient.listAuditLogs(queryParameters).data();\n```\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fstefanbratanov%2Fjvm-openai","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fstefanbratanov%2Fjvm-openai","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fstefanbratanov%2Fjvm-openai/lists"}