Skip to content

Function Calling with Kotlin Functions #922

Closed as not planned
Closed as not planned
@jochenchrist

Description

@jochenchrist

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?,
  )
}

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions