mirror of
https://github.com/amithkoujalgi/ollama4j.git
synced 2025-05-14 19:37:11 +02:00
Merge pull request #119 from ollama4j/113-tests-fix
All checks were successful
Mark stale issues / stale (push) Successful in 27s
All checks were successful
Mark stale issues / stale (push) Successful in 27s
[Fix]: NPE when tool not found
This commit is contained in:
commit
48d0a494ee
@ -37,3 +37,10 @@ jobs:
|
|||||||
|
|
||||||
- name: Run integration tests
|
- name: Run integration tests
|
||||||
run: mvn --file pom.xml -U clean verify -Pintegration-tests
|
run: mvn --file pom.xml -U clean verify -Pintegration-tests
|
||||||
|
|
||||||
|
- name: Use Node.js
|
||||||
|
uses: actions/setup-node@v3
|
||||||
|
with:
|
||||||
|
node-version: '20.x'
|
||||||
|
- run: cd docs && npm ci
|
||||||
|
- run: cd docs && npm run build
|
8
Makefile
8
Makefile
@ -18,8 +18,8 @@ unit-tests:
|
|||||||
integration-tests:
|
integration-tests:
|
||||||
export USE_EXTERNAL_OLLAMA_HOST=false && mvn clean verify -Pintegration-tests
|
export USE_EXTERNAL_OLLAMA_HOST=false && mvn clean verify -Pintegration-tests
|
||||||
|
|
||||||
integration-tests-local:
|
integration-tests-remote:
|
||||||
export USE_EXTERNAL_OLLAMA_HOST=true && export OLLAMA_HOST=http://localhost:11434 && mvn clean verify -Pintegration-tests -Dgpg.skip=true
|
export USE_EXTERNAL_OLLAMA_HOST=true && export OLLAMA_HOST=http://192.168.29.223:11434 && mvn clean verify -Pintegration-tests -Dgpg.skip=true
|
||||||
|
|
||||||
doxygen:
|
doxygen:
|
||||||
doxygen Doxyfile
|
doxygen Doxyfile
|
||||||
@ -29,10 +29,10 @@ list-releases:
|
|||||||
--compressed \
|
--compressed \
|
||||||
--silent | jq -r '.components[].version'
|
--silent | jq -r '.components[].version'
|
||||||
|
|
||||||
build-docs:
|
docs:
|
||||||
npm i --prefix docs && npm run build --prefix docs
|
npm i --prefix docs && npm run build --prefix docs
|
||||||
|
|
||||||
start-docs:
|
docs-dev:
|
||||||
npm i --prefix docs && npm run start --prefix docs
|
npm i --prefix docs && npm run start --prefix docs
|
||||||
|
|
||||||
start-cpu:
|
start-cpu:
|
||||||
|
@ -61,6 +61,9 @@ details.
|
|||||||
class DBQueryFunction implements ToolFunction {
|
class DBQueryFunction implements ToolFunction {
|
||||||
@Override
|
@Override
|
||||||
public Object apply(Map<String, Object> arguments) {
|
public Object apply(Map<String, Object> arguments) {
|
||||||
|
if (arguments == null || arguments.isEmpty() || arguments.get("employee-name") == null || arguments.get("employee-address") == null || arguments.get("employee-phone") == null) {
|
||||||
|
throw new RuntimeException("Tool was called but the model failed to provide all the required arguments.");
|
||||||
|
}
|
||||||
// perform DB operations here
|
// perform DB operations here
|
||||||
return String.format("Employee Details {ID: %s, Name: %s, Address: %s, Phone: %s}", UUID.randomUUID(), arguments.get("employee-name").toString(), arguments.get("employee-address").toString(), arguments.get("employee-phone").toString());
|
return String.format("Employee Details {ID: %s, Name: %s, Address: %s, Phone: %s}", UUID.randomUUID(), arguments.get("employee-name").toString(), arguments.get("employee-address").toString(), arguments.get("employee-phone").toString());
|
||||||
}
|
}
|
||||||
@ -78,14 +81,39 @@ Lets define a sample tool specification called **Fuel Price Tool** for getting t
|
|||||||
Tools.ToolSpecification fuelPriceToolSpecification = Tools.ToolSpecification.builder()
|
Tools.ToolSpecification fuelPriceToolSpecification = Tools.ToolSpecification.builder()
|
||||||
.functionName("current-fuel-price")
|
.functionName("current-fuel-price")
|
||||||
.functionDescription("Get current fuel price")
|
.functionDescription("Get current fuel price")
|
||||||
.properties(
|
.toolFunction(SampleTools::getCurrentFuelPrice)
|
||||||
new Tools.PropsBuilder()
|
.toolPrompt(
|
||||||
.withProperty("location", Tools.PromptFuncDefinition.Property.builder().type("string").description("The city, e.g. New Delhi, India").required(true).build())
|
Tools.PromptFuncDefinition.builder()
|
||||||
.withProperty("fuelType", Tools.PromptFuncDefinition.Property.builder().type("string").description("The fuel type.").enumValues(Arrays.asList("petrol", "diesel")).required(true).build())
|
.type("prompt")
|
||||||
|
.function(
|
||||||
|
Tools.PromptFuncDefinition.PromptFuncSpec.builder()
|
||||||
|
.name("get-location-fuel-info")
|
||||||
|
.description("Get location and fuel type details")
|
||||||
|
.parameters(
|
||||||
|
Tools.PromptFuncDefinition.Parameters.builder()
|
||||||
|
.type("object")
|
||||||
|
.properties(
|
||||||
|
Map.of(
|
||||||
|
"location", Tools.PromptFuncDefinition.Property.builder()
|
||||||
|
.type("string")
|
||||||
|
.description("The city, e.g. New Delhi, India")
|
||||||
|
.required(true)
|
||||||
|
.build(),
|
||||||
|
"fuelType", Tools.PromptFuncDefinition.Property.builder()
|
||||||
|
.type("string")
|
||||||
|
.description("The fuel type.")
|
||||||
|
.enumValues(Arrays.asList("petrol", "diesel"))
|
||||||
|
.required(true)
|
||||||
|
.build()
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.required(java.util.List.of("location", "fuelType"))
|
||||||
|
.build()
|
||||||
|
)
|
||||||
|
.build()
|
||||||
|
)
|
||||||
.build()
|
.build()
|
||||||
)
|
).build();
|
||||||
.toolDefinition(SampleTools::getCurrentFuelPrice)
|
|
||||||
.build();
|
|
||||||
```
|
```
|
||||||
|
|
||||||
Lets also define a sample tool specification called **Weather Tool** for getting the current weather.
|
Lets also define a sample tool specification called **Weather Tool** for getting the current weather.
|
||||||
@ -97,13 +125,33 @@ Lets also define a sample tool specification called **Weather Tool** for getting
|
|||||||
Tools.ToolSpecification weatherToolSpecification = Tools.ToolSpecification.builder()
|
Tools.ToolSpecification weatherToolSpecification = Tools.ToolSpecification.builder()
|
||||||
.functionName("current-weather")
|
.functionName("current-weather")
|
||||||
.functionDescription("Get current weather")
|
.functionDescription("Get current weather")
|
||||||
.properties(
|
.toolFunction(SampleTools::getCurrentWeather)
|
||||||
new Tools.PropsBuilder()
|
.toolPrompt(
|
||||||
.withProperty("city", Tools.PromptFuncDefinition.Property.builder().type("string").description("The city, e.g. New Delhi, India").required(true).build())
|
Tools.PromptFuncDefinition.builder()
|
||||||
|
.type("prompt")
|
||||||
|
.function(
|
||||||
|
Tools.PromptFuncDefinition.PromptFuncSpec.builder()
|
||||||
|
.name("get-location-weather-info")
|
||||||
|
.description("Get location details")
|
||||||
|
.parameters(
|
||||||
|
Tools.PromptFuncDefinition.Parameters.builder()
|
||||||
|
.type("object")
|
||||||
|
.properties(
|
||||||
|
Map.of(
|
||||||
|
"city", Tools.PromptFuncDefinition.Property.builder()
|
||||||
|
.type("string")
|
||||||
|
.description("The city, e.g. New Delhi, India")
|
||||||
|
.required(true)
|
||||||
|
.build()
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.required(java.util.List.of("city"))
|
||||||
|
.build()
|
||||||
|
)
|
||||||
|
.build()
|
||||||
|
)
|
||||||
.build()
|
.build()
|
||||||
)
|
).build();
|
||||||
.toolDefinition(SampleTools::getCurrentWeather)
|
|
||||||
.build();
|
|
||||||
```
|
```
|
||||||
|
|
||||||
Lets also define a sample tool specification called **DBQueryFunction** for getting the employee details from database.
|
Lets also define a sample tool specification called **DBQueryFunction** for getting the employee details from database.
|
||||||
@ -115,14 +163,43 @@ Lets also define a sample tool specification called **DBQueryFunction** for gett
|
|||||||
Tools.ToolSpecification databaseQueryToolSpecification = Tools.ToolSpecification.builder()
|
Tools.ToolSpecification databaseQueryToolSpecification = Tools.ToolSpecification.builder()
|
||||||
.functionName("get-employee-details")
|
.functionName("get-employee-details")
|
||||||
.functionDescription("Get employee details from the database")
|
.functionDescription("Get employee details from the database")
|
||||||
.properties(
|
.toolFunction(new DBQueryFunction())
|
||||||
new Tools.PropsBuilder()
|
.toolPrompt(
|
||||||
.withProperty("employee-name", Tools.PromptFuncDefinition.Property.builder().type("string").description("The name of the employee, e.g. John Doe").required(true).build())
|
Tools.PromptFuncDefinition.builder()
|
||||||
.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())
|
.type("prompt")
|
||||||
.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())
|
.function(
|
||||||
|
Tools.PromptFuncDefinition.PromptFuncSpec.builder()
|
||||||
|
.name("get-employee-details")
|
||||||
|
.description("Get employee details from the database")
|
||||||
|
.parameters(
|
||||||
|
Tools.PromptFuncDefinition.Parameters.builder()
|
||||||
|
.type("object")
|
||||||
|
.properties(
|
||||||
|
Map.of(
|
||||||
|
"employee-name", Tools.PromptFuncDefinition.Property.builder()
|
||||||
|
.type("string")
|
||||||
|
.description("The name of the employee, e.g. John Doe")
|
||||||
|
.required(true)
|
||||||
|
.build(),
|
||||||
|
"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(),
|
||||||
|
"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()
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.required(java.util.List.of("employee-name", "employee-address", "employee-phone"))
|
||||||
|
.build()
|
||||||
|
)
|
||||||
|
.build()
|
||||||
|
)
|
||||||
.build()
|
.build()
|
||||||
)
|
)
|
||||||
.toolDefinition(new DBQueryFunction())
|
|
||||||
.build();
|
.build();
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -239,37 +316,111 @@ public class FunctionCallingWithMistralExample {
|
|||||||
Tools.ToolSpecification fuelPriceToolSpecification = Tools.ToolSpecification.builder()
|
Tools.ToolSpecification fuelPriceToolSpecification = Tools.ToolSpecification.builder()
|
||||||
.functionName("current-fuel-price")
|
.functionName("current-fuel-price")
|
||||||
.functionDescription("Get current fuel price")
|
.functionDescription("Get current fuel price")
|
||||||
.properties(
|
.toolFunction(SampleTools::getCurrentFuelPrice)
|
||||||
new Tools.PropsBuilder()
|
.toolPrompt(
|
||||||
.withProperty("location", Tools.PromptFuncDefinition.Property.builder().type("string").description("The city, e.g. New Delhi, India").required(true).build())
|
Tools.PromptFuncDefinition.builder()
|
||||||
.withProperty("fuelType", Tools.PromptFuncDefinition.Property.builder().type("string").description("The fuel type.").enumValues(Arrays.asList("petrol", "diesel")).required(true).build())
|
.type("prompt")
|
||||||
|
.function(
|
||||||
|
Tools.PromptFuncDefinition.PromptFuncSpec.builder()
|
||||||
|
.name("get-location-fuel-info")
|
||||||
|
.description("Get location and fuel type details")
|
||||||
|
.parameters(
|
||||||
|
Tools.PromptFuncDefinition.Parameters.builder()
|
||||||
|
.type("object")
|
||||||
|
.properties(
|
||||||
|
Map.of(
|
||||||
|
"location", Tools.PromptFuncDefinition.Property.builder()
|
||||||
|
.type("string")
|
||||||
|
.description("The city, e.g. New Delhi, India")
|
||||||
|
.required(true)
|
||||||
|
.build(),
|
||||||
|
"fuelType", Tools.PromptFuncDefinition.Property.builder()
|
||||||
|
.type("string")
|
||||||
|
.description("The fuel type.")
|
||||||
|
.enumValues(Arrays.asList("petrol", "diesel"))
|
||||||
|
.required(true)
|
||||||
|
.build()
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.required(java.util.List.of("location", "fuelType"))
|
||||||
|
.build()
|
||||||
|
)
|
||||||
|
.build()
|
||||||
|
)
|
||||||
.build()
|
.build()
|
||||||
)
|
).build();
|
||||||
.toolDefinition(SampleTools::getCurrentFuelPrice)
|
|
||||||
.build();
|
|
||||||
|
|
||||||
Tools.ToolSpecification weatherToolSpecification = Tools.ToolSpecification.builder()
|
Tools.ToolSpecification weatherToolSpecification = Tools.ToolSpecification.builder()
|
||||||
.functionName("current-weather")
|
.functionName("current-weather")
|
||||||
.functionDescription("Get current weather")
|
.functionDescription("Get current weather")
|
||||||
.properties(
|
.toolFunction(SampleTools::getCurrentWeather)
|
||||||
new Tools.PropsBuilder()
|
.toolPrompt(
|
||||||
.withProperty("city", Tools.PromptFuncDefinition.Property.builder().type("string").description("The city, e.g. New Delhi, India").required(true).build())
|
Tools.PromptFuncDefinition.builder()
|
||||||
|
.type("prompt")
|
||||||
|
.function(
|
||||||
|
Tools.PromptFuncDefinition.PromptFuncSpec.builder()
|
||||||
|
.name("get-location-weather-info")
|
||||||
|
.description("Get location details")
|
||||||
|
.parameters(
|
||||||
|
Tools.PromptFuncDefinition.Parameters.builder()
|
||||||
|
.type("object")
|
||||||
|
.properties(
|
||||||
|
Map.of(
|
||||||
|
"city", Tools.PromptFuncDefinition.Property.builder()
|
||||||
|
.type("string")
|
||||||
|
.description("The city, e.g. New Delhi, India")
|
||||||
|
.required(true)
|
||||||
|
.build()
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.required(java.util.List.of("city"))
|
||||||
|
.build()
|
||||||
|
)
|
||||||
|
.build()
|
||||||
|
)
|
||||||
.build()
|
.build()
|
||||||
)
|
).build();
|
||||||
.toolDefinition(SampleTools::getCurrentWeather)
|
|
||||||
.build();
|
|
||||||
|
|
||||||
Tools.ToolSpecification databaseQueryToolSpecification = Tools.ToolSpecification.builder()
|
Tools.ToolSpecification databaseQueryToolSpecification = Tools.ToolSpecification.builder()
|
||||||
.functionName("get-employee-details")
|
.functionName("get-employee-details")
|
||||||
.functionDescription("Get employee details from the database")
|
.functionDescription("Get employee details from the database")
|
||||||
.properties(
|
.toolFunction(new DBQueryFunction())
|
||||||
new Tools.PropsBuilder()
|
.toolPrompt(
|
||||||
.withProperty("employee-name", Tools.PromptFuncDefinition.Property.builder().type("string").description("The name of the employee, e.g. John Doe").required(true).build())
|
Tools.PromptFuncDefinition.builder()
|
||||||
.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())
|
.type("prompt")
|
||||||
.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())
|
.function(
|
||||||
|
Tools.PromptFuncDefinition.PromptFuncSpec.builder()
|
||||||
|
.name("get-employee-details")
|
||||||
|
.description("Get employee details from the database")
|
||||||
|
.parameters(
|
||||||
|
Tools.PromptFuncDefinition.Parameters.builder()
|
||||||
|
.type("object")
|
||||||
|
.properties(
|
||||||
|
Map.of(
|
||||||
|
"employee-name", Tools.PromptFuncDefinition.Property.builder()
|
||||||
|
.type("string")
|
||||||
|
.description("The name of the employee, e.g. John Doe")
|
||||||
|
.required(true)
|
||||||
|
.build(),
|
||||||
|
"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(),
|
||||||
|
"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()
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.required(java.util.List.of("employee-name", "employee-address", "employee-phone"))
|
||||||
|
.build()
|
||||||
|
)
|
||||||
|
.build()
|
||||||
|
)
|
||||||
.build()
|
.build()
|
||||||
)
|
)
|
||||||
.toolDefinition(new DBQueryFunction())
|
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
ollamaAPI.registerTool(fuelPriceToolSpecification);
|
ollamaAPI.registerTool(fuelPriceToolSpecification);
|
||||||
@ -326,6 +477,9 @@ class SampleTools {
|
|||||||
class DBQueryFunction implements ToolFunction {
|
class DBQueryFunction implements ToolFunction {
|
||||||
@Override
|
@Override
|
||||||
public Object apply(Map<String, Object> arguments) {
|
public Object apply(Map<String, Object> arguments) {
|
||||||
|
if (arguments == null || arguments.isEmpty() || arguments.get("employee-name") == null || arguments.get("employee-address") == null || arguments.get("employee-phone") == null) {
|
||||||
|
throw new RuntimeException("Tool was called but the model failed to provide all the required arguments.");
|
||||||
|
}
|
||||||
// perform DB operations here
|
// perform DB operations here
|
||||||
return String.format("Employee Details {ID: %s, Name: %s, Address: %s, Phone: %s}", UUID.randomUUID(), arguments.get("employee-name").toString(), arguments.get("employee-address").toString(), arguments.get("employee-phone").toString());
|
return String.format("Employee Details {ID: %s, Name: %s, Address: %s, Phone: %s}", UUID.randomUUID(), arguments.get("employee-name").toString(), arguments.get("employee-address").toString(), arguments.get("employee-phone").toString());
|
||||||
}
|
}
|
||||||
@ -347,17 +501,17 @@ Rahul Kumar, Address: King St, Hyderabad, India, Phone: 9876543210}`
|
|||||||
|
|
||||||
### Using tools in Chat-API
|
### Using tools in Chat-API
|
||||||
|
|
||||||
Instead of using the specific `ollamaAPI.generateWithTools` method to call the generate API of ollama with tools, it is
|
Instead of using the specific `ollamaAPI.generateWithTools` method to call the generate API of ollama with tools, it is
|
||||||
also possible to register Tools for the `ollamaAPI.chat` methods. In this case, the tool calling/callback is done
|
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.
|
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
|
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).
|
again (induced with the tool call result).
|
||||||
|
|
||||||
#### Sample:
|
#### Sample:
|
||||||
|
|
||||||
The following shows a sample of an integration test that defines a method specified like the tool-specs above, registers
|
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
|
the tool on the ollamaAPI and then simply calls the chat-API. All intermediate tool calling is wrapped inside the api
|
||||||
call.
|
call.
|
||||||
|
|
||||||
```java
|
```java
|
||||||
@ -405,7 +559,7 @@ public static void main(String[] args) {
|
|||||||
|
|
||||||
A typical final response of the above could be:
|
A typical final response of the above could be:
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"chatHistory" : [
|
"chatHistory" : [
|
||||||
{
|
{
|
||||||
@ -466,7 +620,7 @@ This tool calling can also be done using the streaming API.
|
|||||||
|
|
||||||
### Using Annotation based Tool Registration
|
### Using Annotation based Tool Registration
|
||||||
|
|
||||||
Instead of explicitly registering each tool, ollama4j supports declarative tool specification and registration via java
|
Instead of explicitly registering each tool, ollama4j supports declarative tool specification and registration via java
|
||||||
Annotations and reflection calling.
|
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:
|
To declare a method to be used as a tool for a chat call, the following steps have to be considered:
|
||||||
@ -489,7 +643,7 @@ The answer can only be provided by a method that is part of the BackendService c
|
|||||||
|
|
||||||
```java
|
```java
|
||||||
public class BackendService{
|
public class BackendService{
|
||||||
|
|
||||||
public BackendService(){}
|
public BackendService(){}
|
||||||
|
|
||||||
@ToolSpec(desc = "Computes the most important constant all around the globe!")
|
@ToolSpec(desc = "Computes the most important constant all around the globe!")
|
||||||
@ -505,7 +659,7 @@ import io.github.ollama4j.tools.annotations.OllamaToolService;
|
|||||||
|
|
||||||
@OllamaToolService(providers = BackendService.class)
|
@OllamaToolService(providers = BackendService.class)
|
||||||
public class MyOllamaService{
|
public class MyOllamaService{
|
||||||
|
|
||||||
public void chatWithAnnotatedTool(){
|
public void chatWithAnnotatedTool(){
|
||||||
// inject the annotated method to the ollama toolsregistry
|
// inject the annotated method to the ollama toolsregistry
|
||||||
ollamaAPI.registerAnnotatedTools();
|
ollamaAPI.registerAnnotatedTools();
|
||||||
@ -517,14 +671,14 @@ public class MyOllamaService{
|
|||||||
|
|
||||||
OllamaChatResult chatResult = ollamaAPI.chat(requestModel);
|
OllamaChatResult chatResult = ollamaAPI.chat(requestModel);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
Or, if one needs to provide an object instance directly:
|
Or, if one needs to provide an object instance directly:
|
||||||
```java
|
```java
|
||||||
public class MyOllamaService{
|
public class MyOllamaService{
|
||||||
|
|
||||||
public void chatWithAnnotatedTool(){
|
public void chatWithAnnotatedTool(){
|
||||||
ollamaAPI.registerAnnotatedTools(new BackendService());
|
ollamaAPI.registerAnnotatedTools(new BackendService());
|
||||||
OllamaChatRequest requestModel = builder
|
OllamaChatRequest requestModel = builder
|
||||||
@ -534,7 +688,7 @@ public class MyOllamaService{
|
|||||||
|
|
||||||
OllamaChatResult chatResult = ollamaAPI.chat(requestModel);
|
OllamaChatResult chatResult = ollamaAPI.chat(requestModel);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -639,4 +793,4 @@ public String getCurrentFuelPrice(String location, String fuelType) {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
Updating async/chat APIs with support for tool-based generation.
|
Updating async/chat APIs with support for tool-based generation.
|
||||||
|
@ -1058,7 +1058,7 @@ public class OllamaAPI {
|
|||||||
* @param model the ollama model to ask the question to
|
* @param model the ollama model to ask the question to
|
||||||
* @param messages chat history / message stack to send to the model
|
* @param messages chat history / message stack to send to the model
|
||||||
* @return {@link OllamaChatResult} containing the api response and the message
|
* @return {@link OllamaChatResult} containing the api response and the message
|
||||||
* history including the newly aqcuired assistant response.
|
* history including the newly acquired assistant response.
|
||||||
* @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
|
* @throws InterruptedException in case the server is not reachable or network
|
||||||
@ -1066,9 +1066,10 @@ public class OllamaAPI {
|
|||||||
* @throws OllamaBaseException if the response indicates an error status
|
* @throws OllamaBaseException if the response indicates an error status
|
||||||
* @throws IOException if an I/O error occurs during the HTTP request
|
* @throws IOException if an I/O error occurs during the HTTP request
|
||||||
* @throws InterruptedException if the operation is interrupted
|
* @throws InterruptedException if the operation is interrupted
|
||||||
|
* @throws ToolInvocationException if the tool invocation fails
|
||||||
*/
|
*/
|
||||||
public OllamaChatResult chat(String model, List<OllamaChatMessage> messages)
|
public OllamaChatResult chat(String model, List<OllamaChatMessage> messages)
|
||||||
throws OllamaBaseException, IOException, InterruptedException {
|
throws OllamaBaseException, IOException, InterruptedException, ToolInvocationException {
|
||||||
OllamaChatRequestBuilder builder = OllamaChatRequestBuilder.getInstance(model);
|
OllamaChatRequestBuilder builder = OllamaChatRequestBuilder.getInstance(model);
|
||||||
return chat(builder.withMessages(messages).build());
|
return chat(builder.withMessages(messages).build());
|
||||||
}
|
}
|
||||||
@ -1088,9 +1089,10 @@ public class OllamaAPI {
|
|||||||
* @throws OllamaBaseException if the response indicates an error status
|
* @throws OllamaBaseException if the response indicates an error status
|
||||||
* @throws IOException if an I/O error occurs during the HTTP request
|
* @throws IOException if an I/O error occurs during the HTTP request
|
||||||
* @throws InterruptedException if the operation is interrupted
|
* @throws InterruptedException if the operation is interrupted
|
||||||
|
* @throws ToolInvocationException if the tool invocation fails
|
||||||
*/
|
*/
|
||||||
public OllamaChatResult chat(OllamaChatRequest request)
|
public OllamaChatResult chat(OllamaChatRequest request)
|
||||||
throws OllamaBaseException, IOException, InterruptedException {
|
throws OllamaBaseException, IOException, InterruptedException, ToolInvocationException {
|
||||||
return chat(request, null);
|
return chat(request, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1102,7 +1104,7 @@ public class OllamaAPI {
|
|||||||
*
|
*
|
||||||
* @param request request object to be sent to the server
|
* @param request request object to be sent to the server
|
||||||
* @param streamHandler callback handler to handle the last message from stream
|
* @param streamHandler callback handler to handle the last message from stream
|
||||||
* (caution: all previous messages from stream will be
|
* (caution: all previous tokens from stream will be
|
||||||
* concatenated)
|
* concatenated)
|
||||||
* @return {@link OllamaChatResult}
|
* @return {@link OllamaChatResult}
|
||||||
* @throws OllamaBaseException any response code than 200 has been returned
|
* @throws OllamaBaseException any response code than 200 has been returned
|
||||||
@ -1112,9 +1114,10 @@ public class OllamaAPI {
|
|||||||
* @throws OllamaBaseException if the response indicates an error status
|
* @throws OllamaBaseException if the response indicates an error status
|
||||||
* @throws IOException if an I/O error occurs during the HTTP request
|
* @throws IOException if an I/O error occurs during the HTTP request
|
||||||
* @throws InterruptedException if the operation is interrupted
|
* @throws InterruptedException if the operation is interrupted
|
||||||
|
* @throws ToolInvocationException if the tool invocation fails
|
||||||
*/
|
*/
|
||||||
public OllamaChatResult chat(OllamaChatRequest request, OllamaStreamHandler streamHandler)
|
public OllamaChatResult chat(OllamaChatRequest request, OllamaStreamHandler streamHandler)
|
||||||
throws OllamaBaseException, IOException, InterruptedException {
|
throws OllamaBaseException, IOException, InterruptedException, ToolInvocationException {
|
||||||
return chatStreaming(request, new OllamaChatStreamObserver(streamHandler));
|
return chatStreaming(request, new OllamaChatStreamObserver(streamHandler));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1126,7 +1129,7 @@ public class OllamaAPI {
|
|||||||
*
|
*
|
||||||
* @param request request object to be sent to the server
|
* @param request request object to be sent to the server
|
||||||
* @param tokenHandler callback handler to handle the last token from stream
|
* @param tokenHandler callback handler to handle the last token from stream
|
||||||
* (caution: all previous messages from stream will be
|
* (caution: the previous tokens from stream will not be
|
||||||
* concatenated)
|
* concatenated)
|
||||||
* @return {@link OllamaChatResult}
|
* @return {@link OllamaChatResult}
|
||||||
* @throws OllamaBaseException any response code than 200 has been returned
|
* @throws OllamaBaseException any response code than 200 has been returned
|
||||||
@ -1138,7 +1141,7 @@ public class OllamaAPI {
|
|||||||
* @throws InterruptedException if the operation is interrupted
|
* @throws InterruptedException if the operation is interrupted
|
||||||
*/
|
*/
|
||||||
public OllamaChatResult chatStreaming(OllamaChatRequest request, OllamaTokenHandler tokenHandler)
|
public OllamaChatResult chatStreaming(OllamaChatRequest request, OllamaTokenHandler tokenHandler)
|
||||||
throws OllamaBaseException, IOException, InterruptedException {
|
throws OllamaBaseException, IOException, InterruptedException, ToolInvocationException {
|
||||||
OllamaChatEndpointCaller requestCaller = new OllamaChatEndpointCaller(host, auth, requestTimeoutSeconds,
|
OllamaChatEndpointCaller requestCaller = new OllamaChatEndpointCaller(host, auth, requestTimeoutSeconds,
|
||||||
verbose);
|
verbose);
|
||||||
OllamaChatResult result;
|
OllamaChatResult result;
|
||||||
@ -1161,6 +1164,9 @@ public class OllamaAPI {
|
|||||||
for (OllamaChatToolCalls toolCall : toolCalls) {
|
for (OllamaChatToolCalls toolCall : toolCalls) {
|
||||||
String toolName = toolCall.getFunction().getName();
|
String toolName = toolCall.getFunction().getName();
|
||||||
ToolFunction toolFunction = toolRegistry.getToolFunction(toolName);
|
ToolFunction toolFunction = toolRegistry.getToolFunction(toolName);
|
||||||
|
if (toolFunction == null) {
|
||||||
|
throw new ToolInvocationException("Tool function not found: " + toolName);
|
||||||
|
}
|
||||||
Map<String, Object> arguments = toolCall.getFunction().getArguments();
|
Map<String, Object> arguments = toolCall.getFunction().getArguments();
|
||||||
Object res = toolFunction.apply(arguments);
|
Object res = toolFunction.apply(arguments);
|
||||||
request.getMessages().add(new OllamaChatMessage(OllamaChatMessageRole.TOOL,
|
request.getMessages().add(new OllamaChatMessage(OllamaChatMessageRole.TOOL,
|
||||||
|
@ -2,6 +2,10 @@ package io.github.ollama4j.exceptions;
|
|||||||
|
|
||||||
public class ToolInvocationException extends Exception {
|
public class ToolInvocationException extends Exception {
|
||||||
|
|
||||||
|
public ToolInvocationException(String s) {
|
||||||
|
super(s);
|
||||||
|
}
|
||||||
|
|
||||||
public ToolInvocationException(String s, Exception e) {
|
public ToolInvocationException(String s, Exception e) {
|
||||||
super(s, e);
|
super(s, e);
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,7 @@ package io.github.ollama4j.integrationtests;
|
|||||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||||
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.ToolInvocationException;
|
||||||
import io.github.ollama4j.models.chat.*;
|
import io.github.ollama4j.models.chat.*;
|
||||||
import io.github.ollama4j.models.embeddings.OllamaEmbedResponseModel;
|
import io.github.ollama4j.models.embeddings.OllamaEmbedResponseModel;
|
||||||
import io.github.ollama4j.models.response.LibraryModel;
|
import io.github.ollama4j.models.response.LibraryModel;
|
||||||
@ -233,7 +234,7 @@ public class OllamaAPIIntegrationTest {
|
|||||||
@Test
|
@Test
|
||||||
@Order(8)
|
@Order(8)
|
||||||
void testAskModelWithOptions()
|
void testAskModelWithOptions()
|
||||||
throws OllamaBaseException, IOException, URISyntaxException, InterruptedException {
|
throws OllamaBaseException, IOException, URISyntaxException, InterruptedException, ToolInvocationException {
|
||||||
api.pullModel(CHAT_MODEL_INSTRUCT);
|
api.pullModel(CHAT_MODEL_INSTRUCT);
|
||||||
|
|
||||||
OllamaChatRequestBuilder builder = OllamaChatRequestBuilder.getInstance(CHAT_MODEL_INSTRUCT);
|
OllamaChatRequestBuilder builder = OllamaChatRequestBuilder.getInstance(CHAT_MODEL_INSTRUCT);
|
||||||
@ -253,7 +254,7 @@ public class OllamaAPIIntegrationTest {
|
|||||||
@Test
|
@Test
|
||||||
@Order(9)
|
@Order(9)
|
||||||
void testChatWithSystemPrompt()
|
void testChatWithSystemPrompt()
|
||||||
throws OllamaBaseException, IOException, URISyntaxException, InterruptedException {
|
throws OllamaBaseException, IOException, URISyntaxException, InterruptedException, ToolInvocationException {
|
||||||
api.pullModel(CHAT_MODEL_SYSTEM_PROMPT);
|
api.pullModel(CHAT_MODEL_SYSTEM_PROMPT);
|
||||||
OllamaChatRequestBuilder builder = OllamaChatRequestBuilder.getInstance(CHAT_MODEL_SYSTEM_PROMPT);
|
OllamaChatRequestBuilder builder = OllamaChatRequestBuilder.getInstance(CHAT_MODEL_SYSTEM_PROMPT);
|
||||||
OllamaChatRequest requestModel = builder.withMessage(OllamaChatMessageRole.SYSTEM,
|
OllamaChatRequest requestModel = builder.withMessage(OllamaChatMessageRole.SYSTEM,
|
||||||
@ -318,7 +319,7 @@ public class OllamaAPIIntegrationTest {
|
|||||||
@Test
|
@Test
|
||||||
@Order(10)
|
@Order(10)
|
||||||
void testChatWithImageFromURL()
|
void testChatWithImageFromURL()
|
||||||
throws OllamaBaseException, IOException, InterruptedException, URISyntaxException {
|
throws OllamaBaseException, IOException, InterruptedException, URISyntaxException, ToolInvocationException {
|
||||||
api.pullModel(IMAGE_MODEL_LLAVA);
|
api.pullModel(IMAGE_MODEL_LLAVA);
|
||||||
|
|
||||||
OllamaChatRequestBuilder builder = OllamaChatRequestBuilder.getInstance(IMAGE_MODEL_LLAVA);
|
OllamaChatRequestBuilder builder = OllamaChatRequestBuilder.getInstance(IMAGE_MODEL_LLAVA);
|
||||||
@ -336,7 +337,7 @@ public class OllamaAPIIntegrationTest {
|
|||||||
@Test
|
@Test
|
||||||
@Order(10)
|
@Order(10)
|
||||||
void testChatWithImageFromFileWithHistoryRecognition()
|
void testChatWithImageFromFileWithHistoryRecognition()
|
||||||
throws OllamaBaseException, IOException, URISyntaxException, InterruptedException {
|
throws OllamaBaseException, IOException, URISyntaxException, InterruptedException, ToolInvocationException {
|
||||||
api.pullModel(IMAGE_MODEL_LLAVA);
|
api.pullModel(IMAGE_MODEL_LLAVA);
|
||||||
OllamaChatRequestBuilder builder = OllamaChatRequestBuilder.getInstance(IMAGE_MODEL_LLAVA);
|
OllamaChatRequestBuilder builder = OllamaChatRequestBuilder.getInstance(IMAGE_MODEL_LLAVA);
|
||||||
OllamaChatRequest requestModel = builder.withMessage(OllamaChatMessageRole.USER,
|
OllamaChatRequest requestModel = builder.withMessage(OllamaChatMessageRole.USER,
|
||||||
@ -360,7 +361,7 @@ public class OllamaAPIIntegrationTest {
|
|||||||
@Test
|
@Test
|
||||||
@Order(11)
|
@Order(11)
|
||||||
void testChatWithExplicitToolDefinition()
|
void testChatWithExplicitToolDefinition()
|
||||||
throws OllamaBaseException, IOException, URISyntaxException, InterruptedException {
|
throws OllamaBaseException, IOException, URISyntaxException, InterruptedException, ToolInvocationException {
|
||||||
api.pullModel(CHAT_MODEL_SYSTEM_PROMPT);
|
api.pullModel(CHAT_MODEL_SYSTEM_PROMPT);
|
||||||
OllamaChatRequestBuilder builder = OllamaChatRequestBuilder.getInstance(CHAT_MODEL_SYSTEM_PROMPT);
|
OllamaChatRequestBuilder builder = OllamaChatRequestBuilder.getInstance(CHAT_MODEL_SYSTEM_PROMPT);
|
||||||
|
|
||||||
@ -440,7 +441,7 @@ public class OllamaAPIIntegrationTest {
|
|||||||
@Test
|
@Test
|
||||||
@Order(12)
|
@Order(12)
|
||||||
void testChatWithAnnotatedToolsAndSingleParam()
|
void testChatWithAnnotatedToolsAndSingleParam()
|
||||||
throws OllamaBaseException, IOException, InterruptedException, URISyntaxException {
|
throws OllamaBaseException, IOException, InterruptedException, URISyntaxException, ToolInvocationException {
|
||||||
api.pullModel(CHAT_MODEL_SYSTEM_PROMPT);
|
api.pullModel(CHAT_MODEL_SYSTEM_PROMPT);
|
||||||
OllamaChatRequestBuilder builder = OllamaChatRequestBuilder.getInstance(CHAT_MODEL_SYSTEM_PROMPT);
|
OllamaChatRequestBuilder builder = OllamaChatRequestBuilder.getInstance(CHAT_MODEL_SYSTEM_PROMPT);
|
||||||
|
|
||||||
@ -471,7 +472,7 @@ public class OllamaAPIIntegrationTest {
|
|||||||
@Test
|
@Test
|
||||||
@Order(13)
|
@Order(13)
|
||||||
void testChatWithAnnotatedToolsAndMultipleParams()
|
void testChatWithAnnotatedToolsAndMultipleParams()
|
||||||
throws OllamaBaseException, IOException, URISyntaxException, InterruptedException {
|
throws OllamaBaseException, IOException, URISyntaxException, InterruptedException, ToolInvocationException {
|
||||||
api.pullModel(CHAT_MODEL_SYSTEM_PROMPT);
|
api.pullModel(CHAT_MODEL_SYSTEM_PROMPT);
|
||||||
OllamaChatRequestBuilder builder = OllamaChatRequestBuilder.getInstance(CHAT_MODEL_SYSTEM_PROMPT);
|
OllamaChatRequestBuilder builder = OllamaChatRequestBuilder.getInstance(CHAT_MODEL_SYSTEM_PROMPT);
|
||||||
|
|
||||||
@ -508,7 +509,7 @@ public class OllamaAPIIntegrationTest {
|
|||||||
@Test
|
@Test
|
||||||
@Order(14)
|
@Order(14)
|
||||||
void testChatWithToolsAndStream()
|
void testChatWithToolsAndStream()
|
||||||
throws OllamaBaseException, IOException, URISyntaxException, InterruptedException {
|
throws OllamaBaseException, IOException, URISyntaxException, InterruptedException, ToolInvocationException {
|
||||||
api.pullModel(CHAT_MODEL_SYSTEM_PROMPT);
|
api.pullModel(CHAT_MODEL_SYSTEM_PROMPT);
|
||||||
OllamaChatRequestBuilder builder = OllamaChatRequestBuilder.getInstance(CHAT_MODEL_SYSTEM_PROMPT);
|
OllamaChatRequestBuilder builder = OllamaChatRequestBuilder.getInstance(CHAT_MODEL_SYSTEM_PROMPT);
|
||||||
final Tools.ToolSpecification databaseQueryToolSpecification = Tools.ToolSpecification.builder()
|
final Tools.ToolSpecification databaseQueryToolSpecification = Tools.ToolSpecification.builder()
|
||||||
@ -585,7 +586,7 @@ public class OllamaAPIIntegrationTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
@Order(15)
|
@Order(15)
|
||||||
void testChatWithStream() throws OllamaBaseException, IOException, URISyntaxException, InterruptedException {
|
void testChatWithStream() throws OllamaBaseException, IOException, URISyntaxException, InterruptedException, ToolInvocationException {
|
||||||
api.pullModel(CHAT_MODEL_SYSTEM_PROMPT);
|
api.pullModel(CHAT_MODEL_SYSTEM_PROMPT);
|
||||||
OllamaChatRequestBuilder builder = OllamaChatRequestBuilder.getInstance(CHAT_MODEL_SYSTEM_PROMPT);
|
OllamaChatRequestBuilder builder = OllamaChatRequestBuilder.getInstance(CHAT_MODEL_SYSTEM_PROMPT);
|
||||||
OllamaChatRequest requestModel = builder.withMessage(OllamaChatMessageRole.USER,
|
OllamaChatRequest requestModel = builder.withMessage(OllamaChatMessageRole.USER,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user