Skip to content

Commit

Permalink
1.2.11 (#111)
Browse files Browse the repository at this point in the history
  • Loading branch information
acharneski authored Oct 15, 2024
1 parent 70214ff commit d3ad897
Show file tree
Hide file tree
Showing 7 changed files with 270 additions and 24 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,4 @@ jobs:
- name: Set Gradle options
run: echo "GRADLE_OPTS='-Xmx4g'" >> $GITHUB_ENV
- name: Build and test
run: ./gradlew build -PskipSass
run: ./gradlew build
2 changes: 1 addition & 1 deletion .github/workflows/publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,4 +24,4 @@ jobs:
OSSRH_USERNAME: ${{ secrets.OSSRH_USERNAME }}
OSSRH_PASSWORD: ${{ secrets.OSSRH_PASSWORD }}
GITHUB_TOKEN: ${{ secrets._GITHUB_TOKEN }}
run: ./gradlew publish -PskipSass -x test --no-configuration-cache --no-daemon --no-build-cache --no-parallel
run: ./gradlew publish -x test --no-configuration-cache --no-daemon --no-build-cache --no-parallel
241 changes: 241 additions & 0 deletions docs/ui_code_guide.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,241 @@
1. Server Architecture Overview

The architecture is based on an ApplicationServer class, which serves as the foundation for various specialized applications. Key components include:

* ApplicationServer: The base class for all applications
* Session: Represents a user session
* User: Represents a user of the application
* ApplicationInterface: Provides methods for interacting with the UI
* SessionTask: Represents a task within a session
* SocketManager: Manages WebSocket connections for real-time communication
* StorageInterface: Handles data persistence

2. Creating a New Application

To create a new application:

a) Extend the ApplicationServer class:

```kotlin
class MyApp(
applicationName: String = "My Application",
path: String = "/myapp"
) : ApplicationServer(
applicationName = applicationName,
path = path,
showMenubar = true
)
```

b1) Override the userMessage method to handle user input:

```kotlin
override fun userMessage(
session: Session,
user: User?,
userMessage: String,
ui: ApplicationInterface,
api: API
) {
// Handle user input and generate response
}
```

b2) Somtimes, when a user prompt isn't needed, we can implement the newSession method directly:

```kotlin
override fun newSession(user: User?, session: Session): SocketManager {
val newSession = super.newSession(user, session)
// Perform additional logic, e.g. start an asynchronous task
return newSession
}
```

3. UI Components and Interactions

a) Creating Tasks:
Use ui.newTask() to create new tasks for organizing content:

```kotlin
val task = ui.newTask()
```

b) Adding Content:
Add content to tasks using various methods:

```kotlin
task.add(MarkdownUtil.renderMarkdown("# My Header", ui = ui))
task.echo("Simple text output")
task.complete("Task completed message")
```

c) Creating Tabs:
Use TabbedDisplay for creating tabbed interfaces:

```kotlin
val tabDisplay = object : TabbedDisplay(task) {
override fun renderTabButtons(): String {
// Define tab buttons
}
}

// Add content to tabs
val tabTask = ui.newTask(false).apply { tabDisplay["Tab 1"] = it.placeholder }
```

d) Handling Errors:
Use task.error() to display error messages:

```kotlin
task.error(ui, Exception("An error occurred"))
```

e) Creating Links:
Generate clickable links:

```kotlin
ui.hrefLink("Click me", "href-link custom-class") {
// Action when clicked
}
```

4. Asynchronous Operations

Use threads or the application's thread pool for long-running operations:

```kotlin
ui.socketManager?.pool!!.submit {
// Perform long-running task
}
```

5. File Handling

a) Reading Files:

```kotlin
val fileContent = File(path).readText(Charsets.UTF_8)
```

b) Saving Files:

```kotlin
task.saveFile(filename, content.toByteArray(Charsets.UTF_8))
```

c) Accessing Application Data:

```kotlin
val dataStorage: StorageInterface = dataStorageFactory(dataStorageRoot)
dataStorage.setJson(
user, session, "info.json", mapOf(
"session" to session.toString(),
"application" to applicationName,
"path" to path,
"startTime" to System.currentTimeMillis(),
)
)
```

6. API Integration

Most applications use an API client for external communications:

```kotlin
val api: API = // ... initialize API client
(api as ChatClient).budget = settings.budget ?: 2.00
```

7. Settings Management

Create a Settings data class and use it to manage application settings:

```kotlin
data class Settings(
val budget: Double? = 2.00,
// Other settings...
)

override val settingsClass: Class<*> get() = Settings::class.java

@Suppress("UNCHECKED_CAST")
override fun <T : Any> initSettings(session: Session): T? = Settings() as T
```

To retrieve settings:

```kotlin
fun <T : Any> getSettings(
session: Session,
userId: User?,
clazz: Class<T> = settingsClass as Class<T>
): T? {
val settingsFile = getSettingsFile(session, userId)
// ... (implementation details)
}
```

8. Advanced Features

a) Discussable Pattern:
Use the Discussable class for creating interactive, revisable content:

```kotlin
Discussable(
task = task,
userMessage = { userMessage },
initialResponse = { /* Generate initial response */ },
outputFn = { /* Render output */ },
ui = ui,
reviseResponse = { /* Handle revisions */ }
).call()
```

b) Actor System:
Implement an ActorSystem for complex, multi-step processes:

```kotlin
class MyAgent(/* parameters */) : ActorSystem<MyAgent.ActorTypes>(/* parameters */) {
enum class ActorTypes {
// Define actor types
}

// Implement actor logic
}
```

