Skip to content

Commit be9bf14

Browse files
authored
Implement Executor pattern for handling tool call delegation (#10)
* Refactor Tool execution setup, and rename/move other entities * Fix tests
1 parent c58429b commit be9bf14

108 files changed

Lines changed: 3835 additions & 2997 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

shared/src/androidMain/kotlin/link/socket/ampere/agents/tools/ReadCodebaseTool.android.kt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
package link.socket.ampere.agents.tools
22

33
import java.io.File
4-
import link.socket.ampere.agents.core.AutonomyLevel
5-
import link.socket.ampere.agents.core.Outcome
4+
import link.socket.ampere.agents.core.actions.AgentActionAutonomy
5+
import link.socket.ampere.agents.core.outcomes.Outcome
66

77
/**
88
* Android actual for ReadCodebaseTool. Restricts reads to a sandboxed root.
@@ -12,7 +12,7 @@ actual class ReadCodebaseTool actual constructor(
1212
) : Tool {
1313
actual override val name: String = "read_codebase"
1414
actual override val description: String = "Reads file content or directory structure"
15-
actual override val requiredAutonomyLevel: AutonomyLevel = AutonomyLevel.FULLY_AUTONOMOUS
15+
actual override val agentAutonomy: AgentActionAutonomy = AgentActionAutonomy.FULLY_AUTONOMOUS
1616

1717
private fun resolveSafe(path: String): File {
1818
val base = File(rootDirectory).canonicalFile

shared/src/androidMain/kotlin/link/socket/ampere/agents/tools/RunTestsTool.android.kt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
package link.socket.ampere.agents.tools
22

33
import java.io.File
4-
import link.socket.ampere.agents.core.AutonomyLevel
5-
import link.socket.ampere.agents.core.Outcome
4+
import link.socket.ampere.agents.core.actions.AgentActionAutonomy
5+
import link.socket.ampere.agents.core.outcomes.Outcome
66

77
/**
88
* Android actual for RunTestsTool using the Gradle wrapper.
@@ -12,7 +12,7 @@ actual class RunTestsTool actual constructor(
1212
) : Tool {
1313
actual override val name: String = "run_tests"
1414
actual override val description: String = "Executes tests and returns results"
15-
actual override val requiredAutonomyLevel: AutonomyLevel = AutonomyLevel.FULLY_AUTONOMOUS
15+
actual val agentAutonomy: AgentActionAutonomy = AgentActionAutonomy.FULLY_AUTONOMOUS
1616

1717
actual override suspend fun execute(parameters: Map<String, Any?>): Outcome {
1818
val testPath = parameters["testPath"] as? String

shared/src/androidMain/kotlin/link/socket/ampere/agents/tools/WriteCodeFileTool.android.kt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
package link.socket.ampere.agents.tools
22

33
import java.io.File
4-
import link.socket.ampere.agents.core.AutonomyLevel
5-
import link.socket.ampere.agents.core.Outcome
4+
import link.socket.ampere.agents.core.actions.AgentActionAutonomy
5+
import link.socket.ampere.agents.core.outcomes.Outcome
66

77
/**
88
* Android actual for WriteCodeFileTool using java.io.File APIs.
@@ -12,7 +12,7 @@ actual class WriteCodeFileTool actual constructor(
1212
) : Tool {
1313
actual override val name: String = "write_code_file"
1414
actual override val description: String = "Generates a single code file with specified content"
15-
actual override val requiredAutonomyLevel: AutonomyLevel = AutonomyLevel.ACT_WITH_NOTIFICATION
15+
actual val agentAutonomy: AgentActionAutonomy = AgentActionAutonomy.ACT_WITH_NOTIFICATION
1616

1717
actual override suspend fun execute(parameters: Map<String, Any?>): Outcome {
1818
val filePath = parameters["filePath"]
Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
package link.socket.ampere.agents.core
2+
3+
import kotlinx.coroutines.flow.MutableStateFlow
4+
import kotlinx.coroutines.flow.StateFlow
5+
import kotlinx.coroutines.flow.asStateFlow
6+
import kotlinx.serialization.Serializable
7+
import link.socket.ampere.agents.core.outcomes.ExecutionOutcome
8+
import link.socket.ampere.agents.core.outcomes.Outcome
9+
import link.socket.ampere.agents.core.outcomes.OutcomeId
10+
import link.socket.ampere.agents.core.reasoning.Idea
11+
import link.socket.ampere.agents.core.reasoning.IdeaId
12+
import link.socket.ampere.agents.core.reasoning.Perception
13+
import link.socket.ampere.agents.core.reasoning.PerceptionId
14+
import link.socket.ampere.agents.core.reasoning.Plan
15+
import link.socket.ampere.agents.core.reasoning.PlanId
16+
import link.socket.ampere.agents.core.states.AgentState
17+
import link.socket.ampere.agents.core.tasks.Task
18+
import link.socket.ampere.agents.core.tasks.TaskId
19+
import link.socket.ampere.agents.execution.request.ExecutionRequest
20+
import link.socket.ampere.agents.tools.Tool
21+
import link.socket.ampere.domain.ai.model.AIModel
22+
23+
typealias AgentId = String
24+
25+
@Serializable
26+
sealed class Agent <S : AgentState> {
27+
28+
abstract val id: AgentId
29+
abstract val initialState: S
30+
abstract val agentConfiguration: AgentConfiguration
31+
32+
abstract suspend fun perceiveState(vararg newIdeas: Idea): Idea
33+
abstract suspend fun determinePlanForTask(task: Task, vararg ideas: Idea): Plan
34+
abstract suspend fun executePlan(plan: Plan): Outcome
35+
abstract suspend fun runTask(task: Task): Outcome
36+
abstract suspend fun runTool(tool: Tool<*>, request: ExecutionRequest<*>): ExecutionOutcome
37+
abstract suspend fun evaluateNextIdeaFromOutcomes(vararg outcomes: Outcome): Idea
38+
39+
private val _stateFlow: MutableStateFlow<S> by lazy { MutableStateFlow(initialState) }
40+
val stateFlow: StateFlow<S> by lazy { _stateFlow.asStateFlow() }
41+
42+
fun getCurrentState(): S = stateFlow.value
43+
fun getAIModel(): AIModel = agentConfiguration.aiConfiguration.model
44+
45+
fun getAllIdeas(): List<IdeaId> = with(getCurrentState()) {
46+
val currentIdea = getCurrentMemory().idea.id
47+
val pastIdeas = getPastMemory().ideas
48+
pastIdeas.plus(currentIdea)
49+
}
50+
51+
fun getAllPlans(): List<PlanId> = with(getCurrentState()) {
52+
val currentPlan = getCurrentMemory().plan.id
53+
val pastPlans = getPastMemory().plans
54+
pastPlans.plus(currentPlan)
55+
}
56+
57+
fun getAllTasks(): List<TaskId> = with(getCurrentState()) {
58+
val currentTask = getCurrentMemory().task.id
59+
val pastTasks = getPastMemory().tasks
60+
pastTasks.plus(currentTask)
61+
}
62+
63+
fun getAllOutcomes(): List<OutcomeId> = with(getCurrentState()) {
64+
val currentOutcome = getCurrentMemory().outcome.id
65+
val pastOutcomes = getPastMemory().outcomes
66+
pastOutcomes.plus(currentOutcome)
67+
}
68+
69+
fun getAllPerceptions(): List<PerceptionId> = with(getCurrentState()) {
70+
val currentPerception = getCurrentMemory().perception.id
71+
val pastPerceptions = getPastMemory().perceptions
72+
pastPerceptions.plus(currentPerception)
73+
}
74+
75+
protected fun rememberNewIdea(idea: Idea) {
76+
val currentState = getCurrentState()
77+
currentState.setNewIdea(idea)
78+
_stateFlow.value = currentState
79+
}
80+
81+
protected fun rememberNewOutcome(outcome: Outcome) {
82+
val currentState = getCurrentState()
83+
currentState.setNewOutcome(outcome)
84+
_stateFlow.value = currentState
85+
}
86+
87+
protected fun rememberNewPerception(perception: Perception<*>) {
88+
val currentState = getCurrentState()
89+
currentState.setNewPerception(perception)
90+
_stateFlow.value = currentState
91+
}
92+
93+
protected fun rememberNewPlan(plan: Plan) {
94+
val currentState = getCurrentState()
95+
currentState.setNewPlan(plan)
96+
_stateFlow.value = currentState
97+
}
98+
99+
protected fun rememberNewTask(task: Task) {
100+
val currentState = getCurrentState()
101+
currentState.setNewTask(task)
102+
_stateFlow.value = currentState
103+
}
104+
105+
protected fun finishCurrentIdea() {
106+
val currentState = getCurrentState()
107+
currentState.setNewIdea(Idea.blank)
108+
_stateFlow.value = currentState
109+
}
110+
111+
protected fun finishCurrentOutcome() {
112+
val currentState = getCurrentState()
113+
currentState.setNewOutcome(Outcome.blank)
114+
_stateFlow.value = currentState
115+
}
116+
117+
protected fun finishCurrentPerception() {
118+
val currentState = getCurrentState()
119+
currentState.setNewPerception(Perception.blank)
120+
}
121+
122+
protected fun finishCurrentPlan() {
123+
val currentState = getCurrentState()
124+
currentState.setNewPlan(Plan.blank)
125+
_stateFlow.value = currentState
126+
}
127+
128+
protected fun finishCurrentTask() {
129+
val currentState = getCurrentState()
130+
currentState.setNewTask(Task.blank)
131+
_stateFlow.value = currentState
132+
}
133+
134+
protected fun resetCurrentMemory() {
135+
val currentState = getCurrentState()
136+
currentState.resetCurrentMemoryCell()
137+
_stateFlow.value = currentState
138+
}
139+
140+
protected fun resetPastMemory() {
141+
val currentState = getCurrentState()
142+
currentState.resetPastMemoryCell()
143+
_stateFlow.value = currentState
144+
}
145+
146+
protected fun resetAllMemory() {
147+
resetCurrentMemory()
148+
resetPastMemory()
149+
}
150+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package link.socket.ampere.agents.core
2+
3+
import kotlinx.serialization.Serializable
4+
import link.socket.ampere.domain.agent.bundled.AgentDefinition
5+
import link.socket.ampere.domain.ai.configuration.AIConfiguration
6+
7+
@Serializable
8+
data class AgentConfiguration(
9+
val agentDefinition: AgentDefinition,
10+
val aiConfiguration: AIConfiguration,
11+
)
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
package link.socket.ampere.agents.core
2+
3+
import kotlinx.serialization.Serializable
4+
5+
@Serializable
6+
sealed interface AssignedTo {
7+
8+
@Serializable
9+
data class Agent(val agentId: AgentId) : AssignedTo
10+
11+
@Serializable
12+
data class Team(val teamId: TeamId) : AssignedTo
13+
14+
@Serializable
15+
data object Human : AssignedTo
16+
17+
fun getIdentifier(): String = when (this) {
18+
is Agent -> agentId
19+
is Team -> teamId
20+
is Human -> HUMAN_ID
21+
}
22+
23+
companion object {
24+
const val HUMAN_ID = "human"
25+
}
26+
}

0 commit comments

Comments
 (0)