StefanBratanov / jvm-openai

A minimalistic OpenAI API client for the JVM, written in Java 🤖
Apache License 2.0
35 stars 8 forks source link

Function parameters are serialized with JSON escaping, making it difficult to emit the write JSON #7

Closed bironran closed 1 month ago

bironran commented 1 month ago

It's expected that properties values would not be serialized with escaping.

Example:

` public class SerializeTest {

public static void main(String[] args)
        throws ClassNotFoundException, InvocationTargetException, IllegalAccessException, JsonProcessingException,
        NoSuchMethodException {
    final List<Tool> tools = new LinkedList<>();
    tools.add(Tool.functionTool(
            FunctionTool.Function.newBuilder().name("getFriends").description("Returns the friends of the person").parameters(
                            Map.of("type", "object",
                                    "properties", "{\"person_name\":{\"type\":\"string\", \"description\":\"the persons name, in lower case\"}}",
                                    "required", "[\"person_name\"]"))
                    .build()));
    final Method method =
            Class.forName("io.github.stefanbratanov.jvm.openai.ObjectMapperSingleton").getDeclaredMethod("getInstance");
    method.setAccessible(true);
    final ObjectMapper objectMapper = (ObjectMapper) method.invoke(null);
    System.out.println(objectMapper.writeValueAsString(tools));
    //[{"function":{"name":"getFriends","description":"Returns the friends of the person","parameters":{"properties":"{\"person_name\":{\"type\":\"string\", \"description\":\"the persons name, in lower case\"}}","type":"object","required":"[\"person_name\"]"}},"type":"function"}]
    //pretty print:
    /*
[
    {
        "function":
        {
            "name": "getFriends",
            "description": "Returns the friends of the person",
            "parameters":
            {
                "properties": "{\"person_name\":{\"type\":\"string\", \"description\":\"the persons name, in lower case\"}}",
                "type": "object",
                "required": "[\"person_name\"]"
            }
        },
        "type": "function"
    }
]
     */
}
}

`

Same with required[] really

StefanBratanov commented 1 month ago

Hi, thanks a lot for raising this. Currently, you can go around this by making the raw jsons String values a Map<String,Object> and Jackson will serialize it properly without escaping. However, I fixed this with https://github.com/StefanBratanov/jvm-openai/commit/00ee199df49fbd5fd4a9c94b2dfbb466fee183f8 so will be available with the next version, which I plan to release at some point next week.

StefanBratanov commented 1 month ago

@bironran The fix has been released as part of https://github.com/StefanBratanov/jvm-openai/releases/tag/v0.9.0 so will close this issue. Feel free to reopen if facing the same issue.

bironran commented 1 month ago

There's a bug in deserialization now: For the LLM output

{
  "id": "chatcmpl-9LvJxqTCecmF4cqiT7gK0zz0F95ch",
  "object": "chat.completion",
  "created": 1715012257,
  "model": "gpt-3.5-turbo-0125",
  "choices": [
    {
      "index": 0,
      "message": {
        "role": "assistant",
        "content": null,
        "tool_calls": [
          {
            "id": "call_Wu4tEmwbUVYWScrFe7iN9NXy",
            "type": "function",
            "function": {
              "name": "getFriends",
              "arguments": "{\"person_name\":\"ran\"}"
            }
          }
        ]
      },
      "logprobs": null,
      "finish_reason": "tool_calls"
    }
  ],
  "usage": {
    "prompt_tokens": 67,
    "completion_tokens": 15,
    "total_tokens": 82
  },
  "system_fingerprint": "fp_3b956da36b"
}

I get an exception:

com.fasterxml.jackson.databind.JsonMappingException: Can not set final java.util.List field io.github.stefanbratanov.jvm.openai.ChatCompletion$Choice$Message.toolCalls to java.util.ArrayList (through reference chain: io.github.stefanbratanov.jvm.openai.ChatCompletion["choices"]->java.util.ArrayList[0]->io.github.stefanbratanov.jvm.openai.ChatCompletion$Choice["message"])
java.io.UncheckedIOException: com.fasterxml.jackson.databind.JsonMappingException: Can not set final java.util.List field io.github.stefanbratanov.jvm.openai.ChatCompletion$Choice$Message.toolCalls to java.util.ArrayList (through reference chain: io.github.stefanbratanov.jvm.openai.ChatCompletion["choices"]->java.util.ArrayList[0]->io.github.stefanbratanov.jvm.openai.ChatCompletion$Choice["message"])
    at io.github.stefanbratanov.jvm.openai.OpenAIClient.deserializeResponse(OpenAIClient.java:130)
    at io.github.stefanbratanov.jvm.openai.ChatClient.createChatCompletion(ChatClient.java:38)
...
StefanBratanov commented 1 month ago

Hmm this error is similar to the one described at https://github.com/StefanBratanov/jvm-openai/issues/3 . Are you sure you are not using an old version of Jackson in your project? There is a bug in older ones related to Java records.

bironran commented 1 month ago

This workaround works https://stackoverflow.com/a/68998917/931

bironran commented 1 month ago

old version of Jackson

I just bound Jackson to 2.17.1 explicitly and without the workaround it still fails.

StefanBratanov commented 1 month ago

Thank you, I reopened the issue and will have a look

StefanBratanov commented 1 month ago

It's interesting. I wasn't able to replicate the issue with this JSON. I wonder if it something to do with my OS or Java version. In any case, since it has been more than one user reporting this, I added the workaround in https://github.com/StefanBratanov/jvm-openai/commit/4e9353382099b59146a64be7097fa19bbddae6e0 and will release v0.9.1 at some point tomorrow. If you are able to test with master, please do and let me know if it works.

StefanBratanov commented 1 month ago

Hi @bironran just released https://github.com/StefanBratanov/jvm-openai/releases/tag/v0.9.1 . I will close the issue again but let me know if still facing the problem.