eerste check in

This commit is contained in:
2025-10-10 14:58:29 +02:00
parent d30f2e404e
commit e2c19dd5af
8 changed files with 621 additions and 210 deletions

176
pom.xml
View File

@@ -3,15 +3,19 @@
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.5.3</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>nl.trivion</groupId> <groupId>nl.trivion</groupId>
<artifactId>SoftwareFabric</artifactId> <artifactId>SoftwareFabric</artifactId>
<version>1.0-SNAPSHOT</version> <version>1.0-SNAPSHOT</version>
<properties> <properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <java.version>21</java.version>
<kotlin.code.style>official</kotlin.code.style> <kotlin.version>2.2.10</kotlin.version>
<kotlin.compiler.jvmTarget>21</kotlin.compiler.jvmTarget>
</properties> </properties>
<repositories> <repositories>
@@ -21,80 +25,134 @@
</repository> </repository>
</repositories> </repositories>
<build> <dependencyManagement>
<sourceDirectory>src/main/kotlin</sourceDirectory>
<testSourceDirectory>src/test/kotlin</testSourceDirectory>
<plugins>
<plugin>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-maven-plugin</artifactId>
<version>2.2.10</version>
<executions>
<execution>
<id>compile</id>
<phase>compile</phase>
<goals>
<goal>compile</goal>
</goals>
</execution>
<execution>
<id>test-compile</id>
<phase>test-compile</phase>
<goals>
<goal>test-compile</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.22.2</version>
</plugin>
<plugin>
<artifactId>maven-failsafe-plugin</artifactId>
<version>2.22.2</version>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<version>1.6.0</version>
<configuration>
<mainClass>MainKt</mainClass>
</configuration>
</plugin>
</plugins>
</build>
<dependencies> <dependencies>
<dependency> <dependency>
<groupId>org.jetbrains.kotlin</groupId> <groupId>org.jetbrains.kotlinx</groupId>
<artifactId>kotlin-test-junit5</artifactId> <artifactId>kotlinx-coroutines-bom</artifactId>
<version>2.2.10</version> <version>1.10.2</version>
<scope>test</scope> <type>pom</type>
<scope>import</scope>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.junit.jupiter</groupId> <groupId>org.jetbrains.kotlinx</groupId>
<artifactId>junit-jupiter</artifactId> <artifactId>kotlinx-serialization-bom</artifactId>
<version>5.10.0</version> <version>1.8.1</version> <!-- Of nieuwer -->
<scope>test</scope> <type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<!-- Spring & Kotlin base -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-reflect</artifactId>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.jetbrains.kotlin</groupId> <groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-stdlib</artifactId> <artifactId>kotlin-stdlib</artifactId>
<version>2.2.10</version>
</dependency> </dependency>
<!-- Koog AI dependencies -->
<dependency> <dependency>
<groupId>ai.koog</groupId> <groupId>ai.koog</groupId>
<artifactId>koog-agents-jvm</artifactId> <artifactId>koog-agents-jvm</artifactId>
<version>0.4.2</version> <version>0.5.0</version>
</dependency> </dependency>
<!-- https://mvnrepository.com/artifact/org.eclipse.jgit/org.eclipse.jgit --> <dependency>
<groupId>ai.koog</groupId>
<artifactId>prompt-cache-redis-jvm</artifactId>
<version>0.5.0</version>
</dependency>
<dependency>
<groupId>ai.koog</groupId>
<artifactId>prompt-executor-model-jvm</artifactId>
<version>0.5.0</version>
</dependency>
<!-- Utilities -->
<dependency> <dependency>
<groupId>org.eclipse.jgit</groupId> <groupId>org.eclipse.jgit</groupId>
<artifactId>org.eclipse.jgit</artifactId> <artifactId>org.eclipse.jgit</artifactId>
<version>7.3.0.202506031305-r</version> <version>7.3.0.202506031305-r</version>
<exclusions>
<exclusion>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
<version>4.12.0</version>
</dependency>
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.10.1</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.30</version>
<scope>provided</scope>
</dependency>
<!-- Coroutines version lock -->
<dependency>
<groupId>org.jetbrains.kotlinx</groupId>
<artifactId>kotlinx-coroutines-core</artifactId>
<version>1.10.2</version>
</dependency>
<!-- Test dependencies -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-test-junit5</artifactId>
<scope>test</scope>
</dependency> </dependency>
</dependencies> </dependencies>
<build>
<sourceDirectory>${project.basedir}/src/main/kotlin</sourceDirectory>
<testSourceDirectory>${project.basedir}/src/test/kotlin</testSourceDirectory>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-maven-plugin</artifactId>
<configuration>
<args>
<arg>-Xjsr305=strict</arg>
</args>
<compilerPlugins>
<plugin>spring</plugin>
</compilerPlugins>
</configuration>
<dependencies>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-maven-allopen</artifactId>
<version>${kotlin.version}</version>
</dependency>
</dependencies>
</plugin>
</plugins>
</build>
</project> </project>

