Refactor built-in AppFunctions to internal tools and make them actually working - #46
Refactor built-in AppFunctions to internal tools and make them actually working#46ksemenova wants to merge 4 commits into
Conversation
…ent conversion fallback - Moved `geocodeAddress`, `getCurrentLocation`, and `generateImage` to `AgentInternalTools`. - Updated `AgentOrchestrator` to intercept and execute internal tools in-process, returning `Result<String>`. - Removed `BuiltInAppFunctionService` and cleaned up `AndroidManifest.xml`. - Resolved argument mapping `ClassCastException` by adding a fallback to wrap string inputs (e.g., URIs) into custom serializable object/array parameters in `ConvertInputToAppFunctionDataUseCase`. - Refactored argument conversion to use a unified `toAppFunctionData` helper.
| } | ||
|
|
||
| private fun convertObject( | ||
| private fun toAppFunctionData( |
There was a problem hiding this comment.
I'm not quite sure why this is needed. The Uri handling is done separately already, what was the issue this is trying to resolve?
There was a problem hiding this comment.
and also why can't those internal tools to be implemented as app functions? :)
I thought they were "actually working" before the change...
There was a problem hiding this comment.
To clarify:
- We expect 3P apps to expose AppFunctions to the agent (not vice versa), and exposing sensitive data like location publicly is not recommended.
- The previous AFs were not working as shown on the screenshot above. Possibly because the agent was not able to discover and register them at runtime. I moved these methods out of the AppFunction framework into Agent's internal tools. We now load these 3 tools statically from code (agentInternalTools.getInternalToolsMetadata()) and append them directly to the LLM tool list, bypassing the system's AppFunction indexing entirely. The model is now guaranteed to see these tools.
- When I moved AFs to internal tools I was able to call them succesfully. Then, to test multiple tools calls in one prompt, I asked to generate an image and send it to my my contact in ChatApp (that has send_message AF) and I've got this error: Error: Failed to convert arguments for com.example.chatapp.appfunctions.AppFunctions#send java.lang.ClassCastException: java.lang.String cannot be cast to java.util.Map. This fix resolves a ClassCastException that occurs when the LLM passes a raw String URI to a tool parameter expecting a custom wrapper object (like ChatApp's Attachment), instead of constructing the full nested object structure.
There was a problem hiding this comment.
Error: Failed to convert arguments for com.example.chatapp.appfunctions.AppFunctions#send java.lang.ClassCastException: java.lang.String cannot be cast to java.util.Map. This fix resolves a ClassCastException that occurs when the LLM passes a raw String URI to a tool parameter expecting a custom wrapper object (like ChatApp's Attachment), instead of constructing the full nested object structure.
I see. Thanks for the context.
However, I think the fix should be doing
if (dataType.qualifiedName == "android.net.Uri" && value is String) {
val uriPropertyName = dataType.properties.keys.firstOrNull() ?: "uri"
val uriData =
AppFunctionData.Builder(dataType, components)
.setString(uriPropertyName, value)
.build()
builder.setAppFunctionData(name, uriData)
}
in setArrayValue when the type is AppFunctionObjectTypeMetadata or AppFunctionReferenceTypeMetadata.
Because the issue is that we didn't handle that scenario when the value is List.
Summary
This PR refactors 3 built-in AppFunctions to run as internal agent tools, cleans up unused service declarations, and fixes an argument conversion failure (
ClassCastException) during integration with external apps.Before
After
Key Changes
geocodeAddress,getCurrentLocation, andgenerateImagefromBaseBuiltInAppFunctionServiceto a newAgentInternalToolsclass.AgentOrchestratorto execute in-process and simplified the flow to return KotlinResult<String>.BaseBuiltInAppFunctionService.ktand removed its service declaration fromAndroidManifest.xml.ClassCastExceptionwhere the LLM passes a rawStringURI to a custom object or array parameter (e.g., ChatApp'sAttachment).ConvertInputToAppFunctionDataUseCase.toAppFunctionData().