Skip to content

Commit 096c0dd

Browse files
authored
fix(agents): consider tool's customName in tools from callable (#1883)
<!-- PR title should follow Conventional Commits format: type(scope): description For breaking changes, append ! after type/scope: type(scope)!: description Examples: feat(agents): add streaming response node fix(prompt): handle null responses in PromptExecutor refactor(agents)!: remove deprecated methods from Tool See CONTRIBUTING.md for more details --> Fix the big "@tool(customName = ...) is ignored when using ToolSet.asTools() / ToolRegistryBuilder.tools(ToolSet)" and add a test. <!-- Include references to related issues below, e.g., closes #1, closes KG-1. Otherwise, delete it. --> closes #1881
1 parent 4992fe7 commit 096c0dd

2 files changed

Lines changed: 39 additions & 1 deletion

File tree

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,8 @@ public fun <T : Any> KClass<out T>.asTools(
6565
return this.functions.filter { m ->
6666
m.getPreferredToolAnnotation() != null
6767
}.map {
68-
it.asTool(thisRef = thisRef)
68+
val customName = it.getPreferredToolAnnotation()?.customName?.ifEmpty { null }
69+
it.asTool(thisRef = thisRef, name = customName)
6970
}.apply {
7071
require(isNotEmpty()) { "No tools found in ${this@asTools}" }
7172
}

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

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package ai.koog.agents.core.tools.reflect
22

3+
import ai.koog.agents.core.tools.ToolRegistry
34
import ai.koog.agents.core.tools.annotations.InternalAgentToolsApi
45
import ai.koog.agents.core.tools.annotations.LLMDescription
56
import ai.koog.agents.core.tools.annotations.Tool
@@ -103,6 +104,22 @@ class ComplexToolsImpl : ToolSet {
103104
): String = "${person.name} is ${person.age} years old"
104105
}
105106

107+
class SearchToolA : ToolSet {
108+
@Tool(customName = "tool_a_search")
109+
@LLMDescription("Searches source A")
110+
fun execute(
111+
@LLMDescription("Query") query: String
112+
): String = "result A: $query"
113+
}
114+
115+
class SearchToolB : ToolSet {
116+
@Tool(customName = "tool_b_search")
117+
@LLMDescription("Searches source B")
118+
fun execute(
119+
@LLMDescription("Query") query: String
120+
): String = "result B: $query"
121+
}
122+
106123
@OptIn(InternalAgentToolsApi::class)
107124
class ToolSetAsToolsTest {
108125
private val serializer = KotlinxSerializer()
@@ -241,4 +258,24 @@ class ToolSetAsToolsTest {
241258
"Format tool should return formatted string"
242259
)
243260
}
261+
262+
@Test
263+
@OptIn(InternalAgentToolsApi::class)
264+
fun testCustomToolNamesArePreservedForToolSets() {
265+
val toolA = SearchToolA()
266+
val toolB = SearchToolB()
267+
268+
val toolANames = toolA.asTools().map { it.descriptor.name }
269+
val toolBNames = toolB.asTools().map { it.descriptor.name }
270+
271+
assertEquals(listOf("tool_a_search"), toolANames)
272+
assertEquals(listOf("tool_b_search"), toolBNames)
273+
274+
val registry = ToolRegistry {
275+
tools(toolA)
276+
tools(toolB)
277+
}
278+
279+
assertEquals(listOf("tool_a_search", "tool_b_search"), registry.tools.map { it.name }.sorted())
280+
}
244281
}

0 commit comments

Comments
 (0)