View File

@@ -1,142 +0,0 @@
package org.example.nl.trivion.softwarefabric
import ai.koog.agents.core.agent.AIAgent
import ai.koog.agents.core.agent.config.AIAgentConfig
import ai.koog.agents.core.dsl.builder.forwardTo
import ai.koog.agents.core.dsl.builder.strategy
import ai.koog.agents.core.dsl.extension.nodeExecuteTool
import ai.koog.agents.core.dsl.extension.nodeLLMRequest
import ai.koog.agents.core.dsl.extension.nodeLLMSendToolResult
import ai.koog.agents.core.dsl.extension.onAssistantMessage
import ai.koog.agents.core.dsl.extension.onToolCall
import ai.koog.agents.core.feature.handler.AgentFinishedContext
import ai.koog.agents.core.feature.handler.AgentStartContext
import ai.koog.agents.core.tools.ToolRegistry
import ai.koog.agents.core.tools.annotations.LLMDescription
import ai.koog.agents.core.tools.annotations.Tool
import ai.koog.agents.core.tools.reflect.ToolSet
import ai.koog.agents.core.tools.reflect.asTools
import ai.koog.agents.features.eventHandler.feature.EventHandler
import ai.koog.prompt.dsl.Prompt
import ai.koog.prompt.executor.llms.all.simpleOllamaAIExecutor
import ai.koog.prompt.llm.LLMCapability
import ai.koog.prompt.llm.LLMProvider
import ai.koog.prompt.llm.LLModel
import kotlinx.coroutines.runBlocking
// Use the OpenAI executor with an API key from an environment variable
val promptExecutor = simpleOllamaAIExecutor("http://localhost:11434")
val customModel: LLModel = LLModel(
provider = LLMProvider.Ollama,
id = "gpt-oss:20b",
capabilities = listOf(
LLMCapability.Temperature,
LLMCapability.Tools
),
contextLength = 2048
)
// Create a simple strategy
val agentStrategy = strategy("Simple calculator") {
// Define nodes for the strategy
val nodeSendInput by nodeLLMRequest()
val nodeExecuteTool by nodeExecuteTool()
val nodeSendToolResult by nodeLLMSendToolResult()
// Define edges between nodes
// Start -> Send input
edge(nodeStart forwardTo nodeSendInput)
// Send input -> Finish
edge(
(nodeSendInput forwardTo nodeFinish)
transformed { it }
onAssistantMessage { true }
)
// Send input -> Execute tool
edge(
(nodeSendInput forwardTo nodeExecuteTool)
onToolCall { true }
)
// Execute tool -> Send the tool result
edge(nodeExecuteTool forwardTo nodeSendToolResult)
// Send the tool result -> finish
edge(
(nodeSendToolResult forwardTo nodeFinish)
transformed { it }
onAssistantMessage { true }
)
}
// Configure the agent
val agentConfig = AIAgentConfig(
prompt = Prompt.build("simple-calculator") {
system(
"""
You are a simple calculator assistant.
You can add two numbers together using the calculator tool.
When the user provides input, extract the numbers they want to add.
The input might be in various formats like "add 5 and 7", "5 + 7", or just "5 7".
Extract the two numbers and use the calculator tool to add them.
Always respond with a clear, friendly message showing the calculation and result.
""".trimIndent()
)
},
model = customModel,
maxAgentIterations = 10
)
// Implement a simple calculator tool that can add two numbers
@LLMDescription("Tools for performing basic arithmetic operations")
class CalculatorTools : ToolSet {
@Tool
@LLMDescription("Add two numbers together and return their sum")
fun add(
@LLMDescription("First number to add (integer value)")
num1: Int,
@LLMDescription("Second number to add (integer value)")
num2: Int
): String {
val sum = num1 + num2
return "The sum of $num1 and $num2 is: $sum"
}
}
// Add the tool to the tool registry
val toolRegistry = ToolRegistry {
tools(CalculatorTools().asTools())
}
// Create the agent
val agent = AIAgent(
promptExecutor = promptExecutor,
toolRegistry = toolRegistry,
strategy = agentStrategy,
agentConfig = agentConfig,
installFeatures = {
install(EventHandler) {
onBeforeAgentStarted { eventContext: AgentStartContext<*> ->
println("Starting strategy: ${eventContext.strategy.name}")
}
onAgentFinished { eventContext: AgentFinishedContext ->
println("Result: ${eventContext.result}")
}
}
}
)
fun main() {
runBlocking {
println("Enter two numbers to add (e.g., 'add 5 and 7' or '5 + 7'):")
// Read the user input and send it to the agent
val userInput = readlnOrNull() ?: ""
val agentResult = agent.run(userInput)
println("The agent returned: $agentResult")
}
}

