Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Tool parameters - use indexes #1053

Open
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

edeandrea
Copy link
Collaborator

Tool parameters - Allow using Jandex for classes that aren't in the initial index

Fixes #1036

@geoand
Copy link
Collaborator

geoand commented Nov 6, 2024

Thanks for looking into this, but I am pretty sure this is not the proper solution and likely works by accident (and likely won't work for the next type we attempt to tackle).

Can you paste the request / response to / from OpenAI?

@edeandrea
Copy link
Collaborator Author

image

I cut out the calls to the embedding model - that part is irrelevant.

I think I see what you're saying. The marshaller or whatever doesn't seem to understand how to construct a LocalDate to pass to the tool.

But it did get further than it did before!

Have any thoughts on what the next step might be?

2024-11-06 08:12:55,343 INFO  [io.qua.lan.ope.com.OpenAiRestApi$OpenAiClientLogger] (vert.x-eventloop-thread-4) Request:
- method: POST
- url: https://api.openai.com/v1/chat/completions
- headers: [Accept: application/json], [Authorization: Be...1f], [Content-Type: application/json], [User-Agent: langchain4j-openai], [content-length: 3979]
- body: {
  "model" : "gpt-4o-mini",
  "messages" : [ {
    "role" : "system",
    "content" : "You are a customer chat support agent of an airline named \"Funnair\".\nRespond in a friendly, helpful, and joyful manner.\nYou are interacting with customers through an online chat system.\nBefore providing information about a booking or cancelling a booking,\nyou MUST ensure you have the following information from the user:\nbooking number, customer first name, and last name.\nCheck the message history for this information before asking the user.\nBefore changing a booking, you MUST ensure it is permitted by the terms.\nIf there is a charge for the change, you MUST ask the user to consent before proceeding.\nUse the provided functions to fetch booking details, change bookings, and cancel bookings.\nToday is 2024-11-06.\n"
  }, {
    "role" : "user",
    "content" : "Hello I'm Robert Taylor and my booking number is 105. I'd like to change my flight date to November 16, 2024.\n\nAnswer using the following information:\n2. Changing Bookings\n- Changes allowed up to 24 hours before flight.\n- Change via online or contact our support.\n- Change fee: $50 for Economy, $30 for Premium Economy, Free for Business Class.\n\n1. Booking Flights\n- Book via our website or mobile app.\n- Full payment required at booking.\n- Ensure accuracy of personal information (Name, ID, etc.) as corrections may incur a $25 fee."
  } ],
  "temperature" : 0.0,
  "top_p" : 1.0,
  "presence_penalty" : 0.0,
  "frequency_penalty" : 0.0,
  "tools" : [ {
    "type" : "function",
    "function" : {
      "name" : "getBookingDetails",
      "description" : "Retrieves information about an existing booking,\nsuch as the flight date, booking status, departure and arrival airports, and booking class.\n",
      "parameters" : {
        "type" : "object",
        "properties" : {
          "firstName" : {
            "type" : "string"
          },
          "lastName" : {
            "type" : "string"
          },
          "bookingNumber" : {
            "type" : "string"
          }
        },
        "required" : [ "bookingNumber", "firstName", "lastName" ]
      }
    }
  }, {
    "type" : "function",
    "function" : {
      "name" : "changeBooking",
      "description" : "Modifies an existing booking.\nThis includes making changes to the flight date, and the departure and arrival airports.\n",
      "parameters" : {
        "type" : "object",
        "properties" : {
          "newFlightDate" : {
            "type" : "object",
            "properties" : {
              "month" : {
                "type" : "integer"
              },
              "year" : {
                "type" : "integer"
              },
              "day" : {
                "type" : "integer"
              }
            },
            "required" : [ ]
          },
          "firstName" : {
            "type" : "string"
          },
          "lastName" : {
            "type" : "string"
          },
          "newDepartureAirport" : {
            "type" : "string",
            "description" : "3-letter code for departure airport"
          },
          "newArrivalAirport" : {
            "type" : "string",
            "description" : "3-letter code for arrival airport"
          },
          "bookingNumber" : {
            "type" : "string"
          }
        },
        "required" : [ "bookingNumber", "firstName", "lastName", "newFlightDate", "newDepartureAirport", "newArrivalAirport" ]
      }
    }
  }, {
    "type" : "function",
    "function" : {
      "name" : "cancelBooking",
      "description" : "",
      "parameters" : {
        "type" : "object",
        "properties" : {
          "firstName" : {
            "type" : "string"
          },
          "lastName" : {
            "type" : "string"
          },
          "bookingNumber" : {
            "type" : "string"
          }
        },
        "required" : [ "bookingNumber", "firstName", "lastName" ]
      }
    }
  } ]
}

2024-11-06 08:12:56,230 INFO  [io.qua.lan.ope.com.OpenAiRestApi$OpenAiClientLogger] (vert.x-eventloop-thread-4) Response:
- status code: 200
- headers: [Date: Wed, 06 Nov 2024 13:12:56 GMT], [Content-Type: application/json], [Content-Length: 1092], [Connection: keep-alive], [access-control-expose-headers: X-Request-ID], [openai-organization: red-hat-jdskfr], [openai-processing-ms: 615], [openai-version: 2020-10-01], [x-ratelimit-limit-requests: 10000], [x-ratelimit-limit-tokens: 200000], [x-ratelimit-remaining-requests: 9999], [x-ratelimit-remaining-tokens: 199668], [x-ratelimit-reset-requests: 8.64s], [x-ratelimit-reset-tokens: 99ms], [x-request-id: req_e2742c52e7378e5fdfbee75210d57da1], [strict-transport-security: max-age=31536000; includeSubDomains; preload], [CF-Cache-Status: DYNAMIC], [Set-Cookie: __...ne], [X-Content-Type-Options: nosniff], [Set-Cookie: _c...ne], [Server: cloudflare], [CF-RAY: 8de5568209f03b8e-BOS], [alt-svc: h3=":443"; ma=86400]
- body: {
  "id": "chatcmpl-AQa87Rzs1zIKEipIhYBVzcs8TXGoG",
  "object": "chat.completion",
  "created": 1730898775,
  "model": "gpt-4o-mini-2024-07-18",
  "choices": [
    {
      "index": 0,
      "message": {
        "role": "assistant",
        "content": null,
        "tool_calls": [
          {
            "id": "call_L3JgC8iYqMNmqYrk8GmE899N",
            "type": "function",
            "function": {
              "name": "getBookingDetails",
              "arguments": "{\"firstName\":\"Robert\",\"lastName\":\"Taylor\",\"bookingNumber\":\"105\"}"
            }
          }
        ],
        "refusal": null
      },
      "logprobs": null,
      "finish_reason": "tool_calls"
    }
  ],
  "usage": {
    "prompt_tokens": 459,
    "completion_tokens": 26,
    "total_tokens": 485,
    "prompt_tokens_details": {
      "cached_tokens": 0,
      "audio_tokens": 0
    },
    "completion_tokens_details": {
      "reasoning_tokens": 0,
      "audio_tokens": 0,
      "accepted_prediction_tokens": 0,
      "rejected_prediction_tokens": 0
    }
  },
  "system_fingerprint": "fp_0ba0d124f1"
}


2024-11-06 08:12:56,253 INFO  [io.qua.lan.ope.com.OpenAiRestApi$OpenAiClientLogger] (vert.x-eventloop-thread-4) Request:
- method: POST
- url: https://api.openai.com/v1/chat/completions
- headers: [Accept: application/json], [Authorization: Be...1f], [Content-Type: application/json], [User-Agent: langchain4j-openai], [content-length: 4629]
- body: {
  "model" : "gpt-4o-mini",
  "messages" : [ {
    "role" : "system",
    "content" : "You are a customer chat support agent of an airline named \"Funnair\".\nRespond in a friendly, helpful, and joyful manner.\nYou are interacting with customers through an online chat system.\nBefore providing information about a booking or cancelling a booking,\nyou MUST ensure you have the following information from the user:\nbooking number, customer first name, and last name.\nCheck the message history for this information before asking the user.\nBefore changing a booking, you MUST ensure it is permitted by the terms.\nIf there is a charge for the change, you MUST ask the user to consent before proceeding.\nUse the provided functions to fetch booking details, change bookings, and cancel bookings.\nToday is 2024-11-06.\n"
  }, {
    "role" : "user",
    "content" : "Hello I'm Robert Taylor and my booking number is 105. I'd like to change my flight date to November 16, 2024.\n\nAnswer using the following information:\n2. Changing Bookings\n- Changes allowed up to 24 hours before flight.\n- Change via online or contact our support.\n- Change fee: $50 for Economy, $30 for Premium Economy, Free for Business Class.\n\n1. Booking Flights\n- Book via our website or mobile app.\n- Full payment required at booking.\n- Ensure accuracy of personal information (Name, ID, etc.) as corrections may incur a $25 fee."
  }, {
    "role" : "assistant",
    "tool_calls" : [ {
      "id" : "call_L3JgC8iYqMNmqYrk8GmE899N",
      "type" : "function",
      "function" : {
        "name" : "getBookingDetails",
        "arguments" : "{\"firstName\":\"Robert\",\"lastName\":\"Taylor\",\"bookingNumber\":\"105\"}"
      }
    } ]
  }, {
    "role" : "tool",
    "tool_call_id" : "call_L3JgC8iYqMNmqYrk8GmE899N",
    "content" : "{\n  \"bookingNumber\" : \"105\",\n  \"firstName\" : \"Robert\",\n  \"lastName\" : \"Taylor\",\n  \"date\" : \"2024-11-14\",\n  \"bookingStatus\" : \"CONFIRMED\",\n  \"from\" : \"LHR\",\n  \"to\" : \"FRA\",\n  \"bookingClass\" : \"BUSINESS\"\n}"
  } ],
  "temperature" : 0.0,
  "top_p" : 1.0,
  "presence_penalty" : 0.0,
  "frequency_penalty" : 0.0,
  "tools" : [ {
    "type" : "function",
    "function" : {
      "name" : "getBookingDetails",
      "description" : "Retrieves information about an existing booking,\nsuch as the flight date, booking status, departure and arrival airports, and booking class.\n",
      "parameters" : {
        "type" : "object",
        "properties" : {
          "firstName" : {
            "type" : "string"
          },
          "lastName" : {
            "type" : "string"
          },
          "bookingNumber" : {
            "type" : "string"
          }
        },
        "required" : [ "bookingNumber", "firstName", "lastName" ]
      }
    }
  }, {
    "type" : "function",
    "function" : {
      "name" : "changeBooking",
      "description" : "Modifies an existing booking.\nThis includes making changes to the flight date, and the departure and arrival airports.\n",
      "parameters" : {
        "type" : "object",
        "properties" : {
          "newFlightDate" : {
            "type" : "object",
            "properties" : {
              "month" : {
                "type" : "integer"
              },
              "year" : {
                "type" : "integer"
              },
              "day" : {
                "type" : "integer"
              }
            },
            "required" : [ ]
          },
          "firstName" : {
            "type" : "string"
          },
          "lastName" : {
            "type" : "string"
          },
          "newDepartureAirport" : {
            "type" : "string",
            "description" : "3-letter code for departure airport"
          },
          "newArrivalAirport" : {
            "type" : "string",
            "description" : "3-letter code for arrival airport"
          },
          "bookingNumber" : {
            "type" : "string"
          }
        },
        "required" : [ "bookingNumber", "firstName", "lastName", "newFlightDate", "newDepartureAirport", "newArrivalAirport" ]
      }
    }
  }, {
    "type" : "function",
    "function" : {
      "name" : "cancelBooking",
      "description" : "",
      "parameters" : {
        "type" : "object",
        "properties" : {
          "firstName" : {
            "type" : "string"
          },
          "lastName" : {
            "type" : "string"
          },
          "bookingNumber" : {
            "type" : "string"
          }
        },
        "required" : [ "bookingNumber", "firstName", "lastName" ]
      }
    }
  } ]
}

2024-11-06 08:12:58,006 INFO  [io.qua.lan.ope.com.OpenAiRestApi$OpenAiClientLogger] (vert.x-eventloop-thread-4) Response:
- status code: 200
- headers: [Date: Wed, 06 Nov 2024 13:12:58 GMT], [Content-Type: application/json], [Content-Length: 1135], [Connection: keep-alive], [access-control-expose-headers: X-Request-ID], [openai-organization: red-hat-jdskfr], [openai-processing-ms: 1674], [openai-version: 2020-10-01], [x-ratelimit-limit-requests: 10000], [x-ratelimit-limit-tokens: 200000], [x-ratelimit-remaining-requests: 9998], [x-ratelimit-remaining-tokens: 199616], [x-ratelimit-reset-requests: 16.56s], [x-ratelimit-reset-tokens: 115ms], [x-request-id: req_3750eb354bf8df94aab4a890156d9605], [strict-transport-security: max-age=31536000; includeSubDomains; preload], [CF-Cache-Status: DYNAMIC], [Set-Cookie: __...ne], [X-Content-Type-Options: nosniff], [Set-Cookie: _c...ne], [Server: cloudflare], [CF-RAY: 8de55687bfb63b8e-BOS], [alt-svc: h3=":443"; ma=86400]
- body: {
  "id": "chatcmpl-AQa88rzYQ7ZiKVZljntgHupI5Z3Qw",
  "object": "chat.completion",
  "created": 1730898776,
  "model": "gpt-4o-mini-2024-07-18",
  "choices": [
    {
      "index": 0,
      "message": {
        "role": "assistant",
        "content": "Hello Robert! 🌟 Thank you for reaching out to Funnair! \n\nI see that your current flight is scheduled for November 14, 2024, and you're looking to change it to November 16, 2024. Since you're in Business Class, I'm happy to inform you that there is no change fee for this modification! \n\nWould you like me to proceed with changing your flight date to November 16, 2024? ✈️",
        "refusal": null
      },
      "logprobs": null,
      "finish_reason": "stop"
    }
  ],
  "usage": {
    "prompt_tokens": 575,
    "completion_tokens": 94,
    "total_tokens": 669,
    "prompt_tokens_details": {
      "cached_tokens": 0,
      "audio_tokens": 0
    },
    "completion_tokens_details": {
      "reasoning_tokens": 0,
      "audio_tokens": 0,
      "accepted_prediction_tokens": 0,
      "rejected_prediction_tokens": 0
    }
  },
  "system_fingerprint": "fp_0ba0d124f1"
}


2024-11-06 08:13:10,665 INFO  [io.qua.lan.ope.com.OpenAiRestApi$OpenAiClientLogger] (vert.x-eventloop-thread-4) Request:
- method: POST
- url: https://api.openai.com/v1/chat/completions
- headers: [Accept: application/json], [Authorization: Be...1f], [Content-Type: application/json], [User-Agent: langchain4j-openai], [content-length: 5465]
- body: {
  "model" : "gpt-4o-mini",
  "messages" : [ {
    "role" : "system",
    "content" : "You are a customer chat support agent of an airline named \"Funnair\".\nRespond in a friendly, helpful, and joyful manner.\nYou are interacting with customers through an online chat system.\nBefore providing information about a booking or cancelling a booking,\nyou MUST ensure you have the following information from the user:\nbooking number, customer first name, and last name.\nCheck the message history for this information before asking the user.\nBefore changing a booking, you MUST ensure it is permitted by the terms.\nIf there is a charge for the change, you MUST ask the user to consent before proceeding.\nUse the provided functions to fetch booking details, change bookings, and cancel bookings.\nToday is 2024-11-06.\n"
  }, {
    "role" : "user",
    "content" : "Hello I'm Robert Taylor and my booking number is 105. I'd like to change my flight date to November 16, 2024.\n\nAnswer using the following information:\n2. Changing Bookings\n- Changes allowed up to 24 hours before flight.\n- Change via online or contact our support.\n- Change fee: $50 for Economy, $30 for Premium Economy, Free for Business Class.\n\n1. Booking Flights\n- Book via our website or mobile app.\n- Full payment required at booking.\n- Ensure accuracy of personal information (Name, ID, etc.) as corrections may incur a $25 fee."
  }, {
    "role" : "assistant",
    "tool_calls" : [ {
      "id" : "call_L3JgC8iYqMNmqYrk8GmE899N",
      "type" : "function",
      "function" : {
        "name" : "getBookingDetails",
        "arguments" : "{\"firstName\":\"Robert\",\"lastName\":\"Taylor\",\"bookingNumber\":\"105\"}"
      }
    } ]
  }, {
    "role" : "tool",
    "tool_call_id" : "call_L3JgC8iYqMNmqYrk8GmE899N",
    "content" : "{\n  \"bookingNumber\" : \"105\",\n  \"firstName\" : \"Robert\",\n  \"lastName\" : \"Taylor\",\n  \"date\" : \"2024-11-14\",\n  \"bookingStatus\" : \"CONFIRMED\",\n  \"from\" : \"LHR\",\n  \"to\" : \"FRA\",\n  \"bookingClass\" : \"BUSINESS\"\n}"
  }, {
    "role" : "assistant",
    "content" : "Hello Robert! 🌟 Thank you for reaching out to Funnair! \n\nI see that your current flight is scheduled for November 14, 2024, and you're looking to change it to November 16, 2024. Since you're in Business Class, I'm happy to inform you that there is no change fee for this modification! \n\nWould you like me to proceed with changing your flight date to November 16, 2024? ✈️"
  }, {
    "role" : "user",
    "content" : "Yes please\n\nAnswer using the following information:\nThese Terms of Service govern your experience with Funnair. By booking a flight, you agree to these terms.\n\n1. Booking Flights\n- Book via our website or mobile app.\n- Full payment required at booking.\n- Ensure accuracy of personal information (Name, ID, etc.) as corrections may incur a $25 fee."
  } ],
  "temperature" : 0.0,
  "top_p" : 1.0,
  "presence_penalty" : 0.0,
  "frequency_penalty" : 0.0,
  "tools" : [ {
    "type" : "function",
    "function" : {
      "name" : "getBookingDetails",
      "description" : "Retrieves information about an existing booking,\nsuch as the flight date, booking status, departure and arrival airports, and booking class.\n",
      "parameters" : {
        "type" : "object",
        "properties" : {
          "firstName" : {
            "type" : "string"
          },
          "lastName" : {
            "type" : "string"
          },
          "bookingNumber" : {
            "type" : "string"
          }
        },
        "required" : [ "bookingNumber", "firstName", "lastName" ]
      }
    }
  }, {
    "type" : "function",
    "function" : {
      "name" : "changeBooking",
      "description" : "Modifies an existing booking.\nThis includes making changes to the flight date, and the departure and arrival airports.\n",
      "parameters" : {
        "type" : "object",
        "properties" : {
          "newFlightDate" : {
            "type" : "object",
            "properties" : {
              "month" : {
                "type" : "integer"
              },
              "year" : {
                "type" : "integer"
              },
              "day" : {
                "type" : "integer"
              }
            },
            "required" : [ ]
          },
          "firstName" : {
            "type" : "string"
          },
          "lastName" : {
            "type" : "string"
          },
          "newDepartureAirport" : {
            "type" : "string",
            "description" : "3-letter code for departure airport"
          },
          "newArrivalAirport" : {
            "type" : "string",
            "description" : "3-letter code for arrival airport"
          },
          "bookingNumber" : {
            "type" : "string"
          }
        },
        "required" : [ "bookingNumber", "firstName", "lastName", "newFlightDate", "newDepartureAirport", "newArrivalAirport" ]
      }
    }
  }, {
    "type" : "function",
    "function" : {
      "name" : "cancelBooking",
      "description" : "",
      "parameters" : {
        "type" : "object",
        "properties" : {
          "firstName" : {
            "type" : "string"
          },
          "lastName" : {
            "type" : "string"
          },
          "bookingNumber" : {
            "type" : "string"
          }
        },
        "required" : [ "bookingNumber", "firstName", "lastName" ]
      }
    }
  } ]
}

2024-11-06 08:13:11,690 INFO  [io.qua.lan.ope.com.OpenAiRestApi$OpenAiClientLogger] (vert.x-eventloop-thread-4) Response:
- status code: 200
- headers: [Date: Wed, 06 Nov 2024 13:13:11 GMT], [Content-Type: application/json], [Content-Length: 1208], [Connection: keep-alive], [access-control-expose-headers: X-Request-ID], [openai-organization: red-hat-jdskfr], [openai-processing-ms: 849], [openai-version: 2020-10-01], [x-ratelimit-limit-requests: 10000], [x-ratelimit-limit-tokens: 200000], [x-ratelimit-remaining-requests: 9998], [x-ratelimit-remaining-tokens: 199433], [x-ratelimit-reset-requests: 10.787s], [x-ratelimit-reset-tokens: 170ms], [x-request-id: req_ee075b810b6ac3682497a88a3ff83bf9], [strict-transport-security: max-age=31536000; includeSubDomains; preload], [CF-Cache-Status: DYNAMIC], [Set-Cookie: __...ne], [X-Content-Type-Options: nosniff], [Set-Cookie: _c...ne], [Server: cloudflare], [CF-RAY: 8de556e1cb923b8e-BOS], [alt-svc: h3=":443"; ma=86400]
- body: {
  "id": "chatcmpl-AQa8MwodWHbOsLrIiRaDlEq3WUTWw",
  "object": "chat.completion",
  "created": 1730898790,
  "model": "gpt-4o-mini-2024-07-18",
  "choices": [
    {
      "index": 0,
      "message": {
        "role": "assistant",
        "content": null,
        "tool_calls": [
          {
            "id": "call_3LqwvxFNObNcT6vvwtIVKAml",
            "type": "function",
            "function": {
              "name": "changeBooking",
              "arguments": "{\"newFlightDate\":{\"month\":11,\"year\":2024,\"day\":16},\"firstName\":\"Robert\",\"lastName\":\"Taylor\",\"newDepartureAirport\":\"LHR\",\"newArrivalAirport\":\"FRA\",\"bookingNumber\":\"105\"}"
            }
          }
        ],
        "refusal": null
      },
      "logprobs": null,
      "finish_reason": "tool_calls"
    }
  ],
  "usage": {
    "prompt_tokens": 751,
    "completion_tokens": 56,
    "total_tokens": 807,
    "prompt_tokens_details": {
      "cached_tokens": 0,
      "audio_tokens": 0
    },
    "completion_tokens_details": {
      "reasoning_tokens": 0,
      "audio_tokens": 0,
      "accepted_prediction_tokens": 0,
      "rejected_prediction_tokens": 0
    }
  },
  "system_fingerprint": "fp_0ba0d124f1"
}


2024-11-06 08:13:11,705 ERROR [io.qua.lan.run.too.QuarkusToolExecutor] (executor-thread-3) com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot deserialize value of type `java.time.LocalDate` from Object value (token `JsonToken.START_OBJECT`)
 at [Source: REDACTED (`StreamReadFeature.INCLUDE_SOURCE_IN_LOCATION` disabled); line: 1, column: 18] (through reference chain: org.vaadin.marcus.langchain4j.LangChain4jTools$$QuarkusToolArgumentMapper$changeBooking_c316268f90f72bed55786b9940800e8671df667d["newFlightDate"])
2024-11-06 08:13:11,706 ERROR [com.vaa.hil.EndpointInvoker] (executor-thread-3) Endpoint AssistantService method chat execution failure

Exception in AssistantService.java:19
          17  
          18      public String chat(String chatId, String userMessage) {
        → 19          return langChain4JAssistant.chat(chatId, userMessage);
          20      }
          21  }

