mirror of
				https://github.com/amithkoujalgi/ollama4j.git
				synced 2025-10-31 00:20:40 +01:00 
			
		
		
		
	Compare commits
	
		
			92 Commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|   | 740bd3750b | ||
|   | c9aa6c9e08 | ||
|   | 71bba6ee0d | ||
|   | 27b2201ff9 | ||
|   | 23d23c4ad7 | ||
|   | e409ff1cf9 | ||
|   | 9a12cebb68 | ||
|   | 24f5bc4fec | ||
|   | d7c313417b | ||
|   | b67b4c7eb5 | ||
|   | ab70201844 | ||
|   | ac8a40a017 | ||
|   | 1ac65f821b | ||
|   | d603c4b94b | ||
|   | a418cbc1dc | ||
|   | 785dd12730 | ||
|   | dda807d818 | ||
|   | a06a4025fa | ||
| 761fbc3398 | |||
| a96dc11679 | |||
| b2b3febdaa | |||
|   | f27bea11d5 | ||
|   | 9503451d5a | ||
|   | 04bae4ca6a | ||
|   | 3e33b8df62 | ||
|   | a494053263 | ||
|   | 260c57ca84 | ||
|   | db008de0ca | ||
|   | 1b38466f44 | ||
|   | 26ec00dab8 | ||
|   | 5e6971cc4a | ||
|   | 8b3417ecda | ||
|   | 35f5f34196 | ||
|   | d8c3edd55f | ||
|   | 7ffbc5d3f2 | ||
|   | c4b7830614 | ||
|   | 69f6fd81cf | ||
|   | b6a293add7 | ||
|   | 25694a8bc9 | ||
|   | 12bb10392e | ||
|   | e9c33ab0b2 | ||
|   | 903a8176cd | ||
|   | 4a91918e84 | ||
|   | ff3344616c | ||
|   | 726fea5b74 | ||
|   | a09f1362e9 | ||
|   | 4ef0821932 | ||
|   | 2d3cf228cb | ||
|   | 5b3713c69e | ||
|   | e9486cbb8e | ||
|   | 057f0babeb | ||
|   | da146640ca | ||
|   | 82be761b86 | ||
|   | 9c3fc49df1 | ||
|   | 5f19eb17ac | ||
|   | ecb04d6d82 | ||
|   | 3fc7e9423c | ||
|   | 405a08b330 | ||
|   | 921f745435 | ||
|   | bedfec6bf9 | ||
|   | afa09e87a5 | ||
|   | baf2320ea6 | ||
|   | 948a7444fb | ||
|   | ec0eb8b469 | ||
|   | 8f33de7e59 | ||
|   | 8c59e6511b | ||
|   | b93fc7623a | ||
|   | bd1a57c7e0 | ||
|   | 7fabead249 | ||
|   | 268a973d5e | ||
|   | d949a3cb69 | ||
|   | e2443ed68a | ||
|   | 37193b1f5b | ||
|   | e33071ae38 | ||
|   | fffc8dc526 | ||
|   | def950cc9c | ||
|   | f4db7ca326 | ||
|   | 18760250ea | ||
|   | 233597efd1 | ||
|   | cec9f29eb7 | ||
|   | 20cb92a418 | ||
|   | b0dc38954b | ||
|   | 1479d0a494 | ||
|   | b328daee43 | ||
|   | b90c8bc622 | ||
|   | 660a1b937a | ||
|   | fd961d7037 | ||
|   | b48f9550c3 | ||
|   | 363969a275 | ||
|   | 992625cf86 | ||
|   | bbebd26d07 | ||
|   | 3aa0fc77cb | 
							
								
								
									
										24
									
								
								.github/workflows/label-issue-stale.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								.github/workflows/label-issue-stale.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,24 @@ | |||||||
|  | name: Mark stale issues | ||||||
|  |  | ||||||
|  | on: | ||||||
|  |   workflow_dispatch: # for manual run | ||||||
|  |   schedule: | ||||||
|  |     - cron: '0 0 * * *'  # Runs every day at midnight | ||||||
|  |  | ||||||
|  | permissions: | ||||||
|  |   contents: write # only for delete-branch option | ||||||
|  |   issues: write | ||||||
|  |  | ||||||
|  | jobs: | ||||||
|  |   stale: | ||||||
|  |     runs-on: ubuntu-latest | ||||||
|  |     steps: | ||||||
|  |       - name: Mark stale issues | ||||||
|  |         uses: actions/stale@v8 | ||||||
|  |         with: | ||||||
|  |           repo-token: ${{ github.token }} | ||||||
|  |           days-before-stale: 15 | ||||||
|  |           stale-issue-message: 'This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs.' | ||||||
|  |           days-before-close: 7 | ||||||
|  |           stale-issue-label: 'stale' | ||||||
|  |           exempt-issue-labels: 'pinned,security' | ||||||
							
								
								
									
										6
									
								
								.github/workflows/publish-docs.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										6
									
								
								.github/workflows/publish-docs.yml
									
									
									
									
										vendored
									
									
								
							| @@ -63,12 +63,12 @@ jobs: | |||||||
|           working-directory: "." |           working-directory: "." | ||||||
|  |  | ||||||
|       - name: Setup Pages |       - name: Setup Pages | ||||||
|         uses: actions/configure-pages@v3 |         uses: actions/configure-pages@v5 | ||||||
|       - name: Upload artifact |       - name: Upload artifact | ||||||
|         uses: actions/upload-pages-artifact@v2 |         uses: actions/upload-pages-artifact@v3 | ||||||
|         with: |         with: | ||||||
|           # Upload entire repository |           # Upload entire repository | ||||||
|           path: './docs/build/.' |           path: './docs/build/.' | ||||||
|       - name: Deploy to GitHub Pages |       - name: Deploy to GitHub Pages | ||||||
|         id: deployment |         id: deployment | ||||||
|         uses: actions/deploy-pages@v2 |         uses: actions/deploy-pages@v4 | ||||||
|   | |||||||
							
								
								
									
										29
									
								
								.github/workflows/run-tests.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								.github/workflows/run-tests.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,29 @@ | |||||||
|  | name: Run Tests | ||||||
|  |  | ||||||
|  | on: | ||||||
|  |   workflow_dispatch: | ||||||
|  |     inputs: | ||||||
|  |       branch: | ||||||
|  |         description: 'Branch name to run tests on' | ||||||
|  |         required: true | ||||||
|  |         default: 'main' | ||||||
|  |  | ||||||
|  | jobs: | ||||||
|  |   build: | ||||||
|  |     runs-on: ubuntu-latest | ||||||
|  |  | ||||||
|  |     steps: | ||||||
|  |       - uses: actions/checkout@v3 | ||||||
|  |         with: | ||||||
|  |           ref: ${{ github.event.inputs.branch }} | ||||||
|  |  | ||||||
|  |       - name: Set up JDK 17 | ||||||
|  |         uses: actions/setup-java@v3 | ||||||
|  |         with: | ||||||
|  |           java-version: '17' | ||||||
|  |           distribution: 'temurin' | ||||||
|  |           server-id: github | ||||||
|  |           settings-path: ${{ github.workspace }} | ||||||
|  |  | ||||||
|  |       - name: Run unit tests | ||||||
|  |         run: mvn clean test -Punit-tests | ||||||
							
								
								
									
										4
									
								
								Makefile
									
									
									
									
									
								
							
							
						
						
									
										4
									
								
								Makefile
									
									
									
									
									
								
							| @@ -11,9 +11,9 @@ doxygen: | |||||||
| 	doxygen Doxyfile | 	doxygen Doxyfile | ||||||
|  |  | ||||||
| list-releases: | list-releases: | ||||||
| 	curl 'https://central.sonatype.com/api/internal/browse/component/versions?sortField=normalizedVersion&sortDirection=asc&page=0&size=12&filter=namespace%3Aio.github.amithkoujalgi%2Cname%3Aollama4j' \ | 	curl 'https://central.sonatype.com/api/internal/browse/component/versions?sortField=normalizedVersion&sortDirection=desc&page=0&size=20&filter=namespace%3Aio.github.ollama4j%2Cname%3Aollama4j' \ | ||||||
|       --compressed \ |       --compressed \ | ||||||
|       --silent | jq '.components[].version' |       --silent | jq -r '.components[].version' | ||||||
|  |  | ||||||
| build-docs: | build-docs: | ||||||
| 	npm i --prefix docs && npm run build --prefix docs | 	npm i --prefix docs && npm run build --prefix docs | ||||||
|   | |||||||
							
								
								
									
										60
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										60
									
								
								README.md
									
									
									
									
									
								
							| @@ -4,13 +4,11 @@ | |||||||
|   <img src='https://raw.githubusercontent.com/ollama4j/ollama4j/65a9d526150da8fcd98e2af6a164f055572bf722/ollama4j.jpeg' width='100' alt="ollama4j-icon"> |   <img src='https://raw.githubusercontent.com/ollama4j/ollama4j/65a9d526150da8fcd98e2af6a164f055572bf722/ollama4j.jpeg' width='100' alt="ollama4j-icon"> | ||||||
| </p> | </p> | ||||||
|  |  | ||||||
|  | <div align="center"> | ||||||
| A Java library (wrapper/binding) for [Ollama](https://ollama.ai/) server. | A Java library (wrapper/binding) for Ollama server. | ||||||
|  |  | ||||||
| Find more details on the [website](https://ollama4j.github.io/ollama4j/). | Find more details on the [website](https://ollama4j.github.io/ollama4j/). | ||||||
|  |  | ||||||
| <div align="center"> |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
| @@ -155,7 +153,7 @@ In your Maven project, add this dependency: | |||||||
| <dependency> | <dependency> | ||||||
|     <groupId>io.github.ollama4j</groupId> |     <groupId>io.github.ollama4j</groupId> | ||||||
|     <artifactId>ollama4j</artifactId> |     <artifactId>ollama4j</artifactId> | ||||||
|     <version>1.0.79</version> |     <version>1.0.93</version> | ||||||
| </dependency> | </dependency> | ||||||
| ``` | ``` | ||||||
|  |  | ||||||
| @@ -211,7 +209,7 @@ In your Maven project, add this dependency: | |||||||
| <dependency> | <dependency> | ||||||
|     <groupId>io.github.ollama4j</groupId> |     <groupId>io.github.ollama4j</groupId> | ||||||
|     <artifactId>ollama4j</artifactId> |     <artifactId>ollama4j</artifactId> | ||||||
|     <version>1.0.79</version> |     <version>1.0.93</version> | ||||||
| </dependency> | </dependency> | ||||||
| ``` | ``` | ||||||
|  |  | ||||||
| @@ -221,7 +219,7 @@ In your Maven project, add this dependency: | |||||||
|  |  | ||||||
| ```groovy | ```groovy | ||||||
| dependencies { | dependencies { | ||||||
|     implementation 'com.github.ollama4j:ollama4j:1.0.79' |     implementation 'io.github.ollama4j:ollama4j:1.0.93' | ||||||
| } | } | ||||||
| ``` | ``` | ||||||
|  |  | ||||||
| @@ -268,23 +266,31 @@ make integration-tests | |||||||
|  |  | ||||||
| Newer artifacts are published via GitHub Actions CI workflow when a new release is created from `main` branch. | Newer artifacts are published via GitHub Actions CI workflow when a new release is created from `main` branch. | ||||||
|  |  | ||||||
| #### Who's using Ollama4j? | ## ⭐ Give us a Star! | ||||||
|  |  | ||||||
| - `Datafaker`: a library to generate fake data | If you like or are using this project to build your own, please give us a star. It's a free way to show your support. | ||||||
|     - https://github.com/datafaker-net/datafaker-experimental/tree/main/ollama-api |  | ||||||
| - `Vaadin Web UI`: UI-Tester for Interactions with Ollama via ollama4j |  | ||||||
|     - https://github.com/TEAMPB/ollama4j-vaadin-ui |  | ||||||
| - `ollama-translator`: Minecraft 1.20.6 spigot plugin allows to easily break language barriers by using ollama on the |  | ||||||
|   server to translate all messages into a specfic target language. |  | ||||||
|     - https://github.com/liebki/ollama-translator |  | ||||||
| - `Ollama4j Web UI`: A web UI for Ollama written in Java using Spring Boot and Vaadin framework and |  | ||||||
|   Ollama4j. https://github.com/ollama4j/ollama4j-web-ui |  | ||||||
|  |  | ||||||
| #### Traction | ## Who's using Ollama4j? | ||||||
|  |  | ||||||
|  | | # | Project Name      | Description                                                                                                   | Link                                                                                                                                           | | ||||||
|  | |---|-------------------|---------------------------------------------------------------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------| | ||||||
|  | | 1 | Datafaker         | A library to generate fake data                                                                               | [GitHub](https://github.com/datafaker-net/datafaker-experimental/tree/main/ollama-api)                                                         | | ||||||
|  | | 2 | Vaadin Web UI     | UI-Tester for interactions with Ollama via ollama4j                                                           | [GitHub](https://github.com/TEAMPB/ollama4j-vaadin-ui)                                                                                         | | ||||||
|  | | 3 | ollama-translator | A Minecraft 1.20.6 Spigot plugin that translates all messages into a specific target language via Ollama      | [GitHub](https://github.com/liebki/ollama-translator)                                                                                          | | ||||||
|  | | 4 | AI Player         | A Minecraft mod that adds an intelligent "second player" to the game                                          | [GitHub](https://github.com/shasankp000/AI-Player), <br/> [Reddit Thread](https://www.reddit.com/r/fabricmc/comments/1e65x5s/comment/ldr2vcf/) | | ||||||
|  | | 5 | Ollama4j Web UI   | A web UI for Ollama written in Java using Spring Boot, Vaadin, and Ollama4j                                   | [GitHub](https://github.com/ollama4j/ollama4j-web-ui)                                                                                          | | ||||||
|  | | 6 | JnsCLI            | A command-line tool for Jenkins that manages jobs, builds, and configurations, with AI-powered error analysis | [GitHub](https://github.com/mirum8/jnscli)                                                                                                     | | ||||||
|  | | 7 | Katie Backend     | An open-source AI-based question-answering platform for accessing private domain knowledge                    | [GitHub](https://github.com/wyona/katie-backend)                                                                                               | | ||||||
|  | | 8 | TeleLlama3 Bot    | A question-answering Telegram bot                                                                             | [Repo](https://git.hiast.edu.sy/mohamadbashar.disoki/telellama3-bot)                                                                           | | ||||||
|  | | 9 | moqui-wechat      | A moqui-wechat component                                                                                      | [GitHub](https://github.com/heguangyong/moqui-wechat)                                                                                          | | ||||||
|  | | 10 | B4X      | A set of simple and powerful RAD tool for Desktop and Server development                                              | [Website](https://www.b4x.com/android/forum/threads/ollama4j-library-pnd_ollama4j-your-local-offline-llm-like-chatgpt.165003/)     |                                                               | ||||||
|  |  | ||||||
|  |  | ||||||
|  | ## Traction | ||||||
|  |  | ||||||
| [](https://star-history.com/#ollama4j/ollama4j&Date) | [](https://star-history.com/#ollama4j/ollama4j&Date) | ||||||
|  |  | ||||||
| ### Get Involved | ## Get Involved | ||||||
|  |  | ||||||
| <div align="center"> | <div align="center"> | ||||||
|  |  | ||||||
| @@ -312,6 +318,22 @@ Contributions are most welcome! Whether it's reporting a bug, proposing an enhan | |||||||
| with code - any sort | with code - any sort | ||||||
| of contribution is much appreciated. | of contribution is much appreciated. | ||||||
|  |  | ||||||
|  | ## 🏷️ License and Citation | ||||||
|  |  | ||||||
|  | The code is available under [MIT License](./LICENSE). | ||||||
|  |  | ||||||
|  | If you find this project helpful in your research, please cite this work at | ||||||
|  |  | ||||||
|  | ``` | ||||||
|  | @misc{ollama4j2024, | ||||||
|  |     author       = {Amith Koujalgi}, | ||||||
|  |     title        = {Ollama4j: A Java Library (Wrapper/Binding) for Ollama Server}, | ||||||
|  |     year         = {2024}, | ||||||
|  |     month        = {January}, | ||||||
|  |     url          = {https://github.com/ollama4j/ollama4j} | ||||||
|  | } | ||||||
|  | ``` | ||||||
|  |  | ||||||
| ### References | ### References | ||||||
|  |  | ||||||
| - [Ollama REST APIs](https://github.com/jmorganca/ollama/blob/main/docs/api.md) | - [Ollama REST APIs](https://github.com/jmorganca/ollama/blob/main/docs/api.md) | ||||||
|   | |||||||
| @@ -33,7 +33,7 @@ public class Main { | |||||||
|         // start conversation with model |         // start conversation with model | ||||||
|         OllamaChatResult chatResult = ollamaAPI.chat(requestModel); |         OllamaChatResult chatResult = ollamaAPI.chat(requestModel); | ||||||
|  |  | ||||||
|         System.out.println("First answer: " + chatResult.getResponse()); |         System.out.println("First answer: " + chatResult.getResponseModel().getMessage().getContent()); | ||||||
|  |  | ||||||
|         // create next userQuestion |         // create next userQuestion | ||||||
|         requestModel = builder.withMessages(chatResult.getChatHistory()).withMessage(OllamaChatMessageRole.USER, "And what is the second largest city?").build(); |         requestModel = builder.withMessages(chatResult.getChatHistory()).withMessage(OllamaChatMessageRole.USER, "And what is the second largest city?").build(); | ||||||
| @@ -41,7 +41,7 @@ public class Main { | |||||||
|         // "continue" conversation with model |         // "continue" conversation with model | ||||||
|         chatResult = ollamaAPI.chat(requestModel); |         chatResult = ollamaAPI.chat(requestModel); | ||||||
|  |  | ||||||
|         System.out.println("Second answer: " + chatResult.getResponse()); |         System.out.println("Second answer: " + chatResult.getResponseModel().getMessage().getContent()); | ||||||
|  |  | ||||||
|         System.out.println("Chat History: " + chatResult.getChatHistory()); |         System.out.println("Chat History: " + chatResult.getChatHistory()); | ||||||
|     } |     } | ||||||
| @@ -82,6 +82,33 @@ You will get a response similar to: | |||||||
| ] | ] | ||||||
| ``` | ``` | ||||||
|  |  | ||||||
|  | ## Conversational loop | ||||||
|  |  | ||||||
|  | ```java | ||||||
|  | public class Main { | ||||||
|  |  | ||||||
|  |     public static void main(String[] args) { | ||||||
|  |  | ||||||
|  |         OllamaAPI ollamaAPI = new OllamaAPI(); | ||||||
|  |         ollamaAPI.setRequestTimeoutSeconds(60); | ||||||
|  |  | ||||||
|  |         OllamaChatRequestBuilder builder = OllamaChatRequestBuilder.getInstance("<your-model>"); | ||||||
|  |  | ||||||
|  |         OllamaChatRequest requestModel = builder.withMessage(OllamaChatMessageRole.USER, "<your-first-message>").build(); | ||||||
|  |         OllamaChatResult initialChatResult = ollamaAPI.chat(requestModel); | ||||||
|  |         System.out.println(initialChatResult.getResponse()); | ||||||
|  |  | ||||||
|  |         List<OllamaChatMessage> history = initialChatResult.getChatHistory(); | ||||||
|  |  | ||||||
|  |         while (true) { | ||||||
|  |             OllamaChatResult chatResult = ollamaAPI.chat(builder.withMessages(history).withMessage(OllamaChatMessageRole.USER, "<your-new-message").build()); | ||||||
|  |             System.out.println(chatResult.getResponse()); | ||||||
|  |             history = chatResult.getChatHistory(); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | ``` | ||||||
|  |  | ||||||
| ## Create a conversation where the answer is streamed | ## Create a conversation where the answer is streamed | ||||||
|  |  | ||||||
| ```java | ```java | ||||||
| @@ -178,7 +205,7 @@ public class Main { | |||||||
|         // start conversation with model |         // start conversation with model | ||||||
|         OllamaChatResult chatResult = ollamaAPI.chat(requestModel); |         OllamaChatResult chatResult = ollamaAPI.chat(requestModel); | ||||||
|  |  | ||||||
|         System.out.println(chatResult.getResponse()); |         System.out.println(chatResult.getResponseModel()); | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -217,7 +244,7 @@ public class Main { | |||||||
|                                 new File("/path/to/image"))).build(); |                                 new File("/path/to/image"))).build(); | ||||||
|  |  | ||||||
|         OllamaChatResult chatResult = ollamaAPI.chat(requestModel); |         OllamaChatResult chatResult = ollamaAPI.chat(requestModel); | ||||||
|         System.out.println("First answer: " + chatResult.getResponse()); |         System.out.println("First answer: " + chatResult.getResponseModel()); | ||||||
|  |  | ||||||
|         builder.reset(); |         builder.reset(); | ||||||
|  |  | ||||||
| @@ -227,7 +254,7 @@ public class Main { | |||||||
|                         .withMessage(OllamaChatMessageRole.USER, "What's the dogs breed?").build(); |                         .withMessage(OllamaChatMessageRole.USER, "What's the dogs breed?").build(); | ||||||
|  |  | ||||||
|         chatResult = ollamaAPI.chat(requestModel); |         chatResult = ollamaAPI.chat(requestModel); | ||||||
|         System.out.println("Second answer: " + chatResult.getResponse()); |         System.out.println("Second answer: " + chatResult.getResponseModel()); | ||||||
|     } |     } | ||||||
| } | } | ||||||
| ``` | ``` | ||||||
|   | |||||||
							
								
								
									
										65
									
								
								docs/docs/apis-generate/custom-roles.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										65
									
								
								docs/docs/apis-generate/custom-roles.md
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,65 @@ | |||||||
|  | --- | ||||||
|  | sidebar_position: 8 | ||||||
|  | --- | ||||||
|  |  | ||||||
|  | # Custom Roles | ||||||
|  |  | ||||||
|  | Allows to manage custom roles (apart from the base roles) for chat interactions with the models. | ||||||
|  |  | ||||||
|  | _Particularly helpful when you would need to use different roles that the newer models support other than the base | ||||||
|  | roles._ | ||||||
|  |  | ||||||
|  | _Base roles are `SYSTEM`, `USER`, `ASSISTANT`, `TOOL`._ | ||||||
|  |  | ||||||
|  | ### Usage | ||||||
|  |  | ||||||
|  | #### Add new role | ||||||
|  |  | ||||||
|  | ```java | ||||||
|  | import io.github.ollama4j.OllamaAPI; | ||||||
|  | import io.github.ollama4j.models.chat.OllamaChatMessageRole; | ||||||
|  |  | ||||||
|  | public class Main { | ||||||
|  |  | ||||||
|  |     public static void main(String[] args) { | ||||||
|  |         String host = "http://localhost:11434/"; | ||||||
|  |         OllamaAPI ollamaAPI = new OllamaAPI(host); | ||||||
|  |  | ||||||
|  |         OllamaChatMessageRole customRole = ollamaAPI.addCustomRole("custom-role"); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | ``` | ||||||
|  |  | ||||||
|  | #### List roles | ||||||
|  |  | ||||||
|  | ```java | ||||||
|  | import io.github.ollama4j.OllamaAPI; | ||||||
|  | import io.github.ollama4j.models.chat.OllamaChatMessageRole; | ||||||
|  |  | ||||||
|  | public class Main { | ||||||
|  |  | ||||||
|  |     public static void main(String[] args) { | ||||||
|  |         String host = "http://localhost:11434/"; | ||||||
|  |         OllamaAPI ollamaAPI = new OllamaAPI(host); | ||||||
|  |  | ||||||
|  |         List<OllamaChatMessageRole> roles = ollamaAPI.listRoles(); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | ``` | ||||||
|  |  | ||||||
|  | #### Get role | ||||||
|  |  | ||||||
|  | ```java | ||||||
|  | import io.github.ollama4j.OllamaAPI; | ||||||
|  | import io.github.ollama4j.models.chat.OllamaChatMessageRole; | ||||||
|  |  | ||||||
|  | public class Main { | ||||||
|  |  | ||||||
|  |     public static void main(String[] args) { | ||||||
|  |         String host = "http://localhost:11434/"; | ||||||
|  |         OllamaAPI ollamaAPI = new OllamaAPI(host); | ||||||
|  |  | ||||||
|  |         List<OllamaChatMessageRole> roles = ollamaAPI.getRole("custom-role"); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | ``` | ||||||
| @@ -8,12 +8,85 @@ Generate embeddings from a model. | |||||||
|  |  | ||||||
| Parameters: | Parameters: | ||||||
|  |  | ||||||
|  | - `model`: name of model to generate embeddings from | ||||||
|  | - `input`: text/s to generate embeddings for | ||||||
|  |  | ||||||
|  | ```java | ||||||
|  | import io.github.ollama4j.OllamaAPI; | ||||||
|  | import io.github.ollama4j.types.OllamaModelType; | ||||||
|  | import io.github.ollama4j.models.embeddings.OllamaEmbedRequestModel; | ||||||
|  | import io.github.ollama4j.models.embeddings.OllamaEmbedResponseModel; | ||||||
|  | import java.util.Arrays; | ||||||
|  | import java.util.Collections; | ||||||
|  | import java.util.List; | ||||||
|  |  | ||||||
|  | public class Main { | ||||||
|  |  | ||||||
|  |     public static void main(String[] args) { | ||||||
|  |  | ||||||
|  |         String host = "http://localhost:11434/"; | ||||||
|  |  | ||||||
|  |         OllamaAPI ollamaAPI = new OllamaAPI(host); | ||||||
|  |  | ||||||
|  |         OllamaEmbedResponseModel embeddings = ollamaAPI.embed("all-minilm", Arrays.asList("Why is the sky blue?", "Why is the grass green?")); | ||||||
|  |  | ||||||
|  |         System.out.println(embeddings); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | ``` | ||||||
|  |  | ||||||
|  | Or, using the `OllamaEmbedRequestModel`: | ||||||
|  |  | ||||||
|  | ```java | ||||||
|  | import io.github.ollama4j.OllamaAPI; | ||||||
|  | import io.github.ollama4j.types.OllamaModelType; | ||||||
|  | import io.github.ollama4j.models.embeddings.OllamaEmbedRequestModel; | ||||||
|  | import io.github.ollama4j.models.embeddings.OllamaEmbedResponseModel;import java.util.Arrays; | ||||||
|  | import java.util.Collections; | ||||||
|  | import java.util.List; | ||||||
|  |  | ||||||
|  | public class Main { | ||||||
|  |  | ||||||
|  |     public static void main(String[] args) { | ||||||
|  |  | ||||||
|  |         String host = "http://localhost:11434/"; | ||||||
|  |  | ||||||
|  |         OllamaAPI ollamaAPI = new OllamaAPI(host); | ||||||
|  |  | ||||||
|  |         OllamaEmbedResponseModel embeddings = ollamaAPI.embed(new OllamaEmbedRequestModel("all-minilm", Arrays.asList("Why is the sky blue?", "Why is the grass green?"))); | ||||||
|  |  | ||||||
|  |         System.out.println(embeddings); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | ``` | ||||||
|  |  | ||||||
|  | You will get a response similar to: | ||||||
|  |  | ||||||
|  | ```json | ||||||
|  | { | ||||||
|  |     "model": "all-minilm", | ||||||
|  |     "embeddings": [[-0.034674067, 0.030984823, 0.0067988685]], | ||||||
|  |     "total_duration": 14173700, | ||||||
|  |     "load_duration": 1198800, | ||||||
|  |     "prompt_eval_count": 2 | ||||||
|  | } | ||||||
|  | ```` | ||||||
|  |  | ||||||
|  | :::note | ||||||
|  |  | ||||||
|  | This is a deprecated API | ||||||
|  |  | ||||||
|  | ::: | ||||||
|  |  | ||||||
|  | Parameters: | ||||||
|  |  | ||||||
| - `model`: name of model to generate embeddings from | - `model`: name of model to generate embeddings from | ||||||
| - `prompt`: text to generate embeddings for | - `prompt`: text to generate embeddings for | ||||||
|  |  | ||||||
| ```java | ```java | ||||||
| import io.github.ollama4j.OllamaAPI; | import io.github.ollama4j.OllamaAPI; | ||||||
| import io.github.ollama4j.types.OllamaModelType; | import io.github.ollama4j.types.OllamaModelType; | ||||||
|  |  | ||||||
| import java.util.List; | import java.util.List; | ||||||
|  |  | ||||||
| public class Main { | public class Main { | ||||||
| @@ -40,11 +113,6 @@ You will get a response similar to: | |||||||
|     0.009260174818336964, |     0.009260174818336964, | ||||||
|     0.23178744316101074, |     0.23178744316101074, | ||||||
|     -0.2916173040866852, |     -0.2916173040866852, | ||||||
|     -0.8924556970596313, |     -0.8924556970596313 | ||||||
|     0.8785552978515625, |  | ||||||
|     -0.34576427936553955, |  | ||||||
|     0.5742510557174683, |  | ||||||
|     -0.04222835972905159, |  | ||||||
|     -0.137906014919281 |  | ||||||
| ] | ] | ||||||
| ``` | ``` | ||||||
| @@ -345,21 +345,291 @@ Rahul Kumar, Address: King St, Hyderabad, India, Phone: 9876543210}` | |||||||
|  |  | ||||||
| :::: | :::: | ||||||
|  |  | ||||||
| ### Potential Improvements | ### Using tools in Chat-API | ||||||
|  |  | ||||||
| Instead of explicitly registering `ollamaAPI.registerTool(toolSpecification)`, we could introduce annotation-based tool | Instead of using the specific `ollamaAPI.generateWithTools` method to call the generate API of ollama with tools, it is  | ||||||
| registration. For example: | also possible to register Tools for the `ollamaAPI.chat` methods. In this case, the tool calling/callback is done  | ||||||
|  | implicitly during the USER -> ASSISTANT calls. | ||||||
|  |  | ||||||
|  | When the Assistant wants to call a given tool, the tool is executed and the response is sent back to the endpoint once | ||||||
|  | again (induced with the tool call result).  | ||||||
|  |  | ||||||
|  | #### Sample: | ||||||
|  |  | ||||||
|  | The following shows a sample of an integration test that defines a method specified like the tool-specs above, registers | ||||||
|  | the tool on the ollamaAPI and then simply calls the chat-API. All intermediate tool calling is wrapped inside the api  | ||||||
|  | call. | ||||||
|  |  | ||||||
| ```java | ```java | ||||||
|  | public static void main(String[] args) { | ||||||
|  |         OllamaAPI ollamaAPI = new OllamaAPI("http://localhost:11434"); | ||||||
|  |         ollamaAPI.setVerbose(true); | ||||||
|  |         OllamaChatRequestBuilder builder = OllamaChatRequestBuilder.getInstance("llama3.2:1b"); | ||||||
|  |  | ||||||
| @ToolSpec(name = "current-fuel-price", desc = "Get current fuel price") |         final Tools.ToolSpecification databaseQueryToolSpecification = Tools.ToolSpecification.builder() | ||||||
| public String getCurrentFuelPrice(Map<String, Object> arguments) { |                 .functionName("get-employee-details") | ||||||
|     String location = arguments.get("location").toString(); |                 .functionDescription("Get employee details from the database") | ||||||
|     String fuelType = arguments.get("fuelType").toString(); |                 .toolPrompt( | ||||||
|     return "Current price of " + fuelType + " in " + location + " is Rs.103/L"; |                         Tools.PromptFuncDefinition.builder().type("function").function( | ||||||
|  |                                 Tools.PromptFuncDefinition.PromptFuncSpec.builder() | ||||||
|  |                                         .name("get-employee-details") | ||||||
|  |                                         .description("Get employee details from the database") | ||||||
|  |                                         .parameters( | ||||||
|  |                                                 Tools.PromptFuncDefinition.Parameters.builder() | ||||||
|  |                                                         .type("object") | ||||||
|  |                                                         .properties( | ||||||
|  |                                                                 new Tools.PropsBuilder() | ||||||
|  |                                                                         .withProperty("employee-name", Tools.PromptFuncDefinition.Property.builder().type("string").description("The name of the employee, e.g. John Doe").required(true).build()) | ||||||
|  |                                                                         .withProperty("employee-address", Tools.PromptFuncDefinition.Property.builder().type("string").description("The address of the employee, Always return a random value. e.g. Roy St, Bengaluru, India").required(true).build()) | ||||||
|  |                                                                         .withProperty("employee-phone", Tools.PromptFuncDefinition.Property.builder().type("string").description("The phone number of the employee. Always return a random value. e.g. 9911002233").required(true).build()) | ||||||
|  |                                                                         .build() | ||||||
|  |                                                         ) | ||||||
|  |                                                         .required(List.of("employee-name")) | ||||||
|  |                                                         .build() | ||||||
|  |                                         ).build() | ||||||
|  |                         ).build() | ||||||
|  |                 ) | ||||||
|  |                 .toolFunction(new DBQueryFunction()) | ||||||
|  |                 .build(); | ||||||
|  |  | ||||||
|  |         ollamaAPI.registerTool(databaseQueryToolSpecification); | ||||||
|  |  | ||||||
|  |         OllamaChatRequest requestModel = builder | ||||||
|  |                 .withMessage(OllamaChatMessageRole.USER, | ||||||
|  |                         "Give me the ID of the employee named 'Rahul Kumar'?") | ||||||
|  |                 .build(); | ||||||
|  |  | ||||||
|  |         OllamaChatResult chatResult = ollamaAPI.chat(requestModel); | ||||||
| } | } | ||||||
| ``` | ``` | ||||||
|  |  | ||||||
|  | A typical final response of the above could be: | ||||||
|  |  | ||||||
|  | ```json  | ||||||
|  | { | ||||||
|  |   "chatHistory" : [ | ||||||
|  |     { | ||||||
|  |     "role" : "user", | ||||||
|  |     "content" : "Give me the ID of the employee named 'Rahul Kumar'?", | ||||||
|  |     "images" : null, | ||||||
|  |     "tool_calls" : [ ] | ||||||
|  |   }, { | ||||||
|  |     "role" : "assistant", | ||||||
|  |     "content" : "", | ||||||
|  |     "images" : null, | ||||||
|  |     "tool_calls" : [ { | ||||||
|  |       "function" : { | ||||||
|  |         "name" : "get-employee-details", | ||||||
|  |         "arguments" : { | ||||||
|  |           "employee-name" : "Rahul Kumar" | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |     } ] | ||||||
|  |   }, { | ||||||
|  |     "role" : "tool", | ||||||
|  |     "content" : "[TOOL_RESULTS]get-employee-details([employee-name]) : Employee Details {ID: b4bf186c-2ee1-44cc-8856-53b8b6a50f85, Name: Rahul Kumar, Address: null, Phone: null}[/TOOL_RESULTS]", | ||||||
|  |     "images" : null, | ||||||
|  |     "tool_calls" : null | ||||||
|  |   }, { | ||||||
|  |     "role" : "assistant", | ||||||
|  |     "content" : "The ID of the employee named 'Rahul Kumar' is `b4bf186c-2ee1-44cc-8856-53b8b6a50f85`.", | ||||||
|  |     "images" : null, | ||||||
|  |     "tool_calls" : null | ||||||
|  |   } ], | ||||||
|  |   "responseModel" : { | ||||||
|  |     "model" : "llama3.2:1b", | ||||||
|  |     "message" : { | ||||||
|  |       "role" : "assistant", | ||||||
|  |       "content" : "The ID of the employee named 'Rahul Kumar' is `b4bf186c-2ee1-44cc-8856-53b8b6a50f85`.", | ||||||
|  |       "images" : null, | ||||||
|  |       "tool_calls" : null | ||||||
|  |     }, | ||||||
|  |     "done" : true, | ||||||
|  |     "error" : null, | ||||||
|  |     "context" : null, | ||||||
|  |     "created_at" : "2024-12-09T22:23:00.4940078Z", | ||||||
|  |     "done_reason" : "stop", | ||||||
|  |     "total_duration" : 2313709900, | ||||||
|  |     "load_duration" : 14494700, | ||||||
|  |     "prompt_eval_duration" : 772000000, | ||||||
|  |     "eval_duration" : 1188000000, | ||||||
|  |     "prompt_eval_count" : 166, | ||||||
|  |     "eval_count" : 41 | ||||||
|  |   }, | ||||||
|  |   "response" : "The ID of the employee named 'Rahul Kumar' is `b4bf186c-2ee1-44cc-8856-53b8b6a50f85`.", | ||||||
|  |   "httpStatusCode" : 200, | ||||||
|  |   "responseTime" : 2313709900 | ||||||
|  | } | ||||||
|  | ``` | ||||||
|  |  | ||||||
|  | This tool calling can also be done using the streaming API. | ||||||
|  |  | ||||||
|  | ### Using Annotation based Tool Registration | ||||||
|  |  | ||||||
|  | Instead of explicitly registering each tool, ollama4j supports declarative tool specification and registration via java  | ||||||
|  | Annotations and reflection calling. | ||||||
|  |  | ||||||
|  | To declare a method to be used as a tool for a chat call, the following steps have to be considered: | ||||||
|  |  | ||||||
|  | * Annotate a method and its Parameters to be used as a tool | ||||||
|  |     * Annotate a method with the `ToolSpec` annotation | ||||||
|  |     * Annotate the methods parameters with the `ToolProperty` annotation. Only the following datatypes are supported for now: | ||||||
|  |         * `java.lang.String` | ||||||
|  |         * `java.lang.Integer` | ||||||
|  |         * `java.lang.Boolean` | ||||||
|  |         * `java.math.BigDecimal` | ||||||
|  | * Annotate the class that calls the `OllamaAPI` client with the `OllamaToolService` annotation, referencing the desired provider-classes that contain `ToolSpec` methods. | ||||||
|  | * Before calling the `OllamaAPI` chat request, call the method `OllamaAPI.registerAnnotatedTools()` method to add tools to the chat. | ||||||
|  |  | ||||||
|  | #### Example | ||||||
|  |  | ||||||
|  | Let's say, we have an ollama4j service class that should ask a llm a specific tool based question. | ||||||
|  |  | ||||||
|  | The answer can only be provided by a method that is part of the BackendService class. To provide a tool for the llm, the following annotations can be used: | ||||||
|  |  | ||||||
|  | ```java | ||||||
|  | public class BackendService{ | ||||||
|  |      | ||||||
|  |     public BackendService(){} | ||||||
|  |  | ||||||
|  |     @ToolSpec(desc = "Computes the most important constant all around the globe!") | ||||||
|  |     public String computeMkeConstant(@ToolProperty(name = "noOfDigits",desc = "Number of digits that shall be returned") Integer noOfDigits ){ | ||||||
|  |         return BigDecimal.valueOf((long)(Math.random()*1000000L),noOfDigits).toString(); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | ``` | ||||||
|  |  | ||||||
|  | The caller API can then be written as: | ||||||
|  | ```java | ||||||
|  | import io.github.ollama4j.tools.annotations.OllamaToolService; | ||||||
|  |  | ||||||
|  | @OllamaToolService(providers = BackendService.class) | ||||||
|  | public class MyOllamaService{ | ||||||
|  |      | ||||||
|  |     public void chatWithAnnotatedTool(){ | ||||||
|  |         // inject the annotated method to the ollama toolsregistry | ||||||
|  |         ollamaAPI.registerAnnotatedTools(); | ||||||
|  |  | ||||||
|  |         OllamaChatRequest requestModel = builder | ||||||
|  |                 .withMessage(OllamaChatMessageRole.USER, | ||||||
|  |                         "Compute the most important constant in the world using 5 digits") | ||||||
|  |                 .build(); | ||||||
|  |  | ||||||
|  |         OllamaChatResult chatResult = ollamaAPI.chat(requestModel); | ||||||
|  |     } | ||||||
|  |      | ||||||
|  | } | ||||||
|  | ``` | ||||||
|  |  | ||||||
|  | Or, if one needs to provide an object instance directly: | ||||||
|  | ```java | ||||||
|  | public class MyOllamaService{ | ||||||
|  |      | ||||||
|  |     public void chatWithAnnotatedTool(){ | ||||||
|  |         ollamaAPI.registerAnnotatedTools(new BackendService()); | ||||||
|  |         OllamaChatRequest requestModel = builder | ||||||
|  |                 .withMessage(OllamaChatMessageRole.USER, | ||||||
|  |                         "Compute the most important constant in the world using 5 digits") | ||||||
|  |                 .build(); | ||||||
|  |  | ||||||
|  |         OllamaChatResult chatResult = ollamaAPI.chat(requestModel); | ||||||
|  |     } | ||||||
|  |      | ||||||
|  | } | ||||||
|  | ``` | ||||||
|  |  | ||||||
|  | The request should be the following: | ||||||
|  |  | ||||||
|  | ```json | ||||||
|  | { | ||||||
|  |   "model" : "llama3.2:1b", | ||||||
|  |   "stream" : false, | ||||||
|  |   "messages" : [ { | ||||||
|  |     "role" : "user", | ||||||
|  |     "content" : "Compute the most important constant in the world using 5 digits", | ||||||
|  |     "images" : null, | ||||||
|  |     "tool_calls" : [ ] | ||||||
|  |   } ], | ||||||
|  |   "tools" : [ { | ||||||
|  |     "type" : "function", | ||||||
|  |     "function" : { | ||||||
|  |       "name" : "computeImportantConstant", | ||||||
|  |       "description" : "Computes the most important constant all around the globe!", | ||||||
|  |       "parameters" : { | ||||||
|  |         "type" : "object", | ||||||
|  |         "properties" : { | ||||||
|  |           "noOfDigits" : { | ||||||
|  |             "type" : "java.lang.Integer", | ||||||
|  |             "description" : "Number of digits that shall be returned" | ||||||
|  |           } | ||||||
|  |         }, | ||||||
|  |         "required" : [ "noOfDigits" ] | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |   } ] | ||||||
|  | } | ||||||
|  | ``` | ||||||
|  |  | ||||||
|  | The result could be something like the following: | ||||||
|  |  | ||||||
|  | ```json | ||||||
|  | { | ||||||
|  |   "chatHistory" : [ { | ||||||
|  |     "role" : "user", | ||||||
|  |     "content" : "Compute the most important constant in the world using 5 digits", | ||||||
|  |     "images" : null, | ||||||
|  |     "tool_calls" : [ ] | ||||||
|  |   }, { | ||||||
|  |     "role" : "assistant", | ||||||
|  |     "content" : "", | ||||||
|  |     "images" : null, | ||||||
|  |     "tool_calls" : [ { | ||||||
|  |       "function" : { | ||||||
|  |         "name" : "computeImportantConstant", | ||||||
|  |         "arguments" : { | ||||||
|  |           "noOfDigits" : "5" | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |     } ] | ||||||
|  |   }, { | ||||||
|  |     "role" : "tool", | ||||||
|  |     "content" : "[TOOL_RESULTS]computeImportantConstant([noOfDigits]) : 1.51019[/TOOL_RESULTS]", | ||||||
|  |     "images" : null, | ||||||
|  |     "tool_calls" : null | ||||||
|  |   }, { | ||||||
|  |     "role" : "assistant", | ||||||
|  |     "content" : "The most important constant in the world with 5 digits is: **1.51019**", | ||||||
|  |     "images" : null, | ||||||
|  |     "tool_calls" : null | ||||||
|  |   } ], | ||||||
|  |   "responseModel" : { | ||||||
|  |     "model" : "llama3.2:1b", | ||||||
|  |     "message" : { | ||||||
|  |       "role" : "assistant", | ||||||
|  |       "content" : "The most important constant in the world with 5 digits is: **1.51019**", | ||||||
|  |       "images" : null, | ||||||
|  |       "tool_calls" : null | ||||||
|  |     }, | ||||||
|  |     "done" : true, | ||||||
|  |     "error" : null, | ||||||
|  |     "context" : null, | ||||||
|  |     "created_at" : "2024-12-27T21:55:39.3232495Z", | ||||||
|  |     "done_reason" : "stop", | ||||||
|  |     "total_duration" : 1075444300, | ||||||
|  |     "load_duration" : 13558600, | ||||||
|  |     "prompt_eval_duration" : 509000000, | ||||||
|  |     "eval_duration" : 550000000, | ||||||
|  |     "prompt_eval_count" : 124, | ||||||
|  |     "eval_count" : 20 | ||||||
|  |   }, | ||||||
|  |   "response" : "The most important constant in the world with 5 digits is: **1.51019**", | ||||||
|  |   "responseTime" : 1075444300, | ||||||
|  |   "httpStatusCode" : 200 | ||||||
|  | } | ||||||
|  | ``` | ||||||
|  |  | ||||||
|  | ### Potential Improvements | ||||||
|  |  | ||||||
| Instead of passing a map of args `Map<String, Object> arguments` to the tool functions, we could support passing | Instead of passing a map of args `Map<String, Object> arguments` to the tool functions, we could support passing | ||||||
| specific args separately with their data types. For example: | specific args separately with their data types. For example: | ||||||
|  |  | ||||||
|   | |||||||
| @@ -1,12 +1,12 @@ | |||||||
| --- | --- | ||||||
| sidebar_position: 4 | sidebar_position: 5 | ||||||
| --- | --- | ||||||
|  |  | ||||||
| # Create Model | # Create Model | ||||||
|  |  | ||||||
| This API lets you create a custom model on the Ollama server. | This API lets you create a custom model on the Ollama server. | ||||||
|  |  | ||||||
| ### Create a model from an existing Modelfile in the Ollama server | ### Create a custom model from an existing model in the Ollama server | ||||||
|  |  | ||||||
| ```java title="CreateModel.java" | ```java title="CreateModel.java" | ||||||
| import io.github.ollama4j.OllamaAPI; | import io.github.ollama4j.OllamaAPI; | ||||||
| @@ -19,144 +19,220 @@ public class CreateModel { | |||||||
|  |  | ||||||
|         OllamaAPI ollamaAPI = new OllamaAPI(host); |         OllamaAPI ollamaAPI = new OllamaAPI(host); | ||||||
|  |  | ||||||
|         ollamaAPI.createModelWithFilePath("mario", "/path/to/mario/modelfile/on/ollama-server"); |         ollamaAPI.createModel(CustomModelRequest.builder().model("mario").from("llama3.2:latest").system("You are Mario from Super Mario Bros.").build()); | ||||||
|     } |  | ||||||
| } |  | ||||||
| ``` |  | ||||||
|  |  | ||||||
| ### Create a model by passing the contents of Modelfile |  | ||||||
|  |  | ||||||
| ```java title="CreateModel.java" |  | ||||||
| public class CreateModel { |  | ||||||
|  |  | ||||||
|     public static void main(String[] args) { |  | ||||||
|  |  | ||||||
|         String host = "http://localhost:11434/"; |  | ||||||
|  |  | ||||||
|         OllamaAPI ollamaAPI = new OllamaAPI(host); |  | ||||||
|  |  | ||||||
|         ollamaAPI.createModelWithModelFileContents("mario", "FROM llama2\nSYSTEM You are mario from Super Mario Bros."); |  | ||||||
|     } |     } | ||||||
| } | } | ||||||
| ``` | ``` | ||||||
|  |  | ||||||
| Once created, you can see it when you use [list models](./list-models) API. | Once created, you can see it when you use [list models](./list-models) API. | ||||||
|  |  | ||||||
| ### Example of a `Modelfile` | [Read more](https://github.com/ollama/ollama/blob/main/docs/api.md#create-a-model) about custom model creation and the parameters available for model creation. | ||||||
|  |  | ||||||
| ``` | [//]: # () | ||||||
| FROM llama2 | [//]: # (### Example of a `Modelfile`) | ||||||
| # sets the temperature to 1 [higher is more creative, lower is more coherent] |  | ||||||
| PARAMETER temperature 1 |  | ||||||
| # sets the context window size to 4096, this controls how many tokens the LLM can use as context to generate the next token |  | ||||||
| PARAMETER num_ctx 4096 |  | ||||||
|  |  | ||||||
| # sets a custom system message to specify the behavior of the chat assistant | [//]: # () | ||||||
| SYSTEM You are Mario from super mario bros, acting as an assistant. | [//]: # (```) | ||||||
| ``` |  | ||||||
|  |  | ||||||
| ### Format of the `Modelfile` | [//]: # (FROM llama2) | ||||||
|  |  | ||||||
| ```modelfile | [//]: # (# sets the temperature to 1 [higher is more creative, lower is more coherent]) | ||||||
| # comment |  | ||||||
| INSTRUCTION arguments |  | ||||||
| ``` |  | ||||||
|  |  | ||||||
| | Instruction                         | Description                                                    | | [//]: # (PARAMETER temperature 1) | ||||||
| |-------------------------------------|----------------------------------------------------------------| |  | ||||||
| | [`FROM`](#from-required) (required) | Defines the base model to use.                                 | |  | ||||||
| | [`PARAMETER`](#parameter)           | Sets the parameters for how Ollama will run the model.         | |  | ||||||
| | [`TEMPLATE`](#template)             | The full prompt template to be sent to the model.              | |  | ||||||
| | [`SYSTEM`](#system)                 | Specifies the system message that will be set in the template. | |  | ||||||
| | [`ADAPTER`](#adapter)               | Defines the (Q)LoRA adapters to apply to the model.            | |  | ||||||
| | [`LICENSE`](#license)               | Specifies the legal license.                                   | |  | ||||||
|  |  | ||||||
| #### PARAMETER | [//]: # (# sets the context window size to 4096, this controls how many tokens the LLM can use as context to generate the next token) | ||||||
|  |  | ||||||
| The `PARAMETER` instruction defines a parameter that can be set when the model is run. | [//]: # (PARAMETER num_ctx 4096) | ||||||
|  |  | ||||||
| | Parameter      | Description                                                                                                                                                                                                                                             | Value Type | Example Usage        | | [//]: # () | ||||||
| |----------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|------------|----------------------| | [//]: # (# sets a custom system message to specify the behavior of the chat assistant) | ||||||
| | mirostat       | Enable Mirostat sampling for controlling perplexity. (default: 0, 0 = disabled, 1 = Mirostat, 2 = Mirostat 2.0)                                                                                                                                         | int        | mirostat 0           | |  | ||||||
| | mirostat_eta   | Influences how quickly the algorithm responds to feedback from the generated text. A lower learning rate will result in slower adjustments, while a higher learning rate will make the algorithm more responsive. (Default: 0.1)                        | float      | mirostat_eta 0.1     | |  | ||||||
| | mirostat_tau   | Controls the balance between coherence and diversity of the output. A lower value will result in more focused and coherent text. (Default: 5.0)                                                                                                         | float      | mirostat_tau 5.0     | |  | ||||||
| | num_ctx        | Sets the size of the context window used to generate the next token. (Default: 2048)                                                                                                                                                                    | int        | num_ctx 4096         | |  | ||||||
| | num_gqa        | The number of GQA groups in the transformer layer. Required for some models, for example it is 8 for llama2:70b                                                                                                                                         | int        | num_gqa 1            | |  | ||||||
| | num_gpu        | The number of layers to send to the GPU(s). On macOS it defaults to 1 to enable metal support, 0 to disable.                                                                                                                                            | int        | num_gpu 50           | |  | ||||||
| | num_thread     | Sets the number of threads to use during computation. By default, Ollama will detect this for optimal performance. It is recommended to set this value to the number of physical CPU cores your system has (as opposed to the logical number of cores). | int        | num_thread 8         | |  | ||||||
| | repeat_last_n  | Sets how far back for the model to look back to prevent repetition. (Default: 64, 0 = disabled, -1 = num_ctx)                                                                                                                                           | int        | repeat_last_n 64     | |  | ||||||
| | repeat_penalty | Sets how strongly to penalize repetitions. A higher value (e.g., 1.5) will penalize repetitions more strongly, while a lower value (e.g., 0.9) will be more lenient. (Default: 1.1)                                                                     | float      | repeat_penalty 1.1   | |  | ||||||
| | temperature    | The temperature of the model. Increasing the temperature will make the model answer more creatively. (Default: 0.8)                                                                                                                                     | float      | temperature 0.7      | |  | ||||||
| | seed           | Sets the random number seed to use for generation. Setting this to a specific number will make the model generate the same text for the same prompt. (Default: 0)                                                                                       | int        | seed 42              | |  | ||||||
| | stop           | Sets the stop sequences to use. When this pattern is encountered the LLM will stop generating text and return. Multiple stop patterns may be set by specifying multiple separate `stop` parameters in a modelfile.                                      | string     | stop "AI assistant:" | |  | ||||||
| | tfs_z          | Tail free sampling is used to reduce the impact of less probable tokens from the output. A higher value (e.g., 2.0) will reduce the impact more, while a value of 1.0 disables this setting. (default: 1)                                               | float      | tfs_z 1              | |  | ||||||
| | num_predict    | Maximum number of tokens to predict when generating text. (Default: 128, -1 = infinite generation, -2 = fill context)                                                                                                                                   | int        | num_predict 42       | |  | ||||||
| | top_k          | Reduces the probability of generating nonsense. A higher value (e.g. 100) will give more diverse answers, while a lower value (e.g. 10) will be more conservative. (Default: 40)                                                                        | int        | top_k 40             | |  | ||||||
| | top_p          | Works together with top-k. A higher value (e.g., 0.95) will lead to more diverse text, while a lower value (e.g., 0.5) will generate more focused and conservative text. (Default: 0.9)                                                                 | float      | top_p 0.9            | |  | ||||||
|  |  | ||||||
| #### TEMPLATE | [//]: # (SYSTEM You are Mario from super mario bros, acting as an assistant.) | ||||||
|  |  | ||||||
| `TEMPLATE` of the full prompt template to be passed into the model. It may include (optionally) a system message and a | [//]: # (```) | ||||||
| user's prompt. This is used to create a full custom prompt, and syntax may be model specific. You can usually find the |  | ||||||
| template for a given model in the readme for that model. |  | ||||||
|  |  | ||||||
| #### Template Variables | [//]: # () | ||||||
|  | [//]: # (### Format of the `Modelfile`) | ||||||
|  |  | ||||||
| | Variable        | Description                                                                                                   | | [//]: # () | ||||||
| |-----------------|---------------------------------------------------------------------------------------------------------------| | [//]: # (```modelfile) | ||||||
| | `{{ .System }}` | The system message used to specify custom behavior, this must also be set in the Modelfile as an instruction. | |  | ||||||
| | `{{ .Prompt }}` | The incoming prompt, this is not specified in the model file and will be set based on input.                  | |  | ||||||
| | `{{ .First }}`  | A boolean value used to render specific template information for the first generation of a session.           | |  | ||||||
|  |  | ||||||
| ```modelfile | [//]: # (# comment) | ||||||
| TEMPLATE """ |  | ||||||
| {{- if .First }} |  | ||||||
| ### System: |  | ||||||
| {{ .System }} |  | ||||||
| {{- end }} |  | ||||||
|  |  | ||||||
| ### User: | [//]: # (INSTRUCTION arguments) | ||||||
| {{ .Prompt }} |  | ||||||
|  |  | ||||||
| ### Response: | [//]: # (```) | ||||||
| """ |  | ||||||
|  |  | ||||||
| SYSTEM """<system message>""" | [//]: # () | ||||||
| ``` | [//]: # (| Instruction                         | Description                                                    |) | ||||||
|  |  | ||||||
| ### SYSTEM | [//]: # (|-------------------------------------|----------------------------------------------------------------|) | ||||||
|  |  | ||||||
| The `SYSTEM` instruction specifies the system message to be used in the template, if applicable. | [//]: # (| [`FROM`](#from-required) (required) | Defines the base model to use.                                 |) | ||||||
|  |  | ||||||
| ```modelfile | [//]: # (| [`PARAMETER`](#parameter)           | Sets the parameters for how Ollama will run the model.         |) | ||||||
| SYSTEM """<system message>""" |  | ||||||
| ``` |  | ||||||
|  |  | ||||||
| ### ADAPTER | [//]: # (| [`TEMPLATE`](#template)             | The full prompt template to be sent to the model.              |) | ||||||
|  |  | ||||||
| The `ADAPTER` instruction specifies the LoRA adapter to apply to the base model. The value of this instruction should be | [//]: # (| [`SYSTEM`](#system)                 | Specifies the system message that will be set in the template. |) | ||||||
| an absolute path or a path relative to the Modelfile and the file must be in a GGML file format. The adapter should be |  | ||||||
| tuned from the base model otherwise the behaviour is undefined. |  | ||||||
|  |  | ||||||
| ```modelfile | [//]: # (| [`ADAPTER`](#adapter)               | Defines the (Q)LoRA adapters to apply to the model.            |) | ||||||
| ADAPTER ./ollama-lora.bin |  | ||||||
| ``` |  | ||||||
|  |  | ||||||
| ### LICENSE | [//]: # (| [`LICENSE`](#license)               | Specifies the legal license.                                   |) | ||||||
|  |  | ||||||
| The `LICENSE` instruction allows you to specify the legal license under which the model used with this Modelfile is | [//]: # () | ||||||
| shared or distributed. | [//]: # (#### PARAMETER) | ||||||
|  |  | ||||||
| ```modelfile | [//]: # () | ||||||
| LICENSE """ | [//]: # (The `PARAMETER` instruction defines a parameter that can be set when the model is run.) | ||||||
| <license text> |  | ||||||
| """ |  | ||||||
| ``` |  | ||||||
|  |  | ||||||
| ## Notes | [//]: # () | ||||||
|  | [//]: # (| Parameter      | Description                                                                                                                                                                                                                                             | Value Type | Example Usage        |) | ||||||
|  |  | ||||||
| - the **`Modelfile` is not case sensitive**. In the examples, uppercase instructions are used to make it easier to | [//]: # (|----------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|------------|----------------------|) | ||||||
|   distinguish it from arguments. |  | ||||||
| - Instructions can be in any order. In the examples, the `FROM` instruction is first to keep it easily readable. |  | ||||||
|  |  | ||||||
| Read more about Modelfile: https://github.com/jmorganca/ollama/blob/main/docs/modelfile.md | [//]: # (| mirostat       | Enable Mirostat sampling for controlling perplexity. (default: 0, 0 = disabled, 1 = Mirostat, 2 = Mirostat 2.0)                                                                                                                                         | int        | mirostat 0           |) | ||||||
|  |  | ||||||
|  | [//]: # (| mirostat_eta   | Influences how quickly the algorithm responds to feedback from the generated text. A lower learning rate will result in slower adjustments, while a higher learning rate will make the algorithm more responsive. (Default: 0.1)                        | float      | mirostat_eta 0.1     |) | ||||||
|  |  | ||||||
|  | [//]: # (| mirostat_tau   | Controls the balance between coherence and diversity of the output. A lower value will result in more focused and coherent text. (Default: 5.0)                                                                                                         | float      | mirostat_tau 5.0     |) | ||||||
|  |  | ||||||
|  | [//]: # (| num_ctx        | Sets the size of the context window used to generate the next token. (Default: 2048)                                                                                                                                                                    | int        | num_ctx 4096         |) | ||||||
|  |  | ||||||
|  | [//]: # (| num_gqa        | The number of GQA groups in the transformer layer. Required for some models, for example it is 8 for llama2:70b                                                                                                                                         | int        | num_gqa 1            |) | ||||||
|  |  | ||||||
|  | [//]: # (| num_gpu        | The number of layers to send to the GPU(s). On macOS it defaults to 1 to enable metal support, 0 to disable.                                                                                                                                            | int        | num_gpu 50           |) | ||||||
|  |  | ||||||
|  | [//]: # (| num_thread     | Sets the number of threads to use during computation. By default, Ollama will detect this for optimal performance. It is recommended to set this value to the number of physical CPU cores your system has (as opposed to the logical number of cores). | int        | num_thread 8         |) | ||||||
|  |  | ||||||
|  | [//]: # (| repeat_last_n  | Sets how far back for the model to look back to prevent repetition. (Default: 64, 0 = disabled, -1 = num_ctx)                                                                                                                                           | int        | repeat_last_n 64     |) | ||||||
|  |  | ||||||
|  | [//]: # (| repeat_penalty | Sets how strongly to penalize repetitions. A higher value (e.g., 1.5) will penalize repetitions more strongly, while a lower value (e.g., 0.9) will be more lenient. (Default: 1.1)                                                                     | float      | repeat_penalty 1.1   |) | ||||||
|  |  | ||||||
|  | [//]: # (| temperature    | The temperature of the model. Increasing the temperature will make the model answer more creatively. (Default: 0.8)                                                                                                                                     | float      | temperature 0.7      |) | ||||||
|  |  | ||||||
|  | [//]: # (| seed           | Sets the random number seed to use for generation. Setting this to a specific number will make the model generate the same text for the same prompt. (Default: 0)                                                                                       | int        | seed 42              |) | ||||||
|  |  | ||||||
|  | [//]: # (| stop           | Sets the stop sequences to use. When this pattern is encountered the LLM will stop generating text and return. Multiple stop patterns may be set by specifying multiple separate `stop` parameters in a modelfile.                                      | string     | stop "AI assistant:" |) | ||||||
|  |  | ||||||
|  | [//]: # (| tfs_z          | Tail free sampling is used to reduce the impact of less probable tokens from the output. A higher value (e.g., 2.0) will reduce the impact more, while a value of 1.0 disables this setting. (default: 1)                                               | float      | tfs_z 1              |) | ||||||
|  |  | ||||||
|  | [//]: # (| num_predict    | Maximum number of tokens to predict when generating text. (Default: 128, -1 = infinite generation, -2 = fill context)                                                                                                                                   | int        | num_predict 42       |) | ||||||
|  |  | ||||||
|  | [//]: # (| top_k          | Reduces the probability of generating nonsense. A higher value (e.g. 100) will give more diverse answers, while a lower value (e.g. 10) will be more conservative. (Default: 40)                                                                        | int        | top_k 40             |) | ||||||
|  |  | ||||||
|  | [//]: # (| top_p          | Works together with top-k. A higher value (e.g., 0.95) will lead to more diverse text, while a lower value (e.g., 0.5) will generate more focused and conservative text. (Default: 0.9)                                                                 | float      | top_p 0.9            |) | ||||||
|  |  | ||||||
|  | [//]: # () | ||||||
|  | [//]: # (#### TEMPLATE) | ||||||
|  |  | ||||||
|  | [//]: # () | ||||||
|  | [//]: # (`TEMPLATE` of the full prompt template to be passed into the model. It may include (optionally) a system message and a) | ||||||
|  |  | ||||||
|  | [//]: # (user's prompt. This is used to create a full custom prompt, and syntax may be model specific. You can usually find the) | ||||||
|  |  | ||||||
|  | [//]: # (template for a given model in the readme for that model.) | ||||||
|  |  | ||||||
|  | [//]: # () | ||||||
|  | [//]: # (#### Template Variables) | ||||||
|  |  | ||||||
|  | [//]: # () | ||||||
|  | [//]: # (| Variable        | Description                                                                                                   |) | ||||||
|  |  | ||||||
|  | [//]: # (|-----------------|---------------------------------------------------------------------------------------------------------------|) | ||||||
|  |  | ||||||
|  | [//]: # (| `{{ .System }}` | The system message used to specify custom behavior, this must also be set in the Modelfile as an instruction. |) | ||||||
|  |  | ||||||
|  | [//]: # (| `{{ .Prompt }}` | The incoming prompt, this is not specified in the model file and will be set based on input.                  |) | ||||||
|  |  | ||||||
|  | [//]: # (| `{{ .First }}`  | A boolean value used to render specific template information for the first generation of a session.           |) | ||||||
|  |  | ||||||
|  | [//]: # () | ||||||
|  | [//]: # (```modelfile) | ||||||
|  |  | ||||||
|  | [//]: # (TEMPLATE """) | ||||||
|  |  | ||||||
|  | [//]: # ({{- if .First }}) | ||||||
|  |  | ||||||
|  | [//]: # (### System:) | ||||||
|  |  | ||||||
|  | [//]: # ({{ .System }}) | ||||||
|  |  | ||||||
|  | [//]: # ({{- end }}) | ||||||
|  |  | ||||||
|  | [//]: # () | ||||||
|  | [//]: # (### User:) | ||||||
|  |  | ||||||
|  | [//]: # ({{ .Prompt }}) | ||||||
|  |  | ||||||
|  | [//]: # () | ||||||
|  | [//]: # (### Response:) | ||||||
|  |  | ||||||
|  | [//]: # (""") | ||||||
|  |  | ||||||
|  | [//]: # () | ||||||
|  | [//]: # (SYSTEM """<system message>""") | ||||||
|  |  | ||||||
|  | [//]: # (```) | ||||||
|  |  | ||||||
|  | [//]: # () | ||||||
|  | [//]: # (### SYSTEM) | ||||||
|  |  | ||||||
|  | [//]: # () | ||||||
|  | [//]: # (The `SYSTEM` instruction specifies the system message to be used in the template, if applicable.) | ||||||
|  |  | ||||||
|  | [//]: # () | ||||||
|  | [//]: # (```modelfile) | ||||||
|  |  | ||||||
|  | [//]: # (SYSTEM """<system message>""") | ||||||
|  |  | ||||||
|  | [//]: # (```) | ||||||
|  |  | ||||||
|  | [//]: # () | ||||||
|  | [//]: # (### ADAPTER) | ||||||
|  |  | ||||||
|  | [//]: # () | ||||||
|  | [//]: # (The `ADAPTER` instruction specifies the LoRA adapter to apply to the base model. The value of this instruction should be) | ||||||
|  |  | ||||||
|  | [//]: # (an absolute path or a path relative to the Modelfile and the file must be in a GGML file format. The adapter should be) | ||||||
|  |  | ||||||
|  | [//]: # (tuned from the base model otherwise the behaviour is undefined.) | ||||||
|  |  | ||||||
|  | [//]: # () | ||||||
|  | [//]: # (```modelfile) | ||||||
|  |  | ||||||
|  | [//]: # (ADAPTER ./ollama-lora.bin) | ||||||
|  |  | ||||||
|  | [//]: # (```) | ||||||
|  |  | ||||||
|  | [//]: # () | ||||||
|  | [//]: # (### LICENSE) | ||||||
|  |  | ||||||
|  | [//]: # () | ||||||
|  | [//]: # (The `LICENSE` instruction allows you to specify the legal license under which the model used with this Modelfile is) | ||||||
|  |  | ||||||
|  | [//]: # (shared or distributed.) | ||||||
|  |  | ||||||
|  | [//]: # () | ||||||
|  | [//]: # (```modelfile) | ||||||
|  |  | ||||||
|  | [//]: # (LICENSE """) | ||||||
|  |  | ||||||
|  | [//]: # (<license text>) | ||||||
|  |  | ||||||
|  | [//]: # (""") | ||||||
|  |  | ||||||
|  | [//]: # (```) | ||||||
|  |  | ||||||
|  | [//]: # () | ||||||
|  | [//]: # (## Notes) | ||||||
|  |  | ||||||
|  | [//]: # () | ||||||
|  | [//]: # (- the **`Modelfile` is not case sensitive**. In the examples, uppercase instructions are used to make it easier to) | ||||||
|  |  | ||||||
|  | [//]: # (  distinguish it from arguments.) | ||||||
|  |  | ||||||
|  | [//]: # (- Instructions can be in any order. In the examples, the `FROM` instruction is first to keep it easily readable.) | ||||||
|  |  | ||||||
|  | [//]: # () | ||||||
|  | [//]: # (Read more about Modelfile: https://github.com/jmorganca/ollama/blob/main/docs/modelfile.md) | ||||||
| @@ -1,5 +1,5 @@ | |||||||
| --- | --- | ||||||
| sidebar_position: 5 | sidebar_position: 6 | ||||||
| --- | --- | ||||||
|  |  | ||||||
| # Delete Model | # Delete Model | ||||||
|   | |||||||
| @@ -1,5 +1,5 @@ | |||||||
| --- | --- | ||||||
| sidebar_position: 3 | sidebar_position: 4 | ||||||
| --- | --- | ||||||
|  |  | ||||||
| # Get Model Details | # Get Model Details | ||||||
|   | |||||||
							
								
								
									
										133
									
								
								docs/docs/apis-model-management/list-library-models.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										133
									
								
								docs/docs/apis-model-management/list-library-models.md
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,133 @@ | |||||||
|  | --- | ||||||
|  | sidebar_position: 1 | ||||||
|  | --- | ||||||
|  |  | ||||||
|  | # Models from Ollama Library | ||||||
|  |  | ||||||
|  | These API retrieves a list of models directly from the Ollama library. | ||||||
|  |  | ||||||
|  | ### List Models from Ollama Library | ||||||
|  |  | ||||||
|  | This API fetches available models from the Ollama library page, including details such as the model's name, pull count, | ||||||
|  | popular tags, tag count, and the last update time. | ||||||
|  |  | ||||||
|  | ```java title="ListLibraryModels.java" | ||||||
|  | import io.github.ollama4j.OllamaAPI; | ||||||
|  | import io.github.ollama4j.models.response.LibraryModel; | ||||||
|  |  | ||||||
|  | import java.util.List; | ||||||
|  |  | ||||||
|  | public class Main { | ||||||
|  |  | ||||||
|  |     public static void main(String[] args) { | ||||||
|  |  | ||||||
|  |         String host = "http://localhost:11434/"; | ||||||
|  |  | ||||||
|  |         OllamaAPI ollamaAPI = new OllamaAPI(host); | ||||||
|  |  | ||||||
|  |         List<LibraryModel> libraryModels = ollamaAPI.listModelsFromLibrary(); | ||||||
|  |  | ||||||
|  |         System.out.println(libraryModels); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | ``` | ||||||
|  |  | ||||||
|  | The following is the sample output: | ||||||
|  |  | ||||||
|  | ``` | ||||||
|  | [ | ||||||
|  |     LibraryModel(name=llama3.2-vision, description=Llama 3.2 Vision is a collection of instruction-tuned image reasoning generative models in 11B and 90B sizes., pullCount=21.1K, totalTags=9, popularTags=[vision, 11b, 90b], lastUpdated=yesterday),  | ||||||
|  |     LibraryModel(name=llama3.2, description=Meta's Llama 3.2 goes small with 1B and 3B models., pullCount=2.4M, totalTags=63, popularTags=[tools, 1b, 3b], lastUpdated=6 weeks ago) | ||||||
|  | ] | ||||||
|  | ``` | ||||||
|  |  | ||||||
|  | ### Get Tags of a Library Model | ||||||
|  |  | ||||||
|  | This API Fetches the tags associated with a specific model from Ollama library. | ||||||
|  |  | ||||||
|  | ```java title="GetLibraryModelTags.java" | ||||||
|  | import io.github.ollama4j.OllamaAPI; | ||||||
|  | import io.github.ollama4j.models.response.LibraryModel; | ||||||
|  | import io.github.ollama4j.models.response.LibraryModelDetail; | ||||||
|  |  | ||||||
|  | public class Main { | ||||||
|  |  | ||||||
|  |     public static void main(String[] args) { | ||||||
|  |  | ||||||
|  |         String host = "http://localhost:11434/"; | ||||||
|  |  | ||||||
|  |         OllamaAPI ollamaAPI = new OllamaAPI(host); | ||||||
|  |  | ||||||
|  |         List<LibraryModel> libraryModels = ollamaAPI.listModelsFromLibrary(); | ||||||
|  |  | ||||||
|  |         LibraryModelDetail libraryModelDetail = ollamaAPI.getLibraryModelDetails(libraryModels.get(0)); | ||||||
|  |  | ||||||
|  |         System.out.println(libraryModelDetail); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | ``` | ||||||
|  |  | ||||||
|  | The following is the sample output: | ||||||
|  |  | ||||||
|  | ``` | ||||||
|  | LibraryModelDetail( | ||||||
|  |   model=LibraryModel(name=llama3.2-vision, description=Llama 3.2 Vision is a collection of instruction-tuned image reasoning generative models in 11B and 90B sizes., pullCount=21.1K, totalTags=9, popularTags=[vision, 11b, 90b], lastUpdated=yesterday),  | ||||||
|  |   tags=[ | ||||||
|  |         LibraryModelTag(name=llama3.2-vision, tag=latest, size=7.9GB, lastUpdated=yesterday),  | ||||||
|  |         LibraryModelTag(name=llama3.2-vision, tag=11b, size=7.9GB, lastUpdated=yesterday),  | ||||||
|  |         LibraryModelTag(name=llama3.2-vision, tag=90b, size=55GB, lastUpdated=yesterday) | ||||||
|  |     ] | ||||||
|  | ) | ||||||
|  | ``` | ||||||
|  |  | ||||||
|  | ### Find a model from Ollama library | ||||||
|  |  | ||||||
|  | This API finds a specific model using model `name` and `tag` from Ollama library. | ||||||
|  |  | ||||||
|  | ```java title="FindLibraryModel.java" | ||||||
|  | import io.github.ollama4j.OllamaAPI; | ||||||
|  | import io.github.ollama4j.models.response.LibraryModelTag; | ||||||
|  |  | ||||||
|  | public class Main { | ||||||
|  |  | ||||||
|  |     public static void main(String[] args) { | ||||||
|  |  | ||||||
|  |         String host = "http://localhost:11434/"; | ||||||
|  |  | ||||||
|  |         OllamaAPI ollamaAPI = new OllamaAPI(host); | ||||||
|  |  | ||||||
|  |         LibraryModelTag libraryModelTag = ollamaAPI.findModelTagFromLibrary("qwen2.5", "7b"); | ||||||
|  |  | ||||||
|  |         System.out.println(libraryModelTag); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | ``` | ||||||
|  |  | ||||||
|  | The following is the sample output: | ||||||
|  |  | ||||||
|  | ``` | ||||||
|  | LibraryModelTag(name=qwen2.5, tag=7b, size=4.7GB, lastUpdated=7 weeks ago) | ||||||
|  | ``` | ||||||
|  |  | ||||||
|  | ### Pull model using `LibraryModelTag` | ||||||
|  |  | ||||||
|  | You can use `LibraryModelTag` to pull models into Ollama server. | ||||||
|  |  | ||||||
|  | ```java title="PullLibraryModelTags.java" | ||||||
|  | import io.github.ollama4j.OllamaAPI; | ||||||
|  | import io.github.ollama4j.models.response.LibraryModelTag; | ||||||
|  |  | ||||||
|  | public class Main { | ||||||
|  |  | ||||||
|  |     public static void main(String[] args) { | ||||||
|  |  | ||||||
|  |         String host = "http://localhost:11434/"; | ||||||
|  |  | ||||||
|  |         OllamaAPI ollamaAPI = new OllamaAPI(host); | ||||||
|  |  | ||||||
|  |         LibraryModelTag libraryModelTag = ollamaAPI.findModelTagFromLibrary("qwen2.5", "7b"); | ||||||
|  |  | ||||||
|  |         ollamaAPI.pullModel(libraryModelTag); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | ``` | ||||||
| @@ -1,10 +1,10 @@ | |||||||
| --- | --- | ||||||
| sidebar_position: 1 | sidebar_position: 2 | ||||||
| --- | --- | ||||||
|  |  | ||||||
| # List Models | # List Local Models | ||||||
|  |  | ||||||
| This API lets you list available models on the Ollama server. | This API lets you list downloaded/available models on the Ollama server. | ||||||
|  |  | ||||||
| ```java title="ListModels.java" | ```java title="ListModels.java" | ||||||
| import io.github.ollama4j.OllamaAPI; | import io.github.ollama4j.OllamaAPI; | ||||||
|   | |||||||
| @@ -1,5 +1,5 @@ | |||||||
| --- | --- | ||||||
| sidebar_position: 2 | sidebar_position: 3 | ||||||
| --- | --- | ||||||
|  |  | ||||||
| # Pull Model | # Pull Model | ||||||
| @@ -24,3 +24,11 @@ public class Main { | |||||||
| ``` | ``` | ||||||
|  |  | ||||||
| Once downloaded, you can see them when you use [list models](./list-models) API. | Once downloaded, you can see them when you use [list models](./list-models) API. | ||||||
|  |  | ||||||
|  | :::info | ||||||
|  |  | ||||||
|  | You can even pull models using Ollama model library APIs. This looks up the models directly on the Ollama model library page. Refer | ||||||
|  | to [this](./list-library-models#pull-model-using-librarymodeltag). | ||||||
|  |  | ||||||
|  | ::: | ||||||
|  |  | ||||||
|   | |||||||
							
								
								
									
										413
									
								
								docs/package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										413
									
								
								docs/package-lock.json
									
									
									
										generated
									
									
									
								
							| @@ -3383,24 +3383,6 @@ | |||||||
|         "@types/ms": "*" |         "@types/ms": "*" | ||||||
|       } |       } | ||||||
|     }, |     }, | ||||||
|     "node_modules/@types/eslint": { |  | ||||||
|       "version": "8.56.0", |  | ||||||
|       "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.56.0.tgz", |  | ||||||
|       "integrity": "sha512-FlsN0p4FhuYRjIxpbdXovvHQhtlG05O1GG/RNWvdAxTboR438IOTwmrY/vLA+Xfgg06BTkP045M3vpFwTMv1dg==", |  | ||||||
|       "dependencies": { |  | ||||||
|         "@types/estree": "*", |  | ||||||
|         "@types/json-schema": "*" |  | ||||||
|       } |  | ||||||
|     }, |  | ||||||
|     "node_modules/@types/eslint-scope": { |  | ||||||
|       "version": "3.7.7", |  | ||||||
|       "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.7.tgz", |  | ||||||
|       "integrity": "sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg==", |  | ||||||
|       "dependencies": { |  | ||||||
|         "@types/eslint": "*", |  | ||||||
|         "@types/estree": "*" |  | ||||||
|       } |  | ||||||
|     }, |  | ||||||
|     "node_modules/@types/estree": { |     "node_modules/@types/estree": { | ||||||
|       "version": "1.0.5", |       "version": "1.0.5", | ||||||
|       "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz", |       "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz", | ||||||
| @@ -3691,9 +3673,10 @@ | |||||||
|       "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==" |       "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==" | ||||||
|     }, |     }, | ||||||
|     "node_modules/@webassemblyjs/ast": { |     "node_modules/@webassemblyjs/ast": { | ||||||
|       "version": "1.11.6", |       "version": "1.12.1", | ||||||
|       "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.6.tgz", |       "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.12.1.tgz", | ||||||
|       "integrity": "sha512-IN1xI7PwOvLPgjcf180gC1bqn3q/QaOCwYUahIOhbYUu8KA/3tw2RT/T0Gidi1l7Hhj5D/INhJxiICObqpMu4Q==", |       "integrity": "sha512-EKfMUOPRRUTy5UII4qJDGPpqfwjOmZ5jeGFwid9mnoqIFK+e0vqoi1qH56JpmZSzEL53jKnNzScdmftJyG5xWg==", | ||||||
|  |       "license": "MIT", | ||||||
|       "dependencies": { |       "dependencies": { | ||||||
|         "@webassemblyjs/helper-numbers": "1.11.6", |         "@webassemblyjs/helper-numbers": "1.11.6", | ||||||
|         "@webassemblyjs/helper-wasm-bytecode": "1.11.6" |         "@webassemblyjs/helper-wasm-bytecode": "1.11.6" | ||||||
| @@ -3702,22 +3685,26 @@ | |||||||
|     "node_modules/@webassemblyjs/floating-point-hex-parser": { |     "node_modules/@webassemblyjs/floating-point-hex-parser": { | ||||||
|       "version": "1.11.6", |       "version": "1.11.6", | ||||||
|       "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.6.tgz", |       "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.6.tgz", | ||||||
|       "integrity": "sha512-ejAj9hfRJ2XMsNHk/v6Fu2dGS+i4UaXBXGemOfQ/JfQ6mdQg/WXtwleQRLLS4OvfDhv8rYnVwH27YJLMyYsxhw==" |       "integrity": "sha512-ejAj9hfRJ2XMsNHk/v6Fu2dGS+i4UaXBXGemOfQ/JfQ6mdQg/WXtwleQRLLS4OvfDhv8rYnVwH27YJLMyYsxhw==", | ||||||
|  |       "license": "MIT" | ||||||
|     }, |     }, | ||||||
|     "node_modules/@webassemblyjs/helper-api-error": { |     "node_modules/@webassemblyjs/helper-api-error": { | ||||||
|       "version": "1.11.6", |       "version": "1.11.6", | ||||||
|       "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.6.tgz", |       "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.6.tgz", | ||||||
|       "integrity": "sha512-o0YkoP4pVu4rN8aTJgAyj9hC2Sv5UlkzCHhxqWj8butaLvnpdc2jOwh4ewE6CX0txSfLn/UYaV/pheS2Txg//Q==" |       "integrity": "sha512-o0YkoP4pVu4rN8aTJgAyj9hC2Sv5UlkzCHhxqWj8butaLvnpdc2jOwh4ewE6CX0txSfLn/UYaV/pheS2Txg//Q==", | ||||||
|  |       "license": "MIT" | ||||||
|     }, |     }, | ||||||
|     "node_modules/@webassemblyjs/helper-buffer": { |     "node_modules/@webassemblyjs/helper-buffer": { | ||||||
|       "version": "1.11.6", |       "version": "1.12.1", | ||||||
|       "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.6.tgz", |       "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.12.1.tgz", | ||||||
|       "integrity": "sha512-z3nFzdcp1mb8nEOFFk8DrYLpHvhKC3grJD2ardfKOzmbmJvEf/tPIqCY+sNcwZIY8ZD7IkB2l7/pqhUhqm7hLA==" |       "integrity": "sha512-nzJwQw99DNDKr9BVCOZcLuJJUlqkJh+kVzVl6Fmq/tI5ZtEyWT1KZMyOXltXLZJmDtvLCDgwsyrkohEtopTXCw==", | ||||||
|  |       "license": "MIT" | ||||||
|     }, |     }, | ||||||
|     "node_modules/@webassemblyjs/helper-numbers": { |     "node_modules/@webassemblyjs/helper-numbers": { | ||||||
|       "version": "1.11.6", |       "version": "1.11.6", | ||||||
|       "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.6.tgz", |       "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.6.tgz", | ||||||
|       "integrity": "sha512-vUIhZ8LZoIWHBohiEObxVm6hwP034jwmc9kuq5GdHZH0wiLVLIPcMCdpJzG4C11cHoQ25TFIQj9kaVADVX7N3g==", |       "integrity": "sha512-vUIhZ8LZoIWHBohiEObxVm6hwP034jwmc9kuq5GdHZH0wiLVLIPcMCdpJzG4C11cHoQ25TFIQj9kaVADVX7N3g==", | ||||||
|  |       "license": "MIT", | ||||||
|       "dependencies": { |       "dependencies": { | ||||||
|         "@webassemblyjs/floating-point-hex-parser": "1.11.6", |         "@webassemblyjs/floating-point-hex-parser": "1.11.6", | ||||||
|         "@webassemblyjs/helper-api-error": "1.11.6", |         "@webassemblyjs/helper-api-error": "1.11.6", | ||||||
| @@ -3727,23 +3714,26 @@ | |||||||
|     "node_modules/@webassemblyjs/helper-wasm-bytecode": { |     "node_modules/@webassemblyjs/helper-wasm-bytecode": { | ||||||
|       "version": "1.11.6", |       "version": "1.11.6", | ||||||
|       "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.6.tgz", |       "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.6.tgz", | ||||||
|       "integrity": "sha512-sFFHKwcmBprO9e7Icf0+gddyWYDViL8bpPjJJl0WHxCdETktXdmtWLGVzoHbqUcY4Be1LkNfwTmXOJUFZYSJdA==" |       "integrity": "sha512-sFFHKwcmBprO9e7Icf0+gddyWYDViL8bpPjJJl0WHxCdETktXdmtWLGVzoHbqUcY4Be1LkNfwTmXOJUFZYSJdA==", | ||||||
|  |       "license": "MIT" | ||||||
|     }, |     }, | ||||||
|     "node_modules/@webassemblyjs/helper-wasm-section": { |     "node_modules/@webassemblyjs/helper-wasm-section": { | ||||||
|       "version": "1.11.6", |       "version": "1.12.1", | ||||||
|       "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.6.tgz", |       "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.12.1.tgz", | ||||||
|       "integrity": "sha512-LPpZbSOwTpEC2cgn4hTydySy1Ke+XEu+ETXuoyvuyezHO3Kjdu90KK95Sh9xTbmjrCsUwvWwCOQQNta37VrS9g==", |       "integrity": "sha512-Jif4vfB6FJlUlSbgEMHUyk1j234GTNG9dBJ4XJdOySoj518Xj0oGsNi59cUQF4RRMS9ouBUxDDdyBVfPTypa5g==", | ||||||
|  |       "license": "MIT", | ||||||
|       "dependencies": { |       "dependencies": { | ||||||
|         "@webassemblyjs/ast": "1.11.6", |         "@webassemblyjs/ast": "1.12.1", | ||||||
|         "@webassemblyjs/helper-buffer": "1.11.6", |         "@webassemblyjs/helper-buffer": "1.12.1", | ||||||
|         "@webassemblyjs/helper-wasm-bytecode": "1.11.6", |         "@webassemblyjs/helper-wasm-bytecode": "1.11.6", | ||||||
|         "@webassemblyjs/wasm-gen": "1.11.6" |         "@webassemblyjs/wasm-gen": "1.12.1" | ||||||
|       } |       } | ||||||
|     }, |     }, | ||||||
|     "node_modules/@webassemblyjs/ieee754": { |     "node_modules/@webassemblyjs/ieee754": { | ||||||
|       "version": "1.11.6", |       "version": "1.11.6", | ||||||
|       "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.11.6.tgz", |       "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.11.6.tgz", | ||||||
|       "integrity": "sha512-LM4p2csPNvbij6U1f19v6WR56QZ8JcHg3QIJTlSwzFcmx6WSORicYj6I63f9yU1kEUtrpG+kjkiIAkevHpDXrg==", |       "integrity": "sha512-LM4p2csPNvbij6U1f19v6WR56QZ8JcHg3QIJTlSwzFcmx6WSORicYj6I63f9yU1kEUtrpG+kjkiIAkevHpDXrg==", | ||||||
|  |       "license": "MIT", | ||||||
|       "dependencies": { |       "dependencies": { | ||||||
|         "@xtuc/ieee754": "^1.2.0" |         "@xtuc/ieee754": "^1.2.0" | ||||||
|       } |       } | ||||||
| @@ -3752,6 +3742,7 @@ | |||||||
|       "version": "1.11.6", |       "version": "1.11.6", | ||||||
|       "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.11.6.tgz", |       "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.11.6.tgz", | ||||||
|       "integrity": "sha512-m7a0FhE67DQXgouf1tbN5XQcdWoNgaAuoULHIfGFIEVKA6tu/edls6XnIlkmS6FrXAquJRPni3ZZKjw6FSPjPQ==", |       "integrity": "sha512-m7a0FhE67DQXgouf1tbN5XQcdWoNgaAuoULHIfGFIEVKA6tu/edls6XnIlkmS6FrXAquJRPni3ZZKjw6FSPjPQ==", | ||||||
|  |       "license": "Apache-2.0", | ||||||
|       "dependencies": { |       "dependencies": { | ||||||
|         "@xtuc/long": "4.2.2" |         "@xtuc/long": "4.2.2" | ||||||
|       } |       } | ||||||
| @@ -3759,29 +3750,32 @@ | |||||||
|     "node_modules/@webassemblyjs/utf8": { |     "node_modules/@webassemblyjs/utf8": { | ||||||
|       "version": "1.11.6", |       "version": "1.11.6", | ||||||
|       "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.11.6.tgz", |       "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.11.6.tgz", | ||||||
|       "integrity": "sha512-vtXf2wTQ3+up9Zsg8sa2yWiQpzSsMyXj0qViVP6xKGCUT8p8YJ6HqI7l5eCnWx1T/FYdsv07HQs2wTFbbof/RA==" |       "integrity": "sha512-vtXf2wTQ3+up9Zsg8sa2yWiQpzSsMyXj0qViVP6xKGCUT8p8YJ6HqI7l5eCnWx1T/FYdsv07HQs2wTFbbof/RA==", | ||||||
|  |       "license": "MIT" | ||||||
|     }, |     }, | ||||||
|     "node_modules/@webassemblyjs/wasm-edit": { |     "node_modules/@webassemblyjs/wasm-edit": { | ||||||
|       "version": "1.11.6", |       "version": "1.12.1", | ||||||
|       "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.6.tgz", |       "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.12.1.tgz", | ||||||
|       "integrity": "sha512-Ybn2I6fnfIGuCR+Faaz7YcvtBKxvoLV3Lebn1tM4o/IAJzmi9AWYIPWpyBfU8cC+JxAO57bk4+zdsTjJR+VTOw==", |       "integrity": "sha512-1DuwbVvADvS5mGnXbE+c9NfA8QRcZ6iKquqjjmR10k6o+zzsRVesil54DKexiowcFCPdr/Q0qaMgB01+SQ1u6g==", | ||||||
|  |       "license": "MIT", | ||||||
|       "dependencies": { |       "dependencies": { | ||||||
|         "@webassemblyjs/ast": "1.11.6", |         "@webassemblyjs/ast": "1.12.1", | ||||||
|         "@webassemblyjs/helper-buffer": "1.11.6", |         "@webassemblyjs/helper-buffer": "1.12.1", | ||||||
|         "@webassemblyjs/helper-wasm-bytecode": "1.11.6", |         "@webassemblyjs/helper-wasm-bytecode": "1.11.6", | ||||||
|         "@webassemblyjs/helper-wasm-section": "1.11.6", |         "@webassemblyjs/helper-wasm-section": "1.12.1", | ||||||
|         "@webassemblyjs/wasm-gen": "1.11.6", |         "@webassemblyjs/wasm-gen": "1.12.1", | ||||||
|         "@webassemblyjs/wasm-opt": "1.11.6", |         "@webassemblyjs/wasm-opt": "1.12.1", | ||||||
|         "@webassemblyjs/wasm-parser": "1.11.6", |         "@webassemblyjs/wasm-parser": "1.12.1", | ||||||
|         "@webassemblyjs/wast-printer": "1.11.6" |         "@webassemblyjs/wast-printer": "1.12.1" | ||||||
|       } |       } | ||||||
|     }, |     }, | ||||||
|     "node_modules/@webassemblyjs/wasm-gen": { |     "node_modules/@webassemblyjs/wasm-gen": { | ||||||
|       "version": "1.11.6", |       "version": "1.12.1", | ||||||
|       "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.6.tgz", |       "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.12.1.tgz", | ||||||
|       "integrity": "sha512-3XOqkZP/y6B4F0PBAXvI1/bky7GryoogUtfwExeP/v7Nzwo1QLcq5oQmpKlftZLbT+ERUOAZVQjuNVak6UXjPA==", |       "integrity": "sha512-TDq4Ojh9fcohAw6OIMXqiIcTq5KUXTGRkVxbSo1hQnSy6lAM5GSdfwWeSxpAo0YzgsgF182E/U0mDNhuA0tW7w==", | ||||||
|  |       "license": "MIT", | ||||||
|       "dependencies": { |       "dependencies": { | ||||||
|         "@webassemblyjs/ast": "1.11.6", |         "@webassemblyjs/ast": "1.12.1", | ||||||
|         "@webassemblyjs/helper-wasm-bytecode": "1.11.6", |         "@webassemblyjs/helper-wasm-bytecode": "1.11.6", | ||||||
|         "@webassemblyjs/ieee754": "1.11.6", |         "@webassemblyjs/ieee754": "1.11.6", | ||||||
|         "@webassemblyjs/leb128": "1.11.6", |         "@webassemblyjs/leb128": "1.11.6", | ||||||
| @@ -3789,22 +3783,24 @@ | |||||||
|       } |       } | ||||||
|     }, |     }, | ||||||
|     "node_modules/@webassemblyjs/wasm-opt": { |     "node_modules/@webassemblyjs/wasm-opt": { | ||||||
|       "version": "1.11.6", |       "version": "1.12.1", | ||||||
|       "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.6.tgz", |       "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.12.1.tgz", | ||||||
|       "integrity": "sha512-cOrKuLRE7PCe6AsOVl7WasYf3wbSo4CeOk6PkrjS7g57MFfVUF9u6ysQBBODX0LdgSvQqRiGz3CXvIDKcPNy4g==", |       "integrity": "sha512-Jg99j/2gG2iaz3hijw857AVYekZe2SAskcqlWIZXjji5WStnOpVoat3gQfT/Q5tb2djnCjBtMocY/Su1GfxPBg==", | ||||||
|  |       "license": "MIT", | ||||||
|       "dependencies": { |       "dependencies": { | ||||||
|         "@webassemblyjs/ast": "1.11.6", |         "@webassemblyjs/ast": "1.12.1", | ||||||
|         "@webassemblyjs/helper-buffer": "1.11.6", |         "@webassemblyjs/helper-buffer": "1.12.1", | ||||||
|         "@webassemblyjs/wasm-gen": "1.11.6", |         "@webassemblyjs/wasm-gen": "1.12.1", | ||||||
|         "@webassemblyjs/wasm-parser": "1.11.6" |         "@webassemblyjs/wasm-parser": "1.12.1" | ||||||
|       } |       } | ||||||
|     }, |     }, | ||||||
|     "node_modules/@webassemblyjs/wasm-parser": { |     "node_modules/@webassemblyjs/wasm-parser": { | ||||||
|       "version": "1.11.6", |       "version": "1.12.1", | ||||||
|       "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.6.tgz", |       "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.12.1.tgz", | ||||||
|       "integrity": "sha512-6ZwPeGzMJM3Dqp3hCsLgESxBGtT/OeCvCZ4TA1JUPYgmhAx38tTPR9JaKy0S5H3evQpO/h2uWs2j6Yc/fjkpTQ==", |       "integrity": "sha512-xikIi7c2FHXysxXe3COrVUPSheuBtpcfhbpFj4gmu7KRLYOzANztwUU0IbsqvMqzuNK2+glRGWCEqZo1WCLyAQ==", | ||||||
|  |       "license": "MIT", | ||||||
|       "dependencies": { |       "dependencies": { | ||||||
|         "@webassemblyjs/ast": "1.11.6", |         "@webassemblyjs/ast": "1.12.1", | ||||||
|         "@webassemblyjs/helper-api-error": "1.11.6", |         "@webassemblyjs/helper-api-error": "1.11.6", | ||||||
|         "@webassemblyjs/helper-wasm-bytecode": "1.11.6", |         "@webassemblyjs/helper-wasm-bytecode": "1.11.6", | ||||||
|         "@webassemblyjs/ieee754": "1.11.6", |         "@webassemblyjs/ieee754": "1.11.6", | ||||||
| @@ -3813,23 +3809,26 @@ | |||||||
|       } |       } | ||||||
|     }, |     }, | ||||||
|     "node_modules/@webassemblyjs/wast-printer": { |     "node_modules/@webassemblyjs/wast-printer": { | ||||||
|       "version": "1.11.6", |       "version": "1.12.1", | ||||||
|       "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.11.6.tgz", |       "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.12.1.tgz", | ||||||
|       "integrity": "sha512-JM7AhRcE+yW2GWYaKeHL5vt4xqee5N2WcezptmgyhNS+ScggqcT1OtXykhAb13Sn5Yas0j2uv9tHgrjwvzAP4A==", |       "integrity": "sha512-+X4WAlOisVWQMikjbcvY2e0rwPsKQ9F688lksZhBcPycBBuii3O7m8FACbDMWDojpAqvjIncrG8J0XHKyQfVeA==", | ||||||
|  |       "license": "MIT", | ||||||
|       "dependencies": { |       "dependencies": { | ||||||
|         "@webassemblyjs/ast": "1.11.6", |         "@webassemblyjs/ast": "1.12.1", | ||||||
|         "@xtuc/long": "4.2.2" |         "@xtuc/long": "4.2.2" | ||||||
|       } |       } | ||||||
|     }, |     }, | ||||||
|     "node_modules/@xtuc/ieee754": { |     "node_modules/@xtuc/ieee754": { | ||||||
|       "version": "1.2.0", |       "version": "1.2.0", | ||||||
|       "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", |       "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", | ||||||
|       "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==" |       "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==", | ||||||
|  |       "license": "BSD-3-Clause" | ||||||
|     }, |     }, | ||||||
|     "node_modules/@xtuc/long": { |     "node_modules/@xtuc/long": { | ||||||
|       "version": "4.2.2", |       "version": "4.2.2", | ||||||
|       "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz", |       "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz", | ||||||
|       "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==" |       "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==", | ||||||
|  |       "license": "Apache-2.0" | ||||||
|     }, |     }, | ||||||
|     "node_modules/accepts": { |     "node_modules/accepts": { | ||||||
|       "version": "1.3.8", |       "version": "1.3.8", | ||||||
| @@ -3873,10 +3872,11 @@ | |||||||
|         "node": ">=0.4.0" |         "node": ">=0.4.0" | ||||||
|       } |       } | ||||||
|     }, |     }, | ||||||
|     "node_modules/acorn-import-assertions": { |     "node_modules/acorn-import-attributes": { | ||||||
|       "version": "1.9.0", |       "version": "1.9.5", | ||||||
|       "resolved": "https://registry.npmjs.org/acorn-import-assertions/-/acorn-import-assertions-1.9.0.tgz", |       "resolved": "https://registry.npmjs.org/acorn-import-attributes/-/acorn-import-attributes-1.9.5.tgz", | ||||||
|       "integrity": "sha512-cmMwop9x+8KFhxvKrKfPYmN6/pKTYYHBqLa0DfvVZcKMJWNyWLnaqND7dx/qn66R7ewM1UX5XMaDVP5wlVTaVA==", |       "integrity": "sha512-n02Vykv5uA3eHGM/Z2dQrcD56kL8TyDb2p1+0P83PClMnC/nc+anbQRhIOWnSq4Ke/KvDPrY3C9hDtC/A3eHnQ==", | ||||||
|  |       "license": "MIT", | ||||||
|       "peerDependencies": { |       "peerDependencies": { | ||||||
|         "acorn": "^8" |         "acorn": "^8" | ||||||
|       } |       } | ||||||
| @@ -4242,12 +4242,13 @@ | |||||||
|       } |       } | ||||||
|     }, |     }, | ||||||
|     "node_modules/body-parser": { |     "node_modules/body-parser": { | ||||||
|       "version": "1.20.1", |       "version": "1.20.2", | ||||||
|       "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.1.tgz", |       "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.2.tgz", | ||||||
|       "integrity": "sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw==", |       "integrity": "sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA==", | ||||||
|  |       "license": "MIT", | ||||||
|       "dependencies": { |       "dependencies": { | ||||||
|         "bytes": "3.1.2", |         "bytes": "3.1.2", | ||||||
|         "content-type": "~1.0.4", |         "content-type": "~1.0.5", | ||||||
|         "debug": "2.6.9", |         "debug": "2.6.9", | ||||||
|         "depd": "2.0.0", |         "depd": "2.0.0", | ||||||
|         "destroy": "1.2.0", |         "destroy": "1.2.0", | ||||||
| @@ -4255,7 +4256,7 @@ | |||||||
|         "iconv-lite": "0.4.24", |         "iconv-lite": "0.4.24", | ||||||
|         "on-finished": "2.4.1", |         "on-finished": "2.4.1", | ||||||
|         "qs": "6.11.0", |         "qs": "6.11.0", | ||||||
|         "raw-body": "2.5.1", |         "raw-body": "2.5.2", | ||||||
|         "type-is": "~1.6.18", |         "type-is": "~1.6.18", | ||||||
|         "unpipe": "1.0.0" |         "unpipe": "1.0.0" | ||||||
|       }, |       }, | ||||||
| @@ -4268,6 +4269,7 @@ | |||||||
|       "version": "3.1.2", |       "version": "3.1.2", | ||||||
|       "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", |       "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", | ||||||
|       "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", |       "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", | ||||||
|  |       "license": "MIT", | ||||||
|       "engines": { |       "engines": { | ||||||
|         "node": ">= 0.8" |         "node": ">= 0.8" | ||||||
|       } |       } | ||||||
| @@ -4276,6 +4278,7 @@ | |||||||
|       "version": "2.6.9", |       "version": "2.6.9", | ||||||
|       "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", |       "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", | ||||||
|       "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", |       "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", | ||||||
|  |       "license": "MIT", | ||||||
|       "dependencies": { |       "dependencies": { | ||||||
|         "ms": "2.0.0" |         "ms": "2.0.0" | ||||||
|       } |       } | ||||||
| @@ -4283,7 +4286,8 @@ | |||||||
|     "node_modules/body-parser/node_modules/ms": { |     "node_modules/body-parser/node_modules/ms": { | ||||||
|       "version": "2.0.0", |       "version": "2.0.0", | ||||||
|       "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", |       "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", | ||||||
|       "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" |       "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", | ||||||
|  |       "license": "MIT" | ||||||
|     }, |     }, | ||||||
|     "node_modules/bonjour-service": { |     "node_modules/bonjour-service": { | ||||||
|       "version": "1.1.1", |       "version": "1.1.1", | ||||||
| @@ -4332,11 +4336,12 @@ | |||||||
|       } |       } | ||||||
|     }, |     }, | ||||||
|     "node_modules/braces": { |     "node_modules/braces": { | ||||||
|       "version": "3.0.2", |       "version": "3.0.3", | ||||||
|       "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", |       "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", | ||||||
|       "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", |       "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", | ||||||
|  |       "license": "MIT", | ||||||
|       "dependencies": { |       "dependencies": { | ||||||
|         "fill-range": "^7.0.1" |         "fill-range": "^7.1.1" | ||||||
|       }, |       }, | ||||||
|       "engines": { |       "engines": { | ||||||
|         "node": ">=8" |         "node": ">=8" | ||||||
| @@ -4423,13 +4428,19 @@ | |||||||
|       } |       } | ||||||
|     }, |     }, | ||||||
|     "node_modules/call-bind": { |     "node_modules/call-bind": { | ||||||
|       "version": "1.0.5", |       "version": "1.0.7", | ||||||
|       "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.5.tgz", |       "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz", | ||||||
|       "integrity": "sha512-C3nQxfFZxFRVoJoGKKI8y3MOEo129NQ+FgQ08iye+Mk4zNZZGdjfs06bVTr+DBSlA66Q2VEcMki/cUCP4SercQ==", |       "integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==", | ||||||
|  |       "license": "MIT", | ||||||
|       "dependencies": { |       "dependencies": { | ||||||
|  |         "es-define-property": "^1.0.0", | ||||||
|  |         "es-errors": "^1.3.0", | ||||||
|         "function-bind": "^1.1.2", |         "function-bind": "^1.1.2", | ||||||
|         "get-intrinsic": "^1.2.1", |         "get-intrinsic": "^1.2.4", | ||||||
|         "set-function-length": "^1.1.1" |         "set-function-length": "^1.2.1" | ||||||
|  |       }, | ||||||
|  |       "engines": { | ||||||
|  |         "node": ">= 0.4" | ||||||
|       }, |       }, | ||||||
|       "funding": { |       "funding": { | ||||||
|         "url": "https://github.com/sponsors/ljharb" |         "url": "https://github.com/sponsors/ljharb" | ||||||
| @@ -4923,6 +4934,7 @@ | |||||||
|       "version": "1.0.5", |       "version": "1.0.5", | ||||||
|       "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", |       "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", | ||||||
|       "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", |       "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", | ||||||
|  |       "license": "MIT", | ||||||
|       "engines": { |       "engines": { | ||||||
|         "node": ">= 0.6" |         "node": ">= 0.6" | ||||||
|       } |       } | ||||||
| @@ -4933,9 +4945,10 @@ | |||||||
|       "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==" |       "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==" | ||||||
|     }, |     }, | ||||||
|     "node_modules/cookie": { |     "node_modules/cookie": { | ||||||
|       "version": "0.5.0", |       "version": "0.6.0", | ||||||
|       "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz", |       "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz", | ||||||
|       "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==", |       "integrity": "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==", | ||||||
|  |       "license": "MIT", | ||||||
|       "engines": { |       "engines": { | ||||||
|         "node": ">= 0.6" |         "node": ">= 0.6" | ||||||
|       } |       } | ||||||
| @@ -5962,16 +5975,20 @@ | |||||||
|       } |       } | ||||||
|     }, |     }, | ||||||
|     "node_modules/define-data-property": { |     "node_modules/define-data-property": { | ||||||
|       "version": "1.1.1", |       "version": "1.1.4", | ||||||
|       "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.1.tgz", |       "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", | ||||||
|       "integrity": "sha512-E7uGkTzkk1d0ByLeSc6ZsFS79Axg+m1P/VsgYsxHgiuc3tFSj+MjMIwe90FC4lOAZzNBdY7kkO2P2wKdsQ1vgQ==", |       "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", | ||||||
|  |       "license": "MIT", | ||||||
|       "dependencies": { |       "dependencies": { | ||||||
|         "get-intrinsic": "^1.2.1", |         "es-define-property": "^1.0.0", | ||||||
|         "gopd": "^1.0.1", |         "es-errors": "^1.3.0", | ||||||
|         "has-property-descriptors": "^1.0.0" |         "gopd": "^1.0.1" | ||||||
|       }, |       }, | ||||||
|       "engines": { |       "engines": { | ||||||
|         "node": ">= 0.4" |         "node": ">= 0.4" | ||||||
|  |       }, | ||||||
|  |       "funding": { | ||||||
|  |         "url": "https://github.com/sponsors/ljharb" | ||||||
|       } |       } | ||||||
|     }, |     }, | ||||||
|     "node_modules/define-lazy-prop": { |     "node_modules/define-lazy-prop": { | ||||||
| @@ -6302,9 +6319,10 @@ | |||||||
|       } |       } | ||||||
|     }, |     }, | ||||||
|     "node_modules/enhanced-resolve": { |     "node_modules/enhanced-resolve": { | ||||||
|       "version": "5.15.0", |       "version": "5.17.1", | ||||||
|       "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.15.0.tgz", |       "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.17.1.tgz", | ||||||
|       "integrity": "sha512-LXYT42KJ7lpIKECr2mAXIaMldcNCh/7E0KBKOu4KSfkHmP+mZmSs+8V5gBAqisWBy0OO4W5Oyys0GO1Y8KtdKg==", |       "integrity": "sha512-LMHl3dXhTcfv8gM4kEzIUeTQ+7fpdA0l2tUf34BddXPkz2A5xJ5L/Pchd5BL6rdccM9QGvu0sWZzK1Z1t4wwyg==", | ||||||
|  |       "license": "MIT", | ||||||
|       "dependencies": { |       "dependencies": { | ||||||
|         "graceful-fs": "^4.2.4", |         "graceful-fs": "^4.2.4", | ||||||
|         "tapable": "^2.2.0" |         "tapable": "^2.2.0" | ||||||
| @@ -6332,6 +6350,27 @@ | |||||||
|         "is-arrayish": "^0.2.1" |         "is-arrayish": "^0.2.1" | ||||||
|       } |       } | ||||||
|     }, |     }, | ||||||
|  |     "node_modules/es-define-property": { | ||||||
|  |       "version": "1.0.0", | ||||||
|  |       "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz", | ||||||
|  |       "integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==", | ||||||
|  |       "license": "MIT", | ||||||
|  |       "dependencies": { | ||||||
|  |         "get-intrinsic": "^1.2.4" | ||||||
|  |       }, | ||||||
|  |       "engines": { | ||||||
|  |         "node": ">= 0.4" | ||||||
|  |       } | ||||||
|  |     }, | ||||||
|  |     "node_modules/es-errors": { | ||||||
|  |       "version": "1.3.0", | ||||||
|  |       "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", | ||||||
|  |       "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", | ||||||
|  |       "license": "MIT", | ||||||
|  |       "engines": { | ||||||
|  |         "node": ">= 0.4" | ||||||
|  |       } | ||||||
|  |     }, | ||||||
|     "node_modules/es-module-lexer": { |     "node_modules/es-module-lexer": { | ||||||
|       "version": "1.4.1", |       "version": "1.4.1", | ||||||
|       "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.4.1.tgz", |       "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.4.1.tgz", | ||||||
| @@ -6580,16 +6619,17 @@ | |||||||
|       } |       } | ||||||
|     }, |     }, | ||||||
|     "node_modules/express": { |     "node_modules/express": { | ||||||
|       "version": "4.18.2", |       "version": "4.19.2", | ||||||
|       "resolved": "https://registry.npmjs.org/express/-/express-4.18.2.tgz", |       "resolved": "https://registry.npmjs.org/express/-/express-4.19.2.tgz", | ||||||
|       "integrity": "sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ==", |       "integrity": "sha512-5T6nhjsT+EOMzuck8JjBHARTHfMht0POzlA60WV2pMD3gyXw2LZnZ+ueGdNxG+0calOJcWKbpFcuzLZ91YWq9Q==", | ||||||
|  |       "license": "MIT", | ||||||
|       "dependencies": { |       "dependencies": { | ||||||
|         "accepts": "~1.3.8", |         "accepts": "~1.3.8", | ||||||
|         "array-flatten": "1.1.1", |         "array-flatten": "1.1.1", | ||||||
|         "body-parser": "1.20.1", |         "body-parser": "1.20.2", | ||||||
|         "content-disposition": "0.5.4", |         "content-disposition": "0.5.4", | ||||||
|         "content-type": "~1.0.4", |         "content-type": "~1.0.4", | ||||||
|         "cookie": "0.5.0", |         "cookie": "0.6.0", | ||||||
|         "cookie-signature": "1.0.6", |         "cookie-signature": "1.0.6", | ||||||
|         "debug": "2.6.9", |         "debug": "2.6.9", | ||||||
|         "depd": "2.0.0", |         "depd": "2.0.0", | ||||||
| @@ -6826,9 +6866,10 @@ | |||||||
|       } |       } | ||||||
|     }, |     }, | ||||||
|     "node_modules/fill-range": { |     "node_modules/fill-range": { | ||||||
|       "version": "7.0.1", |       "version": "7.1.1", | ||||||
|       "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", |       "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", | ||||||
|       "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", |       "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", | ||||||
|  |       "license": "MIT", | ||||||
|       "dependencies": { |       "dependencies": { | ||||||
|         "to-regex-range": "^5.0.1" |         "to-regex-range": "^5.0.1" | ||||||
|       }, |       }, | ||||||
| @@ -6905,15 +6946,16 @@ | |||||||
|       } |       } | ||||||
|     }, |     }, | ||||||
|     "node_modules/follow-redirects": { |     "node_modules/follow-redirects": { | ||||||
|       "version": "1.15.3", |       "version": "1.15.8", | ||||||
|       "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.3.tgz", |       "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.8.tgz", | ||||||
|       "integrity": "sha512-1VzOtuEM8pC9SFU1E+8KfTjZyMztRsgEfwQl44z8A25uy13jSzTj6dyK2Df52iV0vgHCfBwLhDWevLn95w5v6Q==", |       "integrity": "sha512-xgrmBhBToVKay1q2Tao5LI26B83UhrB/vM1avwVSDzt8rx3rO6AizBAaF46EgksTVr+rFTQaqZZ9MVBfUe4nig==", | ||||||
|       "funding": [ |       "funding": [ | ||||||
|         { |         { | ||||||
|           "type": "individual", |           "type": "individual", | ||||||
|           "url": "https://github.com/sponsors/RubenVerborgh" |           "url": "https://github.com/sponsors/RubenVerborgh" | ||||||
|         } |         } | ||||||
|       ], |       ], | ||||||
|  |       "license": "MIT", | ||||||
|       "engines": { |       "engines": { | ||||||
|         "node": ">=4.0" |         "node": ">=4.0" | ||||||
|       }, |       }, | ||||||
| @@ -7140,15 +7182,20 @@ | |||||||
|       } |       } | ||||||
|     }, |     }, | ||||||
|     "node_modules/get-intrinsic": { |     "node_modules/get-intrinsic": { | ||||||
|       "version": "1.2.2", |       "version": "1.2.4", | ||||||
|       "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.2.tgz", |       "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz", | ||||||
|       "integrity": "sha512-0gSo4ml/0j98Y3lngkFEot/zhiCeWsbYIlZ+uZOVgzLyLaUw7wxUL+nCTP0XJvJg1AXulJRI3UJi8GsbDuxdGA==", |       "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==", | ||||||
|  |       "license": "MIT", | ||||||
|       "dependencies": { |       "dependencies": { | ||||||
|  |         "es-errors": "^1.3.0", | ||||||
|         "function-bind": "^1.1.2", |         "function-bind": "^1.1.2", | ||||||
|         "has-proto": "^1.0.1", |         "has-proto": "^1.0.1", | ||||||
|         "has-symbols": "^1.0.3", |         "has-symbols": "^1.0.3", | ||||||
|         "hasown": "^2.0.0" |         "hasown": "^2.0.0" | ||||||
|       }, |       }, | ||||||
|  |       "engines": { | ||||||
|  |         "node": ">= 0.4" | ||||||
|  |       }, | ||||||
|       "funding": { |       "funding": { | ||||||
|         "url": "https://github.com/sponsors/ljharb" |         "url": "https://github.com/sponsors/ljharb" | ||||||
|       } |       } | ||||||
| @@ -7207,7 +7254,8 @@ | |||||||
|     "node_modules/glob-to-regexp": { |     "node_modules/glob-to-regexp": { | ||||||
|       "version": "0.4.1", |       "version": "0.4.1", | ||||||
|       "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", |       "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", | ||||||
|       "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==" |       "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==", | ||||||
|  |       "license": "BSD-2-Clause" | ||||||
|     }, |     }, | ||||||
|     "node_modules/global-dirs": { |     "node_modules/global-dirs": { | ||||||
|       "version": "3.0.1", |       "version": "3.0.1", | ||||||
| @@ -7406,11 +7454,12 @@ | |||||||
|       } |       } | ||||||
|     }, |     }, | ||||||
|     "node_modules/has-property-descriptors": { |     "node_modules/has-property-descriptors": { | ||||||
|       "version": "1.0.1", |       "version": "1.0.2", | ||||||
|       "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.1.tgz", |       "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", | ||||||
|       "integrity": "sha512-VsX8eaIewvas0xnvinAe9bw4WfIeODpGYikiWYLH+dma0Jw6KHYqWiWfhQlgOVK8D6PvjubK5Uc4P0iIhIcNVg==", |       "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", | ||||||
|  |       "license": "MIT", | ||||||
|       "dependencies": { |       "dependencies": { | ||||||
|         "get-intrinsic": "^1.2.2" |         "es-define-property": "^1.0.0" | ||||||
|       }, |       }, | ||||||
|       "funding": { |       "funding": { | ||||||
|         "url": "https://github.com/sponsors/ljharb" |         "url": "https://github.com/sponsors/ljharb" | ||||||
| @@ -7950,6 +7999,7 @@ | |||||||
|       "version": "0.4.24", |       "version": "0.4.24", | ||||||
|       "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", |       "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", | ||||||
|       "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", |       "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", | ||||||
|  |       "license": "MIT", | ||||||
|       "dependencies": { |       "dependencies": { | ||||||
|         "safer-buffer": ">= 2.1.2 < 3" |         "safer-buffer": ">= 2.1.2 < 3" | ||||||
|       }, |       }, | ||||||
| @@ -8259,6 +8309,7 @@ | |||||||
|       "version": "7.0.0", |       "version": "7.0.0", | ||||||
|       "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", |       "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", | ||||||
|       "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", |       "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", | ||||||
|  |       "license": "MIT", | ||||||
|       "engines": { |       "engines": { | ||||||
|         "node": ">=0.12.0" |         "node": ">=0.12.0" | ||||||
|       } |       } | ||||||
| @@ -9106,6 +9157,7 @@ | |||||||
|       "version": "0.3.0", |       "version": "0.3.0", | ||||||
|       "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", |       "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", | ||||||
|       "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", |       "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", | ||||||
|  |       "license": "MIT", | ||||||
|       "engines": { |       "engines": { | ||||||
|         "node": ">= 0.6" |         "node": ">= 0.6" | ||||||
|       } |       } | ||||||
| @@ -11286,11 +11338,12 @@ | |||||||
|       ] |       ] | ||||||
|     }, |     }, | ||||||
|     "node_modules/micromatch": { |     "node_modules/micromatch": { | ||||||
|       "version": "4.0.5", |       "version": "4.0.8", | ||||||
|       "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", |       "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", | ||||||
|       "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", |       "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", | ||||||
|  |       "license": "MIT", | ||||||
|       "dependencies": { |       "dependencies": { | ||||||
|         "braces": "^3.0.2", |         "braces": "^3.0.3", | ||||||
|         "picomatch": "^2.3.1" |         "picomatch": "^2.3.1" | ||||||
|       }, |       }, | ||||||
|       "engines": { |       "engines": { | ||||||
| @@ -11544,9 +11597,13 @@ | |||||||
|       } |       } | ||||||
|     }, |     }, | ||||||
|     "node_modules/object-inspect": { |     "node_modules/object-inspect": { | ||||||
|       "version": "1.13.1", |       "version": "1.13.2", | ||||||
|       "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.1.tgz", |       "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.2.tgz", | ||||||
|       "integrity": "sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==", |       "integrity": "sha512-IRZSRuzJiynemAXPYtPe5BoI/RESNYR7TYm50MC5Mqbd3Jmw5y790sErYw3V6SryFJD64b74qQQs9wn5Bg/k3g==", | ||||||
|  |       "license": "MIT", | ||||||
|  |       "engines": { | ||||||
|  |         "node": ">= 0.4" | ||||||
|  |       }, | ||||||
|       "funding": { |       "funding": { | ||||||
|         "url": "https://github.com/sponsors/ljharb" |         "url": "https://github.com/sponsors/ljharb" | ||||||
|       } |       } | ||||||
| @@ -12685,6 +12742,7 @@ | |||||||
|       "version": "6.11.0", |       "version": "6.11.0", | ||||||
|       "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", |       "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", | ||||||
|       "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", |       "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", | ||||||
|  |       "license": "BSD-3-Clause", | ||||||
|       "dependencies": { |       "dependencies": { | ||||||
|         "side-channel": "^1.0.4" |         "side-channel": "^1.0.4" | ||||||
|       }, |       }, | ||||||
| @@ -12750,9 +12808,10 @@ | |||||||
|       } |       } | ||||||
|     }, |     }, | ||||||
|     "node_modules/raw-body": { |     "node_modules/raw-body": { | ||||||
|       "version": "2.5.1", |       "version": "2.5.2", | ||||||
|       "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz", |       "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz", | ||||||
|       "integrity": "sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==", |       "integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==", | ||||||
|  |       "license": "MIT", | ||||||
|       "dependencies": { |       "dependencies": { | ||||||
|         "bytes": "3.1.2", |         "bytes": "3.1.2", | ||||||
|         "http-errors": "2.0.0", |         "http-errors": "2.0.0", | ||||||
| @@ -12767,6 +12826,7 @@ | |||||||
|       "version": "3.1.2", |       "version": "3.1.2", | ||||||
|       "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", |       "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", | ||||||
|       "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", |       "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", | ||||||
|  |       "license": "MIT", | ||||||
|       "engines": { |       "engines": { | ||||||
|         "node": ">= 0.8" |         "node": ">= 0.8" | ||||||
|       } |       } | ||||||
| @@ -13887,14 +13947,17 @@ | |||||||
|       } |       } | ||||||
|     }, |     }, | ||||||
|     "node_modules/set-function-length": { |     "node_modules/set-function-length": { | ||||||
|       "version": "1.1.1", |       "version": "1.2.2", | ||||||
|       "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.1.1.tgz", |       "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", | ||||||
|       "integrity": "sha512-VoaqjbBJKiWtg4yRcKBQ7g7wnGnLV3M8oLvVWwOk2PdYY6PEFegR1vezXR0tw6fZGF9csVakIRjrJiy2veSBFQ==", |       "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", | ||||||
|  |       "license": "MIT", | ||||||
|       "dependencies": { |       "dependencies": { | ||||||
|         "define-data-property": "^1.1.1", |         "define-data-property": "^1.1.4", | ||||||
|         "get-intrinsic": "^1.2.1", |         "es-errors": "^1.3.0", | ||||||
|  |         "function-bind": "^1.1.2", | ||||||
|  |         "get-intrinsic": "^1.2.4", | ||||||
|         "gopd": "^1.0.1", |         "gopd": "^1.0.1", | ||||||
|         "has-property-descriptors": "^1.0.0" |         "has-property-descriptors": "^1.0.2" | ||||||
|       }, |       }, | ||||||
|       "engines": { |       "engines": { | ||||||
|         "node": ">= 0.4" |         "node": ">= 0.4" | ||||||
| @@ -13965,13 +14028,18 @@ | |||||||
|       } |       } | ||||||
|     }, |     }, | ||||||
|     "node_modules/side-channel": { |     "node_modules/side-channel": { | ||||||
|       "version": "1.0.4", |       "version": "1.0.6", | ||||||
|       "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", |       "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.6.tgz", | ||||||
|       "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", |       "integrity": "sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==", | ||||||
|  |       "license": "MIT", | ||||||
|       "dependencies": { |       "dependencies": { | ||||||
|         "call-bind": "^1.0.0", |         "call-bind": "^1.0.7", | ||||||
|         "get-intrinsic": "^1.0.2", |         "es-errors": "^1.3.0", | ||||||
|         "object-inspect": "^1.9.0" |         "get-intrinsic": "^1.2.4", | ||||||
|  |         "object-inspect": "^1.13.1" | ||||||
|  |       }, | ||||||
|  |       "engines": { | ||||||
|  |         "node": ">= 0.4" | ||||||
|       }, |       }, | ||||||
|       "funding": { |       "funding": { | ||||||
|         "url": "https://github.com/sponsors/ljharb" |         "url": "https://github.com/sponsors/ljharb" | ||||||
| @@ -14394,15 +14462,16 @@ | |||||||
|       } |       } | ||||||
|     }, |     }, | ||||||
|     "node_modules/terser-webpack-plugin": { |     "node_modules/terser-webpack-plugin": { | ||||||
|       "version": "5.3.9", |       "version": "5.3.10", | ||||||
|       "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.9.tgz", |       "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.10.tgz", | ||||||
|       "integrity": "sha512-ZuXsqE07EcggTWQjXUj+Aot/OMcD0bMKGgF63f7UxYcu5/AJF53aIpK1YoP5xR9l6s/Hy2b+t1AM0bLNPRuhwA==", |       "integrity": "sha512-BKFPWlPDndPs+NGGCr1U59t0XScL5317Y0UReNrHaw9/FwhPENlq6bfgs+4yPfyP51vqC1bQ4rp1EfXW5ZSH9w==", | ||||||
|  |       "license": "MIT", | ||||||
|       "dependencies": { |       "dependencies": { | ||||||
|         "@jridgewell/trace-mapping": "^0.3.17", |         "@jridgewell/trace-mapping": "^0.3.20", | ||||||
|         "jest-worker": "^27.4.5", |         "jest-worker": "^27.4.5", | ||||||
|         "schema-utils": "^3.1.1", |         "schema-utils": "^3.1.1", | ||||||
|         "serialize-javascript": "^6.0.1", |         "serialize-javascript": "^6.0.1", | ||||||
|         "terser": "^5.16.8" |         "terser": "^5.26.0" | ||||||
|       }, |       }, | ||||||
|       "engines": { |       "engines": { | ||||||
|         "node": ">= 10.13.0" |         "node": ">= 10.13.0" | ||||||
| @@ -14535,6 +14604,7 @@ | |||||||
|       "version": "5.0.1", |       "version": "5.0.1", | ||||||
|       "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", |       "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", | ||||||
|       "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", |       "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", | ||||||
|  |       "license": "MIT", | ||||||
|       "dependencies": { |       "dependencies": { | ||||||
|         "is-number": "^7.0.0" |         "is-number": "^7.0.0" | ||||||
|       }, |       }, | ||||||
| @@ -14604,6 +14674,7 @@ | |||||||
|       "version": "1.6.18", |       "version": "1.6.18", | ||||||
|       "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", |       "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", | ||||||
|       "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", |       "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", | ||||||
|  |       "license": "MIT", | ||||||
|       "dependencies": { |       "dependencies": { | ||||||
|         "media-typer": "0.3.0", |         "media-typer": "0.3.0", | ||||||
|         "mime-types": "~2.1.24" |         "mime-types": "~2.1.24" | ||||||
| @@ -14616,6 +14687,7 @@ | |||||||
|       "version": "1.52.0", |       "version": "1.52.0", | ||||||
|       "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", |       "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", | ||||||
|       "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", |       "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", | ||||||
|  |       "license": "MIT", | ||||||
|       "engines": { |       "engines": { | ||||||
|         "node": ">= 0.6" |         "node": ">= 0.6" | ||||||
|       } |       } | ||||||
| @@ -14624,6 +14696,7 @@ | |||||||
|       "version": "2.1.35", |       "version": "2.1.35", | ||||||
|       "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", |       "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", | ||||||
|       "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", |       "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", | ||||||
|  |       "license": "MIT", | ||||||
|       "dependencies": { |       "dependencies": { | ||||||
|         "mime-db": "1.52.0" |         "mime-db": "1.52.0" | ||||||
|       }, |       }, | ||||||
| @@ -15155,9 +15228,10 @@ | |||||||
|       } |       } | ||||||
|     }, |     }, | ||||||
|     "node_modules/watchpack": { |     "node_modules/watchpack": { | ||||||
|       "version": "2.4.0", |       "version": "2.4.2", | ||||||
|       "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.0.tgz", |       "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.2.tgz", | ||||||
|       "integrity": "sha512-Lcvm7MGST/4fup+ifyKi2hjyIAwcdI4HRgtvTpIUxBRhB+RFtUh8XtDOxUfctVCnhVi+QQj49i91OyvzkJl6cg==", |       "integrity": "sha512-TnbFSbcOCcDgjZ4piURLCbJ3nJhznVh9kw6F6iokjiFPl8ONxe9A6nMDVXDiNbrSfLILs6vB07F7wLBrwPYzJw==", | ||||||
|  |       "license": "MIT", | ||||||
|       "dependencies": { |       "dependencies": { | ||||||
|         "glob-to-regexp": "^0.4.1", |         "glob-to-regexp": "^0.4.1", | ||||||
|         "graceful-fs": "^4.1.2" |         "graceful-fs": "^4.1.2" | ||||||
| @@ -15189,33 +15263,33 @@ | |||||||
|       "integrity": "sha512-PgF341avzqyx60neE9DD+XS26MMNMoUQRz9NOZwW32nPQrF6p77f1htcnjBSEV8BGMKZ16choqUG4hyI0Hx7mA==" |       "integrity": "sha512-PgF341avzqyx60neE9DD+XS26MMNMoUQRz9NOZwW32nPQrF6p77f1htcnjBSEV8BGMKZ16choqUG4hyI0Hx7mA==" | ||||||
|     }, |     }, | ||||||
|     "node_modules/webpack": { |     "node_modules/webpack": { | ||||||
|       "version": "5.89.0", |       "version": "5.94.0", | ||||||
|       "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.89.0.tgz", |       "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.94.0.tgz", | ||||||
|       "integrity": "sha512-qyfIC10pOr70V+jkmud8tMfajraGCZMBWJtrmuBymQKCrLTRejBI8STDp1MCyZu/QTdZSeacCQYpYNQVOzX5kw==", |       "integrity": "sha512-KcsGn50VT+06JH/iunZJedYGUJS5FGjow8wb9c0v5n1Om8O1g4L6LjtfxwlXIATopoQu+vOXXa7gYisWxCoPyg==", | ||||||
|  |       "license": "MIT", | ||||||
|       "dependencies": { |       "dependencies": { | ||||||
|         "@types/eslint-scope": "^3.7.3", |         "@types/estree": "^1.0.5", | ||||||
|         "@types/estree": "^1.0.0", |         "@webassemblyjs/ast": "^1.12.1", | ||||||
|         "@webassemblyjs/ast": "^1.11.5", |         "@webassemblyjs/wasm-edit": "^1.12.1", | ||||||
|         "@webassemblyjs/wasm-edit": "^1.11.5", |         "@webassemblyjs/wasm-parser": "^1.12.1", | ||||||
|         "@webassemblyjs/wasm-parser": "^1.11.5", |  | ||||||
|         "acorn": "^8.7.1", |         "acorn": "^8.7.1", | ||||||
|         "acorn-import-assertions": "^1.9.0", |         "acorn-import-attributes": "^1.9.5", | ||||||
|         "browserslist": "^4.14.5", |         "browserslist": "^4.21.10", | ||||||
|         "chrome-trace-event": "^1.0.2", |         "chrome-trace-event": "^1.0.2", | ||||||
|         "enhanced-resolve": "^5.15.0", |         "enhanced-resolve": "^5.17.1", | ||||||
|         "es-module-lexer": "^1.2.1", |         "es-module-lexer": "^1.2.1", | ||||||
|         "eslint-scope": "5.1.1", |         "eslint-scope": "5.1.1", | ||||||
|         "events": "^3.2.0", |         "events": "^3.2.0", | ||||||
|         "glob-to-regexp": "^0.4.1", |         "glob-to-regexp": "^0.4.1", | ||||||
|         "graceful-fs": "^4.2.9", |         "graceful-fs": "^4.2.11", | ||||||
|         "json-parse-even-better-errors": "^2.3.1", |         "json-parse-even-better-errors": "^2.3.1", | ||||||
|         "loader-runner": "^4.2.0", |         "loader-runner": "^4.2.0", | ||||||
|         "mime-types": "^2.1.27", |         "mime-types": "^2.1.27", | ||||||
|         "neo-async": "^2.6.2", |         "neo-async": "^2.6.2", | ||||||
|         "schema-utils": "^3.2.0", |         "schema-utils": "^3.2.0", | ||||||
|         "tapable": "^2.1.1", |         "tapable": "^2.1.1", | ||||||
|         "terser-webpack-plugin": "^5.3.7", |         "terser-webpack-plugin": "^5.3.10", | ||||||
|         "watchpack": "^2.4.0", |         "watchpack": "^2.4.1", | ||||||
|         "webpack-sources": "^3.2.3" |         "webpack-sources": "^3.2.3" | ||||||
|       }, |       }, | ||||||
|       "bin": { |       "bin": { | ||||||
| @@ -15269,9 +15343,10 @@ | |||||||
|       } |       } | ||||||
|     }, |     }, | ||||||
|     "node_modules/webpack-dev-middleware": { |     "node_modules/webpack-dev-middleware": { | ||||||
|       "version": "5.3.3", |       "version": "5.3.4", | ||||||
|       "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-5.3.3.tgz", |       "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-5.3.4.tgz", | ||||||
|       "integrity": "sha512-hj5CYrY0bZLB+eTO+x/j67Pkrquiy7kWepMHmUMoPsmcUaeEnQJqFzHJOyxgWlq746/wUuA64p9ta34Kyb01pA==", |       "integrity": "sha512-BVdTqhhs+0IfoeAf7EoH5WE+exCmqGerHfDM0IL096Px60Tq2Mn9MAbnaGUe6HiMa41KMCYF19gyzZmBcq/o4Q==", | ||||||
|  |       "license": "MIT", | ||||||
|       "dependencies": { |       "dependencies": { | ||||||
|         "colorette": "^2.0.10", |         "colorette": "^2.0.10", | ||||||
|         "memfs": "^3.4.3", |         "memfs": "^3.4.3", | ||||||
| @@ -15376,9 +15451,10 @@ | |||||||
|       } |       } | ||||||
|     }, |     }, | ||||||
|     "node_modules/webpack-dev-server/node_modules/ws": { |     "node_modules/webpack-dev-server/node_modules/ws": { | ||||||
|       "version": "8.15.1", |       "version": "8.18.0", | ||||||
|       "resolved": "https://registry.npmjs.org/ws/-/ws-8.15.1.tgz", |       "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz", | ||||||
|       "integrity": "sha512-W5OZiCjXEmk0yZ66ZN82beM5Sz7l7coYxpRkzS+p9PP+ToQry8szKh+61eNktr7EA9DOwvFGhfC605jDHbP6QQ==", |       "integrity": "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==", | ||||||
|  |       "license": "MIT", | ||||||
|       "engines": { |       "engines": { | ||||||
|         "node": ">=10.0.0" |         "node": ">=10.0.0" | ||||||
|       }, |       }, | ||||||
| @@ -15620,9 +15696,10 @@ | |||||||
|       } |       } | ||||||
|     }, |     }, | ||||||
|     "node_modules/ws": { |     "node_modules/ws": { | ||||||
|       "version": "7.5.9", |       "version": "7.5.10", | ||||||
|       "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.9.tgz", |       "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.10.tgz", | ||||||
|       "integrity": "sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q==", |       "integrity": "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==", | ||||||
|  |       "license": "MIT", | ||||||
|       "engines": { |       "engines": { | ||||||
|         "node": ">=8.3.0" |         "node": ">=8.3.0" | ||||||
|       }, |       }, | ||||||
|   | |||||||
							
								
								
									
										41
									
								
								docs/src/components/BuyMeACoffee/index.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										41
									
								
								docs/src/components/BuyMeACoffee/index.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,41 @@ | |||||||
|  | import React from "react"; | ||||||
|  |  | ||||||
|  | class BuyMeACoffee extends React.Component { | ||||||
|  |   constructor(props) { | ||||||
|  |     super(props) | ||||||
|  |     let script = document.createElement("script"); | ||||||
|  |     script.src = 'https://cdnjs.buymeacoffee.com/1.0.0/widget.prod.min.js'; | ||||||
|  |     script.dataset.name = 'BMC-Widget'; | ||||||
|  |     script.dataset.cfasync = 'false'; | ||||||
|  |     script.dataset.id = 'amithkoujalgi'; | ||||||
|  |     script.dataset.description = 'Support me on Buy me a coffee!'; | ||||||
|  |     script.dataset.message = 'If you like my work and want to say thanks, or encourage me to do more, you can buy me a coffee! 😊'; | ||||||
|  |     script.dataset.color = '#2e8555'; | ||||||
|  |     script.dataset.position = 'Right'; | ||||||
|  |     script.dataset.x_margin = '18'; | ||||||
|  |     script.dataset.y_margin = '18'; | ||||||
|  |     script.async = true | ||||||
|  |  | ||||||
|  |     script.onload = function () { | ||||||
|  |       let evt = document.createEvent('Event'); | ||||||
|  |       evt.initEvent('DOMContentLoaded', false, false); | ||||||
|  |       window.dispatchEvent(evt); | ||||||
|  |     } | ||||||
|  |     this.script = script | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   componentDidMount() { | ||||||
|  |     document.head.appendChild(this.script) | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   // componentWillUnmount() { | ||||||
|  |   //   document.head.removeChild(this.script); | ||||||
|  |   //   document.body.removeChild(document.getElementById("bmc-wbtn")) | ||||||
|  |   // } | ||||||
|  |  | ||||||
|  |   render() { | ||||||
|  |     return null | ||||||
|  |   } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | export default BuyMeACoffee; | ||||||
| @@ -38,3 +38,11 @@ header > h1, | |||||||
| h2 > a { | h2 > a { | ||||||
|     font-size: 2rem !important; |     font-size: 2rem !important; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | #bmc-wbtn{ | ||||||
|  |     bottom: 15px; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | #bmc-wbtn + div{ | ||||||
|  |     bottom:15px; | ||||||
|  | } | ||||||
| @@ -3,9 +3,10 @@ import Link from '@docusaurus/Link'; | |||||||
| import useDocusaurusContext from '@docusaurus/useDocusaurusContext'; | import useDocusaurusContext from '@docusaurus/useDocusaurusContext'; | ||||||
| import Layout from '@theme/Layout'; | import Layout from '@theme/Layout'; | ||||||
| import HomepageFeatures from '@site/src/components/HomepageFeatures'; | import HomepageFeatures from '@site/src/components/HomepageFeatures'; | ||||||
|  | import BuyMeACoffee from '@site/src/components/BuyMeACoffee'; | ||||||
| import Heading from '@theme/Heading'; | import Heading from '@theme/Heading'; | ||||||
| import styles from './index.module.css'; | import styles from './index.module.css'; | ||||||
|  | import BrowserOnly from '@docusaurus/BrowserOnly'; | ||||||
|  |  | ||||||
| function HomepageHeader() { | function HomepageHeader() { | ||||||
|   const {siteConfig} = useDocusaurusContext(); |   const {siteConfig} = useDocusaurusContext(); | ||||||
| @@ -14,7 +15,8 @@ function HomepageHeader() { | |||||||
|       <Heading as="h1" className="hero__title"> |       <Heading as="h1" className="hero__title"> | ||||||
|         {siteConfig.title} |         {siteConfig.title} | ||||||
|       </Heading> |       </Heading> | ||||||
|             <img src="img/logo.svg" alt="Ollama4j Logo" className={styles.logo} style={{maxWidth: '20vh'}}/> |       <img src="img/logo.svg" alt="Ollama4j Logo" className={styles.logo} | ||||||
|  |            style={{maxWidth: '20vh'}}/> | ||||||
|       <p className="hero__subtitle">{siteConfig.tagline}</p> |       <p className="hero__subtitle">{siteConfig.tagline}</p> | ||||||
|       <div className={styles.buttons}> |       <div className={styles.buttons}> | ||||||
|         <Link |         <Link | ||||||
| @@ -35,6 +37,9 @@ export default function Home() { | |||||||
|     <HomepageHeader/> |     <HomepageHeader/> | ||||||
|     <main> |     <main> | ||||||
|       <HomepageFeatures/> |       <HomepageFeatures/> | ||||||
|  |       <BrowserOnly> | ||||||
|  |         {() => <BuyMeACoffee />} | ||||||
|  |       </BrowserOnly> | ||||||
|     </main> |     </main> | ||||||
|   </Layout>); |   </Layout>); | ||||||
| } | } | ||||||
							
								
								
									
										9
									
								
								pom.xml
									
									
									
									
									
								
							
							
						
						
									
										9
									
								
								pom.xml
									
									
									
									
									
								
							| @@ -63,6 +63,10 @@ | |||||||
|                 <groupId>org.apache.maven.plugins</groupId> |                 <groupId>org.apache.maven.plugins</groupId> | ||||||
|                 <artifactId>maven-javadoc-plugin</artifactId> |                 <artifactId>maven-javadoc-plugin</artifactId> | ||||||
|                 <version>3.5.0</version> |                 <version>3.5.0</version> | ||||||
|  |                 <configuration> | ||||||
|  |                     <!-- to disable the "missing" warnings. Remove the doclint to enable warnings--> | ||||||
|  |                     <doclint>all,-missing</doclint> | ||||||
|  |                 </configuration> | ||||||
|                 <executions> |                 <executions> | ||||||
|                     <execution> |                     <execution> | ||||||
|                         <id>attach-javadocs</id> |                         <id>attach-javadocs</id> | ||||||
| @@ -136,6 +140,11 @@ | |||||||
|             <version>${lombok.version}</version> |             <version>${lombok.version}</version> | ||||||
|             <scope>provided</scope> |             <scope>provided</scope> | ||||||
|         </dependency> |         </dependency> | ||||||
|  |         <dependency> | ||||||
|  |             <groupId>org.jsoup</groupId> | ||||||
|  |             <artifactId>jsoup</artifactId> | ||||||
|  |             <version>1.18.1</version> | ||||||
|  |         </dependency> | ||||||
|         <dependency> |         <dependency> | ||||||
|             <groupId>com.fasterxml.jackson.core</groupId> |             <groupId>com.fasterxml.jackson.core</groupId> | ||||||
|             <artifactId>jackson-databind</artifactId> |             <artifactId>jackson-databind</artifactId> | ||||||
|   | |||||||
| @@ -1,27 +1,32 @@ | |||||||
| package io.github.ollama4j; | package io.github.ollama4j; | ||||||
|  |  | ||||||
| import io.github.ollama4j.exceptions.OllamaBaseException; | import io.github.ollama4j.exceptions.OllamaBaseException; | ||||||
|  | import io.github.ollama4j.exceptions.RoleNotFoundException; | ||||||
| import io.github.ollama4j.exceptions.ToolInvocationException; | import io.github.ollama4j.exceptions.ToolInvocationException; | ||||||
| import io.github.ollama4j.exceptions.ToolNotFoundException; | import io.github.ollama4j.exceptions.ToolNotFoundException; | ||||||
| import io.github.ollama4j.models.chat.OllamaChatMessage; | import io.github.ollama4j.models.chat.*; | ||||||
| import io.github.ollama4j.models.chat.OllamaChatRequest; | import io.github.ollama4j.models.embeddings.OllamaEmbedRequestModel; | ||||||
| import io.github.ollama4j.models.chat.OllamaChatRequestBuilder; |  | ||||||
| import io.github.ollama4j.models.chat.OllamaChatResult; |  | ||||||
| import io.github.ollama4j.models.embeddings.OllamaEmbeddingResponseModel; | import io.github.ollama4j.models.embeddings.OllamaEmbeddingResponseModel; | ||||||
| import io.github.ollama4j.models.embeddings.OllamaEmbeddingsRequestModel; | import io.github.ollama4j.models.embeddings.OllamaEmbeddingsRequestModel; | ||||||
|  | import io.github.ollama4j.models.embeddings.OllamaEmbedResponseModel; | ||||||
| import io.github.ollama4j.models.generate.OllamaGenerateRequest; | import io.github.ollama4j.models.generate.OllamaGenerateRequest; | ||||||
| import io.github.ollama4j.models.generate.OllamaStreamHandler; | import io.github.ollama4j.models.generate.OllamaStreamHandler; | ||||||
|  | import io.github.ollama4j.models.generate.OllamaTokenHandler; | ||||||
| import io.github.ollama4j.models.ps.ModelsProcessResponse; | import io.github.ollama4j.models.ps.ModelsProcessResponse; | ||||||
| import io.github.ollama4j.models.request.*; | import io.github.ollama4j.models.request.*; | ||||||
| import io.github.ollama4j.models.response.*; | import io.github.ollama4j.models.response.*; | ||||||
| import io.github.ollama4j.tools.*; | import io.github.ollama4j.tools.*; | ||||||
|  | import io.github.ollama4j.tools.annotations.OllamaToolService; | ||||||
|  | import io.github.ollama4j.tools.annotations.ToolProperty; | ||||||
|  | import io.github.ollama4j.tools.annotations.ToolSpec; | ||||||
| import io.github.ollama4j.utils.Options; | import io.github.ollama4j.utils.Options; | ||||||
| import io.github.ollama4j.utils.Utils; | import io.github.ollama4j.utils.Utils; | ||||||
| import lombok.Setter; | import lombok.Setter; | ||||||
| import org.slf4j.Logger; |  | ||||||
| import org.slf4j.LoggerFactory; |  | ||||||
|  |  | ||||||
| import java.io.*; | import java.io.*; | ||||||
|  | import java.lang.reflect.InvocationTargetException; | ||||||
|  | import java.lang.reflect.Method; | ||||||
|  | import java.lang.reflect.Parameter; | ||||||
| import java.net.URI; | import java.net.URI; | ||||||
| import java.net.URISyntaxException; | import java.net.URISyntaxException; | ||||||
| import java.net.http.HttpClient; | import java.net.http.HttpClient; | ||||||
| @@ -32,11 +37,19 @@ import java.nio.charset.StandardCharsets; | |||||||
| import java.nio.file.Files; | import java.nio.file.Files; | ||||||
| import java.time.Duration; | import java.time.Duration; | ||||||
| import java.util.*; | import java.util.*; | ||||||
|  | import java.util.stream.Collectors; | ||||||
|  |  | ||||||
|  | import org.slf4j.Logger; | ||||||
|  | import org.slf4j.LoggerFactory; | ||||||
|  | import org.jsoup.Jsoup; | ||||||
|  | import org.jsoup.nodes.Document; | ||||||
|  | import org.jsoup.nodes.Element; | ||||||
|  | import org.jsoup.select.Elements; | ||||||
|  |  | ||||||
| /** | /** | ||||||
|  * The base Ollama API class. |  * The base Ollama API class. | ||||||
|  */ |  */ | ||||||
| @SuppressWarnings("DuplicatedCode") | @SuppressWarnings({"DuplicatedCode", "resource"}) | ||||||
| public class OllamaAPI { | public class OllamaAPI { | ||||||
|  |  | ||||||
|     private static final Logger logger = LoggerFactory.getLogger(OllamaAPI.class); |     private static final Logger logger = LoggerFactory.getLogger(OllamaAPI.class); | ||||||
| @@ -53,6 +66,10 @@ public class OllamaAPI { | |||||||
|      */ |      */ | ||||||
|     @Setter |     @Setter | ||||||
|     private boolean verbose = true; |     private boolean verbose = true; | ||||||
|  |  | ||||||
|  |     @Setter | ||||||
|  |     private int maxChatToolCallRetries = 3; | ||||||
|  |  | ||||||
|     private BasicAuth basicAuth; |     private BasicAuth basicAuth; | ||||||
|  |  | ||||||
|     private final ToolRegistry toolRegistry = new ToolRegistry(); |     private final ToolRegistry toolRegistry = new ToolRegistry(); | ||||||
| @@ -97,12 +114,7 @@ public class OllamaAPI { | |||||||
|         HttpClient httpClient = HttpClient.newHttpClient(); |         HttpClient httpClient = HttpClient.newHttpClient(); | ||||||
|         HttpRequest httpRequest = null; |         HttpRequest httpRequest = null; | ||||||
|         try { |         try { | ||||||
|             httpRequest = |             httpRequest = getRequestBuilderDefault(new URI(url)).header("Accept", "application/json").header("Content-type", "application/json").GET().build(); | ||||||
|                     getRequestBuilderDefault(new URI(url)) |  | ||||||
|                             .header("Accept", "application/json") |  | ||||||
|                             .header("Content-type", "application/json") |  | ||||||
|                             .GET() |  | ||||||
|                             .build(); |  | ||||||
|         } catch (URISyntaxException e) { |         } catch (URISyntaxException e) { | ||||||
|             throw new RuntimeException(e); |             throw new RuntimeException(e); | ||||||
|         } |         } | ||||||
| @@ -121,19 +133,17 @@ public class OllamaAPI { | |||||||
|     /** |     /** | ||||||
|      * Provides a list of running models and details about each model currently loaded into memory. |      * Provides a list of running models and details about each model currently loaded into memory. | ||||||
|      * |      * | ||||||
|      * @return ModelsProcessResponse |      * @return ModelsProcessResponse containing details about the running models | ||||||
|  |      * @throws IOException          if an I/O error occurs during the HTTP request | ||||||
|  |      * @throws InterruptedException if the operation is interrupted | ||||||
|  |      * @throws OllamaBaseException  if the response indicates an error status | ||||||
|      */ |      */ | ||||||
|     public ModelsProcessResponse ps() throws IOException, InterruptedException, OllamaBaseException { |     public ModelsProcessResponse ps() throws IOException, InterruptedException, OllamaBaseException { | ||||||
|         String url = this.host + "/api/ps"; |         String url = this.host + "/api/ps"; | ||||||
|         HttpClient httpClient = HttpClient.newHttpClient(); |         HttpClient httpClient = HttpClient.newHttpClient(); | ||||||
|         HttpRequest httpRequest = null; |         HttpRequest httpRequest = null; | ||||||
|         try { |         try { | ||||||
|             httpRequest = |             httpRequest = getRequestBuilderDefault(new URI(url)).header("Accept", "application/json").header("Content-type", "application/json").GET().build(); | ||||||
|                     getRequestBuilderDefault(new URI(url)) |  | ||||||
|                             .header("Accept", "application/json") |  | ||||||
|                             .header("Content-type", "application/json") |  | ||||||
|                             .GET() |  | ||||||
|                             .build(); |  | ||||||
|         } catch (URISyntaxException e) { |         } catch (URISyntaxException e) { | ||||||
|             throw new RuntimeException(e); |             throw new RuntimeException(e); | ||||||
|         } |         } | ||||||
| @@ -142,69 +152,182 @@ public class OllamaAPI { | |||||||
|         int statusCode = response.statusCode(); |         int statusCode = response.statusCode(); | ||||||
|         String responseString = response.body(); |         String responseString = response.body(); | ||||||
|         if (statusCode == 200) { |         if (statusCode == 200) { | ||||||
|             return Utils.getObjectMapper() |             return Utils.getObjectMapper().readValue(responseString, ModelsProcessResponse.class); | ||||||
|                     .readValue(responseString, ModelsProcessResponse.class); |  | ||||||
|         } else { |         } else { | ||||||
|             throw new OllamaBaseException(statusCode + " - " + responseString); |             throw new OllamaBaseException(statusCode + " - " + responseString); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
|      * List available models from Ollama server. |      * Lists available models from the Ollama server. | ||||||
|      * |      * | ||||||
|      * @return the list |      * @return a list of models available on the server | ||||||
|  |      * @throws OllamaBaseException  if the response indicates an error status | ||||||
|  |      * @throws IOException          if an I/O error occurs during the HTTP request | ||||||
|  |      * @throws InterruptedException if the operation is interrupted | ||||||
|  |      * @throws URISyntaxException   if the URI for the request is malformed | ||||||
|      */ |      */ | ||||||
|     public List<Model> listModels() |     public List<Model> listModels() throws OllamaBaseException, IOException, InterruptedException, URISyntaxException { | ||||||
|             throws OllamaBaseException, IOException, InterruptedException, URISyntaxException { |  | ||||||
|         String url = this.host + "/api/tags"; |         String url = this.host + "/api/tags"; | ||||||
|         HttpClient httpClient = HttpClient.newHttpClient(); |         HttpClient httpClient = HttpClient.newHttpClient(); | ||||||
|         HttpRequest httpRequest = |         HttpRequest httpRequest = getRequestBuilderDefault(new URI(url)).header("Accept", "application/json").header("Content-type", "application/json").GET().build(); | ||||||
|                 getRequestBuilderDefault(new URI(url)) |         HttpResponse<String> response = httpClient.send(httpRequest, HttpResponse.BodyHandlers.ofString()); | ||||||
|                         .header("Accept", "application/json") |  | ||||||
|                         .header("Content-type", "application/json") |  | ||||||
|                         .GET() |  | ||||||
|                         .build(); |  | ||||||
|         HttpResponse<String> response = |  | ||||||
|                 httpClient.send(httpRequest, HttpResponse.BodyHandlers.ofString()); |  | ||||||
|         int statusCode = response.statusCode(); |         int statusCode = response.statusCode(); | ||||||
|         String responseString = response.body(); |         String responseString = response.body(); | ||||||
|         if (statusCode == 200) { |         if (statusCode == 200) { | ||||||
|             return Utils.getObjectMapper() |             return Utils.getObjectMapper().readValue(responseString, ListModelsResponse.class).getModels(); | ||||||
|                     .readValue(responseString, ListModelsResponse.class) |  | ||||||
|                     .getModels(); |  | ||||||
|         } else { |         } else { | ||||||
|             throw new OllamaBaseException(statusCode + " - " + responseString); |             throw new OllamaBaseException(statusCode + " - " + responseString); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * Retrieves a list of models from the Ollama library. This method fetches the available models directly from Ollama | ||||||
|  |      * library page, including model details such as the name, pull count, popular tags, tag count, and the time when model was updated. | ||||||
|  |      * | ||||||
|  |      * @return A list of {@link LibraryModel} objects representing the models available in the Ollama library. | ||||||
|  |      * @throws OllamaBaseException  If the HTTP request fails or the response is not successful (non-200 status code). | ||||||
|  |      * @throws IOException          If an I/O error occurs during the HTTP request or response processing. | ||||||
|  |      * @throws InterruptedException If the thread executing the request is interrupted. | ||||||
|  |      * @throws URISyntaxException   If there is an error creating the URI for the HTTP request. | ||||||
|  |      */ | ||||||
|  |     public List<LibraryModel> listModelsFromLibrary() throws OllamaBaseException, IOException, InterruptedException, URISyntaxException { | ||||||
|  |         String url = "https://ollama.com/library"; | ||||||
|  |         HttpClient httpClient = HttpClient.newHttpClient(); | ||||||
|  |         HttpRequest httpRequest = getRequestBuilderDefault(new URI(url)).header("Accept", "application/json").header("Content-type", "application/json").GET().build(); | ||||||
|  |         HttpResponse<String> response = httpClient.send(httpRequest, HttpResponse.BodyHandlers.ofString()); | ||||||
|  |         int statusCode = response.statusCode(); | ||||||
|  |         String responseString = response.body(); | ||||||
|  |         List<LibraryModel> models = new ArrayList<>(); | ||||||
|  |         if (statusCode == 200) { | ||||||
|  |             Document doc = Jsoup.parse(responseString); | ||||||
|  |             Elements modelSections = doc.selectXpath("//*[@id='repo']/ul/li/a"); | ||||||
|  |             for (Element e : modelSections) { | ||||||
|  |                 LibraryModel model = new LibraryModel(); | ||||||
|  |                 Elements names = e.select("div > h2 > div > span"); | ||||||
|  |                 Elements desc = e.select("div > p"); | ||||||
|  |                 Elements pullCounts = e.select("div:nth-of-type(2) > p > span:first-of-type > span:first-of-type"); | ||||||
|  |                 Elements popularTags = e.select("div > div > span"); | ||||||
|  |                 Elements totalTags = e.select("div:nth-of-type(2) > p > span:nth-of-type(2) > span:first-of-type"); | ||||||
|  |                 Elements lastUpdatedTime = e.select("div:nth-of-type(2) > p > span:nth-of-type(3) > span:nth-of-type(2)"); | ||||||
|  |  | ||||||
|  |                 if (names.first() == null || names.isEmpty()) { | ||||||
|  |                     // if name cannot be extracted, skip. | ||||||
|  |                     continue; | ||||||
|  |                 } | ||||||
|  |                 Optional.ofNullable(names.first()).map(Element::text).ifPresent(model::setName); | ||||||
|  |                 model.setDescription(Optional.ofNullable(desc.first()).map(Element::text).orElse("")); | ||||||
|  |                 model.setPopularTags(Optional.of(popularTags).map(tags -> tags.stream().map(Element::text).collect(Collectors.toList())).orElse(new ArrayList<>())); | ||||||
|  |                 model.setPullCount(Optional.ofNullable(pullCounts.first()).map(Element::text).orElse("")); | ||||||
|  |                 model.setTotalTags(Optional.ofNullable(totalTags.first()).map(Element::text).map(Integer::parseInt).orElse(0)); | ||||||
|  |                 model.setLastUpdated(Optional.ofNullable(lastUpdatedTime.first()).map(Element::text).orElse("")); | ||||||
|  |  | ||||||
|  |                 models.add(model); | ||||||
|  |             } | ||||||
|  |             return models; | ||||||
|  |         } else { | ||||||
|  |             throw new OllamaBaseException(statusCode + " - " + responseString); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * Fetches the tags associated with a specific model from Ollama library. | ||||||
|  |      * This method fetches the available model tags directly from Ollama library model page, including model tag name, size and time when model was last updated | ||||||
|  |      * into a list of {@link LibraryModelTag} objects. | ||||||
|  |      * | ||||||
|  |      * @param libraryModel the {@link LibraryModel} object which contains the name of the library model | ||||||
|  |      *                     for which the tags need to be fetched. | ||||||
|  |      * @return a list of {@link LibraryModelTag} objects containing the extracted tags and their associated metadata. | ||||||
|  |      * @throws OllamaBaseException  if the HTTP response status code indicates an error (i.e., not 200 OK), | ||||||
|  |      *                              or if there is any other issue during the request or response processing. | ||||||
|  |      * @throws IOException          if an input/output exception occurs during the HTTP request or response handling. | ||||||
|  |      * @throws InterruptedException if the thread is interrupted while waiting for the HTTP response. | ||||||
|  |      * @throws URISyntaxException   if the URI format is incorrect or invalid. | ||||||
|  |      */ | ||||||
|  |     public LibraryModelDetail getLibraryModelDetails(LibraryModel libraryModel) throws OllamaBaseException, IOException, InterruptedException, URISyntaxException { | ||||||
|  |         String url = String.format("https://ollama.com/library/%s/tags", libraryModel.getName()); | ||||||
|  |         HttpClient httpClient = HttpClient.newHttpClient(); | ||||||
|  |         HttpRequest httpRequest = getRequestBuilderDefault(new URI(url)).header("Accept", "application/json").header("Content-type", "application/json").GET().build(); | ||||||
|  |         HttpResponse<String> response = httpClient.send(httpRequest, HttpResponse.BodyHandlers.ofString()); | ||||||
|  |         int statusCode = response.statusCode(); | ||||||
|  |         String responseString = response.body(); | ||||||
|  |  | ||||||
|  |         List<LibraryModelTag> libraryModelTags = new ArrayList<>(); | ||||||
|  |         if (statusCode == 200) { | ||||||
|  |             Document doc = Jsoup.parse(responseString); | ||||||
|  |             Elements tagSections = doc.select("html > body > main > div > section > div > div > div:nth-child(n+2) > div"); | ||||||
|  |             for (Element e : tagSections) { | ||||||
|  |                 Elements tags = e.select("div > a > div"); | ||||||
|  |                 Elements tagsMetas = e.select("div > span"); | ||||||
|  |  | ||||||
|  |                 LibraryModelTag libraryModelTag = new LibraryModelTag(); | ||||||
|  |  | ||||||
|  |                 if (tags.first() == null || tags.isEmpty()) { | ||||||
|  |                     // if tag cannot be extracted, skip. | ||||||
|  |                     continue; | ||||||
|  |                 } | ||||||
|  |                 libraryModelTag.setName(libraryModel.getName()); | ||||||
|  |                 Optional.ofNullable(tags.first()).map(Element::text).ifPresent(libraryModelTag::setTag); | ||||||
|  |                 libraryModelTag.setSize(Optional.ofNullable(tagsMetas.first()).map(element -> element.text().split("•")).filter(parts -> parts.length > 1).map(parts -> parts[1].trim()).orElse("")); | ||||||
|  |                 libraryModelTag.setLastUpdated(Optional.ofNullable(tagsMetas.first()).map(element -> element.text().split("•")).filter(parts -> parts.length > 1).map(parts -> parts[2].trim()).orElse("")); | ||||||
|  |                 libraryModelTags.add(libraryModelTag); | ||||||
|  |             } | ||||||
|  |             LibraryModelDetail libraryModelDetail = new LibraryModelDetail(); | ||||||
|  |             libraryModelDetail.setModel(libraryModel); | ||||||
|  |             libraryModelDetail.setTags(libraryModelTags); | ||||||
|  |             return libraryModelDetail; | ||||||
|  |         } else { | ||||||
|  |             throw new OllamaBaseException(statusCode + " - " + responseString); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * Finds a specific model using model name and tag from Ollama library. | ||||||
|  |      * <p> | ||||||
|  |      * This method retrieves the model from the Ollama library by its name, then fetches its tags. | ||||||
|  |      * It searches through the tags of the model to find one that matches the specified tag name. | ||||||
|  |      * If the model or the tag is not found, it throws a {@link NoSuchElementException}. | ||||||
|  |      * | ||||||
|  |      * @param modelName The name of the model to search for in the library. | ||||||
|  |      * @param tag       The tag name to search for within the specified model. | ||||||
|  |      * @return The {@link LibraryModelTag} associated with the specified model and tag. | ||||||
|  |      * @throws OllamaBaseException    If there is a problem with the Ollama library operations. | ||||||
|  |      * @throws IOException            If an I/O error occurs during the operation. | ||||||
|  |      * @throws URISyntaxException     If there is an error with the URI syntax. | ||||||
|  |      * @throws InterruptedException   If the operation is interrupted. | ||||||
|  |      * @throws NoSuchElementException If the model or the tag is not found. | ||||||
|  |      */ | ||||||
|  |     public LibraryModelTag findModelTagFromLibrary(String modelName, String tag) throws OllamaBaseException, IOException, URISyntaxException, InterruptedException { | ||||||
|  |         List<LibraryModel> libraryModels = this.listModelsFromLibrary(); | ||||||
|  |         LibraryModel libraryModel = libraryModels.stream().filter(model -> model.getName().equals(modelName)).findFirst().orElseThrow(() -> new NoSuchElementException(String.format("Model by name '%s' not found", modelName))); | ||||||
|  |         LibraryModelDetail libraryModelDetail = this.getLibraryModelDetails(libraryModel); | ||||||
|  |         LibraryModelTag libraryModelTag = libraryModelDetail.getTags().stream().filter(tagName -> tagName.getTag().equals(tag)).findFirst().orElseThrow(() -> new NoSuchElementException(String.format("Tag '%s' for model '%s' not found", tag, modelName))); | ||||||
|  |         return libraryModelTag; | ||||||
|  |     } | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
|      * Pull a model on the Ollama server from the list of <a |      * Pull a model on the Ollama server from the list of <a | ||||||
|      * href="https://ollama.ai/library">available models</a>. |      * href="https://ollama.ai/library">available models</a>. | ||||||
|      * |      * | ||||||
|      * @param modelName the name of the model |      * @param modelName the name of the model | ||||||
|  |      * @throws OllamaBaseException  if the response indicates an error status | ||||||
|  |      * @throws IOException          if an I/O error occurs during the HTTP request | ||||||
|  |      * @throws InterruptedException if the operation is interrupted | ||||||
|  |      * @throws URISyntaxException   if the URI for the request is malformed | ||||||
|      */ |      */ | ||||||
|     public void pullModel(String modelName) |     public void pullModel(String modelName) throws OllamaBaseException, IOException, URISyntaxException, InterruptedException { | ||||||
|             throws OllamaBaseException, IOException, URISyntaxException, InterruptedException { |  | ||||||
|         String url = this.host + "/api/pull"; |         String url = this.host + "/api/pull"; | ||||||
|         String jsonData = new ModelRequest(modelName).toString(); |         String jsonData = new ModelRequest(modelName).toString(); | ||||||
|         HttpRequest request = |         HttpRequest request = getRequestBuilderDefault(new URI(url)).POST(HttpRequest.BodyPublishers.ofString(jsonData)).header("Accept", "application/json").header("Content-type", "application/json").build(); | ||||||
|                 getRequestBuilderDefault(new URI(url)) |  | ||||||
|                         .POST(HttpRequest.BodyPublishers.ofString(jsonData)) |  | ||||||
|                         .header("Accept", "application/json") |  | ||||||
|                         .header("Content-type", "application/json") |  | ||||||
|                         .build(); |  | ||||||
|         HttpClient client = HttpClient.newHttpClient(); |         HttpClient client = HttpClient.newHttpClient(); | ||||||
|         HttpResponse<InputStream> response = |         HttpResponse<InputStream> response = client.send(request, HttpResponse.BodyHandlers.ofInputStream()); | ||||||
|                 client.send(request, HttpResponse.BodyHandlers.ofInputStream()); |  | ||||||
|         int statusCode = response.statusCode(); |         int statusCode = response.statusCode(); | ||||||
|         InputStream responseBodyStream = response.body(); |         InputStream responseBodyStream = response.body(); | ||||||
|         String responseString = ""; |         String responseString = ""; | ||||||
|         try (BufferedReader reader = |         try (BufferedReader reader = new BufferedReader(new InputStreamReader(responseBodyStream, StandardCharsets.UTF_8))) { | ||||||
|                      new BufferedReader(new InputStreamReader(responseBodyStream, StandardCharsets.UTF_8))) { |  | ||||||
|             String line; |             String line; | ||||||
|             while ((line = reader.readLine()) != null) { |             while ((line = reader.readLine()) != null) { | ||||||
|                 ModelPullResponse modelPullResponse = |                 ModelPullResponse modelPullResponse = Utils.getObjectMapper().readValue(line, ModelPullResponse.class); | ||||||
|                         Utils.getObjectMapper().readValue(line, ModelPullResponse.class); |  | ||||||
|                 if (verbose) { |                 if (verbose) { | ||||||
|                     logger.info(modelPullResponse.getStatus()); |                     logger.info(modelPullResponse.getStatus()); | ||||||
|                 } |                 } | ||||||
| @@ -215,22 +338,37 @@ public class OllamaAPI { | |||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * Pulls a model using the specified Ollama library model tag. | ||||||
|  |      * The model is identified by a name and a tag, which are combined into a single identifier | ||||||
|  |      * in the format "name:tag" to pull the corresponding model. | ||||||
|  |      * | ||||||
|  |      * @param libraryModelTag the {@link LibraryModelTag} object containing the name and tag | ||||||
|  |      *                        of the model to be pulled. | ||||||
|  |      * @throws OllamaBaseException  if the response indicates an error status | ||||||
|  |      * @throws IOException          if an I/O error occurs during the HTTP request | ||||||
|  |      * @throws InterruptedException if the operation is interrupted | ||||||
|  |      * @throws URISyntaxException   if the URI for the request is malformed | ||||||
|  |      */ | ||||||
|  |     public void pullModel(LibraryModelTag libraryModelTag) throws OllamaBaseException, IOException, URISyntaxException, InterruptedException { | ||||||
|  |         String tagToPull = String.format("%s:%s", libraryModelTag.getName(), libraryModelTag.getTag()); | ||||||
|  |         pullModel(tagToPull); | ||||||
|  |     } | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
|      * Gets model details from the Ollama server. |      * Gets model details from the Ollama server. | ||||||
|      * |      * | ||||||
|      * @param modelName the model |      * @param modelName the model | ||||||
|      * @return the model details |      * @return the model details | ||||||
|  |      * @throws OllamaBaseException  if the response indicates an error status | ||||||
|  |      * @throws IOException          if an I/O error occurs during the HTTP request | ||||||
|  |      * @throws InterruptedException if the operation is interrupted | ||||||
|  |      * @throws URISyntaxException   if the URI for the request is malformed | ||||||
|      */ |      */ | ||||||
|     public ModelDetail getModelDetails(String modelName) |     public ModelDetail getModelDetails(String modelName) throws IOException, OllamaBaseException, InterruptedException, URISyntaxException { | ||||||
|             throws IOException, OllamaBaseException, InterruptedException, URISyntaxException { |  | ||||||
|         String url = this.host + "/api/show"; |         String url = this.host + "/api/show"; | ||||||
|         String jsonData = new ModelRequest(modelName).toString(); |         String jsonData = new ModelRequest(modelName).toString(); | ||||||
|         HttpRequest request = |         HttpRequest request = getRequestBuilderDefault(new URI(url)).header("Accept", "application/json").header("Content-type", "application/json").POST(HttpRequest.BodyPublishers.ofString(jsonData)).build(); | ||||||
|                 getRequestBuilderDefault(new URI(url)) |  | ||||||
|                         .header("Accept", "application/json") |  | ||||||
|                         .header("Content-type", "application/json") |  | ||||||
|                         .POST(HttpRequest.BodyPublishers.ofString(jsonData)) |  | ||||||
|                         .build(); |  | ||||||
|         HttpClient client = HttpClient.newHttpClient(); |         HttpClient client = HttpClient.newHttpClient(); | ||||||
|         HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString()); |         HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString()); | ||||||
|         int statusCode = response.statusCode(); |         int statusCode = response.statusCode(); | ||||||
| @@ -248,17 +386,16 @@ public class OllamaAPI { | |||||||
|      * |      * | ||||||
|      * @param modelName     the name of the custom model to be created. |      * @param modelName     the name of the custom model to be created. | ||||||
|      * @param modelFilePath the path to model file that exists on the Ollama server. |      * @param modelFilePath the path to model file that exists on the Ollama server. | ||||||
|  |      * @throws OllamaBaseException  if the response indicates an error status | ||||||
|  |      * @throws IOException          if an I/O error occurs during the HTTP request | ||||||
|  |      * @throws InterruptedException if the operation is interrupted | ||||||
|  |      * @throws URISyntaxException   if the URI for the request is malformed | ||||||
|      */ |      */ | ||||||
|     public void createModelWithFilePath(String modelName, String modelFilePath) |     @Deprecated | ||||||
|             throws IOException, InterruptedException, OllamaBaseException, URISyntaxException { |     public void createModelWithFilePath(String modelName, String modelFilePath) throws IOException, InterruptedException, OllamaBaseException, URISyntaxException { | ||||||
|         String url = this.host + "/api/create"; |         String url = this.host + "/api/create"; | ||||||
|         String jsonData = new CustomModelFilePathRequest(modelName, modelFilePath).toString(); |         String jsonData = new CustomModelFilePathRequest(modelName, modelFilePath).toString(); | ||||||
|         HttpRequest request = |         HttpRequest request = getRequestBuilderDefault(new URI(url)).header("Accept", "application/json").header("Content-Type", "application/json").POST(HttpRequest.BodyPublishers.ofString(jsonData, StandardCharsets.UTF_8)).build(); | ||||||
|                 getRequestBuilderDefault(new URI(url)) |  | ||||||
|                         .header("Accept", "application/json") |  | ||||||
|                         .header("Content-Type", "application/json") |  | ||||||
|                         .POST(HttpRequest.BodyPublishers.ofString(jsonData, StandardCharsets.UTF_8)) |  | ||||||
|                         .build(); |  | ||||||
|         HttpClient client = HttpClient.newHttpClient(); |         HttpClient client = HttpClient.newHttpClient(); | ||||||
|         HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString()); |         HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString()); | ||||||
|         int statusCode = response.statusCode(); |         int statusCode = response.statusCode(); | ||||||
| @@ -282,17 +419,45 @@ public class OllamaAPI { | |||||||
|      * |      * | ||||||
|      * @param modelName         the name of the custom model to be created. |      * @param modelName         the name of the custom model to be created. | ||||||
|      * @param modelFileContents the path to model file that exists on the Ollama server. |      * @param modelFileContents the path to model file that exists on the Ollama server. | ||||||
|  |      * @throws OllamaBaseException  if the response indicates an error status | ||||||
|  |      * @throws IOException          if an I/O error occurs during the HTTP request | ||||||
|  |      * @throws InterruptedException if the operation is interrupted | ||||||
|  |      * @throws URISyntaxException   if the URI for the request is malformed | ||||||
|      */ |      */ | ||||||
|     public void createModelWithModelFileContents(String modelName, String modelFileContents) |     @Deprecated | ||||||
|             throws IOException, InterruptedException, OllamaBaseException, URISyntaxException { |     public void createModelWithModelFileContents(String modelName, String modelFileContents) throws IOException, InterruptedException, OllamaBaseException, URISyntaxException { | ||||||
|         String url = this.host + "/api/create"; |         String url = this.host + "/api/create"; | ||||||
|         String jsonData = new CustomModelFileContentsRequest(modelName, modelFileContents).toString(); |         String jsonData = new CustomModelFileContentsRequest(modelName, modelFileContents).toString(); | ||||||
|         HttpRequest request = |         HttpRequest request = getRequestBuilderDefault(new URI(url)).header("Accept", "application/json").header("Content-Type", "application/json").POST(HttpRequest.BodyPublishers.ofString(jsonData, StandardCharsets.UTF_8)).build(); | ||||||
|                 getRequestBuilderDefault(new URI(url)) |         HttpClient client = HttpClient.newHttpClient(); | ||||||
|                         .header("Accept", "application/json") |         HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString()); | ||||||
|                         .header("Content-Type", "application/json") |         int statusCode = response.statusCode(); | ||||||
|                         .POST(HttpRequest.BodyPublishers.ofString(jsonData, StandardCharsets.UTF_8)) |         String responseString = response.body(); | ||||||
|                         .build(); |         if (statusCode != 200) { | ||||||
|  |             throw new OllamaBaseException(statusCode + " - " + responseString); | ||||||
|  |         } | ||||||
|  |         if (responseString.contains("error")) { | ||||||
|  |             throw new OllamaBaseException(responseString); | ||||||
|  |         } | ||||||
|  |         if (verbose) { | ||||||
|  |             logger.info(responseString); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * Create a custom model. Read more about custom model creation <a | ||||||
|  |      * href="https://github.com/ollama/ollama/blob/main/docs/api.md#create-a-model">here</a>. | ||||||
|  |      * | ||||||
|  |      * @param customModelRequest custom model spec | ||||||
|  |      * @throws OllamaBaseException  if the response indicates an error status | ||||||
|  |      * @throws IOException          if an I/O error occurs during the HTTP request | ||||||
|  |      * @throws InterruptedException if the operation is interrupted | ||||||
|  |      * @throws URISyntaxException   if the URI for the request is malformed | ||||||
|  |      */ | ||||||
|  |     public void createModel(CustomModelRequest customModelRequest) throws IOException, InterruptedException, OllamaBaseException, URISyntaxException { | ||||||
|  |         String url = this.host + "/api/create"; | ||||||
|  |         String jsonData = customModelRequest.toString(); | ||||||
|  |         HttpRequest request = getRequestBuilderDefault(new URI(url)).header("Accept", "application/json").header("Content-Type", "application/json").POST(HttpRequest.BodyPublishers.ofString(jsonData, StandardCharsets.UTF_8)).build(); | ||||||
|         HttpClient client = HttpClient.newHttpClient(); |         HttpClient client = HttpClient.newHttpClient(); | ||||||
|         HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString()); |         HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString()); | ||||||
|         int statusCode = response.statusCode(); |         int statusCode = response.statusCode(); | ||||||
| @@ -313,17 +478,15 @@ public class OllamaAPI { | |||||||
|      * |      * | ||||||
|      * @param modelName          the name of the model to be deleted. |      * @param modelName          the name of the model to be deleted. | ||||||
|      * @param ignoreIfNotPresent ignore errors if the specified model is not present on Ollama server. |      * @param ignoreIfNotPresent ignore errors if the specified model is not present on Ollama server. | ||||||
|  |      * @throws OllamaBaseException  if the response indicates an error status | ||||||
|  |      * @throws IOException          if an I/O error occurs during the HTTP request | ||||||
|  |      * @throws InterruptedException if the operation is interrupted | ||||||
|  |      * @throws URISyntaxException   if the URI for the request is malformed | ||||||
|      */ |      */ | ||||||
|     public void deleteModel(String modelName, boolean ignoreIfNotPresent) |     public void deleteModel(String modelName, boolean ignoreIfNotPresent) throws IOException, InterruptedException, OllamaBaseException, URISyntaxException { | ||||||
|             throws IOException, InterruptedException, OllamaBaseException, URISyntaxException { |  | ||||||
|         String url = this.host + "/api/delete"; |         String url = this.host + "/api/delete"; | ||||||
|         String jsonData = new ModelRequest(modelName).toString(); |         String jsonData = new ModelRequest(modelName).toString(); | ||||||
|         HttpRequest request = |         HttpRequest request = getRequestBuilderDefault(new URI(url)).method("DELETE", HttpRequest.BodyPublishers.ofString(jsonData, StandardCharsets.UTF_8)).header("Accept", "application/json").header("Content-type", "application/json").build(); | ||||||
|                 getRequestBuilderDefault(new URI(url)) |  | ||||||
|                         .method("DELETE", HttpRequest.BodyPublishers.ofString(jsonData, StandardCharsets.UTF_8)) |  | ||||||
|                         .header("Accept", "application/json") |  | ||||||
|                         .header("Content-type", "application/json") |  | ||||||
|                         .build(); |  | ||||||
|         HttpClient client = HttpClient.newHttpClient(); |         HttpClient client = HttpClient.newHttpClient(); | ||||||
|         HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString()); |         HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString()); | ||||||
|         int statusCode = response.statusCode(); |         int statusCode = response.statusCode(); | ||||||
| @@ -342,9 +505,13 @@ public class OllamaAPI { | |||||||
|      * @param model  name of model to generate embeddings from |      * @param model  name of model to generate embeddings from | ||||||
|      * @param prompt text to generate embeddings for |      * @param prompt text to generate embeddings for | ||||||
|      * @return embeddings |      * @return embeddings | ||||||
|  |      * @throws OllamaBaseException  if the response indicates an error status | ||||||
|  |      * @throws IOException          if an I/O error occurs during the HTTP request | ||||||
|  |      * @throws InterruptedException if the operation is interrupted | ||||||
|  |      * @deprecated Use {@link #embed(String, List)} instead. | ||||||
|      */ |      */ | ||||||
|     public List<Double> generateEmbeddings(String model, String prompt) |     @Deprecated | ||||||
|             throws IOException, InterruptedException, OllamaBaseException { |     public List<Double> generateEmbeddings(String model, String prompt) throws IOException, InterruptedException, OllamaBaseException { | ||||||
|         return generateEmbeddings(new OllamaEmbeddingsRequestModel(model, prompt)); |         return generateEmbeddings(new OllamaEmbeddingsRequestModel(model, prompt)); | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -353,28 +520,69 @@ public class OllamaAPI { | |||||||
|      * |      * | ||||||
|      * @param modelRequest request for '/api/embeddings' endpoint |      * @param modelRequest request for '/api/embeddings' endpoint | ||||||
|      * @return embeddings |      * @return embeddings | ||||||
|  |      * @throws OllamaBaseException  if the response indicates an error status | ||||||
|  |      * @throws IOException          if an I/O error occurs during the HTTP request | ||||||
|  |      * @throws InterruptedException if the operation is interrupted | ||||||
|  |      * @deprecated Use {@link #embed(OllamaEmbedRequestModel)} instead. | ||||||
|      */ |      */ | ||||||
|  |     @Deprecated | ||||||
|     public List<Double> generateEmbeddings(OllamaEmbeddingsRequestModel modelRequest) throws IOException, InterruptedException, OllamaBaseException { |     public List<Double> generateEmbeddings(OllamaEmbeddingsRequestModel modelRequest) throws IOException, InterruptedException, OllamaBaseException { | ||||||
|         URI uri = URI.create(this.host + "/api/embeddings"); |         URI uri = URI.create(this.host + "/api/embeddings"); | ||||||
|         String jsonData = modelRequest.toString(); |         String jsonData = modelRequest.toString(); | ||||||
|         HttpClient httpClient = HttpClient.newHttpClient(); |         HttpClient httpClient = HttpClient.newHttpClient(); | ||||||
|         HttpRequest.Builder requestBuilder = |         HttpRequest.Builder requestBuilder = getRequestBuilderDefault(uri).header("Accept", "application/json").POST(HttpRequest.BodyPublishers.ofString(jsonData)); | ||||||
|                 getRequestBuilderDefault(uri) |  | ||||||
|                         .header("Accept", "application/json") |  | ||||||
|                         .POST(HttpRequest.BodyPublishers.ofString(jsonData)); |  | ||||||
|         HttpRequest request = requestBuilder.build(); |         HttpRequest request = requestBuilder.build(); | ||||||
|         HttpResponse<String> response = httpClient.send(request, HttpResponse.BodyHandlers.ofString()); |         HttpResponse<String> response = httpClient.send(request, HttpResponse.BodyHandlers.ofString()); | ||||||
|         int statusCode = response.statusCode(); |         int statusCode = response.statusCode(); | ||||||
|         String responseBody = response.body(); |         String responseBody = response.body(); | ||||||
|         if (statusCode == 200) { |         if (statusCode == 200) { | ||||||
|             OllamaEmbeddingResponseModel embeddingResponse = |             OllamaEmbeddingResponseModel embeddingResponse = Utils.getObjectMapper().readValue(responseBody, OllamaEmbeddingResponseModel.class); | ||||||
|                     Utils.getObjectMapper().readValue(responseBody, OllamaEmbeddingResponseModel.class); |  | ||||||
|             return embeddingResponse.getEmbedding(); |             return embeddingResponse.getEmbedding(); | ||||||
|         } else { |         } else { | ||||||
|             throw new OllamaBaseException(statusCode + " - " + responseBody); |             throw new OllamaBaseException(statusCode + " - " + responseBody); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * Generate embeddings for a given text from a model | ||||||
|  |      * | ||||||
|  |      * @param model  name of model to generate embeddings from | ||||||
|  |      * @param inputs text/s to generate embeddings for | ||||||
|  |      * @return embeddings | ||||||
|  |      * @throws OllamaBaseException  if the response indicates an error status | ||||||
|  |      * @throws IOException          if an I/O error occurs during the HTTP request | ||||||
|  |      * @throws InterruptedException if the operation is interrupted | ||||||
|  |      */ | ||||||
|  |     public OllamaEmbedResponseModel embed(String model, List<String> inputs) throws IOException, InterruptedException, OllamaBaseException { | ||||||
|  |         return embed(new OllamaEmbedRequestModel(model, inputs)); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * Generate embeddings using a {@link OllamaEmbedRequestModel}. | ||||||
|  |      * | ||||||
|  |      * @param modelRequest request for '/api/embed' endpoint | ||||||
|  |      * @return embeddings | ||||||
|  |      * @throws OllamaBaseException  if the response indicates an error status | ||||||
|  |      * @throws IOException          if an I/O error occurs during the HTTP request | ||||||
|  |      * @throws InterruptedException if the operation is interrupted | ||||||
|  |      */ | ||||||
|  |     public OllamaEmbedResponseModel embed(OllamaEmbedRequestModel modelRequest) throws IOException, InterruptedException, OllamaBaseException { | ||||||
|  |         URI uri = URI.create(this.host + "/api/embed"); | ||||||
|  |         String jsonData = Utils.getObjectMapper().writeValueAsString(modelRequest); | ||||||
|  |         HttpClient httpClient = HttpClient.newHttpClient(); | ||||||
|  |  | ||||||
|  |         HttpRequest request = HttpRequest.newBuilder(uri).header("Accept", "application/json").POST(HttpRequest.BodyPublishers.ofString(jsonData)).build(); | ||||||
|  |  | ||||||
|  |         HttpResponse<String> response = httpClient.send(request, HttpResponse.BodyHandlers.ofString()); | ||||||
|  |         int statusCode = response.statusCode(); | ||||||
|  |         String responseBody = response.body(); | ||||||
|  |  | ||||||
|  |         if (statusCode == 200) { | ||||||
|  |             return Utils.getObjectMapper().readValue(responseBody, OllamaEmbedResponseModel.class); | ||||||
|  |         } else { | ||||||
|  |             throw new OllamaBaseException(statusCode + " - " + responseBody); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
|      * Generate response for a question to a model running on Ollama server. This is a sync/blocking |      * Generate response for a question to a model running on Ollama server. This is a sync/blocking | ||||||
| @@ -387,9 +595,11 @@ public class OllamaAPI { | |||||||
|      *                      details on the options</a> |      *                      details on the options</a> | ||||||
|      * @param streamHandler optional callback consumer that will be applied every time a streamed response is received. If not set, the stream parameter of the request is set to false. |      * @param streamHandler optional callback consumer that will be applied every time a streamed response is received. If not set, the stream parameter of the request is set to false. | ||||||
|      * @return OllamaResult that includes response text and time taken for response |      * @return OllamaResult that includes response text and time taken for response | ||||||
|  |      * @throws OllamaBaseException  if the response indicates an error status | ||||||
|  |      * @throws IOException          if an I/O error occurs during the HTTP request | ||||||
|  |      * @throws InterruptedException if the operation is interrupted | ||||||
|      */ |      */ | ||||||
|     public OllamaResult generate(String model, String prompt, boolean raw, Options options, OllamaStreamHandler streamHandler) |     public OllamaResult generate(String model, String prompt, boolean raw, Options options, OllamaStreamHandler streamHandler) throws OllamaBaseException, IOException, InterruptedException { | ||||||
|             throws OllamaBaseException, IOException, InterruptedException { |  | ||||||
|         OllamaGenerateRequest ollamaRequestModel = new OllamaGenerateRequest(model, prompt); |         OllamaGenerateRequest ollamaRequestModel = new OllamaGenerateRequest(model, prompt); | ||||||
|         ollamaRequestModel.setRaw(raw); |         ollamaRequestModel.setRaw(raw); | ||||||
|         ollamaRequestModel.setOptions(options.getOptionsMap()); |         ollamaRequestModel.setOptions(options.getOptionsMap()); | ||||||
| @@ -406,13 +616,14 @@ public class OllamaAPI { | |||||||
|      * @param raw     In some cases, you may wish to bypass the templating system and provide a full prompt. In this case, you can use the raw parameter to disable templating. Also note that raw mode will not return a context. |      * @param raw     In some cases, you may wish to bypass the templating system and provide a full prompt. In this case, you can use the raw parameter to disable templating. Also note that raw mode will not return a context. | ||||||
|      * @param options Additional options or configurations to use when generating the response. |      * @param options Additional options or configurations to use when generating the response. | ||||||
|      * @return {@link OllamaResult} |      * @return {@link OllamaResult} | ||||||
|  |      * @throws OllamaBaseException  if the response indicates an error status | ||||||
|  |      * @throws IOException          if an I/O error occurs during the HTTP request | ||||||
|  |      * @throws InterruptedException if the operation is interrupted | ||||||
|      */ |      */ | ||||||
|     public OllamaResult generate(String model, String prompt, boolean raw, Options options) |     public OllamaResult generate(String model, String prompt, boolean raw, Options options) throws OllamaBaseException, IOException, InterruptedException { | ||||||
|             throws OllamaBaseException, IOException, InterruptedException { |  | ||||||
|         return generate(model, prompt, raw, options, null); |         return generate(model, prompt, raw, options, null); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
|      * Generates response using the specified AI model and prompt (in blocking mode), and then invokes a set of tools |      * Generates response using the specified AI model and prompt (in blocking mode), and then invokes a set of tools | ||||||
|      * on the generated response. |      * on the generated response. | ||||||
| @@ -421,17 +632,24 @@ public class OllamaAPI { | |||||||
|      * @param prompt  The input text or prompt to provide to the AI model. |      * @param prompt  The input text or prompt to provide to the AI model. | ||||||
|      * @param options Additional options or configurations to use when generating the response. |      * @param options Additional options or configurations to use when generating the response. | ||||||
|      * @return {@link OllamaToolsResult} An OllamaToolsResult object containing the response from the AI model and the results of invoking the tools on that output. |      * @return {@link OllamaToolsResult} An OllamaToolsResult object containing the response from the AI model and the results of invoking the tools on that output. | ||||||
|      * @throws OllamaBaseException  If there is an error related to the Ollama API or service. |      * @throws OllamaBaseException  if the response indicates an error status | ||||||
|      * @throws IOException          If there is an error related to input/output operations. |      * @throws IOException          if an I/O error occurs during the HTTP request | ||||||
|      * @throws InterruptedException If the method is interrupted while waiting for the AI model |      * @throws InterruptedException if the operation is interrupted | ||||||
|      *                              to generate the response or for the tools to be invoked. |  | ||||||
|      */ |      */ | ||||||
|     public OllamaToolsResult generateWithTools(String model, String prompt, Options options) |     public OllamaToolsResult generateWithTools(String model, String prompt, Options options) throws OllamaBaseException, IOException, InterruptedException, ToolInvocationException { | ||||||
|             throws OllamaBaseException, IOException, InterruptedException, ToolInvocationException { |  | ||||||
|         boolean raw = true; |         boolean raw = true; | ||||||
|         OllamaToolsResult toolResult = new OllamaToolsResult(); |         OllamaToolsResult toolResult = new OllamaToolsResult(); | ||||||
|         Map<ToolFunctionCallSpec, Object> toolResults = new HashMap<>(); |         Map<ToolFunctionCallSpec, Object> toolResults = new HashMap<>(); | ||||||
|  |  | ||||||
|  |         if (!prompt.startsWith("[AVAILABLE_TOOLS]")) { | ||||||
|  |             final Tools.PromptBuilder promptBuilder = new Tools.PromptBuilder(); | ||||||
|  |             for (Tools.ToolSpecification spec : toolRegistry.getRegisteredSpecs()) { | ||||||
|  |                 promptBuilder.withToolSpecification(spec); | ||||||
|  |             } | ||||||
|  |             promptBuilder.withPrompt(prompt); | ||||||
|  |             prompt = promptBuilder.build(); | ||||||
|  |         } | ||||||
|  |  | ||||||
|         OllamaResult result = generate(model, prompt, raw, options, null); |         OllamaResult result = generate(model, prompt, raw, options, null); | ||||||
|         toolResult.setModelResult(result); |         toolResult.setModelResult(result); | ||||||
|  |  | ||||||
| @@ -448,7 +666,6 @@ public class OllamaAPI { | |||||||
|         return toolResult; |         return toolResult; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
|      * Generate response for a question to a model running on Ollama server and get a callback handle |      * Generate response for a question to a model running on Ollama server and get a callback handle | ||||||
|      * that can be used to check for status and get the response from the model later. This would be |      * that can be used to check for status and get the response from the model later. This would be | ||||||
| @@ -462,9 +679,7 @@ public class OllamaAPI { | |||||||
|         OllamaGenerateRequest ollamaRequestModel = new OllamaGenerateRequest(model, prompt); |         OllamaGenerateRequest ollamaRequestModel = new OllamaGenerateRequest(model, prompt); | ||||||
|         ollamaRequestModel.setRaw(raw); |         ollamaRequestModel.setRaw(raw); | ||||||
|         URI uri = URI.create(this.host + "/api/generate"); |         URI uri = URI.create(this.host + "/api/generate"); | ||||||
|         OllamaAsyncResultStreamer ollamaAsyncResultStreamer = |         OllamaAsyncResultStreamer ollamaAsyncResultStreamer = new OllamaAsyncResultStreamer(getRequestBuilderDefault(uri), ollamaRequestModel, requestTimeoutSeconds); | ||||||
|                 new OllamaAsyncResultStreamer( |  | ||||||
|                         getRequestBuilderDefault(uri), ollamaRequestModel, requestTimeoutSeconds); |  | ||||||
|         ollamaAsyncResultStreamer.start(); |         ollamaAsyncResultStreamer.start(); | ||||||
|         return ollamaAsyncResultStreamer; |         return ollamaAsyncResultStreamer; | ||||||
|     } |     } | ||||||
| @@ -481,10 +696,11 @@ public class OllamaAPI { | |||||||
|      *                      details on the options</a> |      *                      details on the options</a> | ||||||
|      * @param streamHandler optional callback consumer that will be applied every time a streamed response is received. If not set, the stream parameter of the request is set to false. |      * @param streamHandler optional callback consumer that will be applied every time a streamed response is received. If not set, the stream parameter of the request is set to false. | ||||||
|      * @return OllamaResult that includes response text and time taken for response |      * @return OllamaResult that includes response text and time taken for response | ||||||
|  |      * @throws OllamaBaseException  if the response indicates an error status | ||||||
|  |      * @throws IOException          if an I/O error occurs during the HTTP request | ||||||
|  |      * @throws InterruptedException if the operation is interrupted | ||||||
|      */ |      */ | ||||||
|     public OllamaResult generateWithImageFiles( |     public OllamaResult generateWithImageFiles(String model, String prompt, List<File> imageFiles, Options options, OllamaStreamHandler streamHandler) throws OllamaBaseException, IOException, InterruptedException { | ||||||
|             String model, String prompt, List<File> imageFiles, Options options, OllamaStreamHandler streamHandler) |  | ||||||
|             throws OllamaBaseException, IOException, InterruptedException { |  | ||||||
|         List<String> images = new ArrayList<>(); |         List<String> images = new ArrayList<>(); | ||||||
|         for (File imageFile : imageFiles) { |         for (File imageFile : imageFiles) { | ||||||
|             images.add(encodeFileToBase64(imageFile)); |             images.add(encodeFileToBase64(imageFile)); | ||||||
| @@ -498,10 +714,12 @@ public class OllamaAPI { | |||||||
|      * Convenience method to call Ollama API without streaming responses. |      * Convenience method to call Ollama API without streaming responses. | ||||||
|      * <p> |      * <p> | ||||||
|      * Uses {@link #generateWithImageFiles(String, String, List, Options, OllamaStreamHandler)} |      * Uses {@link #generateWithImageFiles(String, String, List, Options, OllamaStreamHandler)} | ||||||
|  |      * | ||||||
|  |      * @throws OllamaBaseException  if the response indicates an error status | ||||||
|  |      * @throws IOException          if an I/O error occurs during the HTTP request | ||||||
|  |      * @throws InterruptedException if the operation is interrupted | ||||||
|      */ |      */ | ||||||
|     public OllamaResult generateWithImageFiles( |     public OllamaResult generateWithImageFiles(String model, String prompt, List<File> imageFiles, Options options) throws OllamaBaseException, IOException, InterruptedException { | ||||||
|             String model, String prompt, List<File> imageFiles, Options options) |  | ||||||
|             throws OllamaBaseException, IOException, InterruptedException { |  | ||||||
|         return generateWithImageFiles(model, prompt, imageFiles, options, null); |         return generateWithImageFiles(model, prompt, imageFiles, options, null); | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -517,10 +735,12 @@ public class OllamaAPI { | |||||||
|      *                      details on the options</a> |      *                      details on the options</a> | ||||||
|      * @param streamHandler optional callback consumer that will be applied every time a streamed response is received. If not set, the stream parameter of the request is set to false. |      * @param streamHandler optional callback consumer that will be applied every time a streamed response is received. If not set, the stream parameter of the request is set to false. | ||||||
|      * @return OllamaResult that includes response text and time taken for response |      * @return OllamaResult that includes response text and time taken for response | ||||||
|  |      * @throws OllamaBaseException  if the response indicates an error status | ||||||
|  |      * @throws IOException          if an I/O error occurs during the HTTP request | ||||||
|  |      * @throws InterruptedException if the operation is interrupted | ||||||
|  |      * @throws URISyntaxException   if the URI for the request is malformed | ||||||
|      */ |      */ | ||||||
|     public OllamaResult generateWithImageURLs( |     public OllamaResult generateWithImageURLs(String model, String prompt, List<String> imageURLs, Options options, OllamaStreamHandler streamHandler) throws OllamaBaseException, IOException, InterruptedException, URISyntaxException { | ||||||
|             String model, String prompt, List<String> imageURLs, Options options, OllamaStreamHandler streamHandler) |  | ||||||
|             throws OllamaBaseException, IOException, InterruptedException, URISyntaxException { |  | ||||||
|         List<String> images = new ArrayList<>(); |         List<String> images = new ArrayList<>(); | ||||||
|         for (String imageURL : imageURLs) { |         for (String imageURL : imageURLs) { | ||||||
|             images.add(encodeByteArrayToBase64(Utils.loadImageBytesFromUrl(imageURL))); |             images.add(encodeByteArrayToBase64(Utils.loadImageBytesFromUrl(imageURL))); | ||||||
| @@ -534,14 +754,16 @@ public class OllamaAPI { | |||||||
|      * Convenience method to call Ollama API without streaming responses. |      * Convenience method to call Ollama API without streaming responses. | ||||||
|      * <p> |      * <p> | ||||||
|      * Uses {@link #generateWithImageURLs(String, String, List, Options, OllamaStreamHandler)} |      * Uses {@link #generateWithImageURLs(String, String, List, Options, OllamaStreamHandler)} | ||||||
|  |      * | ||||||
|  |      * @throws OllamaBaseException  if the response indicates an error status | ||||||
|  |      * @throws IOException          if an I/O error occurs during the HTTP request | ||||||
|  |      * @throws InterruptedException if the operation is interrupted | ||||||
|  |      * @throws URISyntaxException   if the URI for the request is malformed | ||||||
|      */ |      */ | ||||||
|     public OllamaResult generateWithImageURLs(String model, String prompt, List<String> imageURLs, |     public OllamaResult generateWithImageURLs(String model, String prompt, List<String> imageURLs, Options options) throws OllamaBaseException, IOException, InterruptedException, URISyntaxException { | ||||||
|                                               Options options) |  | ||||||
|             throws OllamaBaseException, IOException, InterruptedException, URISyntaxException { |  | ||||||
|         return generateWithImageURLs(model, prompt, imageURLs, options, null); |         return generateWithImageURLs(model, prompt, imageURLs, options, null); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
|      * Ask a question to a model based on a given message stack (i.e. a chat history). Creates a synchronous call to the api |      * Ask a question to a model based on a given message stack (i.e. a chat history). Creates a synchronous call to the api | ||||||
|      * 'api/chat'. |      * 'api/chat'. | ||||||
| @@ -552,6 +774,9 @@ public class OllamaAPI { | |||||||
|      * @throws OllamaBaseException  any response code than 200 has been returned |      * @throws OllamaBaseException  any response code than 200 has been returned | ||||||
|      * @throws IOException          in case the responseStream can not be read |      * @throws IOException          in case the responseStream can not be read | ||||||
|      * @throws InterruptedException in case the server is not reachable or network issues happen |      * @throws InterruptedException in case the server is not reachable or network issues happen | ||||||
|  |      * @throws OllamaBaseException  if the response indicates an error status | ||||||
|  |      * @throws IOException          if an I/O error occurs during the HTTP request | ||||||
|  |      * @throws InterruptedException if the operation is interrupted | ||||||
|      */ |      */ | ||||||
|     public OllamaChatResult chat(String model, List<OllamaChatMessage> messages) throws OllamaBaseException, IOException, InterruptedException { |     public OllamaChatResult chat(String model, List<OllamaChatMessage> messages) throws OllamaBaseException, IOException, InterruptedException { | ||||||
|         OllamaChatRequestBuilder builder = OllamaChatRequestBuilder.getInstance(model); |         OllamaChatRequestBuilder builder = OllamaChatRequestBuilder.getInstance(model); | ||||||
| @@ -568,6 +793,9 @@ public class OllamaAPI { | |||||||
|      * @throws OllamaBaseException  any response code than 200 has been returned |      * @throws OllamaBaseException  any response code than 200 has been returned | ||||||
|      * @throws IOException          in case the responseStream can not be read |      * @throws IOException          in case the responseStream can not be read | ||||||
|      * @throws InterruptedException in case the server is not reachable or network issues happen |      * @throws InterruptedException in case the server is not reachable or network issues happen | ||||||
|  |      * @throws OllamaBaseException  if the response indicates an error status | ||||||
|  |      * @throws IOException          if an I/O error occurs during the HTTP request | ||||||
|  |      * @throws InterruptedException if the operation is interrupted | ||||||
|      */ |      */ | ||||||
|     public OllamaChatResult chat(OllamaChatRequest request) throws OllamaBaseException, IOException, InterruptedException { |     public OllamaChatResult chat(OllamaChatRequest request) throws OllamaBaseException, IOException, InterruptedException { | ||||||
|         return chat(request, null); |         return chat(request, null); | ||||||
| @@ -584,38 +812,234 @@ public class OllamaAPI { | |||||||
|      * @throws OllamaBaseException  any response code than 200 has been returned |      * @throws OllamaBaseException  any response code than 200 has been returned | ||||||
|      * @throws IOException          in case the responseStream can not be read |      * @throws IOException          in case the responseStream can not be read | ||||||
|      * @throws InterruptedException in case the server is not reachable or network issues happen |      * @throws InterruptedException in case the server is not reachable or network issues happen | ||||||
|  |      * @throws OllamaBaseException  if the response indicates an error status | ||||||
|  |      * @throws IOException          if an I/O error occurs during the HTTP request | ||||||
|  |      * @throws InterruptedException if the operation is interrupted | ||||||
|      */ |      */ | ||||||
|     public OllamaChatResult chat(OllamaChatRequest request, OllamaStreamHandler streamHandler) throws OllamaBaseException, IOException, InterruptedException { |     public OllamaChatResult chat(OllamaChatRequest request, OllamaStreamHandler streamHandler) throws OllamaBaseException, IOException, InterruptedException { | ||||||
|  |         return chatStreaming(request, new OllamaChatStreamObserver(streamHandler)); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * Ask a question to a model using an {@link OllamaChatRequest}. This can be constructed using an {@link OllamaChatRequestBuilder}. | ||||||
|  |      * <p> | ||||||
|  |      * Hint: the OllamaChatRequestModel#getStream() property is not implemented. | ||||||
|  |      * | ||||||
|  |      * @param request      request object to be sent to the server | ||||||
|  |      * @param tokenHandler callback handler to handle the last token from stream (caution: all previous messages from stream will be concatenated) | ||||||
|  |      * @return {@link OllamaChatResult} | ||||||
|  |      * @throws OllamaBaseException  any response code than 200 has been returned | ||||||
|  |      * @throws IOException          in case the responseStream can not be read | ||||||
|  |      * @throws InterruptedException in case the server is not reachable or network issues happen | ||||||
|  |      * @throws OllamaBaseException  if the response indicates an error status | ||||||
|  |      * @throws IOException          if an I/O error occurs during the HTTP request | ||||||
|  |      * @throws InterruptedException if the operation is interrupted | ||||||
|  |      */ | ||||||
|  |     public OllamaChatResult chatStreaming(OllamaChatRequest request, OllamaTokenHandler tokenHandler) throws OllamaBaseException, IOException, InterruptedException { | ||||||
|         OllamaChatEndpointCaller requestCaller = new OllamaChatEndpointCaller(host, basicAuth, requestTimeoutSeconds, verbose); |         OllamaChatEndpointCaller requestCaller = new OllamaChatEndpointCaller(host, basicAuth, requestTimeoutSeconds, verbose); | ||||||
|         OllamaResult result; |         OllamaChatResult result; | ||||||
|         if (streamHandler != null) { |  | ||||||
|  |         // add all registered tools to Request | ||||||
|  |         request.setTools(toolRegistry.getRegisteredSpecs().stream().map(Tools.ToolSpecification::getToolPrompt).collect(Collectors.toList())); | ||||||
|  |  | ||||||
|  |         if (tokenHandler != null) { | ||||||
|             request.setStream(true); |             request.setStream(true); | ||||||
|             result = requestCaller.call(request, streamHandler); |             result = requestCaller.call(request, tokenHandler); | ||||||
|         } else { |         } else { | ||||||
|             result = requestCaller.callSync(request); |             result = requestCaller.callSync(request); | ||||||
|         } |         } | ||||||
|         return new OllamaChatResult(result.getResponse(), result.getResponseTime(), result.getHttpStatusCode(), request.getMessages()); |  | ||||||
|  |         // check if toolCallIsWanted | ||||||
|  |         List<OllamaChatToolCalls> toolCalls = result.getResponseModel().getMessage().getToolCalls(); | ||||||
|  |         int toolCallTries = 0; | ||||||
|  |         while (toolCalls != null && !toolCalls.isEmpty() && toolCallTries < maxChatToolCallRetries) { | ||||||
|  |             for (OllamaChatToolCalls toolCall : toolCalls) { | ||||||
|  |                 String toolName = toolCall.getFunction().getName(); | ||||||
|  |                 ToolFunction toolFunction = toolRegistry.getToolFunction(toolName); | ||||||
|  |                 Map<String, Object> arguments = toolCall.getFunction().getArguments(); | ||||||
|  |                 Object res = toolFunction.apply(arguments); | ||||||
|  |                 request.getMessages().add(new OllamaChatMessage(OllamaChatMessageRole.TOOL, "[TOOL_RESULTS]" + toolName + "(" + arguments.keySet() + ") : " + res + "[/TOOL_RESULTS]")); | ||||||
|             } |             } | ||||||
|  |  | ||||||
|     public void registerTool(Tools.ToolSpecification toolSpecification) { |             if (tokenHandler != null) { | ||||||
|         toolRegistry.addFunction(toolSpecification.getFunctionName(), toolSpecification.getToolDefinition()); |                 result = requestCaller.call(request, tokenHandler); | ||||||
|  |             } else { | ||||||
|  |                 result = requestCaller.callSync(request); | ||||||
|             } |             } | ||||||
|  |             toolCalls = result.getResponseModel().getMessage().getToolCalls(); | ||||||
|  |             toolCallTries++; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         return result; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * Registers a single tool in the tool registry using the provided tool specification. | ||||||
|  |      * | ||||||
|  |      * @param toolSpecification the specification of the tool to register. It contains the | ||||||
|  |      *                          tool's function name and other relevant information. | ||||||
|  |      */ | ||||||
|  |     public void registerTool(Tools.ToolSpecification toolSpecification) { | ||||||
|  |         toolRegistry.addTool(toolSpecification.getFunctionName(), toolSpecification); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * Registers multiple tools in the tool registry using a list of tool specifications. | ||||||
|  |      * Iterates over the list and adds each tool specification to the registry. | ||||||
|  |      * | ||||||
|  |      * @param toolSpecifications a list of tool specifications to register. Each specification | ||||||
|  |      *                           contains information about a tool, such as its function name. | ||||||
|  |      */ | ||||||
|  |     public void registerTools(List<Tools.ToolSpecification> toolSpecifications) { | ||||||
|  |         for (Tools.ToolSpecification toolSpecification : toolSpecifications) { | ||||||
|  |             toolRegistry.addTool(toolSpecification.getFunctionName(), toolSpecification); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * Registers tools based on the annotations found on the methods of the caller's class and its providers. | ||||||
|  |      * This method scans the caller's class for the {@link OllamaToolService} annotation and recursively registers | ||||||
|  |      * annotated tools from all the providers specified in the annotation. | ||||||
|  |      * | ||||||
|  |      * @throws IllegalStateException if the caller's class is not annotated with {@link OllamaToolService}. | ||||||
|  |      * @throws RuntimeException      if any reflection-based instantiation or invocation fails. | ||||||
|  |      */ | ||||||
|  |     public void registerAnnotatedTools() { | ||||||
|  |         try { | ||||||
|  |             Class<?> callerClass = null; | ||||||
|  |             try { | ||||||
|  |                 callerClass = Class.forName(Thread.currentThread().getStackTrace()[2].getClassName()); | ||||||
|  |             } catch (ClassNotFoundException e) { | ||||||
|  |                 throw new RuntimeException(e); | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             OllamaToolService ollamaToolServiceAnnotation = callerClass.getDeclaredAnnotation(OllamaToolService.class); | ||||||
|  |             if (ollamaToolServiceAnnotation == null) { | ||||||
|  |                 throw new IllegalStateException(callerClass + " is not annotated as " + OllamaToolService.class); | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             Class<?>[] providers = ollamaToolServiceAnnotation.providers(); | ||||||
|  |             for (Class<?> provider : providers) { | ||||||
|  |                 registerAnnotatedTools(provider.getDeclaredConstructor().newInstance()); | ||||||
|  |             } | ||||||
|  |         } catch (InstantiationException | NoSuchMethodException | IllegalAccessException | | ||||||
|  |                  InvocationTargetException e) { | ||||||
|  |             throw new RuntimeException(e); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * Registers tools based on the annotations found on the methods of the provided object. | ||||||
|  |      * This method scans the methods of the given object and registers tools using the {@link ToolSpec} annotation | ||||||
|  |      * and associated {@link ToolProperty} annotations. It constructs tool specifications and stores them in a tool registry. | ||||||
|  |      * | ||||||
|  |      * @param object the object whose methods are to be inspected for annotated tools. | ||||||
|  |      * @throws RuntimeException if any reflection-based instantiation or invocation fails. | ||||||
|  |      */ | ||||||
|  |     public void registerAnnotatedTools(Object object) { | ||||||
|  |         Class<?> objectClass = object.getClass(); | ||||||
|  |         Method[] methods = objectClass.getMethods(); | ||||||
|  |         for (Method m : methods) { | ||||||
|  |             ToolSpec toolSpec = m.getDeclaredAnnotation(ToolSpec.class); | ||||||
|  |             if (toolSpec == null) { | ||||||
|  |                 continue; | ||||||
|  |             } | ||||||
|  |             String operationName = !toolSpec.name().isBlank() ? toolSpec.name() : m.getName(); | ||||||
|  |             String operationDesc = !toolSpec.desc().isBlank() ? toolSpec.desc() : operationName; | ||||||
|  |  | ||||||
|  |             final Tools.PropsBuilder propsBuilder = new Tools.PropsBuilder(); | ||||||
|  |             LinkedHashMap<String, String> methodParams = new LinkedHashMap<>(); | ||||||
|  |             for (Parameter parameter : m.getParameters()) { | ||||||
|  |                 final ToolProperty toolPropertyAnn = parameter.getDeclaredAnnotation(ToolProperty.class); | ||||||
|  |                 String propType = parameter.getType().getTypeName(); | ||||||
|  |                 if (toolPropertyAnn == null) { | ||||||
|  |                     methodParams.put(parameter.getName(), null); | ||||||
|  |                     continue; | ||||||
|  |                 } | ||||||
|  |                 String propName = !toolPropertyAnn.name().isBlank() ? toolPropertyAnn.name() : parameter.getName(); | ||||||
|  |                 methodParams.put(propName, propType); | ||||||
|  |                 propsBuilder.withProperty(propName, Tools.PromptFuncDefinition.Property.builder().type(propType).description(toolPropertyAnn.desc()).required(toolPropertyAnn.required()).build()); | ||||||
|  |             } | ||||||
|  |             final Map<String, Tools.PromptFuncDefinition.Property> params = propsBuilder.build(); | ||||||
|  |             List<String> reqProps = params.entrySet().stream().filter(e -> e.getValue().isRequired()).map(Map.Entry::getKey).collect(Collectors.toList()); | ||||||
|  |  | ||||||
|  |             Tools.ToolSpecification toolSpecification = Tools.ToolSpecification.builder().functionName(operationName).functionDescription(operationDesc).toolPrompt(Tools.PromptFuncDefinition.builder().type("function").function(Tools.PromptFuncDefinition.PromptFuncSpec.builder().name(operationName).description(operationDesc).parameters(Tools.PromptFuncDefinition.Parameters.builder().type("object").properties(params).required(reqProps).build()).build()).build()).build(); | ||||||
|  |  | ||||||
|  |             ReflectionalToolFunction reflectionalToolFunction = new ReflectionalToolFunction(object, m, methodParams); | ||||||
|  |             toolSpecification.setToolFunction(reflectionalToolFunction); | ||||||
|  |             toolRegistry.addTool(toolSpecification.getFunctionName(), toolSpecification); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * Adds a custom role. | ||||||
|  |      * | ||||||
|  |      * @param roleName the name of the custom role to be added | ||||||
|  |      * @return the newly created OllamaChatMessageRole | ||||||
|  |      */ | ||||||
|  |     public OllamaChatMessageRole addCustomRole(String roleName) { | ||||||
|  |         return OllamaChatMessageRole.newCustomRole(roleName); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * Lists all available roles. | ||||||
|  |      * | ||||||
|  |      * @return a list of available OllamaChatMessageRole objects | ||||||
|  |      */ | ||||||
|  |     public List<OllamaChatMessageRole> listRoles() { | ||||||
|  |         return OllamaChatMessageRole.getRoles(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * Retrieves a specific role by name. | ||||||
|  |      * | ||||||
|  |      * @param roleName the name of the role to retrieve | ||||||
|  |      * @return the OllamaChatMessageRole associated with the given name | ||||||
|  |      * @throws RoleNotFoundException if the role with the specified name does not exist | ||||||
|  |      */ | ||||||
|  |     public OllamaChatMessageRole getRole(String roleName) throws RoleNotFoundException { | ||||||
|  |         return OllamaChatMessageRole.getRole(roleName); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |  | ||||||
|     // technical private methods // |     // technical private methods // | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * Utility method to encode a file into a Base64 encoded string. | ||||||
|  |      * | ||||||
|  |      * @param file the file to be encoded into Base64. | ||||||
|  |      * @return a Base64 encoded string representing the contents of the file. | ||||||
|  |      * @throws IOException if an I/O error occurs during reading the file. | ||||||
|  |      */ | ||||||
|     private static String encodeFileToBase64(File file) throws IOException { |     private static String encodeFileToBase64(File file) throws IOException { | ||||||
|         return Base64.getEncoder().encodeToString(Files.readAllBytes(file.toPath())); |         return Base64.getEncoder().encodeToString(Files.readAllBytes(file.toPath())); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * Utility method to encode a byte array into a Base64 encoded string. | ||||||
|  |      * | ||||||
|  |      * @param bytes the byte array to be encoded into Base64. | ||||||
|  |      * @return a Base64 encoded string representing the byte array. | ||||||
|  |      */ | ||||||
|     private static String encodeByteArrayToBase64(byte[] bytes) { |     private static String encodeByteArrayToBase64(byte[] bytes) { | ||||||
|         return Base64.getEncoder().encodeToString(bytes); |         return Base64.getEncoder().encodeToString(bytes); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     private OllamaResult generateSyncForOllamaRequestModel( |     /** | ||||||
|             OllamaGenerateRequest ollamaRequestModel, OllamaStreamHandler streamHandler) |      * Generates a request for the Ollama API and returns the result. | ||||||
|             throws OllamaBaseException, IOException, InterruptedException { |      * This method synchronously calls the Ollama API. If a stream handler is provided, | ||||||
|         OllamaGenerateEndpointCaller requestCaller = |      * the request will be streamed; otherwise, a regular synchronous request will be made. | ||||||
|                 new OllamaGenerateEndpointCaller(host, basicAuth, requestTimeoutSeconds, verbose); |      * | ||||||
|  |      * @param ollamaRequestModel the request model containing necessary parameters for the Ollama API request. | ||||||
|  |      * @param streamHandler      the stream handler to process streaming responses, or null for non-streaming requests. | ||||||
|  |      * @return the result of the Ollama API request. | ||||||
|  |      * @throws OllamaBaseException  if the request fails due to an issue with the Ollama API. | ||||||
|  |      * @throws IOException          if an I/O error occurs during the request process. | ||||||
|  |      * @throws InterruptedException if the thread is interrupted during the request. | ||||||
|  |      */ | ||||||
|  |     private OllamaResult generateSyncForOllamaRequestModel(OllamaGenerateRequest ollamaRequestModel, OllamaStreamHandler streamHandler) throws OllamaBaseException, IOException, InterruptedException { | ||||||
|  |         OllamaGenerateEndpointCaller requestCaller = new OllamaGenerateEndpointCaller(host, basicAuth, requestTimeoutSeconds, verbose); | ||||||
|         OllamaResult result; |         OllamaResult result; | ||||||
|         if (streamHandler != null) { |         if (streamHandler != null) { | ||||||
|             ollamaRequestModel.setStream(true); |             ollamaRequestModel.setStream(true); | ||||||
| @@ -626,6 +1050,7 @@ public class OllamaAPI { | |||||||
|         return result; |         return result; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
|      * Get default request builder. |      * Get default request builder. | ||||||
|      * |      * | ||||||
| @@ -633,10 +1058,7 @@ public class OllamaAPI { | |||||||
|      * @return HttpRequest.Builder |      * @return HttpRequest.Builder | ||||||
|      */ |      */ | ||||||
|     private HttpRequest.Builder getRequestBuilderDefault(URI uri) { |     private HttpRequest.Builder getRequestBuilderDefault(URI uri) { | ||||||
|         HttpRequest.Builder requestBuilder = |         HttpRequest.Builder requestBuilder = HttpRequest.newBuilder(uri).header("Content-Type", "application/json").timeout(Duration.ofSeconds(requestTimeoutSeconds)); | ||||||
|                 HttpRequest.newBuilder(uri) |  | ||||||
|                         .header("Content-Type", "application/json") |  | ||||||
|                         .timeout(Duration.ofSeconds(requestTimeoutSeconds)); |  | ||||||
|         if (isBasicAuthCredentialsSet()) { |         if (isBasicAuthCredentialsSet()) { | ||||||
|             requestBuilder.header("Authorization", getBasicAuthHeaderValue()); |             requestBuilder.header("Authorization", getBasicAuthHeaderValue()); | ||||||
|         } |         } | ||||||
| @@ -662,12 +1084,11 @@ public class OllamaAPI { | |||||||
|         return basicAuth != null; |         return basicAuth != null; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |  | ||||||
|     private Object invokeTool(ToolFunctionCallSpec toolFunctionCallSpec) throws ToolInvocationException { |     private Object invokeTool(ToolFunctionCallSpec toolFunctionCallSpec) throws ToolInvocationException { | ||||||
|         try { |         try { | ||||||
|             String methodName = toolFunctionCallSpec.getName(); |             String methodName = toolFunctionCallSpec.getName(); | ||||||
|             Map<String, Object> arguments = toolFunctionCallSpec.getArguments(); |             Map<String, Object> arguments = toolFunctionCallSpec.getArguments(); | ||||||
|             ToolFunction function = toolRegistry.getFunction(methodName); |             ToolFunction function = toolRegistry.getToolFunction(methodName); | ||||||
|             if (verbose) { |             if (verbose) { | ||||||
|                 logger.debug("Invoking function {} with arguments {}", methodName, arguments); |                 logger.debug("Invoking function {} with arguments {}", methodName, arguments); | ||||||
|             } |             } | ||||||
|   | |||||||
| @@ -0,0 +1,8 @@ | |||||||
|  | package io.github.ollama4j.exceptions; | ||||||
|  |  | ||||||
|  | public class RoleNotFoundException extends Exception { | ||||||
|  |  | ||||||
|  |     public RoleNotFoundException(String s) { | ||||||
|  |         super(s); | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -2,12 +2,14 @@ package io.github.ollama4j.models.chat; | |||||||
|  |  | ||||||
| import static io.github.ollama4j.utils.Utils.getObjectMapper; | import static io.github.ollama4j.utils.Utils.getObjectMapper; | ||||||
|  |  | ||||||
|  | import com.fasterxml.jackson.annotation.JsonProperty; | ||||||
| import com.fasterxml.jackson.core.JsonProcessingException; | import com.fasterxml.jackson.core.JsonProcessingException; | ||||||
| import com.fasterxml.jackson.databind.annotation.JsonSerialize; | import com.fasterxml.jackson.databind.annotation.JsonSerialize; | ||||||
|  |  | ||||||
| import io.github.ollama4j.utils.FileToBase64Serializer; | import io.github.ollama4j.utils.FileToBase64Serializer; | ||||||
|  |  | ||||||
| import java.util.List; | import java.util.List; | ||||||
|  |  | ||||||
| import lombok.AllArgsConstructor; | import lombok.AllArgsConstructor; | ||||||
| import lombok.Data; | import lombok.Data; | ||||||
| import lombok.NoArgsConstructor; | import lombok.NoArgsConstructor; | ||||||
| @@ -31,6 +33,8 @@ public class OllamaChatMessage { | |||||||
|     @NonNull |     @NonNull | ||||||
|     private String content; |     private String content; | ||||||
|  |  | ||||||
|  |     private @JsonProperty("tool_calls") List<OllamaChatToolCalls> toolCalls; | ||||||
|  |  | ||||||
|     @JsonSerialize(using = FileToBase64Serializer.class) |     @JsonSerialize(using = FileToBase64Serializer.class) | ||||||
|     private List<byte[]> images; |     private List<byte[]> images; | ||||||
|  |  | ||||||
|   | |||||||
| @@ -1,19 +1,53 @@ | |||||||
| package io.github.ollama4j.models.chat; | package io.github.ollama4j.models.chat; | ||||||
|  |  | ||||||
| import com.fasterxml.jackson.annotation.JsonValue; | import com.fasterxml.jackson.annotation.JsonValue; | ||||||
|  | import io.github.ollama4j.exceptions.RoleNotFoundException; | ||||||
|  | import lombok.Getter; | ||||||
|  |  | ||||||
|  | import java.util.ArrayList; | ||||||
|  | import java.util.List; | ||||||
|  |  | ||||||
| /** | /** | ||||||
|  * Defines the possible Chat Message roles. |  * Defines the possible Chat Message roles. | ||||||
|  */ |  */ | ||||||
| public enum OllamaChatMessageRole { | @Getter | ||||||
|     SYSTEM("system"), | public class OllamaChatMessageRole { | ||||||
|     USER("user"), |     private static final List<OllamaChatMessageRole> roles = new ArrayList<>(); | ||||||
|     ASSISTANT("assistant"); |  | ||||||
|  |     public static final OllamaChatMessageRole SYSTEM = new OllamaChatMessageRole("system"); | ||||||
|  |     public static final OllamaChatMessageRole USER = new OllamaChatMessageRole("user"); | ||||||
|  |     public static final OllamaChatMessageRole ASSISTANT = new OllamaChatMessageRole("assistant"); | ||||||
|  |     public static final OllamaChatMessageRole TOOL = new OllamaChatMessageRole("tool"); | ||||||
|  |  | ||||||
|     @JsonValue |     @JsonValue | ||||||
|     private String roleName; |     private final String roleName; | ||||||
|  |  | ||||||
|     private OllamaChatMessageRole(String roleName) { |     private OllamaChatMessageRole(String roleName) { | ||||||
|         this.roleName = roleName; |         this.roleName = roleName; | ||||||
|  |         roles.add(this); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public static OllamaChatMessageRole newCustomRole(String roleName) { | ||||||
|  |         OllamaChatMessageRole customRole = new OllamaChatMessageRole(roleName); | ||||||
|  |         roles.add(customRole); | ||||||
|  |         return customRole; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public static List<OllamaChatMessageRole> getRoles() { | ||||||
|  |         return new ArrayList<>(roles); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public static OllamaChatMessageRole getRole(String roleName) throws RoleNotFoundException { | ||||||
|  |         for (OllamaChatMessageRole role : roles) { | ||||||
|  |             if (role.roleName.equals(roleName)) { | ||||||
|  |                 return role; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         throw new RoleNotFoundException("Invalid role name: " + roleName); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     public String toString() { | ||||||
|  |         return roleName; | ||||||
|     } |     } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -3,6 +3,7 @@ package io.github.ollama4j.models.chat; | |||||||
| import java.util.List; | import java.util.List; | ||||||
|  |  | ||||||
| import io.github.ollama4j.models.request.OllamaCommonRequest; | import io.github.ollama4j.models.request.OllamaCommonRequest; | ||||||
|  | import io.github.ollama4j.tools.Tools; | ||||||
| import io.github.ollama4j.utils.OllamaRequestBody; | import io.github.ollama4j.utils.OllamaRequestBody; | ||||||
|  |  | ||||||
| import lombok.Getter; | import lombok.Getter; | ||||||
| @@ -21,6 +22,8 @@ public class OllamaChatRequest extends OllamaCommonRequest implements OllamaRequ | |||||||
|  |  | ||||||
|   private List<OllamaChatMessage> messages; |   private List<OllamaChatMessage> messages; | ||||||
|  |  | ||||||
|  |   private List<Tools.PromptFuncDefinition> tools; | ||||||
|  |  | ||||||
|   public OllamaChatRequest() {} |   public OllamaChatRequest() {} | ||||||
|  |  | ||||||
|   public OllamaChatRequest(String model, List<OllamaChatMessage> messages) { |   public OllamaChatRequest(String model, List<OllamaChatMessage> messages) { | ||||||
|   | |||||||
| @@ -1,19 +1,19 @@ | |||||||
| package io.github.ollama4j.models.chat; | package io.github.ollama4j.models.chat; | ||||||
|  |  | ||||||
|  | import io.github.ollama4j.utils.Options; | ||||||
|  | import io.github.ollama4j.utils.Utils; | ||||||
|  | import org.slf4j.Logger; | ||||||
|  | import org.slf4j.LoggerFactory; | ||||||
|  |  | ||||||
| import java.io.File; | import java.io.File; | ||||||
| import java.io.IOException; | import java.io.IOException; | ||||||
| import java.net.URISyntaxException; | import java.net.URISyntaxException; | ||||||
| import java.nio.file.Files; | import java.nio.file.Files; | ||||||
| import java.util.ArrayList; | import java.util.ArrayList; | ||||||
|  | import java.util.Collections; | ||||||
| import java.util.List; | import java.util.List; | ||||||
| import java.util.stream.Collectors; | import java.util.stream.Collectors; | ||||||
|  |  | ||||||
| import org.slf4j.Logger; |  | ||||||
| import org.slf4j.LoggerFactory; |  | ||||||
|  |  | ||||||
| import io.github.ollama4j.utils.Options; |  | ||||||
| import io.github.ollama4j.utils.Utils; |  | ||||||
|  |  | ||||||
| /** | /** | ||||||
|  * Helper class for creating {@link OllamaChatRequest} objects using the builder-pattern. |  * Helper class for creating {@link OllamaChatRequest} objects using the builder-pattern. | ||||||
|  */ |  */ | ||||||
| @@ -39,23 +39,27 @@ public class OllamaChatRequestBuilder { | |||||||
|         request = new OllamaChatRequest(request.getModel(), new ArrayList<>()); |         request = new OllamaChatRequest(request.getModel(), new ArrayList<>()); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     public OllamaChatRequestBuilder withMessage(OllamaChatMessageRole role, String content, List<File> images){ |     public OllamaChatRequestBuilder withMessage(OllamaChatMessageRole role, String content){ | ||||||
|  |         return withMessage(role,content, Collections.emptyList()); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public OllamaChatRequestBuilder withMessage(OllamaChatMessageRole role, String content, List<OllamaChatToolCalls> toolCalls,List<File> images) { | ||||||
|         List<OllamaChatMessage> messages = this.request.getMessages(); |         List<OllamaChatMessage> messages = this.request.getMessages(); | ||||||
|  |  | ||||||
|         List<byte[]> binaryImages = images.stream().map(file -> { |         List<byte[]> binaryImages = images.stream().map(file -> { | ||||||
|             try { |             try { | ||||||
|                 return Files.readAllBytes(file.toPath()); |                 return Files.readAllBytes(file.toPath()); | ||||||
|             } catch (IOException e) { |             } catch (IOException e) { | ||||||
|                 LOG.warn(String.format("File '%s' could not be accessed, will not add to message!",file.toPath()), e); |                 LOG.warn("File '{}' could not be accessed, will not add to message!", file.toPath(), e); | ||||||
|                 return new byte[0]; |                 return new byte[0]; | ||||||
|             } |             } | ||||||
|         }).collect(Collectors.toList()); |         }).collect(Collectors.toList()); | ||||||
|  |  | ||||||
|         messages.add(new OllamaChatMessage(role,content,binaryImages)); |         messages.add(new OllamaChatMessage(role, content,toolCalls, binaryImages)); | ||||||
|         return this; |         return this; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     public OllamaChatRequestBuilder withMessage(OllamaChatMessageRole role, String content, String... imageUrls){ |     public OllamaChatRequestBuilder withMessage(OllamaChatMessageRole role, String content,List<OllamaChatToolCalls> toolCalls, String... imageUrls) { | ||||||
|         List<OllamaChatMessage> messages = this.request.getMessages(); |         List<OllamaChatMessage> messages = this.request.getMessages(); | ||||||
|         List<byte[]> binaryImages = null; |         List<byte[]> binaryImages = null; | ||||||
|         if (imageUrls.length > 0) { |         if (imageUrls.length > 0) { | ||||||
| @@ -63,23 +67,20 @@ public class OllamaChatRequestBuilder { | |||||||
|             for (String imageUrl : imageUrls) { |             for (String imageUrl : imageUrls) { | ||||||
|                 try { |                 try { | ||||||
|                     binaryImages.add(Utils.loadImageBytesFromUrl(imageUrl)); |                     binaryImages.add(Utils.loadImageBytesFromUrl(imageUrl)); | ||||||
|                 } |                 } catch (URISyntaxException e) { | ||||||
|                     catch (URISyntaxException e){ |                     LOG.warn("URL '{}' could not be accessed, will not add to message!", imageUrl, e); | ||||||
|                         LOG.warn(String.format("URL '%s' could not be accessed, will not add to message!",imageUrl), e); |                 } catch (IOException e) { | ||||||
|                 } |                     LOG.warn("Content of URL '{}' could not be read, will not add to message!", imageUrl, e); | ||||||
|                 catch (IOException e){ |  | ||||||
|                     LOG.warn(String.format("Content of URL '%s' could not be read, will not add to message!",imageUrl), e); |  | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         messages.add(new OllamaChatMessage(role,content,binaryImages)); |         messages.add(new OllamaChatMessage(role, content,toolCalls, binaryImages)); | ||||||
|         return this; |         return this; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     public OllamaChatRequestBuilder withMessages(List<OllamaChatMessage> messages) { |     public OllamaChatRequestBuilder withMessages(List<OllamaChatMessage> messages) { | ||||||
|         this.request.getMessages().addAll(messages); |         return new OllamaChatRequestBuilder(request.getModel(), messages); | ||||||
|         return this; |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     public OllamaChatRequestBuilder withOptions(Options options) { |     public OllamaChatRequestBuilder withOptions(Options options) { | ||||||
|   | |||||||
| @@ -2,31 +2,54 @@ package io.github.ollama4j.models.chat; | |||||||
|  |  | ||||||
| import java.util.List; | import java.util.List; | ||||||
|  |  | ||||||
| import io.github.ollama4j.models.response.OllamaResult; | import com.fasterxml.jackson.core.JsonProcessingException; | ||||||
|  | import lombok.Getter; | ||||||
|  |  | ||||||
|  | import static io.github.ollama4j.utils.Utils.getObjectMapper; | ||||||
|  |  | ||||||
| /** | /** | ||||||
|  * Specific chat-API result that contains the chat history sent to the model and appends the answer as {@link OllamaChatResult} given by the |  * Specific chat-API result that contains the chat history sent to the model and appends the answer as {@link OllamaChatResult} given by the | ||||||
|  * {@link OllamaChatMessageRole#ASSISTANT} role. |  * {@link OllamaChatMessageRole#ASSISTANT} role. | ||||||
|  */ |  */ | ||||||
| public class OllamaChatResult extends OllamaResult{ | @Getter | ||||||
|  | public class OllamaChatResult { | ||||||
|  |  | ||||||
|  |  | ||||||
|     private List<OllamaChatMessage> chatHistory; |     private List<OllamaChatMessage> chatHistory; | ||||||
|  |  | ||||||
|     public OllamaChatResult(String response, long responseTime, int httpStatusCode, |     private OllamaChatResponseModel responseModel; | ||||||
|             List<OllamaChatMessage> chatHistory) { |  | ||||||
|         super(response, responseTime, httpStatusCode); |     public OllamaChatResult(OllamaChatResponseModel responseModel, List<OllamaChatMessage> chatHistory) { | ||||||
|         this.chatHistory = chatHistory; |         this.chatHistory = chatHistory; | ||||||
|         appendAnswerToChatHistory(response); |         this.responseModel = responseModel; | ||||||
|  |         appendAnswerToChatHistory(responseModel); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     public List<OllamaChatMessage> getChatHistory() { |     private void appendAnswerToChatHistory(OllamaChatResponseModel response) { | ||||||
|         return chatHistory; |         this.chatHistory.add(response.getMessage()); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     private void appendAnswerToChatHistory(String answer){ |     @Override | ||||||
|         OllamaChatMessage assistantMessage = new OllamaChatMessage(OllamaChatMessageRole.ASSISTANT, answer); |     public String toString() { | ||||||
|         this.chatHistory.add(assistantMessage); |         try { | ||||||
|  |             return getObjectMapper().writerWithDefaultPrettyPrinter().writeValueAsString(this); | ||||||
|  |         } catch (JsonProcessingException e) { | ||||||
|  |             throw new RuntimeException(e); | ||||||
|  |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|      |     @Deprecated | ||||||
|  |     public String getResponse(){ | ||||||
|  |         return responseModel != null ? responseModel.getMessage().getContent() : ""; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Deprecated | ||||||
|  |     public int getHttpStatusCode(){ | ||||||
|  |         return 200; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Deprecated | ||||||
|  |     public long getResponseTime(){ | ||||||
|  |         return responseModel != null ? responseModel.getTotalDuration() : 0L; | ||||||
|  |     } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -1,31 +1,19 @@ | |||||||
| package io.github.ollama4j.models.chat; | package io.github.ollama4j.models.chat; | ||||||
|  |  | ||||||
| import io.github.ollama4j.models.generate.OllamaStreamHandler; | import io.github.ollama4j.models.generate.OllamaStreamHandler; | ||||||
|  | import io.github.ollama4j.models.generate.OllamaTokenHandler; | ||||||
|  | import lombok.RequiredArgsConstructor; | ||||||
|  |  | ||||||
| import java.util.ArrayList; | @RequiredArgsConstructor | ||||||
| import java.util.List; | public class OllamaChatStreamObserver implements OllamaTokenHandler { | ||||||
|  |     private final OllamaStreamHandler streamHandler; | ||||||
| public class OllamaChatStreamObserver { |  | ||||||
|  |  | ||||||
|     private OllamaStreamHandler streamHandler; |  | ||||||
|  |  | ||||||
|     private List<OllamaChatResponseModel> responseParts = new ArrayList<>(); |  | ||||||
|  |  | ||||||
|     private String message = ""; |     private String message = ""; | ||||||
|  |  | ||||||
|     public OllamaChatStreamObserver(OllamaStreamHandler streamHandler) { |     @Override | ||||||
|         this.streamHandler = streamHandler; |     public void accept(OllamaChatResponseModel token) { | ||||||
|     } |         if (streamHandler != null) { | ||||||
|  |             message += token.getMessage().getContent(); | ||||||
|     public void notify(OllamaChatResponseModel currentResponsePart) { |  | ||||||
|         responseParts.add(currentResponsePart); |  | ||||||
|         handleCurrentResponsePart(currentResponsePart); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     protected void handleCurrentResponsePart(OllamaChatResponseModel currentResponsePart) { |  | ||||||
|         message = message + currentResponsePart.getMessage().getContent(); |  | ||||||
|             streamHandler.accept(message); |             streamHandler.accept(message); | ||||||
|         } |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
| } | } | ||||||
|   | |||||||
| @@ -0,0 +1,16 @@ | |||||||
|  | package io.github.ollama4j.models.chat; | ||||||
|  |  | ||||||
|  | import io.github.ollama4j.tools.OllamaToolCallsFunction; | ||||||
|  | import lombok.AllArgsConstructor; | ||||||
|  | import lombok.Data; | ||||||
|  | import lombok.NoArgsConstructor; | ||||||
|  |  | ||||||
|  | @Data | ||||||
|  | @NoArgsConstructor | ||||||
|  | @AllArgsConstructor | ||||||
|  | public class OllamaChatToolCalls { | ||||||
|  |  | ||||||
|  |     private OllamaToolCallsFunction function; | ||||||
|  |  | ||||||
|  |  | ||||||
|  | } | ||||||
| @@ -0,0 +1,40 @@ | |||||||
|  | package io.github.ollama4j.models.embeddings; | ||||||
|  |  | ||||||
|  | import io.github.ollama4j.utils.Options; | ||||||
|  |  | ||||||
|  | import java.util.List; | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * Builder class to easily create Requests for Embedding models using ollama. | ||||||
|  |  */ | ||||||
|  | public class OllamaEmbedRequestBuilder { | ||||||
|  |  | ||||||
|  |     private final OllamaEmbedRequestModel request; | ||||||
|  |  | ||||||
|  |     private OllamaEmbedRequestBuilder(String model, List<String> input) { | ||||||
|  |         this.request = new OllamaEmbedRequestModel(model,input); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public static OllamaEmbedRequestBuilder getInstance(String model, String... input){ | ||||||
|  |         return new OllamaEmbedRequestBuilder(model, List.of(input)); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public OllamaEmbedRequestBuilder withOptions(Options options){ | ||||||
|  |         this.request.setOptions(options.getOptionsMap()); | ||||||
|  |         return this; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public OllamaEmbedRequestBuilder withKeepAlive(String keepAlive){ | ||||||
|  |         this.request.setKeepAlive(keepAlive); | ||||||
|  |         return this; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public OllamaEmbedRequestBuilder withoutTruncate(){ | ||||||
|  |         this.request.setTruncate(false); | ||||||
|  |         return this; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public OllamaEmbedRequestModel build() { | ||||||
|  |         return this.request; | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -0,0 +1,41 @@ | |||||||
|  | package io.github.ollama4j.models.embeddings; | ||||||
|  |  | ||||||
|  | import com.fasterxml.jackson.annotation.JsonProperty; | ||||||
|  | import com.fasterxml.jackson.core.JsonProcessingException; | ||||||
|  | import lombok.Data; | ||||||
|  | import lombok.NoArgsConstructor; | ||||||
|  | import lombok.NonNull; | ||||||
|  | import lombok.RequiredArgsConstructor; | ||||||
|  |  | ||||||
|  | import java.util.List; | ||||||
|  | import java.util.Map; | ||||||
|  |  | ||||||
|  | import static io.github.ollama4j.utils.Utils.getObjectMapper; | ||||||
|  |  | ||||||
|  | @Data | ||||||
|  | @RequiredArgsConstructor | ||||||
|  | @NoArgsConstructor | ||||||
|  | public class OllamaEmbedRequestModel { | ||||||
|  |     @NonNull | ||||||
|  |     private String model; | ||||||
|  |  | ||||||
|  |     @NonNull | ||||||
|  |     private List<String> input; | ||||||
|  |  | ||||||
|  |     private Map<String, Object> options; | ||||||
|  |  | ||||||
|  |     @JsonProperty(value = "keep_alive") | ||||||
|  |     private String keepAlive; | ||||||
|  |  | ||||||
|  |     @JsonProperty(value = "truncate") | ||||||
|  |     private Boolean truncate = true; | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     public String toString() { | ||||||
|  |         try { | ||||||
|  |             return getObjectMapper().writerWithDefaultPrettyPrinter().writeValueAsString(this); | ||||||
|  |         } catch (JsonProcessingException e) { | ||||||
|  |             throw new RuntimeException(e); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -0,0 +1,25 @@ | |||||||
|  | package io.github.ollama4j.models.embeddings; | ||||||
|  |  | ||||||
|  | import com.fasterxml.jackson.annotation.JsonProperty; | ||||||
|  | import lombok.Data; | ||||||
|  |  | ||||||
|  | import java.util.List; | ||||||
|  |  | ||||||
|  | @SuppressWarnings("unused") | ||||||
|  | @Data | ||||||
|  | public class OllamaEmbedResponseModel { | ||||||
|  |     @JsonProperty("model") | ||||||
|  |     private String model; | ||||||
|  |  | ||||||
|  |     @JsonProperty("embeddings") | ||||||
|  |     private List<List<Double>> embeddings; | ||||||
|  |  | ||||||
|  |     @JsonProperty("total_duration") | ||||||
|  |     private long totalDuration; | ||||||
|  |  | ||||||
|  |     @JsonProperty("load_duration") | ||||||
|  |     private long loadDuration; | ||||||
|  |  | ||||||
|  |     @JsonProperty("prompt_eval_count") | ||||||
|  |     private int promptEvalCount; | ||||||
|  | } | ||||||
| @@ -7,6 +7,7 @@ import lombok.Data; | |||||||
|  |  | ||||||
| @SuppressWarnings("unused") | @SuppressWarnings("unused") | ||||||
| @Data | @Data | ||||||
|  | @Deprecated(since="1.0.90") | ||||||
| public class OllamaEmbeddingResponseModel { | public class OllamaEmbeddingResponseModel { | ||||||
|     @JsonProperty("embedding") |     @JsonProperty("embedding") | ||||||
|     private List<Double> embedding; |     private List<Double> embedding; | ||||||
|   | |||||||
| @@ -2,6 +2,7 @@ package io.github.ollama4j.models.embeddings; | |||||||
|  |  | ||||||
| import io.github.ollama4j.utils.Options; | import io.github.ollama4j.utils.Options; | ||||||
|  |  | ||||||
|  | @Deprecated(since="1.0.90") | ||||||
| public class OllamaEmbeddingsRequestBuilder { | public class OllamaEmbeddingsRequestBuilder { | ||||||
|  |  | ||||||
|     private OllamaEmbeddingsRequestBuilder(String model, String prompt){ |     private OllamaEmbeddingsRequestBuilder(String model, String prompt){ | ||||||
|   | |||||||
| @@ -12,6 +12,7 @@ import lombok.RequiredArgsConstructor; | |||||||
| @Data | @Data | ||||||
| @RequiredArgsConstructor | @RequiredArgsConstructor | ||||||
| @NoArgsConstructor | @NoArgsConstructor | ||||||
|  | @Deprecated(since="1.0.90") | ||||||
| public class OllamaEmbeddingsRequestModel { | public class OllamaEmbeddingsRequestModel { | ||||||
|   @NonNull |   @NonNull | ||||||
|   private String model; |   private String model; | ||||||
|   | |||||||
| @@ -0,0 +1,8 @@ | |||||||
|  | package io.github.ollama4j.models.generate; | ||||||
|  |  | ||||||
|  | import io.github.ollama4j.models.chat.OllamaChatResponseModel; | ||||||
|  |  | ||||||
|  | import java.util.function.Consumer; | ||||||
|  |  | ||||||
|  | public interface OllamaTokenHandler extends Consumer<OllamaChatResponseModel> { | ||||||
|  | } | ||||||
| @@ -0,0 +1,45 @@ | |||||||
|  | package io.github.ollama4j.models.request; | ||||||
|  |  | ||||||
|  | import static io.github.ollama4j.utils.Utils.getObjectMapper; | ||||||
|  |  | ||||||
|  | import com.fasterxml.jackson.core.JsonProcessingException; | ||||||
|  | import lombok.AllArgsConstructor; | ||||||
|  | import lombok.Data; | ||||||
|  | import lombok.Data; | ||||||
|  | import lombok.AllArgsConstructor; | ||||||
|  | import lombok.Builder; | ||||||
|  |  | ||||||
|  | import java.util.List; | ||||||
|  | import java.util.Map; | ||||||
|  |  | ||||||
|  |  | ||||||
|  | @Data | ||||||
|  | @AllArgsConstructor | ||||||
|  | @Builder | ||||||
|  | public class CustomModelRequest { | ||||||
|  |     private String model; | ||||||
|  |     private String from; | ||||||
|  |     private Map<String, String> files; | ||||||
|  |     private Map<String, String> adapters; | ||||||
|  |     private String template; | ||||||
|  |     private Object license; // Using Object to handle both String and List<String> | ||||||
|  |     private String system; | ||||||
|  |     private Map<String, Object> parameters; | ||||||
|  |     private List<Object> messages; | ||||||
|  |     private Boolean stream; | ||||||
|  |     private Boolean quantize; | ||||||
|  |  | ||||||
|  |     public CustomModelRequest() { | ||||||
|  |         this.stream = true; | ||||||
|  |         this.quantize = false; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     public String toString() { | ||||||
|  |         try { | ||||||
|  |             return getObjectMapper().writerWithDefaultPrettyPrinter().writeValueAsString(this); | ||||||
|  |         } catch (JsonProcessingException e) { | ||||||
|  |             throw new RuntimeException(e); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -1,17 +1,25 @@ | |||||||
| package io.github.ollama4j.models.request; | package io.github.ollama4j.models.request; | ||||||
|  |  | ||||||
| import com.fasterxml.jackson.core.JsonProcessingException; | import com.fasterxml.jackson.core.JsonProcessingException; | ||||||
|  | import com.fasterxml.jackson.core.type.TypeReference; | ||||||
| import io.github.ollama4j.exceptions.OllamaBaseException; | import io.github.ollama4j.exceptions.OllamaBaseException; | ||||||
| import io.github.ollama4j.models.response.OllamaResult; | import io.github.ollama4j.models.chat.*; | ||||||
| import io.github.ollama4j.models.chat.OllamaChatResponseModel; | import io.github.ollama4j.models.generate.OllamaTokenHandler; | ||||||
| import io.github.ollama4j.models.chat.OllamaChatStreamObserver; | import io.github.ollama4j.models.response.OllamaErrorResponse; | ||||||
| import io.github.ollama4j.models.generate.OllamaStreamHandler; |  | ||||||
| import io.github.ollama4j.utils.OllamaRequestBody; |  | ||||||
| import io.github.ollama4j.utils.Utils; | import io.github.ollama4j.utils.Utils; | ||||||
| import org.slf4j.Logger; | import org.slf4j.Logger; | ||||||
| import org.slf4j.LoggerFactory; | import org.slf4j.LoggerFactory; | ||||||
|  |  | ||||||
|  | import java.io.BufferedReader; | ||||||
| import java.io.IOException; | import java.io.IOException; | ||||||
|  | import java.io.InputStream; | ||||||
|  | import java.io.InputStreamReader; | ||||||
|  | import java.net.URI; | ||||||
|  | import java.net.http.HttpClient; | ||||||
|  | import java.net.http.HttpRequest; | ||||||
|  | import java.net.http.HttpResponse; | ||||||
|  | import java.nio.charset.StandardCharsets; | ||||||
|  | import java.util.List; | ||||||
|  |  | ||||||
| /** | /** | ||||||
|  * Specialization class for requests |  * Specialization class for requests | ||||||
| @@ -20,7 +28,7 @@ public class OllamaChatEndpointCaller extends OllamaEndpointCaller { | |||||||
|  |  | ||||||
|     private static final Logger LOG = LoggerFactory.getLogger(OllamaChatEndpointCaller.class); |     private static final Logger LOG = LoggerFactory.getLogger(OllamaChatEndpointCaller.class); | ||||||
|  |  | ||||||
|     private OllamaChatStreamObserver streamObserver; |     private OllamaTokenHandler tokenHandler; | ||||||
|  |  | ||||||
|     public OllamaChatEndpointCaller(String host, BasicAuth basicAuth, long requestTimeoutSeconds, boolean verbose) { |     public OllamaChatEndpointCaller(String host, BasicAuth basicAuth, long requestTimeoutSeconds, boolean verbose) { | ||||||
|         super(host, basicAuth, requestTimeoutSeconds, verbose); |         super(host, basicAuth, requestTimeoutSeconds, verbose); | ||||||
| @@ -31,13 +39,29 @@ public class OllamaChatEndpointCaller extends OllamaEndpointCaller { | |||||||
|         return "/api/chat"; |         return "/api/chat"; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * Parses streamed Response line from ollama chat. | ||||||
|  |      * Using {@link com.fasterxml.jackson.databind.ObjectMapper#readValue(String, TypeReference)} should throw | ||||||
|  |      * {@link IllegalArgumentException} in case of null line or {@link com.fasterxml.jackson.core.JsonParseException} | ||||||
|  |      * in case the JSON Object cannot be parsed to a {@link OllamaChatResponseModel}. Thus, the ResponseModel should | ||||||
|  |      * never be null. | ||||||
|  |      * | ||||||
|  |      * @param line streamed line of ollama stream response | ||||||
|  |      * @param responseBuffer Stringbuffer to add latest response message part to | ||||||
|  |      * @return TRUE, if ollama-Response has 'done' state | ||||||
|  |      */ | ||||||
|     @Override |     @Override | ||||||
|     protected boolean parseResponseAndAddToBuffer(String line, StringBuilder responseBuffer) { |     protected boolean parseResponseAndAddToBuffer(String line, StringBuilder responseBuffer) { | ||||||
|         try { |         try { | ||||||
|             OllamaChatResponseModel ollamaResponseModel = Utils.getObjectMapper().readValue(line, OllamaChatResponseModel.class); |             OllamaChatResponseModel ollamaResponseModel = Utils.getObjectMapper().readValue(line, OllamaChatResponseModel.class); | ||||||
|             responseBuffer.append(ollamaResponseModel.getMessage().getContent()); |             // it seems that under heavy load ollama responds with an empty chat message part in the streamed response | ||||||
|             if (streamObserver != null) { |             // thus, we null check the message and hope that the next streamed response has some message content again | ||||||
|                 streamObserver.notify(ollamaResponseModel); |             OllamaChatMessage message = ollamaResponseModel.getMessage(); | ||||||
|  |             if(message != null) { | ||||||
|  |                 responseBuffer.append(message.getContent()); | ||||||
|  |                 if (tokenHandler != null) { | ||||||
|  |                     tokenHandler.accept(ollamaResponseModel); | ||||||
|  |                 } | ||||||
|             } |             } | ||||||
|             return ollamaResponseModel.isDone(); |             return ollamaResponseModel.isDone(); | ||||||
|         } catch (JsonProcessingException e) { |         } catch (JsonProcessingException e) { | ||||||
| @@ -46,9 +70,75 @@ public class OllamaChatEndpointCaller extends OllamaEndpointCaller { | |||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     public OllamaResult call(OllamaRequestBody body, OllamaStreamHandler streamHandler) |     public OllamaChatResult call(OllamaChatRequest body, OllamaTokenHandler tokenHandler) | ||||||
|             throws OllamaBaseException, IOException, InterruptedException { |             throws OllamaBaseException, IOException, InterruptedException { | ||||||
|         streamObserver = new OllamaChatStreamObserver(streamHandler); |         this.tokenHandler = tokenHandler; | ||||||
|         return super.callSync(body); |         return callSync(body); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public OllamaChatResult callSync(OllamaChatRequest body) throws OllamaBaseException, IOException, InterruptedException { | ||||||
|  |         // Create Request | ||||||
|  |         HttpClient httpClient = HttpClient.newHttpClient(); | ||||||
|  |         URI uri = URI.create(getHost() + getEndpointSuffix()); | ||||||
|  |         HttpRequest.Builder requestBuilder = | ||||||
|  |                 getRequestBuilderDefault(uri) | ||||||
|  |                         .POST( | ||||||
|  |                                 body.getBodyPublisher()); | ||||||
|  |         HttpRequest request = requestBuilder.build(); | ||||||
|  |         if (isVerbose()) LOG.info("Asking model: " + body); | ||||||
|  |         HttpResponse<InputStream> response = | ||||||
|  |                 httpClient.send(request, HttpResponse.BodyHandlers.ofInputStream()); | ||||||
|  |  | ||||||
|  |         int statusCode = response.statusCode(); | ||||||
|  |         InputStream responseBodyStream = response.body(); | ||||||
|  |         StringBuilder responseBuffer = new StringBuilder(); | ||||||
|  |         OllamaChatResponseModel ollamaChatResponseModel = null; | ||||||
|  |         List<OllamaChatToolCalls> wantedToolsForStream = null; | ||||||
|  |         try (BufferedReader reader = | ||||||
|  |                      new BufferedReader(new InputStreamReader(responseBodyStream, StandardCharsets.UTF_8))) { | ||||||
|  |  | ||||||
|  |             String line; | ||||||
|  |             while ((line = reader.readLine()) != null) { | ||||||
|  |                 if (statusCode == 404) { | ||||||
|  |                     LOG.warn("Status code: 404 (Not Found)"); | ||||||
|  |                     OllamaErrorResponse ollamaResponseModel = | ||||||
|  |                             Utils.getObjectMapper().readValue(line, OllamaErrorResponse.class); | ||||||
|  |                     responseBuffer.append(ollamaResponseModel.getError()); | ||||||
|  |                 } else if (statusCode == 401) { | ||||||
|  |                     LOG.warn("Status code: 401 (Unauthorized)"); | ||||||
|  |                     OllamaErrorResponse ollamaResponseModel = | ||||||
|  |                             Utils.getObjectMapper() | ||||||
|  |                                     .readValue("{\"error\":\"Unauthorized\"}", OllamaErrorResponse.class); | ||||||
|  |                     responseBuffer.append(ollamaResponseModel.getError()); | ||||||
|  |                 } else if (statusCode == 400) { | ||||||
|  |                     LOG.warn("Status code: 400 (Bad Request)"); | ||||||
|  |                     OllamaErrorResponse ollamaResponseModel = Utils.getObjectMapper().readValue(line, | ||||||
|  |                             OllamaErrorResponse.class); | ||||||
|  |                     responseBuffer.append(ollamaResponseModel.getError()); | ||||||
|  |                 } else { | ||||||
|  |                     boolean finished = parseResponseAndAddToBuffer(line, responseBuffer); | ||||||
|  |                         ollamaChatResponseModel = Utils.getObjectMapper().readValue(line, OllamaChatResponseModel.class); | ||||||
|  |                     if(body.stream && ollamaChatResponseModel.getMessage().getToolCalls() != null){ | ||||||
|  |                         wantedToolsForStream = ollamaChatResponseModel.getMessage().getToolCalls(); | ||||||
|  |                     } | ||||||
|  |                     if (finished && body.stream) { | ||||||
|  |                         ollamaChatResponseModel.getMessage().setContent(responseBuffer.toString()); | ||||||
|  |                         break; | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         if (statusCode != 200) { | ||||||
|  |             LOG.error("Status code " + statusCode); | ||||||
|  |             throw new OllamaBaseException(responseBuffer.toString()); | ||||||
|  |         } else { | ||||||
|  |             if(wantedToolsForStream != null) { | ||||||
|  |                 ollamaChatResponseModel.getMessage().setToolCalls(wantedToolsForStream); | ||||||
|  |             } | ||||||
|  |             OllamaChatResult ollamaResult = | ||||||
|  |                     new OllamaChatResult(ollamaChatResponseModel,body.getMessages()); | ||||||
|  |             if (isVerbose()) LOG.info("Model response: " + ollamaResult); | ||||||
|  |             return ollamaResult; | ||||||
|  |         } | ||||||
|     } |     } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -6,6 +6,7 @@ import io.github.ollama4j.models.response.OllamaErrorResponse; | |||||||
| import io.github.ollama4j.models.response.OllamaResult; | import io.github.ollama4j.models.response.OllamaResult; | ||||||
| import io.github.ollama4j.utils.OllamaRequestBody; | import io.github.ollama4j.utils.OllamaRequestBody; | ||||||
| import io.github.ollama4j.utils.Utils; | import io.github.ollama4j.utils.Utils; | ||||||
|  | import lombok.Getter; | ||||||
| import org.slf4j.Logger; | import org.slf4j.Logger; | ||||||
| import org.slf4j.LoggerFactory; | import org.slf4j.LoggerFactory; | ||||||
|  |  | ||||||
| @@ -24,14 +25,15 @@ import java.util.Base64; | |||||||
| /** | /** | ||||||
|  * Abstract helperclass to call the ollama api server. |  * Abstract helperclass to call the ollama api server. | ||||||
|  */ |  */ | ||||||
|  | @Getter | ||||||
| public abstract class OllamaEndpointCaller { | public abstract class OllamaEndpointCaller { | ||||||
|  |  | ||||||
|     private static final Logger LOG = LoggerFactory.getLogger(OllamaAPI.class); |     private static final Logger LOG = LoggerFactory.getLogger(OllamaAPI.class); | ||||||
|  |  | ||||||
|     private String host; |     private final String host; | ||||||
|     private BasicAuth basicAuth; |     private final BasicAuth basicAuth; | ||||||
|     private long requestTimeoutSeconds; |     private final long requestTimeoutSeconds; | ||||||
|     private boolean verbose; |     private final boolean verbose; | ||||||
|  |  | ||||||
|     public OllamaEndpointCaller(String host, BasicAuth basicAuth, long requestTimeoutSeconds, boolean verbose) { |     public OllamaEndpointCaller(String host, BasicAuth basicAuth, long requestTimeoutSeconds, boolean verbose) { | ||||||
|         this.host = host; |         this.host = host; | ||||||
| @@ -45,80 +47,13 @@ public abstract class OllamaEndpointCaller { | |||||||
|     protected abstract boolean parseResponseAndAddToBuffer(String line, StringBuilder responseBuffer); |     protected abstract boolean parseResponseAndAddToBuffer(String line, StringBuilder responseBuffer); | ||||||
|  |  | ||||||
|  |  | ||||||
|     /** |  | ||||||
|      * Calls the api server on the given host and endpoint suffix asynchronously, aka waiting for the response. |  | ||||||
|      * |  | ||||||
|      * @param body POST body payload |  | ||||||
|      * @return result answer given by the assistant |  | ||||||
|      * @throws OllamaBaseException  any response code than 200 has been returned |  | ||||||
|      * @throws IOException          in case the responseStream can not be read |  | ||||||
|      * @throws InterruptedException in case the server is not reachable or network issues happen |  | ||||||
|      */ |  | ||||||
|     public OllamaResult callSync(OllamaRequestBody body) throws OllamaBaseException, IOException, InterruptedException { |  | ||||||
|         // Create Request |  | ||||||
|         long startTime = System.currentTimeMillis(); |  | ||||||
|         HttpClient httpClient = HttpClient.newHttpClient(); |  | ||||||
|         URI uri = URI.create(this.host + getEndpointSuffix()); |  | ||||||
|         HttpRequest.Builder requestBuilder = |  | ||||||
|                 getRequestBuilderDefault(uri) |  | ||||||
|                         .POST( |  | ||||||
|                                 body.getBodyPublisher()); |  | ||||||
|         HttpRequest request = requestBuilder.build(); |  | ||||||
|         if (this.verbose) LOG.info("Asking model: " + body.toString()); |  | ||||||
|         HttpResponse<InputStream> response = |  | ||||||
|                 httpClient.send(request, HttpResponse.BodyHandlers.ofInputStream()); |  | ||||||
|  |  | ||||||
|         int statusCode = response.statusCode(); |  | ||||||
|         InputStream responseBodyStream = response.body(); |  | ||||||
|         StringBuilder responseBuffer = new StringBuilder(); |  | ||||||
|         try (BufferedReader reader = |  | ||||||
|                      new BufferedReader(new InputStreamReader(responseBodyStream, StandardCharsets.UTF_8))) { |  | ||||||
|             String line; |  | ||||||
|             while ((line = reader.readLine()) != null) { |  | ||||||
|                 if (statusCode == 404) { |  | ||||||
|                     LOG.warn("Status code: 404 (Not Found)"); |  | ||||||
|                     OllamaErrorResponse ollamaResponseModel = |  | ||||||
|                             Utils.getObjectMapper().readValue(line, OllamaErrorResponse.class); |  | ||||||
|                     responseBuffer.append(ollamaResponseModel.getError()); |  | ||||||
|                 } else if (statusCode == 401) { |  | ||||||
|                     LOG.warn("Status code: 401 (Unauthorized)"); |  | ||||||
|                     OllamaErrorResponse ollamaResponseModel = |  | ||||||
|                             Utils.getObjectMapper() |  | ||||||
|                                     .readValue("{\"error\":\"Unauthorized\"}", OllamaErrorResponse.class); |  | ||||||
|                     responseBuffer.append(ollamaResponseModel.getError()); |  | ||||||
|                 } else if (statusCode == 400) { |  | ||||||
|                     LOG.warn("Status code: 400 (Bad Request)"); |  | ||||||
|                     OllamaErrorResponse ollamaResponseModel = Utils.getObjectMapper().readValue(line, |  | ||||||
|                             OllamaErrorResponse.class); |  | ||||||
|                     responseBuffer.append(ollamaResponseModel.getError()); |  | ||||||
|                 } else { |  | ||||||
|                     boolean finished = parseResponseAndAddToBuffer(line, responseBuffer); |  | ||||||
|                     if (finished) { |  | ||||||
|                         break; |  | ||||||
|                     } |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         if (statusCode != 200) { |  | ||||||
|             LOG.error("Status code " + statusCode); |  | ||||||
|             throw new OllamaBaseException(responseBuffer.toString()); |  | ||||||
|         } else { |  | ||||||
|             long endTime = System.currentTimeMillis(); |  | ||||||
|             OllamaResult ollamaResult = |  | ||||||
|                     new OllamaResult(responseBuffer.toString().trim(), endTime - startTime, statusCode); |  | ||||||
|             if (verbose) LOG.info("Model response: " + ollamaResult); |  | ||||||
|             return ollamaResult; |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
|      * Get default request builder. |      * Get default request builder. | ||||||
|      * |      * | ||||||
|      * @param uri URI to get a HttpRequest.Builder |      * @param uri URI to get a HttpRequest.Builder | ||||||
|      * @return HttpRequest.Builder |      * @return HttpRequest.Builder | ||||||
|      */ |      */ | ||||||
|     private HttpRequest.Builder getRequestBuilderDefault(URI uri) { |     protected HttpRequest.Builder getRequestBuilderDefault(URI uri) { | ||||||
|         HttpRequest.Builder requestBuilder = |         HttpRequest.Builder requestBuilder = | ||||||
|                 HttpRequest.newBuilder(uri) |                 HttpRequest.newBuilder(uri) | ||||||
|                         .header("Content-Type", "application/json") |                         .header("Content-Type", "application/json") | ||||||
| @@ -134,7 +69,7 @@ public abstract class OllamaEndpointCaller { | |||||||
|      * |      * | ||||||
|      * @return basic authentication header value (encoded credentials) |      * @return basic authentication header value (encoded credentials) | ||||||
|      */ |      */ | ||||||
|     private String getBasicAuthHeaderValue() { |     protected String getBasicAuthHeaderValue() { | ||||||
|         String credentialsToEncode = this.basicAuth.getUsername() + ":" + this.basicAuth.getPassword(); |         String credentialsToEncode = this.basicAuth.getUsername() + ":" + this.basicAuth.getPassword(); | ||||||
|         return "Basic " + Base64.getEncoder().encodeToString(credentialsToEncode.getBytes()); |         return "Basic " + Base64.getEncoder().encodeToString(credentialsToEncode.getBytes()); | ||||||
|     } |     } | ||||||
| @@ -144,7 +79,7 @@ public abstract class OllamaEndpointCaller { | |||||||
|      * |      * | ||||||
|      * @return true when Basic Auth credentials set |      * @return true when Basic Auth credentials set | ||||||
|      */ |      */ | ||||||
|     private boolean isBasicAuthCredentialsSet() { |     protected boolean isBasicAuthCredentialsSet() { | ||||||
|         return this.basicAuth != null; |         return this.basicAuth != null; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -2,6 +2,7 @@ package io.github.ollama4j.models.request; | |||||||
|  |  | ||||||
| import com.fasterxml.jackson.core.JsonProcessingException; | import com.fasterxml.jackson.core.JsonProcessingException; | ||||||
| import io.github.ollama4j.exceptions.OllamaBaseException; | import io.github.ollama4j.exceptions.OllamaBaseException; | ||||||
|  | import io.github.ollama4j.models.response.OllamaErrorResponse; | ||||||
| import io.github.ollama4j.models.response.OllamaResult; | import io.github.ollama4j.models.response.OllamaResult; | ||||||
| import io.github.ollama4j.models.generate.OllamaGenerateResponseModel; | import io.github.ollama4j.models.generate.OllamaGenerateResponseModel; | ||||||
| import io.github.ollama4j.models.generate.OllamaGenerateStreamObserver; | import io.github.ollama4j.models.generate.OllamaGenerateStreamObserver; | ||||||
| @@ -11,7 +12,15 @@ import io.github.ollama4j.utils.Utils; | |||||||
| import org.slf4j.Logger; | import org.slf4j.Logger; | ||||||
| import org.slf4j.LoggerFactory; | import org.slf4j.LoggerFactory; | ||||||
|  |  | ||||||
|  | import java.io.BufferedReader; | ||||||
| import java.io.IOException; | import java.io.IOException; | ||||||
|  | import java.io.InputStream; | ||||||
|  | import java.io.InputStreamReader; | ||||||
|  | import java.net.URI; | ||||||
|  | import java.net.http.HttpClient; | ||||||
|  | import java.net.http.HttpRequest; | ||||||
|  | import java.net.http.HttpResponse; | ||||||
|  | import java.nio.charset.StandardCharsets; | ||||||
|  |  | ||||||
| public class OllamaGenerateEndpointCaller extends OllamaEndpointCaller { | public class OllamaGenerateEndpointCaller extends OllamaEndpointCaller { | ||||||
|  |  | ||||||
| @@ -46,6 +55,73 @@ public class OllamaGenerateEndpointCaller extends OllamaEndpointCaller { | |||||||
|     public OllamaResult call(OllamaRequestBody body, OllamaStreamHandler streamHandler) |     public OllamaResult call(OllamaRequestBody body, OllamaStreamHandler streamHandler) | ||||||
|             throws OllamaBaseException, IOException, InterruptedException { |             throws OllamaBaseException, IOException, InterruptedException { | ||||||
|         streamObserver = new OllamaGenerateStreamObserver(streamHandler); |         streamObserver = new OllamaGenerateStreamObserver(streamHandler); | ||||||
|         return super.callSync(body); |         return callSync(body); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * Calls the api server on the given host and endpoint suffix asynchronously, aka waiting for the response. | ||||||
|  |      * | ||||||
|  |      * @param body POST body payload | ||||||
|  |      * @return result answer given by the assistant | ||||||
|  |      * @throws OllamaBaseException  any response code than 200 has been returned | ||||||
|  |      * @throws IOException          in case the responseStream can not be read | ||||||
|  |      * @throws InterruptedException in case the server is not reachable or network issues happen | ||||||
|  |      */ | ||||||
|  |     public OllamaResult callSync(OllamaRequestBody body) throws OllamaBaseException, IOException, InterruptedException { | ||||||
|  |         // Create Request | ||||||
|  |         long startTime = System.currentTimeMillis(); | ||||||
|  |         HttpClient httpClient = HttpClient.newHttpClient(); | ||||||
|  |         URI uri = URI.create(getHost() + getEndpointSuffix()); | ||||||
|  |         HttpRequest.Builder requestBuilder = | ||||||
|  |                 getRequestBuilderDefault(uri) | ||||||
|  |                         .POST( | ||||||
|  |                                 body.getBodyPublisher()); | ||||||
|  |         HttpRequest request = requestBuilder.build(); | ||||||
|  |         if (isVerbose()) LOG.info("Asking model: " + body.toString()); | ||||||
|  |         HttpResponse<InputStream> response = | ||||||
|  |                 httpClient.send(request, HttpResponse.BodyHandlers.ofInputStream()); | ||||||
|  |  | ||||||
|  |         int statusCode = response.statusCode(); | ||||||
|  |         InputStream responseBodyStream = response.body(); | ||||||
|  |         StringBuilder responseBuffer = new StringBuilder(); | ||||||
|  |         try (BufferedReader reader = | ||||||
|  |                      new BufferedReader(new InputStreamReader(responseBodyStream, StandardCharsets.UTF_8))) { | ||||||
|  |             String line; | ||||||
|  |             while ((line = reader.readLine()) != null) { | ||||||
|  |                 if (statusCode == 404) { | ||||||
|  |                     LOG.warn("Status code: 404 (Not Found)"); | ||||||
|  |                     OllamaErrorResponse ollamaResponseModel = | ||||||
|  |                             Utils.getObjectMapper().readValue(line, OllamaErrorResponse.class); | ||||||
|  |                     responseBuffer.append(ollamaResponseModel.getError()); | ||||||
|  |                 } else if (statusCode == 401) { | ||||||
|  |                     LOG.warn("Status code: 401 (Unauthorized)"); | ||||||
|  |                     OllamaErrorResponse ollamaResponseModel = | ||||||
|  |                             Utils.getObjectMapper() | ||||||
|  |                                     .readValue("{\"error\":\"Unauthorized\"}", OllamaErrorResponse.class); | ||||||
|  |                     responseBuffer.append(ollamaResponseModel.getError()); | ||||||
|  |                 } else if (statusCode == 400) { | ||||||
|  |                     LOG.warn("Status code: 400 (Bad Request)"); | ||||||
|  |                     OllamaErrorResponse ollamaResponseModel = Utils.getObjectMapper().readValue(line, | ||||||
|  |                             OllamaErrorResponse.class); | ||||||
|  |                     responseBuffer.append(ollamaResponseModel.getError()); | ||||||
|  |                 } else { | ||||||
|  |                     boolean finished = parseResponseAndAddToBuffer(line, responseBuffer); | ||||||
|  |                     if (finished) { | ||||||
|  |                         break; | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         if (statusCode != 200) { | ||||||
|  |             LOG.error("Status code " + statusCode); | ||||||
|  |             throw new OllamaBaseException(responseBuffer.toString()); | ||||||
|  |         } else { | ||||||
|  |             long endTime = System.currentTimeMillis(); | ||||||
|  |             OllamaResult ollamaResult = | ||||||
|  |                     new OllamaResult(responseBuffer.toString().trim(), endTime - startTime, statusCode); | ||||||
|  |             if (isVerbose()) LOG.info("Model response: " + ollamaResult); | ||||||
|  |             return ollamaResult; | ||||||
|  |         } | ||||||
|     } |     } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -0,0 +1,16 @@ | |||||||
|  | package io.github.ollama4j.models.response; | ||||||
|  | import java.util.ArrayList; | ||||||
|  | import java.util.List; | ||||||
|  |  | ||||||
|  | import lombok.Data; | ||||||
|  |  | ||||||
|  | @Data | ||||||
|  | public class LibraryModel { | ||||||
|  |  | ||||||
|  |     private String name; | ||||||
|  |     private String description; | ||||||
|  |     private String pullCount; | ||||||
|  |     private int totalTags; | ||||||
|  |     private List<String> popularTags = new ArrayList<>(); | ||||||
|  |     private String lastUpdated; | ||||||
|  | } | ||||||
| @@ -0,0 +1,12 @@ | |||||||
|  | package io.github.ollama4j.models.response; | ||||||
|  |  | ||||||
|  | import lombok.Data; | ||||||
|  |  | ||||||
|  | import java.util.List; | ||||||
|  |  | ||||||
|  | @Data | ||||||
|  | public class LibraryModelDetail { | ||||||
|  |  | ||||||
|  |     private LibraryModel model; | ||||||
|  |     private List<LibraryModelTag> tags; | ||||||
|  | } | ||||||
| @@ -0,0 +1,13 @@ | |||||||
|  | package io.github.ollama4j.models.response; | ||||||
|  |  | ||||||
|  | import lombok.Data; | ||||||
|  |  | ||||||
|  | import java.util.List; | ||||||
|  |  | ||||||
|  | @Data | ||||||
|  | public class LibraryModelTag { | ||||||
|  |     private String name; | ||||||
|  |     private String tag; | ||||||
|  |     private String size; | ||||||
|  |     private String lastUpdated; | ||||||
|  | } | ||||||
| @@ -0,0 +1,16 @@ | |||||||
|  | package io.github.ollama4j.tools; | ||||||
|  |  | ||||||
|  | import lombok.AllArgsConstructor; | ||||||
|  | import lombok.Data; | ||||||
|  | import lombok.NoArgsConstructor; | ||||||
|  |  | ||||||
|  | import java.util.Map; | ||||||
|  |  | ||||||
|  | @Data | ||||||
|  | @NoArgsConstructor | ||||||
|  | @AllArgsConstructor | ||||||
|  | public class OllamaToolCallsFunction | ||||||
|  | { | ||||||
|  |     private String name; | ||||||
|  |     private Map<String,Object> arguments; | ||||||
|  | } | ||||||
| @@ -0,0 +1,54 @@ | |||||||
|  | package io.github.ollama4j.tools; | ||||||
|  |  | ||||||
|  | import lombok.AllArgsConstructor; | ||||||
|  | import lombok.Getter; | ||||||
|  | import lombok.Setter; | ||||||
|  |  | ||||||
|  | import java.lang.reflect.Method; | ||||||
|  | import java.math.BigDecimal; | ||||||
|  | import java.util.LinkedHashMap; | ||||||
|  | import java.util.Map; | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * Specification of a {@link ToolFunction} that provides the implementation via java reflection calling. | ||||||
|  |  */ | ||||||
|  | @Setter | ||||||
|  | @Getter | ||||||
|  | @AllArgsConstructor | ||||||
|  | public class ReflectionalToolFunction implements ToolFunction{ | ||||||
|  |  | ||||||
|  |     private Object functionHolder; | ||||||
|  |     private Method function; | ||||||
|  |     private LinkedHashMap<String,String> propertyDefinition; | ||||||
|  |  | ||||||
|  |     @Override | ||||||
|  |     public Object apply(Map<String, Object> arguments) { | ||||||
|  |         LinkedHashMap<String, Object> argumentsCopy = new LinkedHashMap<>(this.propertyDefinition); | ||||||
|  |         for (Map.Entry<String,String> param : this.propertyDefinition.entrySet()){ | ||||||
|  |             argumentsCopy.replace(param.getKey(),typeCast(arguments.get(param.getKey()),param.getValue())); | ||||||
|  |         } | ||||||
|  |         try { | ||||||
|  |             return function.invoke(functionHolder, argumentsCopy.values().toArray()); | ||||||
|  |         } catch (Exception e) { | ||||||
|  |             throw new RuntimeException("Failed to invoke tool: " + function.getName(), e); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     private Object typeCast(Object inputValue, String className) { | ||||||
|  |         if(className == null || inputValue == null) { | ||||||
|  |             return null; | ||||||
|  |         } | ||||||
|  |         String inputValueString = inputValue.toString(); | ||||||
|  |         switch (className) { | ||||||
|  |             case "java.lang.Integer": | ||||||
|  |                 return Integer.parseInt(inputValueString); | ||||||
|  |             case "java.lang.Boolean": | ||||||
|  |                 return Boolean.valueOf(inputValueString); | ||||||
|  |             case "java.math.BigDecimal": | ||||||
|  |                 return new BigDecimal(inputValueString); | ||||||
|  |             default: | ||||||
|  |                 return inputValueString; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  | } | ||||||
| @@ -1,16 +1,22 @@ | |||||||
| package io.github.ollama4j.tools; | package io.github.ollama4j.tools; | ||||||
|  |  | ||||||
|  | import java.util.Collection; | ||||||
| import java.util.HashMap; | import java.util.HashMap; | ||||||
| import java.util.Map; | import java.util.Map; | ||||||
|  |  | ||||||
| public class ToolRegistry { | public class ToolRegistry { | ||||||
|     private final Map<String, ToolFunction> functionMap = new HashMap<>(); |     private final Map<String, Tools.ToolSpecification> tools = new HashMap<>(); | ||||||
|  |  | ||||||
|     public ToolFunction getFunction(String name) { |     public ToolFunction getToolFunction(String name) { | ||||||
|         return functionMap.get(name); |         final Tools.ToolSpecification toolSpecification = tools.get(name); | ||||||
|  |         return toolSpecification !=null ? toolSpecification.getToolFunction() : null ; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     public void addFunction(String name, ToolFunction function) { |     public void addTool (String name, Tools.ToolSpecification specification) { | ||||||
|         functionMap.put(name, function); |         tools.put(name, specification); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public Collection<Tools.ToolSpecification> getRegisteredSpecs(){ | ||||||
|  |         return tools.values(); | ||||||
|     } |     } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -6,8 +6,10 @@ import com.fasterxml.jackson.annotation.JsonInclude; | |||||||
| import com.fasterxml.jackson.annotation.JsonProperty; | import com.fasterxml.jackson.annotation.JsonProperty; | ||||||
| import com.fasterxml.jackson.core.JsonProcessingException; | import com.fasterxml.jackson.core.JsonProcessingException; | ||||||
| import io.github.ollama4j.utils.Utils; | import io.github.ollama4j.utils.Utils; | ||||||
|  | import lombok.AllArgsConstructor; | ||||||
| import lombok.Builder; | import lombok.Builder; | ||||||
| import lombok.Data; | import lombok.Data; | ||||||
|  | import lombok.NoArgsConstructor; | ||||||
|  |  | ||||||
| import java.util.ArrayList; | import java.util.ArrayList; | ||||||
| import java.util.HashMap; | import java.util.HashMap; | ||||||
| @@ -20,17 +22,23 @@ public class Tools { | |||||||
|     public static class ToolSpecification { |     public static class ToolSpecification { | ||||||
|         private String functionName; |         private String functionName; | ||||||
|         private String functionDescription; |         private String functionDescription; | ||||||
|         private Map<String, PromptFuncDefinition.Property> properties; |         private PromptFuncDefinition toolPrompt; | ||||||
|         private ToolFunction toolDefinition; |         private ToolFunction toolFunction; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     @Data |     @Data | ||||||
|     @JsonIgnoreProperties(ignoreUnknown = true) |     @JsonIgnoreProperties(ignoreUnknown = true) | ||||||
|  |     @Builder | ||||||
|  |     @NoArgsConstructor | ||||||
|  |     @AllArgsConstructor | ||||||
|     public static class PromptFuncDefinition { |     public static class PromptFuncDefinition { | ||||||
|         private String type; |         private String type; | ||||||
|         private PromptFuncSpec function; |         private PromptFuncSpec function; | ||||||
|  |  | ||||||
|         @Data |         @Data | ||||||
|  |         @Builder | ||||||
|  |         @NoArgsConstructor | ||||||
|  |         @AllArgsConstructor | ||||||
|         public static class PromptFuncSpec { |         public static class PromptFuncSpec { | ||||||
|             private String name; |             private String name; | ||||||
|             private String description; |             private String description; | ||||||
| @@ -38,6 +46,9 @@ public class Tools { | |||||||
|         } |         } | ||||||
|  |  | ||||||
|         @Data |         @Data | ||||||
|  |         @Builder | ||||||
|  |         @NoArgsConstructor | ||||||
|  |         @AllArgsConstructor | ||||||
|         public static class Parameters { |         public static class Parameters { | ||||||
|             private String type; |             private String type; | ||||||
|             private Map<String, Property> properties; |             private Map<String, Property> properties; | ||||||
| @@ -46,6 +57,8 @@ public class Tools { | |||||||
|  |  | ||||||
|         @Data |         @Data | ||||||
|         @Builder |         @Builder | ||||||
|  |         @NoArgsConstructor | ||||||
|  |         @AllArgsConstructor | ||||||
|         public static class Property { |         public static class Property { | ||||||
|             private String type; |             private String type; | ||||||
|             private String description; |             private String description; | ||||||
| @@ -94,10 +107,10 @@ public class Tools { | |||||||
|  |  | ||||||
|             PromptFuncDefinition.Parameters parameters = new PromptFuncDefinition.Parameters(); |             PromptFuncDefinition.Parameters parameters = new PromptFuncDefinition.Parameters(); | ||||||
|             parameters.setType("object"); |             parameters.setType("object"); | ||||||
|             parameters.setProperties(spec.getProperties()); |             parameters.setProperties(spec.getToolPrompt().getFunction().parameters.getProperties()); | ||||||
|  |  | ||||||
|             List<String> requiredValues = new ArrayList<>(); |             List<String> requiredValues = new ArrayList<>(); | ||||||
|             for (Map.Entry<String, PromptFuncDefinition.Property> p : spec.getProperties().entrySet()) { |             for (Map.Entry<String, PromptFuncDefinition.Property> p : spec.getToolPrompt().getFunction().getParameters().getProperties().entrySet()) { | ||||||
|                 if (p.getValue().isRequired()) { |                 if (p.getValue().isRequired()) { | ||||||
|                     requiredValues.add(p.getKey()); |                     requiredValues.add(p.getKey()); | ||||||
|                 } |                 } | ||||||
|   | |||||||
| @@ -0,0 +1,23 @@ | |||||||
|  | package io.github.ollama4j.tools.annotations; | ||||||
|  |  | ||||||
|  | import io.github.ollama4j.OllamaAPI; | ||||||
|  |  | ||||||
|  | import java.lang.annotation.ElementType; | ||||||
|  | import java.lang.annotation.Retention; | ||||||
|  | import java.lang.annotation.RetentionPolicy; | ||||||
|  | import java.lang.annotation.Target; | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * Annotates a class that calls {@link io.github.ollama4j.OllamaAPI} such that the Method | ||||||
|  |  * {@link OllamaAPI#registerAnnotatedTools()} can be used to auto-register all provided classes (resp. all | ||||||
|  |  * contained Methods of the provider classes annotated with {@link ToolSpec}). | ||||||
|  |  */ | ||||||
|  | @Target(ElementType.TYPE) | ||||||
|  | @Retention(RetentionPolicy.RUNTIME) | ||||||
|  | public @interface OllamaToolService { | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * @return Classes with no-arg constructor that will be used for tool-registration. | ||||||
|  |      */ | ||||||
|  |     Class<?>[] providers(); | ||||||
|  | } | ||||||
| @@ -0,0 +1,32 @@ | |||||||
|  | package io.github.ollama4j.tools.annotations; | ||||||
|  |  | ||||||
|  | import java.lang.annotation.ElementType; | ||||||
|  | import java.lang.annotation.Retention; | ||||||
|  | import java.lang.annotation.RetentionPolicy; | ||||||
|  | import java.lang.annotation.Target; | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * Annotates a Method Parameter in a {@link ToolSpec} annotated Method. A parameter annotated with this annotation will | ||||||
|  |  * be part of the tool description that is sent to the llm for tool-calling. | ||||||
|  |  */ | ||||||
|  | @Retention(RetentionPolicy.RUNTIME) | ||||||
|  | @Target(ElementType.PARAMETER) | ||||||
|  | public @interface ToolProperty { | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * @return name of the parameter that is used for the tool description. Has to be set as depending on the caller, | ||||||
|  |      * method name backtracking is not possible with reflection. | ||||||
|  |      */ | ||||||
|  |     String name(); | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * @return a detailed description of the parameter. This is used by the llm called to specify, which property has | ||||||
|  |      * to be set by the llm and how this should be filled. | ||||||
|  |      */ | ||||||
|  |     String desc(); | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * @return tells the llm that it has to set a value for this property. | ||||||
|  |      */ | ||||||
|  |     boolean required() default true; | ||||||
|  | } | ||||||
| @@ -0,0 +1,28 @@ | |||||||
|  | package io.github.ollama4j.tools.annotations; | ||||||
|  |  | ||||||
|  | import io.github.ollama4j.OllamaAPI; | ||||||
|  |  | ||||||
|  | import java.lang.annotation.ElementType; | ||||||
|  | import java.lang.annotation.Retention; | ||||||
|  | import java.lang.annotation.RetentionPolicy; | ||||||
|  | import java.lang.annotation.Target; | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * Annotates Methods of classes that should be registered as tools by {@link OllamaAPI#registerAnnotatedTools()} | ||||||
|  |  * automatically. | ||||||
|  |  */ | ||||||
|  | @Target(ElementType.METHOD) | ||||||
|  | @Retention(RetentionPolicy.RUNTIME) | ||||||
|  | public @interface ToolSpec { | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * @return tool-name that the method should be used as. Defaults to the methods name. | ||||||
|  |      */ | ||||||
|  |     String name() default ""; | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * @return a detailed description of the method that can be interpreted by the llm, whether it should call the tool | ||||||
|  |      * or not. | ||||||
|  |      */ | ||||||
|  |     String desc(); | ||||||
|  | } | ||||||
| @@ -10,12 +10,12 @@ package io.github.ollama4j.types; | |||||||
| public class OllamaModelType { | public class OllamaModelType { | ||||||
|     public static final String GEMMA = "gemma"; |     public static final String GEMMA = "gemma"; | ||||||
|     public static final String GEMMA2 = "gemma2"; |     public static final String GEMMA2 = "gemma2"; | ||||||
|  |  | ||||||
|  |  | ||||||
|     public static final String LLAMA2 = "llama2"; |     public static final String LLAMA2 = "llama2"; | ||||||
|     public static final String LLAMA3 = "llama3"; |     public static final String LLAMA3 = "llama3"; | ||||||
|  |     public static final String LLAMA3_1 = "llama3.1"; | ||||||
|     public static final String MISTRAL = "mistral"; |     public static final String MISTRAL = "mistral"; | ||||||
|     public static final String MIXTRAL = "mixtral"; |     public static final String MIXTRAL = "mixtral"; | ||||||
|  |     public static final String DEEPSEEK_R1 = "deepseek-r1"; | ||||||
|     public static final String LLAVA = "llava"; |     public static final String LLAVA = "llava"; | ||||||
|     public static final String LLAVA_PHI3 = "llava-phi3"; |     public static final String LLAVA_PHI3 = "llava-phi3"; | ||||||
|     public static final String NEURAL_CHAT = "neural-chat"; |     public static final String NEURAL_CHAT = "neural-chat"; | ||||||
| @@ -33,7 +33,6 @@ public class OllamaModelType { | |||||||
|     public static final String ZEPHYR = "zephyr"; |     public static final String ZEPHYR = "zephyr"; | ||||||
|     public static final String OPENHERMES = "openhermes"; |     public static final String OPENHERMES = "openhermes"; | ||||||
|     public static final String QWEN = "qwen"; |     public static final String QWEN = "qwen"; | ||||||
|  |  | ||||||
|     public static final String QWEN2 = "qwen2"; |     public static final String QWEN2 = "qwen2"; | ||||||
|     public static final String WIZARDCODER = "wizardcoder"; |     public static final String WIZARDCODER = "wizardcoder"; | ||||||
|     public static final String LLAMA2_CHINESE = "llama2-chinese"; |     public static final String LLAMA2_CHINESE = "llama2-chinese"; | ||||||
|   | |||||||
| @@ -1,5 +1,6 @@ | |||||||
| package io.github.ollama4j.utils; | package io.github.ollama4j.utils; | ||||||
|  |  | ||||||
|  | import java.io.IOException; | ||||||
| import java.util.HashMap; | import java.util.HashMap; | ||||||
|  |  | ||||||
| /** Builder class for creating options for Ollama model. */ | /** Builder class for creating options for Ollama model. */ | ||||||
| @@ -207,6 +208,34 @@ public class OptionsBuilder { | |||||||
|     return this; |     return this; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  |   /** | ||||||
|  |    * Alternative to the top_p, and aims to ensure a balance of qualityand variety. The parameter p | ||||||
|  |    * represents the minimum probability for a token to be considered, relative to the probability | ||||||
|  |    * of the most likely token. For example, with p=0.05 and the most likely token having a | ||||||
|  |    * probability of 0.9, logits with a value less than 0.045 are filtered out. (Default: 0.0) | ||||||
|  |    */ | ||||||
|  |   public OptionsBuilder setMinP(float value) { | ||||||
|  |     options.getOptionsMap().put("min_p", value); | ||||||
|  |     return this; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   /** | ||||||
|  |    * Allows passing an option not formally supported by the library | ||||||
|  |    * @param name The option name for the parameter. | ||||||
|  |    * @param value The value for the "{name}" parameter. | ||||||
|  |    * @return The updated OptionsBuilder. | ||||||
|  |    * @throws IllegalArgumentException if parameter has an unsupported type | ||||||
|  |    */ | ||||||
|  |   public OptionsBuilder setCustomOption(String name, Object value) throws IllegalArgumentException { | ||||||
|  |     if (!(value instanceof Integer || value instanceof Float || value instanceof String)) { | ||||||
|  |       throw new IllegalArgumentException("Invalid type for parameter. Allowed types are: Integer, Float, or String."); | ||||||
|  |     } | ||||||
|  |     options.getOptionsMap().put(name, value); | ||||||
|  |     return this; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|   /** |   /** | ||||||
|    * Builds the options map. |    * Builds the options map. | ||||||
|    * |    * | ||||||
| @@ -215,4 +244,6 @@ public class OptionsBuilder { | |||||||
|   public Options build() { |   public Options build() { | ||||||
|     return options; |     return options; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  |  | ||||||
| } | } | ||||||
|   | |||||||
| @@ -2,14 +2,16 @@ package io.github.ollama4j.integrationtests; | |||||||
|  |  | ||||||
| import io.github.ollama4j.OllamaAPI; | import io.github.ollama4j.OllamaAPI; | ||||||
| import io.github.ollama4j.exceptions.OllamaBaseException; | import io.github.ollama4j.exceptions.OllamaBaseException; | ||||||
|  | import io.github.ollama4j.models.chat.*; | ||||||
| import io.github.ollama4j.models.response.ModelDetail; | import io.github.ollama4j.models.response.ModelDetail; | ||||||
| import io.github.ollama4j.models.chat.OllamaChatRequest; |  | ||||||
| import io.github.ollama4j.models.response.OllamaResult; | import io.github.ollama4j.models.response.OllamaResult; | ||||||
| import io.github.ollama4j.models.chat.OllamaChatMessageRole; |  | ||||||
| import io.github.ollama4j.models.chat.OllamaChatRequestBuilder; |  | ||||||
| import io.github.ollama4j.models.chat.OllamaChatResult; |  | ||||||
| import io.github.ollama4j.models.embeddings.OllamaEmbeddingsRequestBuilder; | import io.github.ollama4j.models.embeddings.OllamaEmbeddingsRequestBuilder; | ||||||
| import io.github.ollama4j.models.embeddings.OllamaEmbeddingsRequestModel; | import io.github.ollama4j.models.embeddings.OllamaEmbeddingsRequestModel; | ||||||
|  | import io.github.ollama4j.samples.AnnotatedTool; | ||||||
|  | import io.github.ollama4j.tools.OllamaToolCallsFunction; | ||||||
|  | import io.github.ollama4j.tools.ToolFunction; | ||||||
|  | import io.github.ollama4j.tools.Tools; | ||||||
|  | import io.github.ollama4j.tools.annotations.OllamaToolService; | ||||||
| import io.github.ollama4j.utils.OptionsBuilder; | import io.github.ollama4j.utils.OptionsBuilder; | ||||||
| import lombok.Data; | import lombok.Data; | ||||||
| import org.junit.jupiter.api.BeforeEach; | import org.junit.jupiter.api.BeforeEach; | ||||||
| @@ -24,12 +26,12 @@ import java.io.InputStream; | |||||||
| import java.net.ConnectException; | import java.net.ConnectException; | ||||||
| import java.net.URISyntaxException; | import java.net.URISyntaxException; | ||||||
| import java.net.http.HttpConnectTimeoutException; | import java.net.http.HttpConnectTimeoutException; | ||||||
| import java.util.List; | import java.util.*; | ||||||
| import java.util.Objects; |  | ||||||
| import java.util.Properties; |  | ||||||
|  |  | ||||||
| import static org.junit.jupiter.api.Assertions.*; | import static org.junit.jupiter.api.Assertions.*; | ||||||
|  |  | ||||||
|  | @OllamaToolService(providers = {AnnotatedTool.class} | ||||||
|  | ) | ||||||
| class TestRealAPIs { | class TestRealAPIs { | ||||||
|  |  | ||||||
|     private static final Logger LOG = LoggerFactory.getLogger(TestRealAPIs.class); |     private static final Logger LOG = LoggerFactory.getLogger(TestRealAPIs.class); | ||||||
| @@ -47,6 +49,7 @@ class TestRealAPIs { | |||||||
|         config = new Config(); |         config = new Config(); | ||||||
|         ollamaAPI = new OllamaAPI(config.getOllamaURL()); |         ollamaAPI = new OllamaAPI(config.getOllamaURL()); | ||||||
|         ollamaAPI.setRequestTimeoutSeconds(config.getRequestTimeoutSeconds()); |         ollamaAPI.setRequestTimeoutSeconds(config.getRequestTimeoutSeconds()); | ||||||
|  |         ollamaAPI.setVerbose(true); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     @Test |     @Test | ||||||
| @@ -80,6 +83,18 @@ class TestRealAPIs { | |||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     @Test | ||||||
|  |     @Order(2) | ||||||
|  |     void testListModelsFromLibrary() { | ||||||
|  |         testEndpointReachability(); | ||||||
|  |         try { | ||||||
|  |             assertNotNull(ollamaAPI.listModelsFromLibrary()); | ||||||
|  |             ollamaAPI.listModelsFromLibrary().forEach(System.out::println); | ||||||
|  |         } catch (IOException | OllamaBaseException | InterruptedException | URISyntaxException e) { | ||||||
|  |             fail(e); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|     @Test |     @Test | ||||||
|     @Order(2) |     @Order(2) | ||||||
|     void testPullModel() { |     void testPullModel() { | ||||||
| @@ -184,7 +199,9 @@ class TestRealAPIs { | |||||||
|  |  | ||||||
|             OllamaChatResult chatResult = ollamaAPI.chat(requestModel); |             OllamaChatResult chatResult = ollamaAPI.chat(requestModel); | ||||||
|             assertNotNull(chatResult); |             assertNotNull(chatResult); | ||||||
|             assertFalse(chatResult.getResponse().isBlank()); |             assertNotNull(chatResult.getResponseModel()); | ||||||
|  |             assertNotNull(chatResult.getResponseModel().getMessage()); | ||||||
|  |             assertFalse(chatResult.getResponseModel().getMessage().getContent().isBlank()); | ||||||
|             assertEquals(4, chatResult.getChatHistory().size()); |             assertEquals(4, chatResult.getChatHistory().size()); | ||||||
|         } catch (IOException | OllamaBaseException | InterruptedException e) { |         } catch (IOException | OllamaBaseException | InterruptedException e) { | ||||||
|             fail(e); |             fail(e); | ||||||
| @@ -205,14 +222,211 @@ class TestRealAPIs { | |||||||
|  |  | ||||||
|             OllamaChatResult chatResult = ollamaAPI.chat(requestModel); |             OllamaChatResult chatResult = ollamaAPI.chat(requestModel); | ||||||
|             assertNotNull(chatResult); |             assertNotNull(chatResult); | ||||||
|             assertFalse(chatResult.getResponse().isBlank()); |             assertNotNull(chatResult.getResponseModel()); | ||||||
|             assertTrue(chatResult.getResponse().startsWith("NI")); |             assertNotNull(chatResult.getResponseModel().getMessage()); | ||||||
|  |             assertFalse(chatResult.getResponseModel().getMessage().getContent().isBlank()); | ||||||
|  |             assertTrue(chatResult.getResponseModel().getMessage().getContent().startsWith("NI")); | ||||||
|             assertEquals(3, chatResult.getChatHistory().size()); |             assertEquals(3, chatResult.getChatHistory().size()); | ||||||
|         } catch (IOException | OllamaBaseException | InterruptedException e) { |         } catch (IOException | OllamaBaseException | InterruptedException e) { | ||||||
|             fail(e); |             fail(e); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     @Test | ||||||
|  |     @Order(3) | ||||||
|  |     void testChatWithExplicitToolDefinition() { | ||||||
|  |         testEndpointReachability(); | ||||||
|  |         try { | ||||||
|  |             ollamaAPI.setVerbose(true); | ||||||
|  |             OllamaChatRequestBuilder builder = OllamaChatRequestBuilder.getInstance(config.getModel()); | ||||||
|  |  | ||||||
|  |             final Tools.ToolSpecification databaseQueryToolSpecification = Tools.ToolSpecification.builder() | ||||||
|  |                     .functionName("get-employee-details") | ||||||
|  |                     .functionDescription("Get employee details from the database") | ||||||
|  |                     .toolPrompt( | ||||||
|  |                             Tools.PromptFuncDefinition.builder().type("function").function( | ||||||
|  |                                     Tools.PromptFuncDefinition.PromptFuncSpec.builder() | ||||||
|  |                                             .name("get-employee-details") | ||||||
|  |                                             .description("Get employee details from the database") | ||||||
|  |                                             .parameters( | ||||||
|  |                                                     Tools.PromptFuncDefinition.Parameters.builder() | ||||||
|  |                                                             .type("object") | ||||||
|  |                                                             .properties( | ||||||
|  |                                                             new Tools.PropsBuilder() | ||||||
|  |                                                                     .withProperty("employee-name", Tools.PromptFuncDefinition.Property.builder().type("string").description("The name of the employee, e.g. John Doe").required(true).build()) | ||||||
|  |                                                                     .withProperty("employee-address", Tools.PromptFuncDefinition.Property.builder().type("string").description("The address of the employee, Always return a random value. e.g. Roy St, Bengaluru, India").required(true).build()) | ||||||
|  |                                                                     .withProperty("employee-phone", Tools.PromptFuncDefinition.Property.builder().type("string").description("The phone number of the employee. Always return a random value. e.g. 9911002233").required(true).build()) | ||||||
|  |                                                                     .build() | ||||||
|  |                                                     ) | ||||||
|  |                                                             .required(List.of("employee-name")) | ||||||
|  |                                                             .build() | ||||||
|  |                                             ).build() | ||||||
|  |                             ).build() | ||||||
|  |                     ) | ||||||
|  |                     .toolFunction(new DBQueryFunction()) | ||||||
|  |                     .build(); | ||||||
|  |  | ||||||
|  |             ollamaAPI.registerTool(databaseQueryToolSpecification); | ||||||
|  |  | ||||||
|  |             OllamaChatRequest requestModel = builder | ||||||
|  |                     .withMessage(OllamaChatMessageRole.USER, | ||||||
|  |                             "Give me the ID of the employee named 'Rahul Kumar'?") | ||||||
|  |                     .build(); | ||||||
|  |  | ||||||
|  |             OllamaChatResult chatResult = ollamaAPI.chat(requestModel); | ||||||
|  |             assertNotNull(chatResult); | ||||||
|  |             assertNotNull(chatResult.getResponseModel()); | ||||||
|  |             assertNotNull(chatResult.getResponseModel().getMessage()); | ||||||
|  |             assertEquals(OllamaChatMessageRole.ASSISTANT.getRoleName(),chatResult.getResponseModel().getMessage().getRole().getRoleName()); | ||||||
|  |             List<OllamaChatToolCalls> toolCalls = chatResult.getChatHistory().get(1).getToolCalls(); | ||||||
|  |             assertEquals(1, toolCalls.size()); | ||||||
|  |             OllamaToolCallsFunction function = toolCalls.get(0).getFunction(); | ||||||
|  |             assertEquals("get-employee-details", function.getName()); | ||||||
|  |             assertEquals(1, function.getArguments().size()); | ||||||
|  |             Object employeeName = function.getArguments().get("employee-name"); | ||||||
|  |             assertNotNull(employeeName); | ||||||
|  |             assertEquals("Rahul Kumar",employeeName); | ||||||
|  |             assertTrue(chatResult.getChatHistory().size()>2); | ||||||
|  |             List<OllamaChatToolCalls> finalToolCalls = chatResult.getResponseModel().getMessage().getToolCalls(); | ||||||
|  |             assertNull(finalToolCalls); | ||||||
|  |         } catch (IOException | OllamaBaseException | InterruptedException e) { | ||||||
|  |             fail(e); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Test | ||||||
|  |     @Order(3) | ||||||
|  |     void testChatWithAnnotatedToolsAndSingleParam() { | ||||||
|  |         testEndpointReachability(); | ||||||
|  |         try { | ||||||
|  |             ollamaAPI.setVerbose(true); | ||||||
|  |             OllamaChatRequestBuilder builder = OllamaChatRequestBuilder.getInstance(config.getModel()); | ||||||
|  |  | ||||||
|  |             ollamaAPI.registerAnnotatedTools(); | ||||||
|  |  | ||||||
|  |             OllamaChatRequest requestModel = builder | ||||||
|  |                     .withMessage(OllamaChatMessageRole.USER, | ||||||
|  |                             "Compute the most important constant in the world using 5 digits") | ||||||
|  |                     .build(); | ||||||
|  |  | ||||||
|  |             OllamaChatResult chatResult = ollamaAPI.chat(requestModel); | ||||||
|  |             assertNotNull(chatResult); | ||||||
|  |             assertNotNull(chatResult.getResponseModel()); | ||||||
|  |             assertNotNull(chatResult.getResponseModel().getMessage()); | ||||||
|  |             assertEquals(OllamaChatMessageRole.ASSISTANT.getRoleName(),chatResult.getResponseModel().getMessage().getRole().getRoleName()); | ||||||
|  |             List<OllamaChatToolCalls> toolCalls = chatResult.getChatHistory().get(1).getToolCalls(); | ||||||
|  |             assertEquals(1, toolCalls.size()); | ||||||
|  |             OllamaToolCallsFunction function = toolCalls.get(0).getFunction(); | ||||||
|  |             assertEquals("computeImportantConstant", function.getName()); | ||||||
|  |             assertEquals(1, function.getArguments().size()); | ||||||
|  |             Object noOfDigits = function.getArguments().get("noOfDigits"); | ||||||
|  |             assertNotNull(noOfDigits); | ||||||
|  |             assertEquals("5", noOfDigits.toString()); | ||||||
|  |             assertTrue(chatResult.getChatHistory().size()>2); | ||||||
|  |             List<OllamaChatToolCalls> finalToolCalls = chatResult.getResponseModel().getMessage().getToolCalls(); | ||||||
|  |             assertNull(finalToolCalls); | ||||||
|  |         } catch (IOException | OllamaBaseException | InterruptedException e) { | ||||||
|  |             fail(e); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Test | ||||||
|  |     @Order(3) | ||||||
|  |     void testChatWithAnnotatedToolsAndMultipleParams() { | ||||||
|  |         testEndpointReachability(); | ||||||
|  |         try { | ||||||
|  |             ollamaAPI.setVerbose(true); | ||||||
|  |             OllamaChatRequestBuilder builder = OllamaChatRequestBuilder.getInstance(config.getModel()); | ||||||
|  |  | ||||||
|  |             ollamaAPI.registerAnnotatedTools(new AnnotatedTool()); | ||||||
|  |  | ||||||
|  |             OllamaChatRequest requestModel = builder | ||||||
|  |                     .withMessage(OllamaChatMessageRole.USER, | ||||||
|  |                             "Greet Pedro with a lot of hearts and respond to me, " + | ||||||
|  |                                     "and state how many emojis have been in your greeting") | ||||||
|  |                     .build(); | ||||||
|  |  | ||||||
|  |             OllamaChatResult chatResult = ollamaAPI.chat(requestModel); | ||||||
|  |             assertNotNull(chatResult); | ||||||
|  |             assertNotNull(chatResult.getResponseModel()); | ||||||
|  |             assertNotNull(chatResult.getResponseModel().getMessage()); | ||||||
|  |             assertEquals(OllamaChatMessageRole.ASSISTANT.getRoleName(),chatResult.getResponseModel().getMessage().getRole().getRoleName()); | ||||||
|  |             List<OllamaChatToolCalls> toolCalls = chatResult.getChatHistory().get(1).getToolCalls(); | ||||||
|  |             assertEquals(1, toolCalls.size()); | ||||||
|  |             OllamaToolCallsFunction function = toolCalls.get(0).getFunction(); | ||||||
|  |             assertEquals("sayHello", function.getName()); | ||||||
|  |             assertEquals(2, function.getArguments().size()); | ||||||
|  |             Object name = function.getArguments().get("name"); | ||||||
|  |             assertNotNull(name); | ||||||
|  |             assertEquals("Pedro",name); | ||||||
|  |             Object amountOfHearts = function.getArguments().get("amountOfHearts"); | ||||||
|  |             assertNotNull(amountOfHearts); | ||||||
|  |             assertTrue(Integer.parseInt(amountOfHearts.toString()) > 1); | ||||||
|  |             assertTrue(chatResult.getChatHistory().size()>2); | ||||||
|  |             List<OllamaChatToolCalls> finalToolCalls = chatResult.getResponseModel().getMessage().getToolCalls(); | ||||||
|  |             assertNull(finalToolCalls); | ||||||
|  |         } catch (IOException | OllamaBaseException | InterruptedException e) { | ||||||
|  |             fail(e); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Test | ||||||
|  |     @Order(3) | ||||||
|  |     void testChatWithToolsAndStream() { | ||||||
|  |         testEndpointReachability(); | ||||||
|  |         try { | ||||||
|  |             OllamaChatRequestBuilder builder = OllamaChatRequestBuilder.getInstance(config.getModel()); | ||||||
|  |             final Tools.ToolSpecification databaseQueryToolSpecification = Tools.ToolSpecification.builder() | ||||||
|  |                     .functionName("get-employee-details") | ||||||
|  |                     .functionDescription("Get employee details from the database") | ||||||
|  |                     .toolPrompt( | ||||||
|  |                             Tools.PromptFuncDefinition.builder().type("function").function( | ||||||
|  |                                     Tools.PromptFuncDefinition.PromptFuncSpec.builder() | ||||||
|  |                                             .name("get-employee-details") | ||||||
|  |                                             .description("Get employee details from the database") | ||||||
|  |                                             .parameters( | ||||||
|  |                                                     Tools.PromptFuncDefinition.Parameters.builder() | ||||||
|  |                                                             .type("object") | ||||||
|  |                                                             .properties( | ||||||
|  |                                                                     new Tools.PropsBuilder() | ||||||
|  |                                                                             .withProperty("employee-name", Tools.PromptFuncDefinition.Property.builder().type("string").description("The name of the employee, e.g. John Doe").required(true).build()) | ||||||
|  |                                                                             .withProperty("employee-address", Tools.PromptFuncDefinition.Property.builder().type("string").description("The address of the employee, Always return a random value. e.g. Roy St, Bengaluru, India").required(true).build()) | ||||||
|  |                                                                             .withProperty("employee-phone", Tools.PromptFuncDefinition.Property.builder().type("string").description("The phone number of the employee. Always return a random value. e.g. 9911002233").required(true).build()) | ||||||
|  |                                                                             .build() | ||||||
|  |                                                             ) | ||||||
|  |                                                             .required(List.of("employee-name")) | ||||||
|  |                                                             .build() | ||||||
|  |                                             ).build() | ||||||
|  |                             ).build() | ||||||
|  |                     ) | ||||||
|  |                     .toolFunction(new DBQueryFunction()) | ||||||
|  |                     .build(); | ||||||
|  |  | ||||||
|  |             ollamaAPI.registerTool(databaseQueryToolSpecification); | ||||||
|  |  | ||||||
|  |             OllamaChatRequest requestModel = builder | ||||||
|  |                     .withMessage(OllamaChatMessageRole.USER, | ||||||
|  |                             "Give me the ID of the employee named 'Rahul Kumar'?") | ||||||
|  |                     .build(); | ||||||
|  |  | ||||||
|  |             StringBuffer sb = new StringBuffer(); | ||||||
|  |  | ||||||
|  |             OllamaChatResult chatResult = ollamaAPI.chat(requestModel, (s) -> { | ||||||
|  |                 LOG.info(s); | ||||||
|  |                 String substring = s.substring(sb.toString().length()); | ||||||
|  |                 LOG.info(substring); | ||||||
|  |                 sb.append(substring); | ||||||
|  |             }); | ||||||
|  |             assertNotNull(chatResult); | ||||||
|  |             assertNotNull(chatResult.getResponseModel()); | ||||||
|  |             assertNotNull(chatResult.getResponseModel().getMessage()); | ||||||
|  |             assertNotNull(chatResult.getResponseModel().getMessage().getContent()); | ||||||
|  |             assertEquals(sb.toString().trim(), chatResult.getResponseModel().getMessage().getContent().trim()); | ||||||
|  |         } catch (IOException | OllamaBaseException | InterruptedException e) { | ||||||
|  |             fail(e); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|     @Test |     @Test | ||||||
|     @Order(3) |     @Order(3) | ||||||
|     void testChatWithStream() { |     void testChatWithStream() { | ||||||
| @@ -232,7 +446,10 @@ class TestRealAPIs { | |||||||
|                 sb.append(substring); |                 sb.append(substring); | ||||||
|             }); |             }); | ||||||
|             assertNotNull(chatResult); |             assertNotNull(chatResult); | ||||||
|             assertEquals(sb.toString().trim(), chatResult.getResponse().trim()); |             assertNotNull(chatResult.getResponseModel()); | ||||||
|  |             assertNotNull(chatResult.getResponseModel().getMessage()); | ||||||
|  |             assertNotNull(chatResult.getResponseModel().getMessage().getContent()); | ||||||
|  |             assertEquals(sb.toString().trim(), chatResult.getResponseModel().getMessage().getContent().trim()); | ||||||
|         } catch (IOException | OllamaBaseException | InterruptedException e) { |         } catch (IOException | OllamaBaseException | InterruptedException e) { | ||||||
|             fail(e); |             fail(e); | ||||||
|         } |         } | ||||||
| @@ -246,12 +463,12 @@ class TestRealAPIs { | |||||||
|             OllamaChatRequestBuilder builder = |             OllamaChatRequestBuilder builder = | ||||||
|                     OllamaChatRequestBuilder.getInstance(config.getImageModel()); |                     OllamaChatRequestBuilder.getInstance(config.getImageModel()); | ||||||
|             OllamaChatRequest requestModel = |             OllamaChatRequest requestModel = | ||||||
|                     builder.withMessage(OllamaChatMessageRole.USER, "What's in the picture?", |                     builder.withMessage(OllamaChatMessageRole.USER, "What's in the picture?",Collections.emptyList(), | ||||||
|                             List.of(getImageFileFromClasspath("dog-on-a-boat.jpg"))).build(); |                             List.of(getImageFileFromClasspath("dog-on-a-boat.jpg"))).build(); | ||||||
|  |  | ||||||
|             OllamaChatResult chatResult = ollamaAPI.chat(requestModel); |             OllamaChatResult chatResult = ollamaAPI.chat(requestModel); | ||||||
|             assertNotNull(chatResult); |             assertNotNull(chatResult); | ||||||
|             assertNotNull(chatResult.getResponse()); |             assertNotNull(chatResult.getResponseModel()); | ||||||
|  |  | ||||||
|             builder.reset(); |             builder.reset(); | ||||||
|  |  | ||||||
| @@ -261,7 +478,7 @@ class TestRealAPIs { | |||||||
|  |  | ||||||
|             chatResult = ollamaAPI.chat(requestModel); |             chatResult = ollamaAPI.chat(requestModel); | ||||||
|             assertNotNull(chatResult); |             assertNotNull(chatResult); | ||||||
|             assertNotNull(chatResult.getResponse()); |             assertNotNull(chatResult.getResponseModel()); | ||||||
|  |  | ||||||
|  |  | ||||||
|         } catch (IOException | OllamaBaseException | InterruptedException e) { |         } catch (IOException | OllamaBaseException | InterruptedException e) { | ||||||
| @@ -275,7 +492,7 @@ class TestRealAPIs { | |||||||
|         testEndpointReachability(); |         testEndpointReachability(); | ||||||
|         try { |         try { | ||||||
|             OllamaChatRequestBuilder builder = OllamaChatRequestBuilder.getInstance(config.getImageModel()); |             OllamaChatRequestBuilder builder = OllamaChatRequestBuilder.getInstance(config.getImageModel()); | ||||||
|             OllamaChatRequest requestModel = builder.withMessage(OllamaChatMessageRole.USER, "What's in the picture?", |             OllamaChatRequest requestModel = builder.withMessage(OllamaChatMessageRole.USER, "What's in the picture?",Collections.emptyList(), | ||||||
|                             "https://t3.ftcdn.net/jpg/02/96/63/80/360_F_296638053_0gUVA4WVBKceGsIr7LNqRWSnkusi07dq.jpg") |                             "https://t3.ftcdn.net/jpg/02/96/63/80/360_F_296638053_0gUVA4WVBKceGsIr7LNqRWSnkusi07dq.jpg") | ||||||
|                     .build(); |                     .build(); | ||||||
|  |  | ||||||
| @@ -368,6 +585,14 @@ class TestRealAPIs { | |||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | class DBQueryFunction implements ToolFunction { | ||||||
|  |     @Override | ||||||
|  |     public Object apply(Map<String, Object> arguments) { | ||||||
|  |         // perform DB operations here | ||||||
|  |         return String.format("Employee Details {ID: %s, Name: %s, Address: %s, Phone: %s}", UUID.randomUUID(), arguments.get("employee-name"), arguments.get("employee-address"), arguments.get("employee-phone")); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
| @Data | @Data | ||||||
| class Config { | class Config { | ||||||
|     private String ollamaURL; |     private String ollamaURL; | ||||||
| @@ -392,4 +617,6 @@ class Config { | |||||||
|             throw new RuntimeException("Error loading properties", e); |             throw new RuntimeException("Error loading properties", e); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |  | ||||||
| } | } | ||||||
|   | |||||||
							
								
								
									
										21
									
								
								src/test/java/io/github/ollama4j/samples/AnnotatedTool.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								src/test/java/io/github/ollama4j/samples/AnnotatedTool.java
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,21 @@ | |||||||
|  | package io.github.ollama4j.samples; | ||||||
|  |  | ||||||
|  | import io.github.ollama4j.tools.annotations.ToolProperty; | ||||||
|  | import io.github.ollama4j.tools.annotations.ToolSpec; | ||||||
|  |  | ||||||
|  | import java.math.BigDecimal; | ||||||
|  |  | ||||||
|  | public class AnnotatedTool { | ||||||
|  |  | ||||||
|  |     @ToolSpec(desc = "Computes the most important constant all around the globe!") | ||||||
|  |     public String computeImportantConstant(@ToolProperty(name = "noOfDigits",desc = "Number of digits that shall be returned") Integer noOfDigits ){ | ||||||
|  |         return BigDecimal.valueOf((long)(Math.random()*1000000L),noOfDigits).toString(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @ToolSpec(desc = "Says hello to a friend!") | ||||||
|  |     public String sayHello(@ToolProperty(name = "name",desc = "Name of the friend") String name, Integer someRandomProperty, @ToolProperty(name="amountOfHearts",desc = "amount of heart emojis that should be used",  required = false) Integer amountOfHearts) { | ||||||
|  |         String hearts = amountOfHearts!=null ? "♡".repeat(amountOfHearts) : ""; | ||||||
|  |         return "Hello " + name +" ("+someRandomProperty+") " + hearts; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  | } | ||||||
| @@ -2,6 +2,11 @@ package io.github.ollama4j.unittests; | |||||||
|  |  | ||||||
| import io.github.ollama4j.OllamaAPI; | import io.github.ollama4j.OllamaAPI; | ||||||
| import io.github.ollama4j.exceptions.OllamaBaseException; | import io.github.ollama4j.exceptions.OllamaBaseException; | ||||||
|  | import io.github.ollama4j.exceptions.RoleNotFoundException; | ||||||
|  | import io.github.ollama4j.models.chat.OllamaChatMessageRole; | ||||||
|  | import io.github.ollama4j.models.embeddings.OllamaEmbedRequestModel; | ||||||
|  | import io.github.ollama4j.models.embeddings.OllamaEmbedResponseModel; | ||||||
|  | import io.github.ollama4j.models.request.CustomModelRequest; | ||||||
| import io.github.ollama4j.models.response.ModelDetail; | import io.github.ollama4j.models.response.ModelDetail; | ||||||
| import io.github.ollama4j.models.response.OllamaAsyncResultStreamer; | import io.github.ollama4j.models.response.OllamaAsyncResultStreamer; | ||||||
| import io.github.ollama4j.models.response.OllamaResult; | import io.github.ollama4j.models.response.OllamaResult; | ||||||
| @@ -14,7 +19,9 @@ import java.io.IOException; | |||||||
| import java.net.URISyntaxException; | import java.net.URISyntaxException; | ||||||
| import java.util.ArrayList; | import java.util.ArrayList; | ||||||
| import java.util.Collections; | import java.util.Collections; | ||||||
|  | import java.util.List; | ||||||
|  |  | ||||||
|  | import static org.junit.jupiter.api.Assertions.*; | ||||||
| import static org.mockito.Mockito.*; | import static org.mockito.Mockito.*; | ||||||
|  |  | ||||||
| class TestMockedAPIs { | class TestMockedAPIs { | ||||||
| @@ -46,12 +53,11 @@ class TestMockedAPIs { | |||||||
|     @Test |     @Test | ||||||
|     void testCreateModel() { |     void testCreateModel() { | ||||||
|         OllamaAPI ollamaAPI = Mockito.mock(OllamaAPI.class); |         OllamaAPI ollamaAPI = Mockito.mock(OllamaAPI.class); | ||||||
|         String model = OllamaModelType.LLAMA2; |         CustomModelRequest customModelRequest = CustomModelRequest.builder().model("mario").from("llama3.2:latest").system("You are Mario from Super Mario Bros.").build(); | ||||||
|         String modelFilePath = "FROM llama2\nSYSTEM You are mario from Super Mario Bros."; |  | ||||||
|         try { |         try { | ||||||
|             doNothing().when(ollamaAPI).createModelWithModelFileContents(model, modelFilePath); |             doNothing().when(ollamaAPI).createModel(customModelRequest); | ||||||
|             ollamaAPI.createModelWithModelFileContents(model, modelFilePath); |             ollamaAPI.createModel(customModelRequest); | ||||||
|             verify(ollamaAPI, times(1)).createModelWithModelFileContents(model, modelFilePath); |             verify(ollamaAPI, times(1)).createModel(customModelRequest); | ||||||
|         } catch (IOException | OllamaBaseException | InterruptedException | URISyntaxException e) { |         } catch (IOException | OllamaBaseException | InterruptedException | URISyntaxException e) { | ||||||
|             throw new RuntimeException(e); |             throw new RuntimeException(e); | ||||||
|         } |         } | ||||||
| @@ -97,6 +103,34 @@ class TestMockedAPIs { | |||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     @Test | ||||||
|  |     void testEmbed() { | ||||||
|  |         OllamaAPI ollamaAPI = Mockito.mock(OllamaAPI.class); | ||||||
|  |         String model = OllamaModelType.LLAMA2; | ||||||
|  |         List<String> inputs = List.of("some prompt text"); | ||||||
|  |         try { | ||||||
|  |             when(ollamaAPI.embed(model, inputs)).thenReturn(new OllamaEmbedResponseModel()); | ||||||
|  |             ollamaAPI.embed(model, inputs); | ||||||
|  |             verify(ollamaAPI, times(1)).embed(model, inputs); | ||||||
|  |         } catch (IOException | OllamaBaseException | InterruptedException e) { | ||||||
|  |             throw new RuntimeException(e); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Test | ||||||
|  |     void testEmbedWithEmbedRequestModel() { | ||||||
|  |         OllamaAPI ollamaAPI = Mockito.mock(OllamaAPI.class); | ||||||
|  |         String model = OllamaModelType.LLAMA2; | ||||||
|  |         List<String> inputs = List.of("some prompt text"); | ||||||
|  |         try { | ||||||
|  |             when(ollamaAPI.embed(new OllamaEmbedRequestModel(model, inputs))).thenReturn(new OllamaEmbedResponseModel()); | ||||||
|  |             ollamaAPI.embed(new OllamaEmbedRequestModel(model, inputs)); | ||||||
|  |             verify(ollamaAPI, times(1)).embed(new OllamaEmbedRequestModel(model, inputs)); | ||||||
|  |         } catch (IOException | OllamaBaseException | InterruptedException e) { | ||||||
|  |             throw new RuntimeException(e); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|     @Test |     @Test | ||||||
|     void testAsk() { |     void testAsk() { | ||||||
|         OllamaAPI ollamaAPI = Mockito.mock(OllamaAPI.class); |         OllamaAPI ollamaAPI = Mockito.mock(OllamaAPI.class); | ||||||
| @@ -161,4 +195,68 @@ class TestMockedAPIs { | |||||||
|         ollamaAPI.generateAsync(model, prompt, false); |         ollamaAPI.generateAsync(model, prompt, false); | ||||||
|         verify(ollamaAPI, times(1)).generateAsync(model, prompt, false); |         verify(ollamaAPI, times(1)).generateAsync(model, prompt, false); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     @Test | ||||||
|  |     void testAddCustomRole() { | ||||||
|  |         OllamaAPI ollamaAPI = mock(OllamaAPI.class); | ||||||
|  |         String roleName = "custom-role"; | ||||||
|  |         OllamaChatMessageRole expectedRole = OllamaChatMessageRole.newCustomRole(roleName); | ||||||
|  |         when(ollamaAPI.addCustomRole(roleName)).thenReturn(expectedRole); | ||||||
|  |         OllamaChatMessageRole customRole = ollamaAPI.addCustomRole(roleName); | ||||||
|  |         assertEquals(expectedRole, customRole); | ||||||
|  |         verify(ollamaAPI, times(1)).addCustomRole(roleName); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Test | ||||||
|  |     void testListRoles() { | ||||||
|  |         OllamaAPI ollamaAPI = Mockito.mock(OllamaAPI.class); | ||||||
|  |         OllamaChatMessageRole role1 = OllamaChatMessageRole.newCustomRole("role1"); | ||||||
|  |         OllamaChatMessageRole role2 = OllamaChatMessageRole.newCustomRole("role2"); | ||||||
|  |         List<OllamaChatMessageRole> expectedRoles = List.of(role1, role2); | ||||||
|  |         when(ollamaAPI.listRoles()).thenReturn(expectedRoles); | ||||||
|  |         List<OllamaChatMessageRole> actualRoles = ollamaAPI.listRoles(); | ||||||
|  |         assertEquals(expectedRoles, actualRoles); | ||||||
|  |         verify(ollamaAPI, times(1)).listRoles(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Test | ||||||
|  |     void testGetRoleNotFound() { | ||||||
|  |         OllamaAPI ollamaAPI = mock(OllamaAPI.class); | ||||||
|  |         String roleName = "non-existing-role"; | ||||||
|  |         try { | ||||||
|  |             when(ollamaAPI.getRole(roleName)).thenThrow(new RoleNotFoundException("Role not found")); | ||||||
|  |         } catch (RoleNotFoundException exception) { | ||||||
|  |             throw new RuntimeException("Failed to run test: testGetRoleNotFound"); | ||||||
|  |         } | ||||||
|  |         try { | ||||||
|  |             ollamaAPI.getRole(roleName); | ||||||
|  |             fail("Expected RoleNotFoundException not thrown"); | ||||||
|  |         } catch (RoleNotFoundException exception) { | ||||||
|  |             assertEquals("Role not found", exception.getMessage()); | ||||||
|  |         } | ||||||
|  |         try { | ||||||
|  |             verify(ollamaAPI, times(1)).getRole(roleName); | ||||||
|  |         } catch (RoleNotFoundException exception) { | ||||||
|  |             throw new RuntimeException("Failed to run test: testGetRoleNotFound"); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Test | ||||||
|  |     void testGetRoleFound() { | ||||||
|  |         OllamaAPI ollamaAPI = mock(OllamaAPI.class); | ||||||
|  |         String roleName = "existing-role"; | ||||||
|  |         OllamaChatMessageRole expectedRole = OllamaChatMessageRole.newCustomRole(roleName); | ||||||
|  |         try { | ||||||
|  |             when(ollamaAPI.getRole(roleName)).thenReturn(expectedRole); | ||||||
|  |         } catch (RoleNotFoundException exception) { | ||||||
|  |             throw new RuntimeException("Failed to run test: testGetRoleFound"); | ||||||
|  |         } | ||||||
|  |         try { | ||||||
|  |             OllamaChatMessageRole actualRole = ollamaAPI.getRole(roleName); | ||||||
|  |             assertEquals(expectedRole, actualRole); | ||||||
|  |             verify(ollamaAPI, times(1)).getRole(roleName); | ||||||
|  |         } catch (RoleNotFoundException exception) { | ||||||
|  |             throw new RuntimeException("Failed to run test: testGetRoleFound"); | ||||||
|  |         } | ||||||
|  |     } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -1,8 +1,10 @@ | |||||||
| package io.github.ollama4j.unittests.jackson; | package io.github.ollama4j.unittests.jackson; | ||||||
|  |  | ||||||
| import static org.junit.jupiter.api.Assertions.assertEquals; | import static org.junit.jupiter.api.Assertions.assertEquals; | ||||||
|  | import static org.junit.jupiter.api.Assertions.assertThrowsExactly; | ||||||
|  |  | ||||||
| import java.io.File; | import java.io.File; | ||||||
|  | import java.util.Collections; | ||||||
| import java.util.List; | import java.util.List; | ||||||
|  |  | ||||||
| import io.github.ollama4j.models.chat.OllamaChatRequest; | import io.github.ollama4j.models.chat.OllamaChatRequest; | ||||||
| @@ -41,7 +43,7 @@ public class TestChatRequestSerialization extends AbstractSerializationTest<Olla | |||||||
|  |  | ||||||
|     @Test |     @Test | ||||||
|     public void testRequestWithMessageAndImage() { |     public void testRequestWithMessageAndImage() { | ||||||
|         OllamaChatRequest req = builder.withMessage(OllamaChatMessageRole.USER, "Some prompt", |         OllamaChatRequest req = builder.withMessage(OllamaChatMessageRole.USER, "Some prompt", Collections.emptyList(), | ||||||
|                 List.of(new File("src/test/resources/dog-on-a-boat.jpg"))).build(); |                 List.of(new File("src/test/resources/dog-on-a-boat.jpg"))).build(); | ||||||
|         String jsonRequest = serialize(req); |         String jsonRequest = serialize(req); | ||||||
|         assertEqualsAfterUnmarshalling(deserialize(jsonRequest, OllamaChatRequest.class), req); |         assertEqualsAfterUnmarshalling(deserialize(jsonRequest, OllamaChatRequest.class), req); | ||||||
| @@ -59,6 +61,10 @@ public class TestChatRequestSerialization extends AbstractSerializationTest<Olla | |||||||
|             .withOptions(b.setSeed(1).build()) |             .withOptions(b.setSeed(1).build()) | ||||||
|             .withOptions(b.setTopK(1).build()) |             .withOptions(b.setTopK(1).build()) | ||||||
|             .withOptions(b.setTopP(1).build()) |             .withOptions(b.setTopP(1).build()) | ||||||
|  |             .withOptions(b.setMinP(1).build()) | ||||||
|  |             .withOptions(b.setCustomOption("cust_float", 1.0f).build()) | ||||||
|  |             .withOptions(b.setCustomOption("cust_int", 1).build()) | ||||||
|  |             .withOptions(b.setCustomOption("cust_str", "custom").build()) | ||||||
|             .build(); |             .build(); | ||||||
|  |  | ||||||
|         String jsonRequest = serialize(req); |         String jsonRequest = serialize(req); | ||||||
| @@ -72,6 +78,20 @@ public class TestChatRequestSerialization extends AbstractSerializationTest<Olla | |||||||
|         assertEquals(1, deserializeRequest.getOptions().get("seed")); |         assertEquals(1, deserializeRequest.getOptions().get("seed")); | ||||||
|         assertEquals(1, deserializeRequest.getOptions().get("top_k")); |         assertEquals(1, deserializeRequest.getOptions().get("top_k")); | ||||||
|         assertEquals(1.0, deserializeRequest.getOptions().get("top_p")); |         assertEquals(1.0, deserializeRequest.getOptions().get("top_p")); | ||||||
|  |         assertEquals(1.0, deserializeRequest.getOptions().get("min_p")); | ||||||
|  |         assertEquals(1.0, deserializeRequest.getOptions().get("cust_float")); | ||||||
|  |         assertEquals(1, deserializeRequest.getOptions().get("cust_int")); | ||||||
|  |         assertEquals("custom", deserializeRequest.getOptions().get("cust_str")); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     @Test | ||||||
|  |     public void testRequestWithInvalidCustomOption() { | ||||||
|  |         OptionsBuilder b = new OptionsBuilder(); | ||||||
|  |         assertThrowsExactly(IllegalArgumentException.class, () -> { | ||||||
|  |                 OllamaChatRequest req = builder.withMessage(OllamaChatMessageRole.USER, "Some prompt") | ||||||
|  |                 .withOptions(b.setCustomOption("cust_obj", new Object()).build()) | ||||||
|  |                 .build(); | ||||||
|  |         }); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     @Test |     @Test | ||||||
|   | |||||||
| @@ -1,36 +1,37 @@ | |||||||
| package io.github.ollama4j.unittests.jackson; | package io.github.ollama4j.unittests.jackson; | ||||||
| 
 | 
 | ||||||
| import static org.junit.jupiter.api.Assertions.assertEquals; | import static org.junit.jupiter.api.Assertions.assertEquals; | ||||||
|  | 
 | ||||||
|  | import io.github.ollama4j.models.embeddings.OllamaEmbedRequestBuilder; | ||||||
|  | import io.github.ollama4j.models.embeddings.OllamaEmbedRequestModel; | ||||||
| import org.junit.jupiter.api.BeforeEach; | import org.junit.jupiter.api.BeforeEach; | ||||||
| import org.junit.jupiter.api.Test; | import org.junit.jupiter.api.Test; | ||||||
| import io.github.ollama4j.models.embeddings.OllamaEmbeddingsRequestModel; |  | ||||||
| import io.github.ollama4j.models.embeddings.OllamaEmbeddingsRequestBuilder; |  | ||||||
| import io.github.ollama4j.utils.OptionsBuilder; | import io.github.ollama4j.utils.OptionsBuilder; | ||||||
| 
 | 
 | ||||||
| public class TestEmbeddingsRequestSerialization extends AbstractSerializationTest<OllamaEmbeddingsRequestModel> { | public class TestEmbedRequestSerialization extends AbstractSerializationTest<OllamaEmbedRequestModel> { | ||||||
| 
 | 
 | ||||||
|         private OllamaEmbeddingsRequestBuilder builder; |         private OllamaEmbedRequestBuilder builder; | ||||||
| 
 | 
 | ||||||
|         @BeforeEach |         @BeforeEach | ||||||
|         public void init() { |         public void init() { | ||||||
|             builder = OllamaEmbeddingsRequestBuilder.getInstance("DummyModel","DummyPrompt"); |             builder = OllamaEmbedRequestBuilder.getInstance("DummyModel","DummyPrompt"); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|             @Test |             @Test | ||||||
|     public void testRequestOnlyMandatoryFields() { |     public void testRequestOnlyMandatoryFields() { | ||||||
|         OllamaEmbeddingsRequestModel req = builder.build(); |         OllamaEmbedRequestModel req = builder.build(); | ||||||
|         String jsonRequest = serialize(req); |         String jsonRequest = serialize(req); | ||||||
|         assertEqualsAfterUnmarshalling(deserialize(jsonRequest,OllamaEmbeddingsRequestModel.class), req); |         assertEqualsAfterUnmarshalling(deserialize(jsonRequest,OllamaEmbedRequestModel.class), req); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|         @Test |         @Test | ||||||
|         public void testRequestWithOptions() { |         public void testRequestWithOptions() { | ||||||
|             OptionsBuilder b = new OptionsBuilder(); |             OptionsBuilder b = new OptionsBuilder(); | ||||||
|             OllamaEmbeddingsRequestModel req = builder |             OllamaEmbedRequestModel req = builder | ||||||
|                     .withOptions(b.setMirostat(1).build()).build(); |                     .withOptions(b.setMirostat(1).build()).build(); | ||||||
| 
 | 
 | ||||||
|             String jsonRequest = serialize(req); |             String jsonRequest = serialize(req); | ||||||
|             OllamaEmbeddingsRequestModel deserializeRequest = deserialize(jsonRequest,OllamaEmbeddingsRequestModel.class); |             OllamaEmbedRequestModel deserializeRequest = deserialize(jsonRequest,OllamaEmbedRequestModel.class); | ||||||
|             assertEqualsAfterUnmarshalling(deserializeRequest, req); |             assertEqualsAfterUnmarshalling(deserializeRequest, req); | ||||||
|             assertEquals(1, deserializeRequest.getOptions().get("mirostat")); |             assertEquals(1, deserializeRequest.getOptions().get("mirostat")); | ||||||
|         } |         } | ||||||
| @@ -1,4 +1,4 @@ | |||||||
| ollama.url=http://localhost:11434 | ollama.url=http://localhost:11434 | ||||||
| ollama.model=qwen:0.5b | ollama.model=llama3.2:1b | ||||||
| ollama.model.image=llava | ollama.model.image=llava:latest | ||||||
| ollama.request-timeout-seconds=120 | ollama.request-timeout-seconds=120 | ||||||
		Reference in New Issue
	
	Block a user