View File

@@ -0,0 +1,13 @@
package nl.trivion.softwarefabric
import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.boot.runApplication
import org.springframework.scheduling.annotation.EnableScheduling
@SpringBootApplication
@EnableScheduling
class SoftwareFabricApplication
fun main(args: Array<String>) {
runApplication<SoftwareFabricApplication>(*args)
}

View File

@@ -0,0 +1,184 @@
package nl.trivion.softwarefabric.agents
import ai.koog.agents.core.agent.AIAgent
import ai.koog.agents.core.agent.config.AIAgentConfig
import ai.koog.agents.core.dsl.builder.forwardTo
import ai.koog.agents.core.dsl.builder.strategy
import ai.koog.agents.core.dsl.extension.nodeExecuteTool
import ai.koog.agents.core.dsl.extension.nodeLLMRequest
import ai.koog.agents.core.dsl.extension.nodeLLMSendToolResult
import ai.koog.agents.core.dsl.extension.onAssistantMessage
import ai.koog.agents.core.dsl.extension.onToolCall
import ai.koog.agents.core.tools.ToolRegistry
import ai.koog.agents.core.tools.reflect.asTools
import ai.koog.agents.features.eventHandler.feature.EventHandler
import ai.koog.prompt.dsl.Prompt
import ai.koog.prompt.executor.llms.all.simpleOllamaAIExecutor
import ai.koog.prompt.llm.LLMCapability
import ai.koog.prompt.llm.LLMProvider
import ai.koog.prompt.llm.LLModel
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import lombok.extern.slf4j.Slf4j
import nl.trivion.softwarefabric.service.GiteaService
import nl.trivion.softwarefabric.service.Issue
import org.example.nl.trivion.softwarefabric.tools.GitTools
import org.slf4j.LoggerFactory
import org.springframework.scheduling.annotation.Scheduled
import org.springframework.stereotype.Service
import java.util.concurrent.atomic.AtomicBoolean
@Service
@Slf4j
class DeveloperAgent(private val giteaService: GiteaService) {
companion object {
private val log = LoggerFactory.getLogger(DeveloperAgent::class.java)
}
// Flag to track if agent is busy
private val isAgentBusy = AtomicBoolean(false)
val promptExecutor = simpleOllamaAIExecutor("http://localhost:11434")
val customModel: LLModel = LLModel(
provider = LLMProvider.Ollama,
id = "gpt-oss:20b",
capabilities = listOf(
LLMCapability.Temperature,
LLMCapability.Tools
),
contextLength = 2048
)
// Create a simple strategy
val agentStrategy = strategy("Developer") {
// Define nodes for the strategy
val nodeSendInput by nodeLLMRequest()
val nodeExecuteTool by nodeExecuteTool()
val nodeSendToolResult by nodeLLMSendToolResult()
// Define edges between nodes
// Start -> Send input
edge(nodeStart forwardTo nodeSendInput)
// Send input -> Finish
edge(
(nodeSendInput forwardTo nodeFinish)
transformed { it }
onAssistantMessage { true }
)
// Send input -> Execute tool
edge(
(nodeSendInput forwardTo nodeExecuteTool)
onToolCall { true }
)
// Execute tool -> Send the tool result
edge(nodeExecuteTool forwardTo nodeSendToolResult)
// Send the tool result -> finish
edge(
(nodeSendToolResult forwardTo nodeFinish)
transformed { it }
onAssistantMessage { true }
)
}
// Configure the agent
val agentConfig = AIAgentConfig(
prompt = Prompt.build("developer") {
system(
"""
You are a git repository managing agent. You have the git tool to clone a remote git repo.
The remote url you can use is "http://dixienas:3001/Trivion/tasklist.git".
The project name is "tasklist".
""".trimIndent()
)
},
model = customModel,
maxAgentIterations = 10
)
// Add the tool to the tool registry
val toolRegistry = ToolRegistry {
tools(GitTools().asTools()
)
}
// Create the agent
// val agent = AIAgent(
// promptExecutor = promptExecutor,
// toolRegistry = toolRegistry,
// strategy = agentStrategy,
// agentConfig = agentConfig,
// installFeatures = {
// install(EventHandler) {
// onAgentCompleted { it ->
// log.info("Result: ${it.result}")
// }
// }
// }
// )
fun createAgent(): AIAgent<String, String> {
return AIAgent(
promptExecutor = promptExecutor,
toolRegistry = toolRegistry,
strategy = agentStrategy,
agentConfig = agentConfig,
installFeatures = {
install(EventHandler) {
onAgentCompleted { it ->
log.info("Result: ${it.result}")
isAgentBusy.set(false)
}
}
}
)
}
@Scheduled(fixedRate = 5000)
fun checkIssueToDo() {
// Check if agent is already busy
if (isAgentBusy.get()) {
log.info("Agent is still busy processing previous issue, skipping this cycle")
return
}
val issues = giteaService.getIssues().filter { it: Issue ->
it.labels.contains("Backlog")
&& it.assigned.isNullOrEmpty()
}
if (issues.isEmpty()) {
log.info("No issues found")
return
}
val issue = issues.first()
log.info("Processing issue ${issue.number}: ${issue.title}")
// Set agent as busy before starting
if (!isAgentBusy.compareAndSet(false, true)) {
log.info("Agent became busy while checking, skipping")
return
}
GlobalScope.launch {
try {
giteaService.updateIssueLabels(issue.number, listOf("Build"))
val agent = createAgent()
val result = agent.run(issue.body)
log.info("Agent completed with result: $result")
giteaService.updateIssueLabels(issue.number, listOf("Verify"))
} catch (e: Exception) {
log.error("Error processing issue: ${e.message}", e)
} finally {
// Always reset the busy flag
isAgentBusy.set(false)
}
}
}
}