: java.lang.reflect.InvocationTargetException
        at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:118)
        at java.base/java.lang.reflect.Method.invoke(Method.java:580)
        at com.vaadin.hilla.EndpointInvoker.invokeVaadinEndpointMethod(EndpointInvoker.java:454)
        at com.vaadin.hilla.EndpointInvoker.invoke(EndpointInvoker.java:203)
        at com.vaadin.hilla.EndpointController.doServeEndpoint(EndpointController.java:251)
        at com.vaadin.hilla.EndpointController.serveEndpoint(EndpointController.java:199)
        at com.github.mcollovati.quarkus.hilla.QuarkusEndpointController.serveEndpoint(QuarkusEndpointController.java:79)
        at com.github.mcollovati.quarkus.hilla.QuarkusEndpointController$quarkusrestinvoker$serveEndpoint_19ea4aa8e36421f8414cd9ce9157408f2c9d0890.invoke(Unknown Source)
        at org.jboss.resteasy.reactive.server.handlers.InvocationHandler.handle(InvocationHandler.java:29)
        at org.jboss.resteasy.reactive.server.handlers.InvocationHandler.handle(InvocationHandler.java:7)
        at org.jboss.resteasy.reactive.common.core.AbstractResteasyReactiveContext.invokeHandler(AbstractResteasyReactiveContext.java:231)
        at org.jboss.resteasy.reactive.common.core.AbstractResteasyReactiveContext.run(AbstractResteasyReactiveContext.java:147)
        at org.jboss.resteasy.reactive.server.handlers.RestInitialHandler.beginProcessing(RestInitialHandler.java:48)
        at io.quarkus.resteasy.reactive.server.servlet.runtime.ResteasyReactiveServlet.service(ResteasyReactiveServlet.java:31)
        at jakarta.servlet.http.HttpServlet.service(HttpServlet.java:614)
        at io.undertow.servlet.handlers.ServletHandler.handleRequest(ServletHandler.java:74)
        at io.undertow.servlet.handlers.security.ServletSecurityRoleHandler.handleRequest(ServletSecurityRoleHandler.java:63)
        at io.undertow.servlet.handlers.ServletChain$1.handleRequest(ServletChain.java:68)
        at io.undertow.servlet.handlers.ServletDispatchingHandler.handleRequest(ServletDispatchingHandler.java:36)
        at io.undertow.servlet.handlers.RedirectDirHandler.handleRequest(RedirectDirHandler.java:67)
        at io.undertow.servlet.handlers.security.SSLInformationAssociationHandler.handleRequest(SSLInformationAssociationHandler.java:133)
        at io.undertow.servlet.handlers.security.ServletAuthenticationCallHandler.handleRequest(ServletAuthenticationCallHandler.java:57)
        at io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:43)
        at io.undertow.security.handlers.AbstractConfidentialityHandler.handleRequest(AbstractConfidentialityHandler.java:46)
        at io.undertow.servlet.handlers.security.ServletConfidentialityConstraintHandler.handleRequest(ServletConfidentialityConstraintHandler.java:65)
        at io.undertow.security.handlers.AuthenticationMechanismsHandler.handleRequest(AuthenticationMechanismsHandler.java:60)
        at io.undertow.servlet.handlers.security.CachedAuthenticatedSessionHandler.handleRequest(CachedAuthenticatedSessionHandler.java:77)
        at io.undertow.security.handlers.NotificationReceiverHandler.handleRequest(NotificationReceiverHandler.java:50)
        at io.undertow.security.handlers.AbstractSecurityContextAssociationHandler.handleRequest(AbstractSecurityContextAssociationHandler.java:43)
        at io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:43)
        at io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:43)
        at io.undertow.servlet.handlers.ServletInitialHandler.handleFirstRequest(ServletInitialHandler.java:247)
        at io.undertow.servlet.handlers.ServletInitialHandler$2.call(ServletInitialHandler.java:111)
        at io.undertow.servlet.handlers.ServletInitialHandler$2.call(ServletInitialHandler.java:108)
        at io.undertow.servlet.core.ServletRequestContextThreadSetupAction$1.call(ServletRequestContextThreadSetupAction.java:48)
        at io.undertow.servlet.core.ContextClassLoaderSetupAction$1.call(ContextClassLoaderSetupAction.java:43)
        at io.quarkus.undertow.runtime.UndertowDeploymentRecorder$8$1.call(UndertowDeploymentRecorder.java:643)
        at io.undertow.servlet.handlers.ServletInitialHandler.dispatchRequest(ServletInitialHandler.java:227)
        at io.undertow.servlet.handlers.ServletInitialHandler.handleRequest(ServletInitialHandler.java:152)
        at io.undertow.server.handlers.CanonicalPathHandler.handleRequest(CanonicalPathHandler.java:49)
        at io.quarkus.undertow.runtime.UndertowDeploymentRecorder$1.handleRequest(UndertowDeploymentRecorder.java:126)
        at io.undertow.server.Connectors.executeRootHandler(Connectors.java:284)
        at io.undertow.server.DefaultExchangeHandler.handle(DefaultExchangeHandler.java:18)
        at io.quarkus.undertow.runtime.UndertowDeploymentRecorder$4$2.run(UndertowDeploymentRecorder.java:443)
        at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:572)
        at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:317)
        at io.quarkus.vertx.core.runtime.VertxCoreRecorder$14.runWith(VertxCoreRecorder.java:627)
        at org.jboss.threads.EnhancedQueueExecutor$Task.doRunWith(EnhancedQueueExecutor.java:2675)
        at org.jboss.threads.EnhancedQueueExecutor$Task.run(EnhancedQueueExecutor.java:2654)
        at org.jboss.threads.EnhancedQueueExecutor.runThreadBody(EnhancedQueueExecutor.java:1627)
        at org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1594)
        at org.jboss.threads.DelegatingRunnable.run(DelegatingRunnable.java:11)
        at org.jboss.threads.ThreadLocalResettingRunnable.run(ThreadLocalResettingRunnable.java:11)
        at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)
        at java.base/java.lang.Thread.run(Thread.java:1583)
