-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Ported all modules from prototype (#2)
* Ported all modules from prototype * Added agent example * Fix build
- Loading branch information
Showing
62 changed files
with
313,515 additions
and
25 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,12 +1,51 @@ | ||
[versions] | ||
kotlin = "1.9.23" | ||
coroutines = "1.7.2" | ||
serialization = "1.6.3" | ||
ktor = "2.3.10" | ||
nexus-publish = "2.0.0" | ||
kotlinOpenapiBindings = "0.0.24" | ||
kotest = "5.8.1" | ||
mockk = "1.13.10" | ||
|
||
[libraries] | ||
kotlin-test = { module = "org.jetbrains.kotlin:kotlin-test", version.ref = "kotlin" } | ||
kotlin-reflect = { module = "org.jetbrains.kotlin:kotlin-reflect", version.ref = "kotlin" } | ||
# Coroutines | ||
coroutines-core = { group = "org.jetbrains.kotlinx", name = "kotlinx-coroutines-core", version.ref = "coroutines" } | ||
coroutines-test = { group = "org.jetbrains.kotlinx", name = "kotlinx-coroutines-test", version.ref = "coroutines" } | ||
# Serialization | ||
serialization-core = { group = "org.jetbrains.kotlinx", name = "kotlinx-serialization-core", version.ref = "serialization" } | ||
serialization-json = { group = "org.jetbrains.kotlinx", name = "kotlinx-serialization-json", version.ref = "serialization" } | ||
nexus-publish = { module = "io.github.gradle-nexus.publish-plugin:io.github.gradle-nexus.publish-plugin.gradle.plugin", version.ref = "nexus-publish" } | ||
# Ktor | ||
ktor-client-core = { group = "io.ktor", name = "ktor-client-core", version.ref = "ktor" } | ||
ktor-client-logging = { group = "io.ktor", name = "ktor-client-logging", version.ref = "ktor" } | ||
ktor-client-curl = { group = "io.ktor", name = "ktor-client-curl", version.ref = "ktor" } | ||
ktor-client-auth = { group = "io.ktor", name = "ktor-client-auth", version.ref = "ktor" } | ||
ktor-client-content-negotiation = { group = "io.ktor", name = "ktor-client-content-negotiation", version.ref = "ktor" } | ||
ktor-client-serialization-json = { group = "io.ktor", name = "ktor-serialization-kotlinx-json", version.ref = "ktor" } | ||
# Ktor engines | ||
ktor-client-apache = { group = "io.ktor", name = "ktor-client-apache", version.ref = "ktor" } | ||
ktor-client-cio = { group = "io.ktor", name = "ktor-client-cio", version.ref = "ktor" } | ||
ktor-client-java = { group = "io.ktor", name = "ktor-client-java", version.ref = "ktor" } | ||
ktor-client-jetty = { group = "io.ktor", name = "ktor-client-jetty", version.ref = "ktor" } | ||
ktor-client-mock = { group = "io.ktor", name = "ktor-client-mock", version.ref = "ktor" } | ||
ktor-client-okhttp = { group = "io.ktor", name = "ktor-client-okhttp", version.ref = "ktor" } | ||
ktor-client-darwin = { group = "io.ktor", name = "ktor-client-darwin", version.ref = "ktor" } | ||
# OpenAPI | ||
kotlinx-openapi-bindings = { group = "community.flock.kotlinx.openapi.bindings", name = "kotlin-openapi-bindings", version.ref = "kotlinOpenapiBindings" } | ||
#Test | ||
kotest-assertions-core = { module = "io.kotest:kotest-assertions-core", version.ref = "kotest" } | ||
kotest-framework-engine = { module = "io.kotest:kotest-framework-engine", version.ref = "kotest" } | ||
kotest-framework-datatest = { module = "io.kotest:kotest-framework-datatest", version.ref = "kotest" } | ||
kotest-property = { module = "io.kotest:kotest-property", version.ref = "kotest" } | ||
kotest-runner-junit5 = { module = "io.kotest:kotest-runner-junit5", version.ref = "kotest" } | ||
mockk = { module = "io.mockk:mockk", version.ref = "mockk" } | ||
|
||
[plugins] | ||
kotlinMultiplatform = { id = "org.jetbrains.kotlin.multiplatform", version.ref = "kotlin" } | ||
kotlinx-serialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "kotlin" } | ||
spotless = { id = "com.diffplug.gradle.spotless", version = "6.20.0" } | ||
dokka = { id = "org.jetbrains.dokka", version = "1.8.20" } | ||
dokka = { id = "org.jetbrains.dokka", version = "1.8.20" } | ||
kotest-multiplatform = { id = "io.kotest.multiplatform", version.ref = "kotest" } |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,6 +2,13 @@ | |
# yarn lockfile v1 | ||
|
||
|
||
[email protected]: | ||
version "3.0.0" | ||
resolved "https://registry.yarnpkg.com/abort-controller/-/abort-controller-3.0.0.tgz#eaf54d53b62bae4138e809ca225c8439a6efb392" | ||
integrity sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg== | ||
dependencies: | ||
event-target-shim "^5.0.0" | ||
|
||
[email protected]: | ||
version "4.1.1" | ||
resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-4.1.1.tgz#cbb9ae256bf750af1eab344f229aa27fe94ba348" | ||
|
@@ -160,6 +167,11 @@ [email protected]: | |
resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34" | ||
integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== | ||
|
||
event-target-shim@^5.0.0: | ||
version "5.0.1" | ||
resolved "https://registry.yarnpkg.com/event-target-shim/-/event-target-shim-5.0.1.tgz#5d4d3ebdf9583d63a5333ce2deb7480ab2b05789" | ||
integrity sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ== | ||
|
||
fill-range@^7.0.1: | ||
version "7.0.1" | ||
resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40" | ||
|
@@ -359,6 +371,13 @@ [email protected]: | |
resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.3.tgz#fd8e8b7aa761fe807dba2d1b98fb7241bb724a25" | ||
integrity sha512-p1sjXuopFs0xg+fPASzQ28agW1oHD7xDsd9Xkf3T15H3c/cifrFHVwrh74PdoklAPi+i7MdRsE47vm2r6JoB+w== | ||
|
||
[email protected]: | ||
version "2.6.7" | ||
resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.7.tgz#24de9fba827e3b4ae44dc8b20256a379160052ad" | ||
integrity sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ== | ||
dependencies: | ||
whatwg-url "^5.0.0" | ||
|
||
normalize-path@^3.0.0, normalize-path@~3.0.0: | ||
version "3.0.0" | ||
resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" | ||
|
@@ -486,11 +505,29 @@ to-regex-range@^5.0.1: | |
dependencies: | ||
is-number "^7.0.0" | ||
|
||
tr46@~0.0.3: | ||
version "0.0.3" | ||
resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a" | ||
integrity sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw== | ||
|
||
[email protected]: | ||
version "5.0.4" | ||
resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.0.4.tgz#b217fd20119bd61a94d4011274e0ab369058da3b" | ||
integrity sha512-cW9T5W9xY37cc+jfEnaUvX91foxtHkza3Nw3wkoF4sSlKn0MONdkdEndig/qPBWXNkmplh3NzayQzCiHM4/hqw== | ||
|
||
webidl-conversions@^3.0.0: | ||
version "3.0.1" | ||
resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871" | ||
integrity sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ== | ||
|
||
whatwg-url@^5.0.0: | ||
version "5.0.0" | ||
resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-5.0.0.tgz#966454e8765462e37644d3626f6742ce8b70965d" | ||
integrity sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw== | ||
dependencies: | ||
tr46 "~0.0.3" | ||
webidl-conversions "^3.0.0" | ||
|
||
[email protected]: | ||
version "6.2.1" | ||
resolved "https://registry.yarnpkg.com/workerpool/-/workerpool-6.2.1.tgz#46fc150c17d826b86a008e5a4508656777e9c343" | ||
|
@@ -510,6 +547,11 @@ wrappy@1: | |
resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" | ||
integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ== | ||
|
||
[email protected]: | ||
version "8.5.0" | ||
resolved "https://registry.yarnpkg.com/ws/-/ws-8.5.0.tgz#bfb4be96600757fe5382de12c670dab984a1ed4f" | ||
integrity sha512-BWX0SWVgLPzYwF8lTzEy1egjhS4S4OEAHfsO8o65WOVsrnSRGaSiUaa9e0ggGlkMTtBlmOpEXiie9RUcBO86qg== | ||
|
||
y18n@^5.0.5: | ||
version "5.0.8" | ||
resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.8.tgz#7f4934d0f7ca8c56f95314939ddcd2dd91ce1d55" | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
34 changes: 34 additions & 0 deletions
34
src/core/src/commonMain/kotlin/community/flock/aigentic/core/agent/Agent.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
package community.flock.aigentic.core.agent | ||
|
||
import community.flock.aigentic.core.agent.prompt.SystemPromptBuilder | ||
import community.flock.aigentic.core.message.Message | ||
import community.flock.aigentic.core.tool.ToolName | ||
import community.flock.aigentic.core.model.Model | ||
import community.flock.aigentic.core.tool.InternalTool | ||
import community.flock.aigentic.core.tool.Tool | ||
import kotlinx.coroutines.flow.MutableSharedFlow | ||
|
||
data class Task( | ||
val description: String, | ||
val instructions: List<Instruction> | ||
) | ||
|
||
data class Instruction(val text: String) | ||
|
||
sealed interface Context { | ||
data class Text(val text: String) : Context | ||
data class Image(val base64: String) : Context | ||
} | ||
|
||
data class Agent( | ||
val id: String, | ||
val systemPromptBuilder: SystemPromptBuilder, | ||
val model: Model, | ||
val task: Task, | ||
val contexts: List<Context>, | ||
val tools: Map<ToolName, Tool>, | ||
) { | ||
|
||
internal val messages = MutableSharedFlow<Message>(replay = 100) | ||
internal val internalTools = mutableMapOf<ToolName, InternalTool<*>>() | ||
} |
127 changes: 127 additions & 0 deletions
127
src/core/src/commonMain/kotlin/community/flock/aigentic/core/agent/AgentExecutor.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,127 @@ | ||
package community.flock.aigentic.core.agent | ||
|
||
import community.flock.aigentic.core.agent.tool.FinishedOrStuck | ||
import community.flock.aigentic.core.agent.tool.finishOrStuckTool | ||
import community.flock.aigentic.core.message.* | ||
import community.flock.aigentic.core.tool.DefaultToolPermissionHandler | ||
import community.flock.aigentic.core.tool.ToolName | ||
import community.flock.aigentic.core.tool.ToolPermissionHandler | ||
import community.flock.aigentic.core.model.ModelResponse | ||
import kotlinx.coroutines.* | ||
import kotlinx.coroutines.flow.MutableSharedFlow | ||
|
||
class AgentExecutor( | ||
val schedules: List<Schedule>, | ||
val permissionHandler: ToolPermissionHandler = DefaultToolPermissionHandler() | ||
) { | ||
private val listeners: MutableList<suspend (event: Pair<String, Message>) -> Unit> = mutableListOf() | ||
|
||
suspend fun start() = | ||
schedules | ||
.flatMap { it.agents } | ||
.forEach { runAgent(it) } | ||
|
||
suspend fun runAgent(agent: Agent): FinishedOrStuck { | ||
applyListeners(agent) | ||
agent.initialize() // Maybe move to Agent builder? | ||
val modelResponse = agent.sendModelRequest() | ||
|
||
val result = CompletableDeferred<FinishedOrStuck>() | ||
processResponse(agent, modelResponse) { result.complete(it) } | ||
return result.await() | ||
} | ||
|
||
private suspend fun Agent.initialize() { | ||
internalTools[finishOrStuckTool.name] = finishOrStuckTool | ||
messages.emit(systemPromptBuilder.buildSystemPrompt(this)) | ||
contexts.map { | ||
when (it) { | ||
is Context.Image -> Message.Image(Sender.Aigentic, it.base64) | ||
is Context.Text -> Message.Text(Sender.Aigentic, it.text) | ||
} | ||
}.forEach { messages.emit(it) } | ||
} | ||
|
||
private suspend fun processResponse(agent: Agent, response: ModelResponse, onFinished: (FinishedOrStuck) -> Unit) { | ||
val message = response.message | ||
agent.messages.emit(message) | ||
|
||
when (message) { | ||
is Message.ToolCalls -> { | ||
val shouldSendNextRequest = message.toolCalls | ||
.map { toolCall -> | ||
when (toolCall.name) { | ||
finishOrStuckTool.name.value -> { | ||
val finishedOrStuck = finishOrStuckTool.handler(toolCall.argumentsAsJson()) | ||
onFinished(finishedOrStuck) | ||
false | ||
} | ||
|
||
else -> { | ||
val toolResult = agent.execute(toolCall) | ||
agent.messages.emit(toolResult) | ||
true | ||
} | ||
} | ||
} | ||
.contains(true) | ||
|
||
if (shouldSendNextRequest) { | ||
sendToolResponse(agent, onFinished) | ||
} | ||
} | ||
|
||
else -> error("Expected ToolCalls message, got $message") | ||
} | ||
} | ||
|
||
private suspend fun Agent.execute(toolCall: ToolCall): Message.ToolResult { | ||
val functionArgs = toolCall.argumentsAsJson() | ||
val tool = tools[ToolName(toolCall.name)] ?: error("Tool not registered: $toolCall") | ||
while (!permissionHandler.hasPermission(tool.toolConfiguration, toolCall)) { | ||
println("Waiting for permission for ${toolCall.name}") | ||
delay(300) | ||
} | ||
val result = tool.handler(functionArgs) | ||
return Message.ToolResult(toolCall.id, toolCall.name, ToolResultContent(result)) | ||
} | ||
|
||
private suspend fun sendToolResponse(agent: Agent, onFinished: (FinishedOrStuck) -> Unit) { | ||
val response = agent.sendModelRequest() | ||
processResponse(agent, response, onFinished) | ||
} | ||
|
||
private suspend fun Agent.sendModelRequest(): ModelResponse = | ||
model.sendRequest(messages.replayCache, tools.values.toList() + internalTools.values.toList()) | ||
|
||
fun getMessages(): Map<String, MutableSharedFlow<Message>> = | ||
schedules.flatMap { it.agents }.associate { it.id to it.messages } | ||
|
||
fun addListener(function: suspend (event: Pair<String, Message>) -> Unit) { | ||
listeners.add(function) | ||
} | ||
|
||
@OptIn(DelicateCoroutinesApi::class) | ||
private fun applyListeners(agent: Agent) { | ||
listeners.forEach { function -> | ||
GlobalScope.launch { | ||
agent.messages.collect { function.invoke(Pair(agent.id, it)) } | ||
} | ||
} | ||
} | ||
} | ||
|
||
class Schedule( | ||
val agents: List<Agent>, | ||
val type: ScheduleType | ||
) | ||
|
||
sealed interface ScheduleType { | ||
/** | ||
* Just run a single time | ||
*/ | ||
data object Single : ScheduleType | ||
} | ||
|
||
|
||
//suspend fun Agent.execute() = runAgent(this) |
Oops, something went wrong.