useChat sending message to Google ADK API
Description
I found the return body of google adk is slightly different than other AI providers' endpoints. The content contains an array of parts and the role is inside the content
[
{
"content": {
"parts": [
{
"text": "Okay! 😊\n\nEaster is on April 25th or 26th. Christmas is on December 25th."
}
],
"role": "model"
},
"partial": false,
"invocation_id": "e-73a5d2be-d454-4696-bf7a-86b831f0f7e1",
"author": "ollama_agent",
"actions": {
"state_delta": {},
"artifact_delta": {},
"requested_auth_configs": {}
},
"id": "PgjeZkgs",
"timestamp": 1745205698.571864
}
]
Besides, I have some troubles to put the input to the useChat as it requires a body of
body: {
app_name: "ollama_agent",
user_id: "u_123",
session_id: "s_123",
new_message: {
role: "user",
parts: [{
text: input
}]
},
"streaming": false
},
I am using react native. seems the handleInputChange handleSubmit do not put the input text to the body
<TextInput
style={{ backgroundColor: 'white', padding: 8 }}
placeholder="Say something..."
value={input}
onChange={e =>
handleInputChange({
...e,
target: {
...e.target,
value: e.nativeEvent.text,
},
} as unknown as React.ChangeEvent<HTMLInputElement>)
}
onSubmitEditing={e => {
//console.log('Submit', e);
handleSubmit(e);
e.preventDefault();
}}
autoFocus={true}
/>
it would be easier if you shared more of the code you are using so it is repoducible
I have update the input provided to be clear. And here is my code. Ctrl+F MyNote as my current issue
import { useEffect, useRef } from "react";
import { anthropic } from "@ai-sdk/anthropic";
import { streamText } from "ai";
import { Send, X } from "lucide-react-native";
import { View, TextInput, ScrollView, Text, SafeAreaView } from 'react-native';
import { generateAPIUrl } from "../utils/ai"
import { useChat } from "@ai-sdk/react"
import { fetch as expoFetch } from 'expo/fetch';
import type { UIMessage } from "ai";
import { z } from 'zod';
function Messages({ messages }: { messages: Array<UIMessage> }) {
const messagesContainerRef = useRef<HTMLDivElement>(null);
useEffect(() => {
if (messagesContainerRef.current) {
messagesContainerRef.current.scrollTop =
messagesContainerRef.current.scrollHeight;
}
}, [messages]);
if (!messages.length) {
return (
<Text>"Ask me anything! I'm here to help."</Text>
);
}
console.log('MSG', messages);
return (
<ScrollView style={{ flex: 1 }}>
{messages.map(m => {
console.log('User said', m.content);
try {
if (m.role === "user") {
return(<View key={m.id} style={{ marginVertical: 8 }}>
<View>
<Text style={{ fontWeight: 700 }}>{m.role}</Text>
{m.toolInvocations ? (
<Text>{JSON.stringify(m.toolInvocations, null, 2)}</Text>
) : (
<Text>{m.content}</Text>
)}
</View>
</View>
)
}
} catch (e) {
//console.log('Error', e)
//console.log('Model Replied:', messages)
{m.parts.map(part => {
return(<View key={m.id} style={{ marginVertical: 8 }}>
<View>
<Text style={{ fontWeight: 700 }}>{m.role}</Text>
<Text>{part.text}</Text>
{
/*
m.toolInvocations ? (
<Text>{JSON.stringify(m.toolInvocations, null, 2)}</Text>
) : (
<Text>{m.content}</Text>
)
*/
}
</View>
</View>
)})}
}
})}
</ScrollView>
)
}
const SYSTEM_PROMPT = `You are an AI for a music store.
There are products available for purchase. You can recommend a product to the user.
You can get a list of products by using the getProducts tool.
You also have access to a fulfillment server that can be used to purchase products.
You can get a list of products by using the getInventory tool.
You can purchase a product by using the purchase tool.
After purchasing a product tell the customer they've made a great choice and their order will be processed soon and they will be playing their new guitar in no time.
`;
export default function AIAssistant() {
const { messages, error, handleInputChange, input, handleSubmit, data } = useChat({
initialMessages: [],
fetch: expoFetch as unknown as typeof globalThis.fetch,
headers: {
'Content-Type': 'application/json',
Connection: 'keep-alive',
'Accept-Encoding': 'gzip, deflate, br',
Accept: '*/*' },
body: { // MyNote: In the doc, it is called the ADDITIONAL body to be sent
app_name: "ollama_agent",
user_id: "u_123",
session_id: "s_123",
new_message: {
role: "user",
parts: [{
text: input //<----it shows error here as I put variable"input" of useChat. Even I hard code it here, the value I have put here cannot be reflected on the server side log. So if I think Google ADK can recognise the body, but the useChat is fail to put the value of TextInput below. Now My workaround is using simply fetch POST without useChat
}]
},
"streaming": false
},
api: "https://adkagentapi-dev.local/run",
onToolCall: (call) => {
if (call.toolCall.toolName === "recommendGuitar") {
return "Handled by the UI";
}
},
onResponse: (response) => {
console.log('Response', response);
//console.log('data', data)
},
onFinish: (response) => {
console.log('Finnish', response);
}, //console.log('data', data)
onError: error => {
console.error(error, 'ERROR for message: '+ input)
console.log('data', data);
},
});
return (
<>
<SafeAreaView style={{ height: '70vh' }}>
<View
style={{
height: '60%',
display: 'flex',
flexDirection: 'column',
paddingHorizontal: 8,
}}
>
<Messages messages={messages} />
<View style={{ marginTop: 8 }}>
<TextInput
style={{ backgroundColor: 'white', padding: 8 }}
placeholder="Say something..."
value={input}
onChange={e =>
handleInputChange({
...e,
target: {
...e.target,
value: e.nativeEvent.text,
},
} as unknown as React.ChangeEvent<HTMLInputElement>)
}
onSubmitEditing={e => {
//console.log('Submit', e);
handleSubmit(e);
e.preventDefault();
}}
autoFocus={true}
/>
</View>
<Send />
</View>
</SafeAreaView>
</>
);
}
I'm also facing this issue. The key point is how to pass incompatible AI protocols to useChat.
in ai sdk 5 you can set up custom chat transports and modify the api calls when messages are submitted.