Caused by: java.lang.IllegalArgumentException: params '{"newFlightDate":{"month":11,"year":2024,"day":16},"firstName":"Robert","lastName":"Taylor","newDepartureAirport":"LHR","newArrivalAirport":"FRA","bookingNumber":"105"}' from request do not map onto the parameters needed by 'org.vaadin.marcus.langchain4j.LangChain4jTools_ClientProxy#changeBooking'
        at io.quarkiverse.langchain4j.runtime.tool.QuarkusToolExecutor.invalidMethodParams(QuarkusToolExecutor.java:140)
        at io.quarkiverse.langchain4j.runtime.tool.QuarkusToolExecutor.prepareArguments(QuarkusToolExecutor.java:95)
        at io.quarkiverse.langchain4j.runtime.tool.QuarkusToolExecutor.execute(QuarkusToolExecutor.java:43)
        at io.quarkiverse.langchain4j.runtime.tool.QuarkusToolExecutorFactory$1$1.apply(QuarkusToolExecutorFactory.java:37)
        at io.quarkiverse.langchain4j.runtime.tool.QuarkusToolExecutorFactory$1$1.apply(QuarkusToolExecutorFactory.java:34)
        at io.quarkiverse.langchain4j.runtime.tool.ToolSpanWrapper.wrap(ToolSpanWrapper.java:26)
        at io.quarkiverse.langchain4j.runtime.tool.QuarkusToolExecutorFactory$1$2.apply(QuarkusToolExecutorFactory.java:46)
        at io.quarkiverse.langchain4j.runtime.tool.QuarkusToolExecutorFactory$1$2.apply(QuarkusToolExecutorFactory.java:43)
        at io.quarkiverse.langchain4j.runtime.tool.QuarkusToolExecutorFactory$1.execute(QuarkusToolExecutorFactory.java:52)
        at io.quarkiverse.langchain4j.runtime.aiservice.AiServiceMethodImplementationSupport.doImplement(AiServiceMethodImplementationSupport.java:346)
        at io.quarkiverse.langchain4j.runtime.aiservice.AiServiceMethodImplementationSupport.implement(AiServiceMethodImplementationSupport.java:128)
        at io.quarkiverse.langchain4j.runtime.aiservice.MethodImplementationSupportProducer$1$1.apply(MethodImplementationSupportProducer.java:31)
        at io.quarkiverse.langchain4j.runtime.aiservice.MethodImplementationSupportProducer$1$1.apply(MethodImplementationSupportProducer.java:28)
        at io.quarkiverse.langchain4j.runtime.aiservice.SpanWrapper.wrap(SpanWrapper.java:32)
        at io.quarkiverse.langchain4j.runtime.aiservice.MethodImplementationSupportProducer$1$2.aphodImplementationSupportProducer.java:40)
        at io.quarkiverse.langchain4j.runtime.aiservice.MethodImplementationSupportProducer$1$2.apply(MethodImplementationSupportProducer.java:37)
        at io.quarkiverse.langchain4j.runtime.aiservice.MethodImplementationSupportProducer$1.implement(MethodImplementationSupportProducer.java:46)
        at org.vaadin.marcus.langchain4j.LangChain4jAssistant$$QuarkusImpl.chat(Unknown Source)
        at org.vaadin.marcus.langchain4j.LangChain4jAssistant$$QuarkusImpl_ClientProxy.chat(Unknown Source)
        at org.vaadin.marcus.client.AssistantService.chat(AssistantService.java:19)
        at org.vaadin.marcus.client.AssistantService_ClientProxy.chat(Unknown Source)
        at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103)
        ... 54 more

