Skip to content

Commit d8088d6

Browse files
committed
Added cache control support
1 parent 85f2581 commit d8088d6

12 files changed

Lines changed: 654 additions & 95 deletions

File tree

agents/agents-core/src/commonMain/kotlin/ai/koog/agents/core/agent/context/AIAgentLLMContext.kt

Lines changed: 0 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -17,27 +17,6 @@ import ai.koog.prompt.processor.ResponseProcessor
1717
import kotlin.time.Clock
1818
import kotlin.jvm.JvmName
1919

20-
/**
21-
* Annotation for marking APIs as detached prompt executors within the `AIAgentLLMContext`.
22-
*
23-
* Using APIs annotated with this requires opting in, as calls to `PromptExecutor` will be disconnected
24-
* from the agent logic. This means these calls will not affect the agent's state or adhere to the
25-
* `ToolsConversionStrategy`.
26-
*
27-
* This API should be used with caution, as it provides functionality that operates outside the
28-
* standard agent lifecycle and processing logic.
29-
*/
30-
@MustBeDocumented
31-
@Retention(AnnotationRetention.BINARY)
32-
@RequiresOptIn(
33-
level = RequiresOptIn.Level.ERROR,
34-
message = "Calls to PromptExecutor used from `AIAgentLLMContext` will not be connected to the agent logic, " +
35-
"and will not impact the agent's state. " +
36-
"Other than that, `ToolsConversionStrategy` will not be applied. " +
37-
"Please be cautious when using this API."
38-
)
39-
public annotation class DetachedPromptExecutorAPI
40-
4120
/**
4221
* Represents the context for an AI agent LLM, managing tools, prompt handling, and interaction with the
4322
* environment and execution layers. It provides mechanisms for concurrent read and write operations

agents/agents-tools/src/commonMain/kotlin/ai/koog/agents/core/tools/Tool.kt

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package ai.koog.agents.core.tools
22

33
import ai.koog.agents.core.tools.annotations.InternalAgentToolsApi
44
import ai.koog.agents.core.tools.serialization.ToolJson
5+
import ai.koog.prompt.params.LLMParams
56
import kotlinx.serialization.KSerializer
67
import kotlinx.serialization.Serializable
78
import kotlinx.serialization.json.Json
@@ -262,3 +263,53 @@ public abstract class Tool<TArgs, TResult>(
262263
@Serializable
263264
public data object EmptyArgs : Args
264265
}
266+
267+
/**
268+
* A wrapper tool that delegates all operations to the original tool but uses a modified descriptor
269+
* with cache control settings.
270+
*
271+
* This class enables adding cache control to existing tools without modifying their implementation.
272+
* When cache control is set on a tool, LLM providers like Anthropic will cache tool definitions
273+
* up to and including this tool, reducing latency and cost for repeated requests.
274+
*
275+
* @param TArgs The type of arguments the tool accepts.
276+
* @param TResult The type of result the tool returns.
277+
* @param delegate The original tool to wrap.
278+
* @param cacheControl The cache control setting to apply to this tool's descriptor.
279+
*/
280+
public class CachedTool<TArgs, TResult>(
281+
private val delegate: Tool<TArgs, TResult>,
282+
cacheControl: LLMParams.CacheControl,
283+
) : Tool<TArgs, TResult>(
284+
argsSerializer = delegate.argsSerializer,
285+
resultSerializer = delegate.resultSerializer,
286+
descriptor = delegate.descriptor.copy(cacheControl = cacheControl),
287+
) {
288+
override suspend fun execute(args: TArgs): TResult = delegate.execute(args)
289+
290+
override fun encodeResultToString(result: TResult): String = delegate.encodeResultToString(result)
291+
}
292+
293+
/**
294+
* Creates a new tool with the specified cache control setting.
295+
*
296+
* This extension function wraps the original tool in a [CachedTool] that delegates all operations
297+
* to the original but uses a modified descriptor with cache control.
298+
*
299+
* When cache control is set on a tool, LLM providers like Anthropic will cache tool definitions
300+
* up to and including this tool, reducing latency and cost for repeated requests.
301+
*
302+
* Usage example:
303+
* ```kotlin
304+
* val registry = ToolRegistry {
305+
* tool(myTool)
306+
* tool(expensiveTool.withCacheControl(LLMParams.CacheControl.Ephemeral))
307+
* }
308+
* ```
309+
*
310+
* @param cacheControl The cache control setting to apply to this tool.
311+
* @return A new tool with the cache control setting applied.
312+
*/
313+
public fun <TArgs, TResult> Tool<TArgs, TResult>.withCacheControl(
314+
cacheControl: LLMParams.CacheControl
315+
): Tool<TArgs, TResult> = CachedTool(this, cacheControl)

