Skip to content

Commit b53a06a

Browse files
authored
fix(agents): KG-704 AIAgentError populated incorrectly for reflective… (#1548)
… tool call failures When a tool defined via reflection throws an exception, the AIAgentError is incorrectly populated, becase the exception thrown from the reflective call is wrapped in an InvocationTargetException with null message: ``` Tool with name 'exceptionTool' failed to execute with arguments: VarArgs(args={}) java.lang.reflect.InvocationTargetException: null at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:119) at java.base/java.lang.reflect.Method.invoke(Method.java:565) at kotlin.reflect.jvm.internal.calls.CallerImpl$Method.callMethod(CallerImpl.kt:97) at kotlin.reflect.jvm.internal.calls.CallerImpl$Method$Static.call(CallerImpl.kt:106) at kotlin.reflect.jvm.internal.KCallableImpl.callDefaultMethod$kotlin_reflection(KCallableImpl.kt:159) at kotlin.reflect.jvm.internal.KCallableImpl.callBy(KCallableImpl.kt:112) at kotlin.jvm.internal.CallableReference.callBy(CallableReference.java:166) at kotlin.reflect.full.KCallables.callSuspendBy(KCallables.kt:71) at ai.koog.agents.core.tools.reflect.ToolFromCallable.execute(ToolFromCallable.kt:110) ... Caused by: java.lang.RuntimeException: This is a test exception ``` The AIAgentError is then populated with the default message "Unknown error": `AIAgentError(message=Unknown error, stackTrace=java.lang.reflect.InvocationTargetException ...` This results in the agent receiving the tool call result without the exception message. ``` { content: "Tool with name 'exceptionTool' failed to execute due to the error: null!" role: "tool" } ``` closes KG-704 Co-authored-by: Michal Borowiecki <6325352+mihbor@users.noreply.github.com>
1 parent a8483e0 commit b53a06a

2 files changed

Lines changed: 19 additions & 1 deletion

File tree

agents/agents-tools/src/jvmCommonMain/kotlin/ai/koog/agents/core/tools/reflect/ToolFromCallable.kt

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import ai.koog.serialization.JSONObject
88
import ai.koog.serialization.JSONSerializer
99
import ai.koog.serialization.KotlinTypeToken
1010
import ai.koog.serialization.typeToken
11+
import java.lang.reflect.InvocationTargetException
1112
import kotlin.reflect.KCallable
1213
import kotlin.reflect.KParameter
1314
import kotlin.reflect.full.callSuspendBy
@@ -91,7 +92,11 @@ public class ToolFromCallable<TResult>(
9192
putAll(args.parameters)
9293
}
9394

94-
return callable.callSuspendBy(argsMap)
95+
return try {
96+
callable.callSuspendBy(argsMap)
97+
} catch (e: InvocationTargetException) {
98+
throw e.cause ?: e
99+
}
95100
}
96101
}
97102

agents/agents-tools/src/jvmTest/kotlin/ai/koog/agents/core/tools/reflect/ToolsFromCallableTest.kt

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import kotlinx.serialization.json.JsonPrimitive
1515
import kotlinx.serialization.json.buildJsonObject
1616
import kotlinx.serialization.json.put
1717
import kotlinx.serialization.json.putJsonObject
18+
import org.junit.jupiter.api.assertThrows
1819
import org.junit.jupiter.params.ParameterizedTest
1920
import org.junit.jupiter.params.provider.Arguments
2021
import org.junit.jupiter.params.provider.MethodSource
@@ -30,6 +31,12 @@ suspend fun globalTool(
3031
return "Global tool called: $count"
3132
}
3233

34+
@Tool
35+
@LLMDescription("Global tool that always throws a RuntimeException")
36+
suspend fun globalToolThatThrows(): String {
37+
throw RuntimeException("test exception")
38+
}
39+
3340
object ObjectArgumentTool {
3441
@Serializable
3542
data class TestArg(
@@ -517,6 +524,12 @@ class ToolsFromCallableTest {
517524
)
518525
}
519526

527+
@Test
528+
fun testCorrectExceptionThrown(): Unit = runBlocking {
529+
val exception = assertThrows<RuntimeException> { ::globalToolThatThrows.asTool().execute(ToolFromCallable.Args(emptyMap())) }
530+
assertEquals("test exception", exception.message)
531+
}
532+
520533
@ParameterizedTest
521534
@MethodSource("descriptionTestVariants")
522535
fun testOnClasses(tools: List<ToolFromCallable<*>>, expectedDescription: String) {

0 commit comments

Comments
 (0)