@geoand
Copy link
Collaborator

geoand commented Nov 6, 2024

"newFlightDate" : {
            "type" : "object",
            "properties" : {
              "month" : {
                "type" : "integer"
              },
              "year" : {
                "type" : "integer"
              },
              "day" : {
                "type" : "integer"
              }
            },
            "required" : [ ]
          }

is exactly what I wanted, thanks.

That's the thing we don't want as LocalDate represents something on its own, we don't want to inspect its content

@edeandrea
Copy link
Collaborator Author

edeandrea commented Nov 6, 2024

FWIW - it works fine using upstream LangChain4j...

image

 024-11-06T08:22:47.043-05:00 DEBUG 88034 --- [.openai.com/...] d.l.service.tool.DefaultToolExecutor     : About to execute ToolExecutionRequest { id = "call_hEbb9SJCO087v6O7dllhqILl", name = "changeBooking", arguments = "{"newFlightDate":{"month":11,"year":2024,"day":16},"firstName":"Robert","lastName":"Taylor","newDepartureAirport":"CDG","newArrivalAirport":"SJC","bookingNumber":"105"}" } for memoryId kfpK0APcYeUkLswrS27mc
2024-11-06T08:22:47.048-05:00 DEBUG 88034 --- [.openai.com/...] d.l.service.tool.DefaultToolExecutor     : Tool execution result: Success
2024-11-06T08:22:47.051-05:00 DEBUG 88034 --- [.openai.com/...] d.a.openai4j.RequestLoggingInterceptor   : Request:
- method: POST
- url: https://api.openai.com/v1/chat/completions
- headers: [Accept: text/event-stream], [Authorization: Bearer sk-su...1f], [User-Agent: langchain4j-openai]
- body: {
  "model" : "gpt-4-turbo",
  "messages" : [ {
    "role" : "system",
    "content" : "You are a customer chat support agent of an airline named \"Funnair\".\nRespond in a friendly, helpful, and joyful manner.\nYou are interacting with customers through an online chat system.\nBefore providing information about a booking or cancelling a booking,\nyou MUST ensure you have the following information from the user:\nbooking number, customer first name, and last name.\nCheck the message history for this information before asking the user.\nBefore changing a booking, you MUST ensure it is permitted by the terms.\nIf there is a charge for the change, you MUST ask the user to consent before proceeding.\nUse the provided functions to fetch booking details, change bookings, and cancel bookings.\nToday is 2024-11-06.\n"
  }, {
    "role" : "user",
    "content" : "Hello I'm Robert Taylor and my booking number is 105. I'd like to change my flight date to November 16, 2024.\n\nAnswer using the following information:\n2. Changing Bookings\n- Changes allowed up to 24 hours before flight.\n- Change via online or contact our support.\n- Change fee: $50 for Economy, $30 for Premium Economy, Free for Business Class.\n\n1. Booking Flights\n- Book via our website or mobile app.\n- Full payment required at booking.\n- Ensure accuracy of personal information (Name, ID, etc.) as corrections may incur a $25 fee."
  }, {
    "role" : "assistant",
    "tool_calls" : [ {
      "id" : "call_O4DkCII4z5ftvWThCK9STUzd",
      "type" : "function",
      "function" : {
        "name" : "getBookingDetails",
        "arguments" : "{\"firstName\":\"Robert\",\"lastName\":\"Taylor\",\"bookingNumber\":\"105\"}"
      }
    } ]
  }, {
    "role" : "tool",
    "tool_call_id" : "call_O4DkCII4z5ftvWThCK9STUzd",
    "content" : "{\n  \"bookingNumber\": \"105\",\n  \"firstName\": \"Robert\",\n  \"lastName\": \"Taylor\",\n  \"date\": \"2024-11-14\",\n  \"bookingStatus\": \"CONFIRMED\",\n  \"from\": \"CDG\",\n  \"to\": \"SJC\",\n  \"bookingClass\": \"PREMIUM_ECONOMY\"\n}"
  }, {
    "role" : "assistant",
    "content" : "Hello Robert! I see that you'd like to change your flight date to November 16, 2024. Your current flight is scheduled for November 14, 2024, from CDG to SJC in Premium Economy class.\n\nSince you're in Premium Economy, there's a change fee of $30. Would you like to proceed with this change? If so, I'll go ahead and update your booking! 😊"
  }, {
    "role" : "user",
    "content" : "yes please\n\nAnswer using the following information:\nThese Terms of Service govern your experience with Funnair. By booking a flight, you agree to these terms.\n\n1. Booking Flights\n- Book via our website or mobile app.\n- Full payment required at booking.\n- Ensure accuracy of personal information (Name, ID, etc.) as corrections may incur a $25 fee."
  }, {
    "role" : "assistant",
    "tool_calls" : [ {
      "id" : "call_hEbb9SJCO087v6O7dllhqILl",
      "type" : "function",
      "function" : {
        "name" : "changeBooking",
        "arguments" : "{\"newFlightDate\":{\"month\":11,\"year\":2024,\"day\":16},\"firstName\":\"Robert\",\"lastName\":\"Taylor\",\"newDepartureAirport\":\"CDG\",\"newArrivalAirport\":\"SJC\",\"bookingNumber\":\"105\"}"
      }
    } ]
  }, {
    "role" : "tool",
    "tool_call_id" : "call_hEbb9SJCO087v6O7dllhqILl",
    "content" : "Success"
  } ],
  "temperature" : 0.0,
  "stream" : true,
  "stream_options" : {
    "include_usage" : true
  },
  "tools" : [ {
    "type" : "function",
    "function" : {
      "name" : "getBookingDetails",
      "description" : "Retrieves information about an existing booking,\nsuch as the flight date, booking status, departure and arrival airports, and booking class.\n",
      "strict" : true,
      "parameters" : {
        "type" : "object",
        "properties" : {
          "firstName" : {
            "type" : "string"
          },
          "lastName" : {
            "type" : "string"
          },
          "bookingNumber" : {
            "type" : "string"
          }
        },
        "required" : [ "firstName", "lastName", "bookingNumber" ],
        "additionalProperties" : false
      }
    }
  }, {
    "type" : "function",
    "function" : {
      "name" : "changeBooking",
      "description" : "Modifies an existing booking.\nThis includes making changes to the flight date, and the departure and arrival airports.\n",
      "strict" : true,
      "parameters" : {
        "type" : "object",
        "properties" : {
          "newFlightDate" : {
            "type" : "object",
            "properties" : {
              "month" : {
                "type" : "integer"
              },
              "year" : {
                "type" : "integer"
              },
              "day" : {
                "type" : "integer"
              }
            },
            "required" : [ "month", "year", "day" ],
            "additionalProperties" : false
          },
          "firstName" : {
            "type" : "string"
          },
          "lastName" : {
            "type" : "string"
          },
          "newDepartureAirport" : {
            "type" : "string",
            "description" : "3-letter code for departure airport"
          },
          "newArrivalAirport" : {
            "type" : "string",
            "description" : "3-letter code for arrival airport"
          },
          "bookingNumber" : {
            "type" : "string"
          }
        },
        "required" : [ "newFlightDate", "firstName", "lastName", "newDepartureAirport", "newArrivalAirport", "bookingNumber" ],
        "additionalProperties" : false
      }
    }
  }, {
    "type" : "function",
    "function" : {
      "name" : "cancelBooking",
      "description" : "",
      "strict" : true,
      "parameters" : {
        "type" : "object",
        "properties" : {
          "firstName" : {
            "type" : "string"
          },
          "lastName" : {
            "type" : "string"
          },
          "bookingNumber" : {
            "type" : "string"
          }
        },
        "required" : [ "firstName", "lastName", "bookingNumber" ],
        "additionalProperties" : false
      }
    }
  } ]
}

@geoand
Copy link
Collaborator

geoand commented Nov 6, 2024

Yeah and that is wrong as well :)

Like I said, it works for this case so we can go ahead and include, but I guarantee you it will fail soon for some other JDK type one of us is going to use as a Tool parameter.

@edeandrea
Copy link
Collaborator Author

Yeah and that is wrong as well :)

Like I said, it works for this case so we can go ahead and include, but I guarantee you it will fail soon for some other JDK type one of us is going to use as a Tool parameter.

Well right now it works in upstream LangChain4j but fails in Quarkus LangChain4j.

@geoand
Copy link
Collaborator

geoand commented Nov 6, 2024

We are talking past each other, so let me try and put it another way.

I'll include this now, but your on the hook for providing a proper solution when the next JDK type fails (because of this erroneous way of representing types) 😉

@edeandrea
Copy link
Collaborator Author

edeandrea commented Nov 6, 2024

We are talking past each other, so let me try and put it another way.

Maybe we are, but I'm still confused :)

I'll include this now, but your on the hook for providing a proper solution when the next JDK type fails (because of this erroneous way of representing types) 😉

As it stands, this PR doesn't solve the problem. It successfully describes the parameters, but something is still missing on the receiving side when the tool wants to be invoked. Quarkus LangChain4j doesn't know how to create the object to pass to the tool.