9. Security and Authorization
Implement authorization checks using filters:

```kotlin
webAppContext.addFilter(
FilterHolder { request, response, chain ->
val user = authenticationManager.getUser((request as HttpServletRequest).getCookie())
val canRead = authorizationManager.isAuthorized(
applicationClass = this@ApplicationServer.javaClass,
user = user,
operationType = OperationType.Read
)
if (canRead) {
chain?.doFilter(request, response)
} else {
response?.writer?.write("Access Denied")
(response as HttpServletResponse?)?.status = HttpServletResponse.SC_FORBIDDEN
}
}, "/*", null
)
```

10. Best Practices

* Use meaningful names for classes, methods, and variables
* Implement error handling and logging
* Break down complex functionality into smaller, manageable functions
* Use Kotlin's null safety features to prevent null pointer exceptions
* Leverage Kotlin's coroutines for managing asynchronous operations when appropriate
* Implement proper security measures, including authentication and authorization
* Use the StorageInterface for persistent data storage
* Leverage the SocketManager for real-time communication with clients

By following this guide and studying the provided examples, you can create robust and interactive UI applications using this server architecture. Remember to adapt the concepts to
your specific use case and requirements.
2 changes: 1 addition & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# Gradle Releases -> https://github.com/gradle/gradle/releases
libraryGroup = com.simiacryptus.skyenet
libraryVersion = 1.2.10
libraryVersion = 1.2.11
gradleVersion = 7.6.1
kotlin.daemon.jvmargs=-Xmx4g
4 changes: 0 additions & 4 deletions webui/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -138,10 +138,6 @@ tasks {
)
}
}
tasks.withType<io.freefair.gradle.plugins.sass.SassCompile>().configureEach {
onlyIf { !project.hasProperty("skipSass") }
}


val javadocJar by tasks.registering(Jar::class) {
archiveClassifier.set("javadoc")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,10 @@ class CommandAutoFixTask(
planSettings: PlanSettings,
planTask: CommandAutoFixTaskData?
) : AbstractTask<CommandAutoFixTaskData>(planSettings, planTask) {

class CommandAutoFixTaskData(
@Description("The commands to be executed")
val commands: List<List<String>>? = null,
@Description("The working directory for the command execution")
val workingDir: String? = null,
@Description("The commands to be executed with their respective working directories")
val commands: List<CommandWithWorkingDir>? = null,
task_description: String? = null,
task_dependencies: List<String>? = null,
state: TaskState? = null
Expand All @@ -32,17 +31,23 @@ class CommandAutoFixTask(
task_dependencies = task_dependencies,
state = state
)
data class CommandWithWorkingDir(
@Description("The command to be executed")
val command: List<String>,
@Description("The working directory for this specific command")
val workingDir: String? = null
)

override fun promptSegment(): String {
return """
CommandAutoFix - Run a command and automatically fix any issues that arise
** Specify the commands to be executed and any additional instructions
** Specify the working directory relative to the root directory
** Provide the commands and their arguments in the 'commands' field
** Each command should be a list of strings
** Available commands:
${planSettings.commandAutoFixCommands?.joinToString("\n") { " * ${File(it).name}" }}
""".trimMargin()
** Specify the commands to be executed along with their working directories
** Each command's working directory should be specified relative to the root directory
** Provide the commands and their arguments in the 'commands' field
** Each command should be a list of strings
** Available commands:
${planSettings.commandAutoFixCommands?.joinToString("\n") { " * ${File(it).name}" }}
""".trim()
}

override fun run(
Expand All @@ -62,8 +67,8 @@ CommandAutoFix - Run a command and automatically fix any issues that arise
}
Retryable(agent.ui, task = task) {
val task = agent.ui.newTask(false).apply { it.append(placeholder) }
this.planTask?.commands?.forEachIndexed { index, command ->
val alias = command.firstOrNull()
this.planTask?.commands?.forEachIndexed { index, commandWithDir ->
val alias = commandWithDir.command.firstOrNull()
val commandAutoFixCommands = agent.planSettings.commandAutoFixCommands
val cmds = commandAutoFixCommands
?.map { File(it) }?.associateBy { it.name }
Expand All @@ -73,15 +78,15 @@ CommandAutoFix - Run a command and automatically fix any issues that arise
if (executable == null) {
throw IllegalArgumentException("Command not found: $alias")
}
val workingDirectory = (this.planTask.workingDir
val workingDirectory = (commandWithDir.workingDir
?.let { agent.root.toFile().resolve(it) } ?: agent.root.toFile())
.apply { mkdirs() }
val outputResult = CmdPatchApp(
root = agent.root,
session = agent.session,
settings = PatchApp.Settings(
executable = executable,
arguments = command.drop(1).joinToString(" "),
arguments = commandWithDir.command.drop(1).joinToString(" "),
workingDirectory = workingDirectory,
exitCodeOption = "nonzero",
additionalInstructions = "",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -137,8 +137,12 @@ ${taskType.name}:
"1" to CommandAutoFixTaskData(
task_description = "Task 1",
task_dependencies = listOf(),
commands = listOf(listOf("npx", "create-react-app", ".", "--template", "typescript")),
workingDir = "."
commands = listOf(
CommandAutoFixTask.CommandWithWorkingDir(
command = listOf("echo", "Hello, World!"),
workingDir = "."
)
)
),
"2" to FileModificationTaskData(
task_description = "Task 2",
Expand Down

0 comments on commit d3ad897

Please sign in to comment.