agents/agents-tools/src/commonMain/kotlin/ai/koog/agents/core/tools/ToolDescriptor.kt

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
package ai.koog.agents.core.tools
22

3+
import ai.koog.prompt.params.LLMParams
4+
35
/**
46
* Represents a descriptor for a tool that contains information about the tool's name, description, required parameters,
57
* and optional parameters.
@@ -10,12 +12,18 @@ package ai.koog.agents.core.tools
1012
* @property description The description of the tool.
1113
* @property requiredParameters A list of ToolParameterDescriptor representing the required parameters for the tool.
1214
* @property optionalParameters A list of ToolParameterDescriptor representing the optional parameters for the tool.
15+
* @property cacheControl Optional cache control setting for this tool definition.
16+
* When set, signals to LLM providers (like Anthropic) that tool definitions up to and including
17+
* this tool should be cached. This is useful for reducing latency and cost when the same tools
18+
* are used across multiple requests. Cache control is applied per-tool, allowing fine-grained
19+
* control over which tools act as cache breakpoints.
1320
*/
1421
public open class ToolDescriptor(
1522
public val name: String,
1623
public val description: String,
1724
public val requiredParameters: List<ToolParameterDescriptor> = emptyList(),
1825
public val optionalParameters: List<ToolParameterDescriptor> = emptyList(),
26+
public val cacheControl: LLMParams.CacheControl? = null,
1927
) {
2028
/**
2129
* Creates a copy of the current ToolDescriptor with the option to modify specific attributes.
@@ -26,19 +34,22 @@ public open class ToolDescriptor(
2634
* Defaults to the current required parameters if not provided.
2735
* @param optionalParameters A list of ToolParameterDescriptor representing the optional parameters for the tool.
2836
* Defaults to the current optional parameters if not provided.
37+
* @param cacheControl Optional cache control setting for this tool. Defaults to the current cache control if not provided.
2938
* @return A new instance of ToolDescriptor with the updated attributes.
3039
*/
3140
public fun copy(
3241
name: String = this.name,
3342
description: String = this.description,
3443
requiredParameters: List<ToolParameterDescriptor> = this.requiredParameters.toList(),
3544
optionalParameters: List<ToolParameterDescriptor> = this.optionalParameters.toList(),
45+
cacheControl: LLMParams.CacheControl? = this.cacheControl,
3646
): ToolDescriptor {
3747
return ToolDescriptor(
3848
name = name,
3949
description = description,
4050
requiredParameters = requiredParameters,
4151
optionalParameters = optionalParameters,
52+
cacheControl = cacheControl,
4253
)
4354
}
4455

@@ -50,19 +61,34 @@ public open class ToolDescriptor(
5061
if (description != other.description) return false
5162
if (requiredParameters != other.requiredParameters) return false
5263
if (optionalParameters != other.optionalParameters) return false
64+
if (cacheControl != other.cacheControl) return false
5365

5466
return true
5567
}
5668

5769
override fun toString(): String {
58-
return "ToolDescriptor(name=$name, description=$description, requiredParameters=$requiredParameters, optionalParameters=$optionalParameters)"
70+
return "ToolDescriptor(name=$name, description=$description, requiredParameters=$requiredParameters, optionalParameters=$optionalParameters, cacheControl=$cacheControl)"
5971
}
6072

6173
override fun hashCode(): Int {
6274
var result = name.hashCode()
6375
result = 31 * result + description.hashCode()
6476
result = 31 * result + requiredParameters.hashCode()
6577
result = 31 * result + optionalParameters.hashCode()
78+
result = 31 * result + (cacheControl?.hashCode() ?: 0)
6679
return result
6780
}
6881
}
82+
83+
/**
84+
* Creates a copy of this ToolDescriptor with the specified cache control setting.
85+
*
86+
* This is a convenience extension function for setting cache control on tool definitions.
87+
* When cache control is set, LLM providers like Anthropic will cache tool definitions
88+
* up to and including this tool, reducing latency and cost for repeated requests.
89+
*
90+
* @param cacheControl The cache control setting to apply to this tool.
91+
* @return A new ToolDescriptor with the cache control setting applied.
92+
*/
93+
public fun ToolDescriptor.withCacheControl(cacheControl: LLMParams.CacheControl): ToolDescriptor =
94+
copy(cacheControl = cacheControl)

build.gradle.kts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ version = run {
7777
}
7878
}
7979

80-
"0.6.0-flopiq-1"
80+
"0.6.0-flopiq-3"
8181
}
8282

8383
fun isCustomReleaseBranch(branchName: String): Boolean = branchName.matches(Regex("""^\d+\.\d+\.\d+$"""))

0 commit comments

Comments
 (0)