@geoand
Copy link
Collaborator

geoand commented Nov 6, 2024

As it stands, this PR doesn't solve the problem...

Ah okay, I didn't get that. Wht doesn't it solve it? What issue are you facing?

@edeandrea
Copy link
Collaborator Author

edeandrea commented Nov 6, 2024

This PR as it stands gets Quarkus LangChain4j to describe the parameter, but Quarkus LangChain4j doesn't know how to create the object to pass to the tool, whereas upstream LangChain4j does.

Quarkus barfs on the tool invocation:

Caused by: java.lang.IllegalArgumentException: params '{"newFlightDate":{"month":11,"year":2024,"day":16},"firstName":"Robert","lastName":"Taylor","newDepartureAirport":"SJC","newArrivalAirport":"MUC","bookingNumber":"105"}' from request do not map onto the parameters needed by 'org.vaadin.marcus.langchain4j.LangChain4jTools_ClientProxy#changeBooking'

whereas when I run it with pure LangChain4j (using Spring Boot), the tool invocation works fine. LangChain4j knows how to take {"newFlightDate":{"month":11,"year":2024,"day":16} and create a LocalDate from it.

@geoand
Copy link
Collaborator

geoand commented Nov 6, 2024

I don't know why it would work in upstream LangChain4j as it uses the Jackson to deserialize as well. You'll need to debug it

@edeandrea
Copy link
Collaborator Author

I don't know why it would work in upstream LangChain4j as it uses the Jackson to deserialize as well. You'll need to debug it

Well, it does work. I'll see if I can debug it a bit and see what LangChain4j is doing.

@geoand
Copy link
Collaborator

geoand commented Nov 6, 2024

Yeah, I am not denying it works, just saying that the only way to see why is to debug the working and non working versions

@edeandrea
Copy link
Collaborator Author

The other interesting thing is that with upstream I can use tools with streaming responses - something I can't do in Quarkus :/

@geoand
Copy link
Collaborator

geoand commented Nov 6, 2024

I remember trying tools with streaming response recently and seeing that work (whereas I didn't expect it to)

@edeandrea
Copy link
Collaborator Author

edeandrea commented Nov 6, 2024

I've debugged upstream a bit. All the magic happens in dev.langchain4j.service.tool.DefaultToolExecutor.

Screenshot 2024-11-06 at 8 44 42 AM

Screenshot 2024-11-06 at 8 48 38 AM

Seems for object type arguments LangChain4j delegates to Gson. It converts the argument to Json

Screenshot 2024-11-06 at 8 49 25 AM

and then un-marshals from Json into the object. Gson must know how to do it.

https://github.com/langchain4j/langchain4j/blob/69a0a154548900bade4db3f36ab9c6a28a7314ce/langchain4j/src/main/java/dev/langchain4j/service/tool/DefaultToolExecutor.java#L212-L213

@geoand
Copy link
Collaborator

geoand commented Nov 6, 2024

Interesting. That means that upstream will soon break as well as it is going to move to Jackson.

One more reason to not use this solution :)

@edeandrea
Copy link
Collaborator Author

One more reason to not use this solution :)

