{"id":13492385,"url":"https://github.com/linux-china/chatgpt-spring-boot-starter","last_synced_at":"2025-05-15T17:07:10.862Z","repository":{"id":176064557,"uuid":"654918718","full_name":"linux-china/chatgpt-spring-boot-starter","owner":"linux-china","description":"Spring Boot ChatGPT Starter","archived":false,"fork":false,"pushed_at":"2025-02-19T07:00:25.000Z","size":434,"stargazers_count":490,"open_issues_count":0,"forks_count":77,"subscribers_count":12,"default_branch":"main","last_synced_at":"2025-05-14T07:17:00.264Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"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/linux-china.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.txt","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,"publiccode":null,"codemeta":null}},"created_at":"2023-06-17T10:28:13.000Z","updated_at":"2025-04-12T07:10:01.000Z","dependencies_parsed_at":"2023-12-26T16:33:17.487Z","dependency_job_id":"30c238b7-2a0a-4c55-a6a3-0c4c36228845","html_url":"https://github.com/linux-china/chatgpt-spring-boot-starter","commit_stats":null,"previous_names":["linux-china/chatgpt-spring-boot-starter"],"tags_count":10,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/linux-china%2Fchatgpt-spring-boot-starter","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/linux-china%2Fchatgpt-spring-boot-starter/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/linux-china%2Fchatgpt-spring-boot-starter/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/linux-china%2Fchatgpt-spring-boot-starter/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/linux-china","download_url":"https://codeload.github.com/linux-china/chatgpt-spring-boot-starter/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254384987,"owners_count":22062422,"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":[],"created_at":"2024-07-31T19:01:05.502Z","updated_at":"2025-05-15T17:07:05.853Z","avatar_url":"https://github.com/linux-china.png","language":"Java","funding_links":[],"categories":["Java","Others","人工智能"],"sub_categories":[],"readme":"\u003cp align=\"center\"\u003e\n  \u003ca href=\"https://repo1.maven.org/maven2/com/alibaba/rsocket/\"\u003e\n    \u003cimg alt=\"Maven\" src=\"https://img.shields.io/maven-central/v/org.mvnsearch/chatgpt-spring-boot-starter\"\u003e\u003c/a\u003e\n  \u003ca href=\"https://github.com/linux-china/chatgpt-spring-boot-starter\"\u003e\n    \u003cimg alt=\"GitHub repo size\" src=\"https://img.shields.io/github/repo-size/linux-china/chatgpt-spring-boot-starter\"\u003e\u003c/a\u003e\n  \u003ca href=\"https://github.com/linux-china/chatgpt-spring-boot-starter/issues\"\u003e\n    \u003cimg alt = \"Open Issues\" src=\"https://img.shields.io/github/issues-raw/linux-china/chatgpt-spring-boot-starter.svg\"\u003e\u003c/a\u003e\n  \u003ca href=\"https://www.apache.org/licenses/LICENSE-2.0.txt\"\u003e\n    \u003cimg alt = \"Apache License 2\" src=\"https://img.shields.io/badge/license-ASF2-blue.svg\"\u003e\u003c/a\u003e\n  \u003ca href=\"https://sourcespy.com/github/linuxchinachatgptspringbootstarter/\"\u003e\n    \u003cimg alt=\"\" src=\"https://sourcespy.com/shield.svg\"\u003e\u003c/a\u003e\n\u003c/p\u003e\n\nChatGPT Spring Boot Starter\n===========================\n\nSpring Boot ChatGPT starter with ChatGPT chat and functions support.\n\n# Features\n\n* Base on Spring Boot 3.0+\n* Async with Spring Webflux\n* Support ChatGPT Chat Stream\n* Support ChatGPT functions: `@GPTFunction` annotation\n* Support structured output: `@StructuredOutput` annotation for record\n* Prompt Management: load prompt templates from `prompt.properties` with `@PropertyKey`, and friendly with IntelliJ IDEA\n* Prompt as Lambda: convert prompt template to lambda expression and call it with FP style\n* ChatGPT interface: Declare ChatGPT service interface with `@ChatGPTExchange` and `@ChatCompletion` annotations.\n* No third-party library: base on Spring 6 HTTP interface\n* GraalVM native image support\n* Azure OpenAI support\n\n# Get Started\n\n### Add dependency\n\nAdd `chatgpt-spring-boot-starter` dependency in your pom.xml.\n\n```xml\n\n\u003cdependency\u003e\n    \u003cgroupId\u003eorg.mvnsearch\u003c/groupId\u003e\n    \u003cartifactId\u003echatgpt-spring-boot-starter\u003c/artifactId\u003e\n    \u003cversion\u003e0.8.0\u003c/version\u003e\n\u003c/dependency\u003e\n```\n\n### Adjust configuration\n\nAdd `openai.api.key` in `application.properties`:\n\n```properties\n# OpenAI API Token, or you can set environment variable OPENAI_API_KEY\nopenai.api.key=sk-proj-xxxx\n```\n\nIf you want to use Azure OpenAI, you can add `openai.api.url` in `application.properties`:\n\n```properties\nopenai.api.key=1138xxxx9037\nopenai.api.url=https://YOUR_RESOURCE_NAME.openai.azure.com/openai/deployments/YOUR_DEPLOYMENT_NAME/chat/completions?api-version=2023-05-15\n```\n\n### Call ChatGPT Service\n\n```java\n\n@RestController\npublic class ChatRobotController {\n    @Autowired\n    private ChatGPTService chatGPTService;\n\n    @PostMapping(\"/chat\")\n    public Mono\u003cString\u003e chat(@RequestBody String content) {\n        return chatGPTService.chat(ChatCompletionRequest.of(content))\n                .map(ChatCompletionResponse::getReplyText);\n    }\n\n    @GetMapping(\"/stream-chat\")\n    public Flux\u003cString\u003e streamChat(@RequestParam String content) {\n        return chatGPTService.stream(ChatCompletionRequest.of(content))\n                .map(ChatCompletionResponse::getReplyText);\n    }\n}\n```\n\n# ChatGPT Service Interface\n\nChatGPT service interface is almost\nlike [Spring 6 HTTP Interface](https://docs.spring.io/spring-framework/reference/integration/rest-clients.html#rest-http-interface).\nYou can declare a ChatGPT service interface with `@ChatGPTExchange` annotation, and declare completion methods\nwith `@ChatCompletion` annotation, then you just call service interface directly.\n\n```java\n\n@GPTExchange\npublic interface GPTHelloService {\n\n    @ChatCompletion(\"You are a language translator, please translate the below text to Chinese.\\n\")\n    Mono\u003cString\u003e translateIntoChinese(String text);\n\n    @ChatCompletion(\"You are a language translator, please translate the below text from {0} to {1}.\\n {2}\")\n    Mono\u003cString\u003e translate(String sourceLanguage, String targetLanguage, String text);\n\n    @Completion(\"please complete poem: {0}\")\n    Mono\u003cString\u003e completePoem(String text);\n\n}\n```\n\nCreate ChatGPT interface service bean:\n\n```\n    @Bean\n    public GPTHelloService gptHelloService(ChatGPTServiceProxyFactory proxyFactory) {\n        return proxyFactory.createClient(GPTHelloService.class);\n    }\n```\n\n# ChatGPT functions\n\n* Create a Spring Bean with `@Component`. Annotate GPT functions\n  with `@GPTFunction` annotation, and annotate function parameters with `@Parameter` annotation.  `@Nonnull` means that\n  the parameter is required.\n\n```java\n\nimport jakarta.annotation.Nonnull;\n\n@Component\npublic class GPTFunctions {\n\n    public record SendEmailRequest(\n            @Nonnull @Parameter(\"Recipients of email\") List\u003cString\u003e recipients,\n            @Nonnull @Parameter(\"Subject of email\") String subject,\n            @Parameter(\"Content of email\") String content) {\n    }\n\n    @GPTFunction(name = \"send_email\", value = \"Send email to receiver\")\n    public String sendEmail(SendEmailRequest request) {\n        System.out.println(\"Recipients: \" + String.join(\",\", request.recipients));\n        System.out.println(\"Subject: \" + request.subject);\n        System.out.println(\"Content:\\n\" + request.content);\n        return \"Email sent to \" + String.join(\",\", request.recipients) + \" successfully!\";\n    }\n\n    public record SQLQueryRequest(\n            @Parameter(required = true, value = \"SQL to query\") String sql) {\n    }\n\n    @GPTFunction(name = \"execute_sql_query\", value = \"Execute SQL query and return the result set\")\n    public String executeSQLQuery(SQLQueryRequest request) {\n        System.out.println(\"Execute SQL: \" + request.sql);\n        return \"id, name, salary\\n1,Jackie,8000\\n2,Libing,78000\\n3,Sam,7500\";\n    }\n}\n```\n\n* Call GPT function by `response.getReplyCombinedText()` or `chatMessage.getFunctionCall().getFunctionStub().call()`:\n\n```java\npublic class ChatGPTServiceImplTest {\n    @Test\n    public void testChatWithFunctions() throws Exception {\n        final String prompt = \"Hi Jackie, could you write an email to Libing(libing.chen@gmail.com) and Sam(linux_china@hotmail.com) and invite them to join Mike's birthday party at 4 pm tomorrow? Thanks!\";\n        final ChatCompletionRequest request = ChatCompletionRequest.functions(prompt, List.of(\"send_email\"));\n        final ChatCompletionResponse response = chatGPTService.chat(request).block();\n        // display reply combined text with function call\n        System.out.println(response.getReplyCombinedText());\n        // call function manually\n        for (ChatMessage chatMessage : response.getReply()) {\n            final FunctionCall functionCall = chatMessage.getFunctionCall();\n            if (functionCall != null) {\n                final Object result = functionCall.getFunctionStub().call();\n                System.out.println(result);\n            }\n        }\n    }\n\n    @Test\n    public void testExecuteSQLQuery() {\n        String context = \"You are SQL developer. Write SQL according to requirements, and execute it in MySQL database.\";\n        final String prompt = \"Query all employees whose salary is greater than the average.\";\n        final ChatCompletionRequest request = ChatCompletionRequest.functions(prompt, List.of(\"execute_sql_query\"));\n        // add prompt context as system message\n        request.addMessage(ChatMessage.systemMessage(context));\n        final ChatCompletionResponse response = chatGPTService.chat(request).block();\n        System.out.println(response.getReplyCombinedText());\n    }\n}\n```\n\n**Note**: `@GPTExchange` and `@ChatCompletion` has functions built-in, so you just need to fill functions parameters.\n\n### ChatGPT Functions use cases:\n\n* Structure Output: such as SQL, JSON, CSV, YAML etc., then delegate functions to process them.\n* Commands: such as send_email, post on Twitter.\n* DevOps: such as generate K8S yaml file, then call K8S functions to deploy it.\n* Search Matching: bind search with functions, such as search for a book, then call function to show it.\n* Spam detection: email spam, advertisement spam etc\n* PipeLine: you can think function as a node in pipeline. After process by function, and you can pass it to ChatGPT\n  again.\n* Data types supported: `string`, `number`, `integer`, `array`. Nested `object` not supported now!\n\nIf you want to have a simple test for ChatGPT functions, you can\ninstall [ChatGPT with Markdown JetBrains IDE Plugin](https://plugins.jetbrains.com/plugin/21671-chatgpt-with-markdown),\nand take a look at [chat.gpt file](./chat.gpt).\n\n# Structured Output\n\nPlease refer [OpenAI Structured Outputs](https://platform.openai.com/docs/guides/structured-outputs) for detail.\n\nFirst you need to define record for structured output:\n\n```java\n\n@StructuredOutput(name = \"java_example\")\npublic record JavaExample(@Nonnull @Parameter(\"explanation\") String explanation,\n                          @Nonnull @Parameter(\"answer\") String answer,\n                          @Nonnull @Parameter(\"code\") String code,\n                          @Nonnull @Parameter(\"dependencies\") List\u003cString\u003e dependencies) {\n}\n```\n\nThen you can use structured output record as return type as following:\n\n```java\n\n@ChatCompletion(system = \"You are a helpful Java language assistant.\")\nMono\u003cJavaExample\u003e generateJavaExample(String question);\n\n@ChatCompletion(system = \"You are a helpful assistant.\", user = \"Say hello to {0}!\")\nMono\u003cString\u003e hello(String word);\n```\n\n**Attention**: if the return type is not `Mono\u003cString\u003e`, and it means structured output.\n\n# Prompt Templates\n\nHow to manage prompts in Java? Now ChatGPT starter adopts `prompts.properties` to save prompt templates,\nand uses MessageFormat to format template value.`PromptPropertiesStoreImpl` will load all ` prompts.properties` files\nfrom classpath. You can extend `PromptStore` to load prompts from database or other sources.\n\nYou can load prompt template by [PromptManager](src/main/java/org/mvnsearch/chatgpt/spring/service/PromptManager.java).\n\nTips:\n\n* Prompt template code completion: support by `@PropertyKey(resourceBundle = PROMPTS_FQN)`\n* `@ChatCompletion` annotation has built-in prompt template support for `user`,`system` and `assistant` messages.\n* Prompt value could be from classpath and URL: `conversation=classpath:///conversation-prompt.txt` or\n  `conversation=https://example.com/conversation-prompt.txt`\n\n### Prompt Template as Lambda\n\nFor some case you want to use prompt template as lambda, such as translate first, then send it as email.\nYou can declare prompt as function and chain them together.\n\n```java\npublic class PromptLambdaTest {\n    @Test\n    public void testPromptAsFunction() {\n        Function\u003cString, Mono\u003cString\u003e\u003e translateIntoChineseFunction = chatGPTService.promptAsLambda(\"translate-into-chinese\");\n        Function\u003cString, Mono\u003cString\u003e\u003e sendEmailFunction = chatGPTService.promptAsLambda(\"send-email\");\n        String result = Mono.just(\"Hi Jackie, could you write an email to Libing(libing.chen@exaple.com) and Sam(linux_china@example.com) and invite them to join Mike's birthday party at 4 pm tomorrow? Thanks!\")\n                .flatMap(translateIntoChineseFunction)\n                .flatMap(sendEmailFunction)\n                .block();\n        System.out.println(result);\n    }\n}\n```\n\nTo keep placeholders safe in prompt template, you can use record as Lambda parameter.\n\n```java\npublic class PromptTest {\n    public record TranslateRequest(String from, String to, String text) {\n    }\n\n    @Test\n    public void testLambdaWithRecord() {\n        Function\u003cTranslateRequest, Mono\u003cString\u003e\u003e translateFunction = chatGPTService.promptAsLambda(\"translate\");\n        String result = Mono.just(new TranslateRequest(\"Chinese\", \"English\", \"你好！\"))\n                .flatMap(translateFunction)\n                .block();\n        System.out.println(result);\n    }\n}\n```\n\n# [Batch API](https://platform.openai.com/docs/guides/batch)\n\n- Convert multi requests to JSONL format\n- Upload JSONL file to OpenAI\n- Create batch with file id\n\n```\n\t@Autowired\n\tprivate OpenAIFileAPI openAIFileAPI;\n\n\t@Autowired\n\tprivate OpenAIBatchAPI openAIBatchAPI;\n\n\t@Test\n\tpublic void testUpload() {\n\t\tString jsonl = Stream.of(\"What's Java Language?\", \"What's Kotlin Language?\")\n\t\t\t.map(ChatCompletionRequest::of)\n\t\t\t.map(ChatCompletionBatchRequest::new)\n\t\t\t.map(this::toJson)\n\t\t\t.filter(Strings::isNotBlank)\n\t\t\t.collect(Collectors.joining(\"\\n\"));\n\t\tResource resource = new ByteArrayResource(jsonl.getBytes());\n\t\tFileObject fileObject = openAIFileAPI.upload(\"batch\", resource).block();\n\t\tCreateBatchRequest createBatchRequest = new CreateBatchRequest(fileObject.getId());\n\t\tBatchObject batchObject = openAIBatchAPI.create(createBatchRequest).block();\n\t}\n```\n\nAfter `completion_window(24h)`, and you can call `openAIBatchAPI.retrieve(batchId)` to get the `BatchObject`.\nGet `BatchObject.outputFileId` and call `OpenAIFileAPI.retrieve(outputFileId)` to get jsonl response,\nand use follow code to parse every chat response.\n\n```\nList\u003cString\u003e lines = new BufferedReader(new InputStreamReader(inputStream)).lines().toList();\nfor (String line : lines) {\n\tif (line.startsWith(\"{\")) {\n\t\tChatCompletionBatchResponse response = objectMapper.readValue(line, ChatCompletionBatchResponse.class);\n\t\tSystem.out.println(response.getCustomId());\n\t}\n}\n```\n\n# FAQ\n            \n### How to integrate with DeepSeek?\n\n```properties\nopenai.api.url=https://api.deepseek.com/v1\nopenai.api.key=sk-xxxx\nopenai.model=deepseek-chat\n```\n\n### OpenAI REST API proxy\n\nPlease refer [OpenAIProxyController](src/test/java/org/mvnsearch/chatgpt/demo/OpenAIProxyController.java).\n\n```java\n\n@RestController\npublic class OpenAIProxyController {\n    @Autowired\n    private OpenAIChatAPI openAIChatAPI;\n\n    @PostMapping(\"/v1/chat/completions\")\n    public Publisher\u003cChatCompletionResponse\u003e completions(@RequestBody ChatCompletionRequest request) {\n        return openAIChatAPI.proxy(request);\n    }\n}\n```\n\nOf course, you can use standard URL `http://localhost:8080/v1/chat/completions` to call Azure OpenAI API.\n\n### How to use ChatGPT with Spring Web?\n\nNow ChatGPT starter use Reactive style API, and you know Reactive still hard to understand.\nCould ChatGPT starter work with Spring Web? Yes, you can use `Mono` or `Flux` with Spring Web and Virtual Threads,\nplease\nrefer [Support for Virtual Threads on Spring Boot 3.2](https://github.com/spring-projects/spring-boot/wiki/Spring-Boot-3.2-Release-Notes#support-for-virtual-threads)\nfor details.\n\n# Building the Code\n\nThe code uses the Spring Java Formatter Maven plugin, which keeps the code consistent. In order to build the code, run:\n\n```shell {#javaformat} \n./mvnw spring-javaformat:apply\n```\n\nThis will ensure that all contributions have the exact same code formatting, allowing us to focus on bigger issues, like\nfunctionality,\n\n# References\n\n* [OpenAI chat API](https://platform.openai.com/docs/api-reference/chat)\n* [Spring Boot Docs](https://docs.spring.io/spring-boot/docs/current/reference/html/)\n* [Spring Boot Webflux](https://docs.spring.io/spring-framework/reference/web/webflux.html)\n* [Spring 6 HTTP interface](https://docs.spring.io/spring-framework/reference/integration/rest-clients.html#rest-http-interface)\n* [Properties File Format](https://docs.oracle.com/cd/E23095_01/Platform.93/ATGProgGuide/html/s0204propertiesfileformat01.html)\n* [MessageFormat JavaDoc](https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/text/MessageFormat.html)\n* [OpenAI Prompt engineering](https://platform.openai.com/docs/guides/prompt-engineering)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flinux-china%2Fchatgpt-spring-boot-starter","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Flinux-china%2Fchatgpt-spring-boot-starter","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flinux-china%2Fchatgpt-spring-boot-starter/lists"}