View File

@@ -0,0 +1,261 @@
package nl.trivion.softwarefabric.service
import ai.koog.agents.core.tools.annotations.LLMDescription
import ai.koog.agents.core.tools.annotations.Tool
import com.google.gson.Gson
import com.google.gson.annotations.SerializedName
import lombok.extern.slf4j.Slf4j
import nl.trivion.softwarefabric.agents.DeveloperAgent
import okhttp3.*
import okhttp3.MediaType.Companion.toMediaType
import okhttp3.RequestBody.Companion.toRequestBody
import org.slf4j.LoggerFactory
import org.springframework.beans.factory.annotation.Value
import org.springframework.stereotype.Component
import org.springframework.stereotype.Service
// Model classes
data class Issue(
val number: Int,
val title: String,
val body: String,
val assigned: String?,
val labels: List<String>
)
// Response classes voor parsing
data class GiteaIssueResponse(
val id: Int,
val number: Int,
val title: String,
val body: String,
val assignee: Assignee?,
val labels: List<Label>
)
data class Assignee(
val login: String,
@SerializedName("full_name")
val fullName: String
)
data class Label(
val id: Int,
val name: String,
val color: String
)
// Request classes
data class CreateIssueRequest(
val title: String,
val body: String,
val assignee: String? = null,
val labels: List<Int>? = null
)
data class UpdateLabelsRequest(
val labels: List<Int>
)
@Service
@Slf4j
class GiteaService {
@Value("\${gitea.username}")
private lateinit var username: String
@Value("\${gitea.password}")
private lateinit var password: String
@Value("\${gitea.owner}")
private lateinit var owner: String
@Value("\${gitea.repo}")
private lateinit var repo: String
@Value("\${gitea.base-url}")
private lateinit var apiBaseUrl: String
private val baseUrl: String
get() = "$apiBaseUrl/repos/$owner/$repo"
private val gson = Gson()
private val client = OkHttpClient()
private val jsonMediaType = "application/json; charset=utf-8".toMediaType()
companion object {
private val log = LoggerFactory.getLogger(GiteaService::class.java)
}
// Generic private functions
private fun createAuthHeader(): String {
return Credentials.basic(username, password)
}
private fun executeRequest(request: Request): Response {
return client.newCall(request).execute()
}
private fun buildGetRequest(url: String): Request {
return Request.Builder()
.url(url)
.addHeader("Authorization", createAuthHeader())
.build()
}
private fun buildPostRequest(url: String, body: Any): Request {
val jsonBody = gson.toJson(body)
val requestBody = jsonBody.toRequestBody(jsonMediaType)
return Request.Builder()
.url(url)
.addHeader("Authorization", createAuthHeader())
.post(requestBody)
.build()
}
private fun buildPutRequest(url: String, body: Any): Request {
val jsonBody = gson.toJson(body)
val requestBody = jsonBody.toRequestBody(jsonMediaType)
return Request.Builder()
.url(url)
.addHeader("Authorization", createAuthHeader())
.put(requestBody)
.build()
}
private fun mapToIssue(giteaIssue: GiteaIssueResponse): Issue {
log.info("Gitea issue: {}", giteaIssue.toString())
return Issue(
number = giteaIssue.number,
title = giteaIssue.title,
body = giteaIssue.body,
assigned = giteaIssue.assignee?.let {
if (it.fullName.isNotBlank()) it.fullName else it.login
},
labels = giteaIssue.labels.map { it.name }
)
}
private fun getIssueById(issueNumber: Int): Issue? {
val url = "$baseUrl/issues/$issueNumber"
val request = buildGetRequest(url)
executeRequest(request).use { response ->
if (!response.isSuccessful) {
println("Failed to get issue: $response")
return null
}
val json = response.body?.string() ?: return null
val giteaIssue = gson.fromJson(json, GiteaIssueResponse::class.java)
return mapToIssue(giteaIssue)
}
}
private fun getAvailableLabels(): List<Label> {
val url = "$baseUrl/labels"
val request = buildGetRequest(url)
executeRequest(request).use { response ->
if (!response.isSuccessful) {
println("Failed to get labels: $response")
return emptyList()
}
val json = response.body?.string() ?: return emptyList()
return gson.fromJson(json, Array<Label>::class.java).toList()
}
}
private fun getLabelIdsByNames(labelNames: List<String>): List<Int> {
val availableLabels = getAvailableLabels()
return labelNames.mapNotNull { name ->
availableLabels.find { it.name.equals(name, ignoreCase = true) }?.id
}
}
fun getIssues(): List<Issue> {
val url = "$baseUrl/issues"
val request = buildGetRequest(url)
executeRequest(request).use { response ->
if (!response.isSuccessful) {
println("Unexpected code $response")
return emptyList()
}
val json = response.body?.string() ?: return emptyList()
val giteaIssues = gson.fromJson(json, Array<GiteaIssueResponse>::class.java)
return giteaIssues.map { mapToIssue(it) }
}
}
private fun createIssueLabelIds(
title: String,
body: String,
assignee: String? = null,
labelIds: List<Int>? = null
): Issue? {
val url = "$baseUrl/issues"
val createRequest = CreateIssueRequest(
title = title,
body = body,
assignee = assignee,
labels = labelIds
)
val request = buildPostRequest(url, createRequest)
executeRequest(request).use { response ->
if (!response.isSuccessful) {
println("Failed to create issue: $response")
println("Response body: ${response.body?.string()}")
return null
}
val json = response.body?.string() ?: return null
val giteaIssue = gson.fromJson(json, GiteaIssueResponse::class.java)
return mapToIssue(giteaIssue)
}
}
fun createIssue(
title: String,
body: String,
assignee: String? = null,
labelNames: List<String>? = null
): Issue? {
val labelIds = labelNames?.let { getLabelIdsByNames(it) }
return createIssueLabelIds(title, body, assignee, labelIds)
}
private fun updateIssueLabelsIds(
issueNumber: Int,
labelIds: List<Int>
): Issue? {
val url = "$baseUrl/issues/$issueNumber/labels"
log.info("url: {}", url)
val updateRequest = UpdateLabelsRequest(labels = labelIds)
val request = buildPutRequest(url, updateRequest)
executeRequest(request).use { response ->
if (!response.isSuccessful) {
println("Failed to update labels: $response")
println("Response body: ${response.body?.string()}")
return null
}
return getIssueById(issueNumber)
}
}
fun updateIssueLabels(
issueNumber: Int,
labelNames: List<String>
): Issue? {
val labelIds = getLabelIdsByNames(labelNames)
return updateIssueLabelsIds(issueNumber, labelIds)
}
}

View File

@@ -5,37 +5,62 @@ import ai.koog.agents.core.tools.annotations.Tool
import ai.koog.agents.core.tools.reflect.ToolSet import ai.koog.agents.core.tools.reflect.ToolSet
import org.eclipse.jgit.api.Git import org.eclipse.jgit.api.Git
import org.eclipse.jgit.transport.UsernamePasswordCredentialsProvider import org.eclipse.jgit.transport.UsernamePasswordCredentialsProvider
import org.slf4j.LoggerFactory
import org.springframework.beans.factory.annotation.Value
import java.io.File import java.io.File
val username: String? = System.getenv("GIT_USERNAME") //val username: String? = System.getenv("GIT_USERNAME")
val password: String? = System.getenv("GIT_PASSWORD") //val password: String? = System.getenv("GIT_PASSWORD")
//val workspace: String? = System.getenv("SF_WORKSPACE")
// Implement a simple calculator tool that can add two numbers // Implement a simple calculator tool that can add two numbers
@LLMDescription("Tools for performing basic git operations") @LLMDescription("Tools for performing basic git operations")
class GitTools : ToolSet { class GitTools : ToolSet {
companion object {
private val log = LoggerFactory.getLogger(GitTools::class.java)
}
val username = "developer-ai"
val password = "XCO1bsNyhyIzk2"
val workspace = "/Users/erik/projecten/workspace"
@Tool @Tool
@LLMDescription("Clone een git repository") @LLMDescription("Clone een git repository")
fun cloneRepository( fun cloneRepository(
@LLMDescription("The remote url from the git repository") @LLMDescription("The remote url from the git repository")
remoteUrl: String, remoteUrl: String,
@LLMDescription("The local path to clone the repository to") @LLMDescription("The name of the project to clone")
localPath: File) { projectName: String): String {
val localPath = workspace + File.separatorChar + projectName
log.info("localPath: {}", localPath)
if (File(localPath).exists()) {
return "\"$projectName\" already cloned"
}
try {
Git.cloneRepository() Git.cloneRepository()
.setURI(remoteUrl) .setURI(remoteUrl)
.setDirectory(localPath) .setDirectory(File(localPath))
.call() .call()
} catch (e: Exception) {
log.error("Error during clone $projectName", e)
return "Error found: ${e.message}"
}
return "Repository $projectName successfully cloned"
} }
@Tool @Tool
@LLMDescription("Pull a git repository") @LLMDescription("Pull a git repository")
fun pull( fun pull(
@LLMDescription("The local path from the local git repository") @LLMDescription("The local path from the local git repository")
localPath: String, projectName: String,
@LLMDescription("The branch to pull") @LLMDescription("The branch to pull")
branch: String) { branch: String) {
val localPath = workspace + File.pathSeparator + projectName
Git.open(File(localPath)).use { git -> Git.open(File(localPath)).use { git ->
git.pull() git.pull()
.setRemote("origin") .setRemote("origin")

View File

@@ -0,0 +1,5 @@
gitea.username=developer-ai
gitea.password=XCO1bsNyhyIzk2
gitea.owner=Trivion
gitea.repo=tasklist
gitea.base-url=http://dixienas:3001/api/v1

View File

@@ -0,0 +1,7 @@
gitea.username=developer-ai
gitea.password=XCO1bsNyhyIzk2
gitea.owner=Trivion
gitea.repo=tasklist
gitea.base-url=http://dixienas:3001/api/v1
workspace=/Users/erik/projecten/workspace