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

Function Calling with Kotlin Functions #922

Closed
jochenchrist opened this issue Jun 22, 2024 · 3 comments
Closed

Function Calling with Kotlin Functions #922

jochenchrist opened this issue Jun 22, 2024 · 3 comments
Assignees
Labels
bug Something isn't working duplicate This issue or pull request already exists function calling kotlin

Comments

@jochenchrist
Copy link

Bug description
When using a Kotlin function, the input type is not inferred.
This causes an invalid_request_error error:

com.azure.core.exception.HttpResponseException: Status code 400, "{
  "error": {
    "message": "Invalid schema for function 'Weather': schema must be a JSON Schema of 'type: \"object\"', got 'type: \"None\"'.",
    "type": "invalid_request_error",
    "param": "tools[0].function.parameters",
    "code": "invalid_function_parameters"
  }
}"

Workaround is to specify the input type explictly withInputType (and use a custom object mapper)

Environment
Latest Snapshot.

Steps to reproduce
See example below

Expected behavior
Spring AI should also work with Kotlin or update the documentation.

Minimal Complete Reproducible example


@Component
class MyAi(
  val functionCallbacks: List<FunctionCallback>,
) : ApplicationRunner {

  override fun run(args: ApplicationArguments) {

    val openAIClient = OpenAIClientBuilder()
      .credential(AzureKeyCredential(System.getenv("AZURE_OPENAI_API_KEY")))
      .endpoint(System.getenv("AZURE_OPENAI_ENDPOINT"))
      .buildClient()

    val openAIChatOptions = AzureOpenAiChatOptions.builder()
      .withDeploymentName("policy-ai-gpt-4o")
      .withTemperature(0.0f)
      .withFunctionCallbacks(functionCallbacks)
      .build()

    val chatModel = AzureOpenAiChatModel(openAIClient, openAIChatOptions)

    val response = chatModel.call(
      Prompt("Is it rainy in Paris?", AzureOpenAiChatOptions.builder().withFunction("Weather").build())
    )

    println(response.result.output)

  }
}

@Configuration
class AiFunctionCallbacks(val objectMapper: ObjectMapper) {

  @Bean
  fun weatherFunctionCallback(): FunctionCallback {
    return FunctionCallbackWrapper.builder { request: WeatherRequest ->
      WeatherResponse(rainy = false)
    }
      .withName("Weather")
      .withDescription("Get weather information for a city")
      .withObjectMapper(objectMapper)
//      .withInputType(WeatherRequest::class.java) // this is required, but should not
      .build()
  }

  data class WeatherRequest(
    val city: String?,
  )

  data class WeatherResponse(
    val rainy: Boolean?,
  )
}

@KAMO030
Copy link
Contributor

KAMO030 commented Jun 24, 2024

I reproduced the problem. The cause is that when inputType is not specified, the resolveInputType function uses reflection to get the generic type. However, when using a function with a trailing lambda, it is not possible to obtain the generic type declaration.

public FunctionCallbackWrapper<I, O> build() {
// ...
            if (this.inputType == null) {
                this.inputType = FunctionCallbackWrapper.resolveInputType(this.function);
            }
// ...
}

Currently, the issue can be resolved through the aforementioned method, or by passing in a specific implementation class. Alternatively, it could be addressed by adding support for reified generic function extensions in the library:

inline fun<reified I,O> functionCallbackWrapperBuild(
	noinline function:(I)->O
): FunctionCallbackWrapper.Builder<I,O> =
	FunctionCallbackWrapper.builder(function).withInputType(I::class.java)

or perhaps by import other reflection packages to obtain the Metadata info?

@csterwa csterwa added bug Something isn't working function calling kotlin labels Sep 4, 2024
@tzolov
Copy link
Contributor

tzolov commented Oct 4, 2024

@jochenchrist , @KAMO030 not Kotlin user (yet) but can imagine that the generic inference can get messed up.
@KAMO030 we can not plug Kotlin code in the project. Is there a pure java way to extend the reflection generic libs?

@markpollack markpollack added this to the 1.0.0-M4 milestone Oct 25, 2024
@sdeleuze sdeleuze removed this from the 1.0.0-M4 milestone Nov 4, 2024
@sdeleuze
Copy link
Contributor

sdeleuze commented Nov 4, 2024

The issue is superseded by #1666. See also #1667.

@sdeleuze sdeleuze closed this as not planned Won't fix, can't repro, duplicate, stale Nov 4, 2024
@sdeleuze sdeleuze added the duplicate This issue or pull request already exists label Nov 4, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working duplicate This issue or pull request already exists function calling kotlin
Projects
None yet
Development

No branches or pull requests

6 participants