Description
Description
When utilizing a model that can call multiple function calls in one pass (i.e. gemini-1.5-flash-002), there is no way to provide multiple responses without a failure occurring.
In my example code, when asking about one exchange rate, I return one FunctionResponse and it succeeds. However, when asking about two exchange rates, and two FunctionCalls appear in one turn, I can't send a response without getting an error. I tried sending them as [FunctionResponse1, FunctionResponse2] and also separately, as two separate responses. Each time I get the following error:
11.4.0 - [FirebaseVertexAI][I-VTX002004] Response payload: {
"error": {
"code": 400,
"message": "Please ensure that function response turn comes immediately after a function call turn. And the number of function response parts should be equal to number of function call parts of the function call turn.",
"status": "INVALID_ARGUMENT"
}
}
Reproducing the issue
import Foundation
import FirebaseVertexAI
// Creating a TestAgent() will execute the testTwoFunctionCallsStreaming function.
class TestAgent {
var systemInstructions = """
Users will ask you information about exchange rates. Use your tools to answer them."
"""
private var modelName = "gemini-1.5-flash-002" // gemini-1.5-pro-exp-0801", //gemini-1.5-pro", //gemini-1.5-flash",
private var chat : Chat?
// Initialize the Vertex AI service
let vertex = VertexAI.vertexAI()
let config = GenerationConfig(
temperature: 0,
topP: 0.95,
topK: 40,
maxOutputTokens: 8192,
responseMIMEType: "text/plain"
)
init() {
Task {
await testTwoFunctionCallsStreaming()
}
}
let getExchangeRate = FunctionDeclaration(
name: "getExchangeRate",
description: "Get the exchange rate for currencies between countries",
parameters: [
"currencyFrom": .string(
description: "The currency to convert from."
),
"currencyTo": .string(
description: "The currency to convert to."
),
]
)
func makeAPIRequest(currencyFrom: String,
currencyTo: String) -> JSONObject {
// This hypothetical API returns a JSON such as:
// {"base":"USD","rates":{"SEK": 10.99}}
var number = 10.99
if currencyTo != "SEK" {
number = 5.0
}
return [
"base": .string(currencyFrom),
"rates": .object([currencyTo: .number(number)]),
]
}
func testTwoFunctionCallsStreaming() async {
print("testTwoFunctionCallsStreaming")
let model = vertex.generativeModel(
modelName: modelName,
generationConfig: config,
tools: [Tool.functionDeclarations( [getExchangeRate])],
systemInstruction: ModelContent(role: "system", parts: self.systemInstructions)
)
// create some fake history.
let mc1 = ModelContent(role: "user", parts: "Hello, how are you?")
let mc2 = ModelContent(role: "model", parts: "I'm great! How are you?")
let mc3 = ModelContent(role: "user", parts: "I'm good. Thanks for asking.")
let mc4 = ModelContent(role: "model", parts: "My pleasure. What can I do for you today?")
let history = [mc1, mc2, mc3, mc4]
chat = model.startChat(history: history)
var response = try! chat?.sendMessageStream("Hello")
await processResult(response: response!)
response = try! chat?.sendMessageStream("What can you do?")
await processResult(response: response!)
response = try! chat?.sendMessageStream("How much is 50 US dollars worth in Swedish krona?")
await processResult(response: response!)
//Get one response to match the functioncall
var apiResponse = makeAPIRequest(currencyFrom: "USD", currencyTo: "SEK")
// Send the API response back to the model so it can generate a text response that can be
// displayed to the user.
response = try! chat?.sendMessageStream([ModelContent(
role: "function",
parts: [FunctionResponsePart(
name: "getExchangeRate",
response: apiResponse
)]
)])
await processResult(response: response!)
// Now ask it a question that will cause two function calls.
response = try! chat?.sendMessageStream("How much is 50 US dollars worth in Swedish krona and the Euro?")
await processResult(response: response!)
//there should be two function calls.
apiResponse = makeAPIRequest(currencyFrom: "USD", currencyTo: "SEK")
let functionResponse1 = ModelContent(
role: "function",
parts: [FunctionResponsePart(
name: "getExchangeRate",
response: apiResponse
)]
)
apiResponse = makeAPIRequest(currencyFrom: "USD", currencyTo: "EUR")
let functionResponse2 = ModelContent(
role: "function",
parts: [FunctionResponsePart(
name: "getExchangeRate",
response: apiResponse
)]
)
//METHOD 1
// THIS FAILS
let result = try! chat?.sendMessageStream([functionResponse1, functionResponse2])
await processResult(response: result!)
// END METHOD 1
//METHOD 2
// THIS FAILS TOO
/*
let result = try! chat?.sendMessageStream([functionResponse1])
await processResult(response: result!)
let result2 = try! chat?.sendMessageStream([functionResponse2])
await processResult(response: result2!)
*/
// END METHOD 2
print(chat?.history.debugDescription ?? "No History")
print("END testTwoFunctionCalls")
}
func processResult(response : AsyncThrowingStream<GenerateContentResponse, Error>) async {
do {
for try await chunk in response {
processResponseContent(content: chunk)
}
} catch {
}
}
func processResponseContent(content: GenerateContentResponse) {
guard let candidate = content.candidates.first else {
fatalError("No candidate.")
}
for part in candidate.content.parts {
switch part {
case let textPart as TextPart:
print(textPart.text)
case let functionCallPart as FunctionCallPart:
print(functionCallPart)
default:
fatalError("Unsupported response part: \(part)")
}
}
}
}
private extension [FunctionResponsePart] {
func modelContent() -> [ModelContent] {
return self.map { ModelContent(role: "function", parts: [$0]) }
}
}
Firebase SDK Version
11.4.0
Xcode Version
16.1
Installation Method
Swift Package Manager
Firebase Product(s)
VertexAI
Targeted Platforms
iOS
Relevant Log Output
11.4.0 - [FirebaseVertexAI][I-VTX002003] The server responded with an error: <NSHTTPURLResponse: 0x3017c8240> { URL: https://firebasevertexai.googleapis.com/v1beta/projects/expense-tracking-59dac/locations/us-central1/publishers/google/models/gemini-1.5-flash-002:streamGenerateContent?alt=sse } { Status Code: 400, Headers {
"Alt-Svc" = (
"h3=\":443\"; ma=2592000,h3-29=\":443\"; ma=2592000"
);
"Content-Length" = (
295
);
"Content-Type" = (
"text/event-stream"
);
Date = (
"Mon, 04 Nov 2024 17:06:07 GMT"
);
Server = (
ESF
);
Vary = (
Origin,
"X-Origin",
Referer
);
"x-content-type-options" = (
nosniff
);
"x-frame-options" = (
SAMEORIGIN
);
"x-xss-protection" = (
0
);
} }
11.4.0 - [FirebaseVertexAI][I-VTX002004] Response payload: {
"error": {
"code": 400,
"message": "Please ensure that function response turn comes immediately after a function call turn. And the number of function response parts should be equal to number of function call parts of the function call turn.",
"status": "INVALID_ARGUMENT"
}
}
If using Swift Package Manager, the project's Package.resolved
Expand Package.resolved
snippet
Replace this line with the contents of your Package.resolved.
If using CocoaPods, the project's Podfile.lock
Expand Podfile.lock
snippet
Replace this line with the contents of your Podfile.lock!