Or we can see it as an opportunity to improve what we do :) Let me debug what we're doing a bit.

@edeandrea
Copy link
Collaborator Author

2024-11-06 09:00:33,642 ERROR [io.qua.lan.run.too.QuarkusToolExecutor] (executor-thread-1) com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot deserialize value of type java.time.LocalDatefrom Object value (tokenJsonToken.START_OBJECT) at [Source: REDACTED (StreamReadFeature.INCLUDE_SOURCE_IN_LOCATION disabled); line: 1, column: 18] (through reference chain: org.vaadin.marcus.langchain4j.LangChain4jTools$$QuarkusToolArgumentMapper$changeBooking_c316268f90f72bed55786b9940800e8671df667d["newFlightDate"])

@edeandrea
Copy link
Collaborator Author

Here's the magic that makes it work in upstream....

https://github.com/langchain4j/langchain4j/blob/69a0a154548900bade4db3f36ab9c6a28a7314ce/langchain4j-core/src/main/java/dev/langchain4j/internal/GsonJsonCodec.java#L35-L48

    private static final Gson GSON = new GsonBuilder()
            .setPrettyPrinting()
            .registerTypeAdapter(
                    LocalDate.class,
                    (JsonSerializer<LocalDate>) (localDate, type, context) ->
                            new JsonPrimitive(localDate.format(ISO_LOCAL_DATE))
            )
            .registerTypeAdapter(
                    LocalDate.class,
                    (JsonDeserializer<LocalDate>) (json, type, context) -> {
                        if (json.isJsonObject()) {
                            JsonObject jsonObject = (JsonObject) json;
                            int year = jsonObject.get("year").getAsInt();
                            int month = jsonObject.get("month").getAsInt();
                            int day = jsonObject.get("day").getAsInt();
                            return LocalDate.of(year, month, day);
                        } else {
                            return LocalDate.parse(json.getAsString(), ISO_LOCAL_DATE);
                        }
                    }
            )

@edeandrea
Copy link
Collaborator Author

I can just change my tool to be

@Tool("""
            Modifies an existing booking.
            This includes making changes to the flight date, and the departure and arrival airports.
            """)
    public void changeBooking(
        String bookingNumber,
        String firstName,
        String lastName,
        @P("month of the new flight date") int newFlightDateMonth,
        @P("day of the month of the new flight date") int newFlightDateDayOfMonth,
        @P("year of the new flight date") int newFlightDateYear,
        @P("3-letter code for departure airport") String newDepartureAirport,
        @P("3-letter code for arrival airport") String newArrivalAirport
    ) {
        service.changeBooking(bookingNumber, firstName, lastName, LocalDate.of(newFlightDateYear, newFlightDateMonth, newFlightDateDayOfMonth), newDepartureAirport, newArrivalAirport);
    }

and now everything works :) This is probably a better solution anyways?

@geoand
Copy link
Collaborator

geoand commented Nov 6, 2024

It's a solution, I wouldn't say it's better :)

@geoand
Copy link
Collaborator

geoand commented Nov 7, 2024

There is no way around having special handling for some JDK types, because these types actually represent things

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Trying to use token-window memory type with easy rag results in exception
2 participants