Skip to content

Commit

Permalink
feat: next step
Browse files Browse the repository at this point in the history
  • Loading branch information
Angular2Guy committed Mar 9, 2024
1 parent d5262d0 commit a3bcfe1
Show file tree
Hide file tree
Showing 2 changed files with 119 additions and 11 deletions.
Original file line number Diff line number Diff line change
@@ -1,12 +1,17 @@
package ch.xxx.aidoclibchat.domain.client;

import java.util.Map;
import java.util.function.Function;

import org.springframework.boot.context.properties.bind.ConstructorBinding;

import com.fasterxml.jackson.annotation.JsonClassDescription;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonInclude.Include;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonPropertyDescription;
import com.fasterxml.jackson.annotation.JsonInclude.Include;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;



Expand All @@ -15,4 +20,68 @@ public interface OpenLibraryClient extends Function<OpenLibraryClient.Request, O
@JsonClassDescription("OpenLibrary API request")
record Request(@JsonProperty(required=false, value="author") @JsonPropertyDescription("The book author") String author ) {}
record Response(String title) {}

@JsonInclude(Include.NON_NULL)
record FunctionTool(
@JsonProperty("type") Type type,
@JsonProperty("function") Function function) {

/**
* Create a tool of type 'function' and the given function definition.
* @param function function definition.
*/
@ConstructorBinding
public FunctionTool(Function function) {
this(Type.FUNCTION, function);
}

/**
* Create a tool of type 'function' and the given function definition.
*/
public enum Type {
/**
* Function tool type.
*/
@JsonProperty("function") FUNCTION
}

/**
* Function definition.
*
* @param description A description of what the function does, used by the model to choose when and how to call
* the function.
* @param name The name of the function to be called. Must be a-z, A-Z, 0-9, or contain underscores and dashes,
* with a maximum length of 64.
* @param parameters The parameters the functions accepts, described as a JSON Schema object. To describe a
* function that accepts no parameters, provide the value {"type": "object", "properties": {}}.
*/
public record Function(
@JsonProperty("description") String description,
@JsonProperty("name") String name,
@JsonProperty("parameters") Map<String, Object> parameters) {

/**
* Create tool function definition.
*
* @param description tool function description.
* @param name tool function name.
* @param jsonSchema tool function schema as json.
*/
@ConstructorBinding
public Function(String description, String name, String jsonSchema) {
this(description, name, parseJson(jsonSchema));
}
}
}

static Map<String, Object> parseJson(String jsonSchema) {
try {
return new ObjectMapper().readValue(jsonSchema,
new TypeReference<Map<String, Object>>() {
});
}
catch (Exception e) {
throw new RuntimeException("Failed to parse schema: " + jsonSchema, e);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,39 +12,78 @@
*/
package ch.xxx.aidoclibchat.usecase.service;

import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicReference;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.ai.chat.ChatClient;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;

import ch.xxx.aidoclibchat.domain.client.OpenLibraryClient;

import ch.xxx.aidoclibchat.domain.client.OpenLibraryClient.FunctionTool.Type;

@Service
public class FunctionService {
private static final Logger LOGGER = LoggerFactory.getLogger(FunctionService.class);
private static final Logger LOGGER = LoggerFactory.getLogger(FunctionService.class);
private final ObjectMapper objectMapper;
private final ChatClient chatClient;
private final List<String> nullCodes = List.of("none", "string");
private final String promptStr = """
You have access to the following tools:
%s
You must follow these instructions:
Always select one or more of the above tools based on the user query
If a tool is found, you must respond in the JSON format matching the following schema:
{
"tools": [{
"tool": "<name of the selected tool>",
"tool_input": "<parameters for the selected tool, matching the tool's JSON schema>"
}]
}
Make sure to include all tool parameters in the JSON at tool_input.
If there is no tool that match the user request, you will respond with empty json.
Do not add any additional Notes or Explanations. Respond only with the JSON.
User Query:
%s
""";
@Value("${spring.profiles.active:}")
private String activeProfile;
public FunctionService(ObjectMapper objectMapper) {

public FunctionService(ObjectMapper objectMapper,ChatClient chatClient) {
this.objectMapper = objectMapper;
this.chatClient = chatClient;
}

public String functionCall(String question) {
if(!this.activeProfile.contains("ollama")) {
if (!this.activeProfile.contains("ollama")) {
return "false";
}
var request = new OpenLibraryClient.Request(question);
var description = "Search for books by author, title or subject.";
var name = "booksearch";
var aiFunction = new OpenLibraryClient.FunctionTool(Type.FUNCTION, new OpenLibraryClient.FunctionTool.Function(
description, name, Map.of("author", "string", "title", "string", "subject", "string")));
String jsonStr = "";
try {
jsonStr = this.objectMapper.writeValueAsString(request);
jsonStr = this.objectMapper.writeValueAsString(aiFunction);
} catch (JsonProcessingException e) {
LOGGER.error("Json Mapping failed.", e);
}
return jsonStr;
}
var query = String.format(this.promptStr, jsonStr, question);
var response = this.chatClient.call(query);
response = response.substring(response.indexOf("{"), response.lastIndexOf("}"));
final var atomicResponse = new AtomicReference<String>(response);
this.nullCodes.forEach(myCode -> {
var myResponse = atomicResponse.get();
atomicResponse.set(myResponse.replaceAll(myCode, ""));
});
return response;
}
}

0 comments on commit a3bcfe1

Please sign in to comment.