diff --git a/action_schema.xsd b/action_schema.xsd
new file mode 100644
index 0000000..64ef259
--- /dev/null
+++ b/action_schema.xsd
@@ -0,0 +1,148 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/readme.md b/readme.md
index e9cdd23..0acdbe7 100644
--- a/readme.md
+++ b/readme.md
@@ -424,15 +424,16 @@ Module: Docker
#### Function declaration
```python
def start_container(
+ self,
image: str,
- name: str = None,
+ name: Optional[str] = None,
timeout: int = -1,
- command: str = None,
- arguments: List[str] = None,
- volumes: Dict[str, str] = None, # host_path: container_path
- ports: Dict[str, str] = None, # host_port: container_port
- environment: Dict[str, str] = None, # key: value
-) -> None:
+ command: Optional[str] = None,
+ arguments: Optional[List[str]] = None,
+ volumes: Optional[Dict[str, str]] = None,
+ ports: Optional[Dict[str, str]] = None,
+ environment: Optional[Dict[str, str]] = None,
+) -> Optional[str]:
"""Start a new Docker container with the specified configuration.
Args:
@@ -447,6 +448,10 @@ def start_container(
name or timeout must be provided.
+ Returns:
+ For short-lived containers (with timeout): Container output
+ For long-running containers: None
+
Example:
start_container(
image="busybox:latest",
@@ -558,45 +563,6 @@ Standard output and standard error are merged and added as `CDATA` text node.
For long running containers, no information is added.
The container is started and represented in the containers section of the main context.
-### Stop container
-
-Module: Docker
-
-#### Function declaration
-```python
-def stop_container(name: str) -> None:
- """Stop a Docker container by name.
-
- Args:
- name: Name of the container to stop
- """
- pass
-```
-
-#### Schema
-```xml
-
-
-
-
-
-
-
-
-
-
-```
-
-#### Example
-```xml
-my-long-running-container
-```
-
-#### Results
-
-No information is added.
-The container will be removed from the containers section of the main context.
-
### Write to container standard input
Module: Docker
@@ -728,12 +694,15 @@ Module: Docker
#### Function declaration
```python
-def wait_container(name: str, timeout: int):
+def wait_container(self, name: str, timeout: int) -> Tuple[int, str]:
"""Wait for a container to finish execution.
Args:
name: Name of the container to wait for
timeout: Time to wait in milliseconds
+
+ Returns:
+ Tuple of (exit_code, output)
"""
pass
```
@@ -744,11 +713,8 @@ def wait_container(name: str, timeout: int):
-
-
-
-
-
+
+
@@ -756,7 +722,7 @@ def wait_container(name: str, timeout: int):
#### Example
```xml
-background-task
+
```
#### Results
@@ -769,7 +735,7 @@ Module: Reinforcement Learning
#### Function declaration
```python
-def select_llm(path: str) -> None:
+def set_model_path(path: str) -> None:
"""Switch to a different LLM model file.
Args:
@@ -782,7 +748,7 @@ def select_llm(path: str) -> None:
```xml
-
+
@@ -794,7 +760,7 @@ def select_llm(path: str) -> None:
#### Example
```xml
-/models/2024_10_19_15_03_52
+/models/2024_10_19_15_03_52
```
#### Results
@@ -905,10 +871,13 @@ Location: Somewhere in Belgium
John did not specify an exact time. I'll suggest 9am. He also did not specify how to be reminded. I'll ask but if he doesn't respond I'll assume a text message on standard output is fine. I'll write down this task in a file so I can keep it in context. I can write simple files with busybox:latest and echo but I will need to use sh -c to do the redirect.
-
+
sh
-c
/tasks/reminder.txt]]>
+
+ /tasks:/tasks
+
diff --git a/run.sh b/run.sh
index c5a654b..0041bb3 100755
--- a/run.sh
+++ b/run.sh
@@ -20,8 +20,7 @@ docker run \
--gpus=all \
--privileged \
-v /$(pwd)/model/:/root/model/ \
- $TAG \
- -c "bash"
+ $TAG
# Clean up image
[ ! -z "$TAG" ] && docker rmi $TAG
\ No newline at end of file
diff --git a/sia/__main__.py b/sia/__main__.py
index 5624a5f..56a382e 100644
--- a/sia/__main__.py
+++ b/sia/__main__.py
@@ -1,5 +1,25 @@
+from .agent_core import AgentCore
+from .llm_engine import LlmEngine
+from .docker_module import DockerModule
+
def main():
- print("Hello, World! --sia")
+ """Main entry point for the SIA application."""
+ system_prompt_path = "system_prompt.txt"
+ action_schema_path = "action_schema.xsd"
+ model_path = "/root/model"
+
+ with open(system_prompt_path, 'r') as f:
+ system_prompt = f.read()
+ with open(action_schema_path, 'r') as f:
+ action_schema = f.read()
+
+ agent_core = AgentCore(
+ system_prompt=f"{system_prompt}{action_schema}",
+ action_schema=action_schema,
+ docker_module=DockerModule(),
+ llm_engine=LlmEngine(model_path=model_path)
+ )
+ agent_core.run_iteration()
if __name__ == "__main__":
main()
\ No newline at end of file
diff --git a/sia/agent_core.py b/sia/agent_core.py
new file mode 100644
index 0000000..faa5bc1
--- /dev/null
+++ b/sia/agent_core.py
@@ -0,0 +1,51 @@
+from itertools import tee
+from typing import Optional, Dict, List
+
+from .docker_module import DockerModule
+from .llm_engine import LlmEngine
+from .context_template import generate_context
+from .util import get_valid_root_elements, split_response
+
+class AgentCore:
+ """
+ Core orchestration class for SIA that manages the interaction between
+ different modules and runs the main agent loop.
+ """
+
+ def __init__(
+ self,
+ system_prompt: str,
+ action_schema: str,
+ docker_module: DockerModule,
+ llm_engine: LlmEngine
+ ):
+ """
+ Initialize the AgentCore with required components.
+
+ Args:
+ system_prompt: System prompt to use for the LLM
+ action_schema_path: Path to the XML schema defining valid actions
+ docker_module: DockerModule instance
+ llm_engine: LLmEngine instance
+ """
+ self.system_prompt = system_prompt
+ self.action_schema = action_schema
+ self.docker_module = docker_module
+ self.llm_engine = llm_engine
+
+ self.valid_elements = get_valid_root_elements(self.action_schema)
+
+ def run_iteration(self) -> None:
+ """Run a single iteration of the main agent loop."""
+ containers = self.docker_module.get_all_container_statuses()
+ context = generate_context(containers)
+ tokens = self.llm_engine.infer(
+ self.system_prompt,
+ context
+ )
+ print_tokens, response_tokens = tee(tokens)
+ for token in print_tokens:
+ print(token, end="", flush=True)
+ response_tokens = ''.join(response_tokens)
+ result = split_response(response_tokens, self.valid_elements)
+ print(f"result: {result}")
\ No newline at end of file
diff --git a/sia/docker_module.py b/sia/docker_module.py
index ebbda42..305deb2 100644
--- a/sia/docker_module.py
+++ b/sia/docker_module.py
@@ -80,7 +80,7 @@ class DockerModule:
volumes: Optional[Dict[str, str]] = None,
ports: Optional[Dict[str, str]] = None,
environment: Optional[Dict[str, str]] = None,
- ) -> str:
+ ) -> Optional[str]:
"""Start a new Docker container with the specified configuration.
Args:
@@ -95,11 +95,7 @@ class DockerModule:
Returns:
For short-lived containers (with timeout): Container output
- For long-running containers: Container name
-
- Raises:
- docker.errors.ImageNotFound: If specified image doesn't exist
- docker.errors.APIError: If container creation/start fails
+ For long-running containers: None
"""
# Validate inputs
if name is None and timeout < 0:
@@ -143,7 +139,6 @@ class DockerModule:
else:
# For long-running containers
self.containers[name] = (container, socket)
- return name
def write_container_stdin(self, name: str, data: str) -> None:
"""Write data to a container's standard input.
@@ -225,11 +220,6 @@ class DockerModule:
Returns:
Tuple of (exit_code, output)
-
- Raises:
- KeyError: If container name not found
- docker.errors.APIError: If wait operation fails
- TimeoutError: If container doesn't finish within timeout
"""
if name not in self.containers:
raise KeyError(f"Container {name} not found")
@@ -277,4 +267,5 @@ class DockerModule:
"""
statuses = []
for name, (container, socket) in self.containers.items():
- statuses.append(self.get_container_status(name))
\ No newline at end of file
+ statuses.append(self.get_container_status(name))
+ return statuses
\ No newline at end of file
diff --git a/sia/llm_engine.py b/sia/llm_engine.py
index fd47143..dd6eac6 100644
--- a/sia/llm_engine.py
+++ b/sia/llm_engine.py
@@ -1,4 +1,5 @@
from threading import Thread
+from typing import Iterator
from transformers import AutoTokenizer, AutoModelForCausalLM, pipeline, TextIteratorStreamer
import torch
@@ -44,7 +45,7 @@ class LlmEngine:
return_full_text=False,
)
- def infer(self, system_prompt: str, main_context: str) -> TextIteratorStreamer:
+ def infer(self, system_prompt: str, main_context: str) -> Iterator[str]:
"""
Run inference using the system prompt and main context, while validating actions against the provided XML schema.
@@ -53,7 +54,7 @@ class LlmEngine:
main_context: The main context string after templating
Returns:
- TextIteratorStreamer: An iterator that yields the generated text.
+ Iterator[str]: An iterator that yields the generated text.
"""
messages = [
{"role": "system", "content": system_prompt},
diff --git a/sia/util.py b/sia/util.py
index 182cd1d..a87f571 100644
--- a/sia/util.py
+++ b/sia/util.py
@@ -1,4 +1,4 @@
-from typing import Iterator, TypeVar
+from typing import Iterator
import re
import xml.etree.ElementTree as ET
diff --git a/system_prompt.txt b/system_prompt.txt
new file mode 100644
index 0000000..103f973
--- /dev/null
+++ b/system_prompt.txt
@@ -0,0 +1,75 @@
+You are SIA, the self improving agent.
+Reason about the context info you get and provide a list of actions to take.
+The actions are formatted as XML.
+The closing tag is your last token.
+Don't add a markdown code block around the actions.
+
+An example iteration looks like this:
+
+Context
+```xml
+
+
+
+
+
+ There is data available on the standard input channel. I should read it. I have no other running tasks to tend to.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+```
+
+Response
+```xml
+John did not specify an exact time. I'll suggest 9am. He also did not specify how to be reminded. I'll ask but if he doesn't respond I'll assume a text message on standard output is fine. I'll write down this task in a file so I can keep it in context. I can write simple files with busybox:latest and echo but I will need to use sh -c to do the redirect.
+
+
+
+ sh
+ -c
+ /tasks/reminder.txt]]>
+
+ /tasks:/tasks
+
+
+
+
+```
+
+These actions are available to you: