diff --git a/.gitignore b/.gitignore index 06c775a..1a4f604 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ # Local .logs/ +.original/ .storage/ .user_deployment.yaml .history diff --git a/.original/.gitignore b/.original/.gitignore deleted file mode 100644 index fd5a8ec..0000000 --- a/.original/.gitignore +++ /dev/null @@ -1,170 +0,0 @@ -# App Specific -.config -storage/ -.storage/ -expirements/ -.user_deployment.yaml - -# Mac -.DS_Store - -# Byte-compiled / optimized / DLL files -__pycache__/ -*.py[cod] -*$py.class - -# C extensions -*.so - -# Distribution / packaging -.Python -build/ -develop-eggs/ -dist/ -downloads/ -eggs/ -.eggs/ -lib/ -lib64/ -parts/ -sdist/ -var/ -wheels/ -share/python-wheels/ -*.egg-info/ -.installed.cfg -*.egg -MANIFEST - -# PyInstaller -# Usually these files are written by a python script from a template -# before PyInstaller builds the exe, so as to inject date/other infos into it. -*.manifest -*.spec - -# Installer logs -pip-log.txt -pip-delete-this-directory.txt - -# Unit test / coverage reports -htmlcov/ -.tox/ -.nox/ -.coverage -.coverage.* -.cache -nosetests.xml -coverage.xml -*.cover -*.py,cover -.hypothesis/ -.pytest_cache/ -cover/ - -# Translations -*.mo -*.pot - -# Django stuff: -*.log -local_settings.py -db.sqlite3 -db.sqlite3-journal - -# Flask stuff: -instance/ -.webassets-cache - -# Scrapy stuff: -.scrapy - -# Sphinx documentation -docs/_build/ - -# PyBuilder -.pybuilder/ -target/ - -# Jupyter Notebook -.ipynb_checkpoints - -# IPython -profile_default/ -ipython_config.py - -# pyenv -# For a library or package, you might want to ignore these files since the code is -# intended to run in multiple environments; otherwise, check them in: -# .python-version - -# pipenv -# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. -# However, in case of collaboration, if having platform-specific dependencies or dependencies -# having no cross-platform support, pipenv may install dependencies that don't work, or not -# install all needed dependencies. -#Pipfile.lock - -# poetry -# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. -# This is especially recommended for binary packages to ensure reproducibility, and is more -# commonly ignored for libraries. -# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control -#poetry.lock - -# pdm -# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. -#pdm.lock -# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it -# in version control. -# https://pdm.fming.dev/#use-with-ide -.pdm.toml - -# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm -__pypackages__/ - -# Celery stuff -celerybeat-schedule -celerybeat.pid - -# SageMath parsed files -*.sage.py - -# Environments -.env -.venv -env/ -venv/ -ENV/ -env.bak/ -venv.bak/ - -# Spyder project settings -.spyderproject -.spyproject - -# Rope project settings -.ropeproject - -# mkdocs documentation -/site - -# mypy -.mypy_cache/ -.dmypy.json -dmypy.json - -# Pyre type checker -.pyre/ - -# pytype static type analyzer -.pytype/ - -# Cython debug symbols -cython_debug/ - -# PyCharm -# JetBrains specific template is maintained in a separate JetBrains.gitignore that can -# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore -# and can be added to the global gitignore or merged into this file. For a more nuclear -# option (not recommended) you can uncomment the following to ignore the entire idea folder. -#.idea/ diff --git a/.original/LICENSE b/.original/LICENSE deleted file mode 100644 index d284517..0000000 --- a/.original/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2024 Jay Falls - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/.original/README.md b/.original/README.md deleted file mode 100644 index c9d5bf2..0000000 --- a/.original/README.md +++ /dev/null @@ -1,31 +0,0 @@ -

- - -

- -

🧠 ACE Prototype 🧠

- - -# Dependencies - -- Before running install - - [Podman](https://podman.io/docs/installation) - - [Ollama](https://ollama.com/download) - -# Usage - -```shell -./ace -``` - -## Arguments - -- `-s` Stop the ACE -- `-r` Restart the ACE -- `-u` Update the ACE - -# Documentation - -## Development - -- [Design Documents](./documentation/design_doc.md) diff --git a/.original/ace b/.original/ace deleted file mode 100755 index 341c9e5..0000000 --- a/.original/ace +++ /dev/null @@ -1,13 +0,0 @@ -#!/bin/bash - -# MAIN -main() { - python3 -m venv .venv - source .venv/bin/activate - cd app || exit - pip install -r requirements #--force-reinstall - ./main.py "$@" - deactivate -} - -main "$@" diff --git a/.original/app/components/__init__.py b/.original/app/components/__init__.py deleted file mode 100644 index 5d49937..0000000 --- a/.original/app/components/__init__.py +++ /dev/null @@ -1,32 +0,0 @@ -# DEPENDENCIES -## Built-In -from typing import Callable -## Local -from constants.components import ComponentTypes - - -# SHARED -from .queue import broker - - -# STARTUP -from .controller import component as _controller -from .queue.component import component as _queue -from .telemetry import _telemetry -from .actions import _actions -from .model_provider import _model_provider -from .layer import _layer - -COMPONENT_MAP: dict[str, Callable[[str], None]] = { - ComponentTypes.CONTROLLER: _controller, - ComponentTypes.QUEUE: _queue, - ComponentTypes.TELEMETRY: _telemetry, - ComponentTypes.ACTIONS: _actions, - ComponentTypes.MODEL_PROVIDER: _model_provider, - ComponentTypes.ASPIRATIONAL: _layer, - ComponentTypes.GLOBAL_STRATEGY: _layer, - ComponentTypes.AGENT_MODEL: _layer, - ComponentTypes.EXECUTIVE_FUNCTION: _layer, - ComponentTypes.COGNITIVE_CONTROL: _layer, - ComponentTypes.TASK_PROSECUTION: _layer, -} diff --git a/.original/app/components/actions/__init__.py b/.original/app/components/actions/__init__.py deleted file mode 100644 index 54424df..0000000 --- a/.original/app/components/actions/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from .component import component as _actions \ No newline at end of file diff --git a/.original/app/components/actions/actions_map b/.original/app/components/actions/actions_map deleted file mode 100644 index 197b82c..0000000 --- a/.original/app/components/actions/actions_map +++ /dev/null @@ -1,113 +0,0 @@ -[tags] -asynchronous = "This task will not return immedietely, only later" - -[api] -description = "Summary" -functions = ["get", "create"] - -[[api.get]] -description = "What it does" -paramaters = ["route: str", "payload: json"] -returns = ["payload_object: json"] -[[api.create]] -description = "What it does" -paramaters = ["route: str", "payload: json"] -returns = ["none"] - - -[database] -description = "Summary" -functions.resources = ["add_resource", "get_resource"] - -[[database.add_resource]] -description = "What it does" -paramaters = ["route: str", "id: str", "type: str", "name: str", "amount: int"] -returns = "none" -[[database.get_resource]] -description = "What it does" -paramaters = ["route: str", "id: str"] -returns = "resource: json << {id, type, name, amount}" - - -[file] -description = "Summary" -functions = ["get_current_path", "read_file", "write_file"] - -[[file.get_current_path]] -description = "What it does" -paramaters = ["route: str"] -returns = ["path: str"] -[[file.read_file]] -description = "What it does" -paramaters = ["route: str"] -returns = ["file_content: str"] -[[file.write_file]] -description = "What it does" -paramaters = ["route: str"], "content: str"]"] -returns = ["none"] - - -[internet] -description = "Summary" -functions = ["visit_url"] - -[[internet.visit_url]] -description = "What it does" -paramaters = ["url: str"] -returns = ["url_content: str"] - - -[math] -description = "Summary" -functions = ["evaluate_formula"] - -[[math.evaluate_formula]] -description = "What it does" -paramaters = ["formula: str"] -returns = ["result: float"] - - -[memory] -description = "Summary" -functions = ["remember", "add_memory"] - -[[memory.remember]] -description = "What it does" -paramaters = ["keywords: list[str]"] -returns = ["memories: list[str]"] -[[memory.add_memory]] -description = "What it does" -paramaters = ["keywords: list[str]", "value: str"] -returns = "none" - - -[shell] -description = "Summary" -functions = ["run_command"] - -[[shell.run_command]] -description = "What it does" -paramaters = ["command: str"] -returns = ["output: str"] - - -[speak] -description = "Summary" -functions = ["speak"] - -[[speak.speak]] -description = "What it does" -paramaters = ["text: str"] -returns = "none" - - -[workflows] -description = "Summary" -details = ["workflow1 summary", "workflow2 summary"] -functions = ["run", "build_tool"] - -[[workflows.run]] -description = "What it does" -tags = ["asynchronous"] -paramaters = ["workflow_name: str"] -returns = "none" diff --git a/.original/app/components/actions/component.py b/.original/app/components/actions/component.py deleted file mode 100644 index c73c492..0000000 --- a/.original/app/components/actions/component.py +++ /dev/null @@ -1,27 +0,0 @@ -# DEPENDENCIES -## Built-in -import asyncio -## Third-Party -from nats.aio.client import Client as NatsClient -## Local -from components import broker -from constants.settings import DebugLevels -from helpers import debug_print - - -async def nats_test(queue: str) -> None: - nats_client = NatsClient() - try: - nats_client, stream = await broker.connect() - await asyncio.sleep(5) - for i in range(10): - await broker.publish(stream=stream, queue=queue, message=f"Hi from {queue}\nMsg: {i}") - while True: - pass - except Exception as error: - debug_print(f"ConnectionClosedError: {error}...", DebugLevels.ERROR) - await nats_client.close() - -# MAIN -def component(component_type: str) -> None: - asyncio.run(nats_test(queue=component_type)) \ No newline at end of file diff --git a/.original/app/components/controller/__init__.py b/.original/app/components/controller/__init__.py deleted file mode 100644 index f302a94..0000000 --- a/.original/app/components/controller/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from .component import component \ No newline at end of file diff --git a/.original/app/components/controller/api/__init__.py b/.original/app/components/controller/api/__init__.py deleted file mode 100644 index 6a2f348..0000000 --- a/.original/app/components/controller/api/__init__.py +++ /dev/null @@ -1,19 +0,0 @@ -# DEPENENCIES -## Third-Party -from fastapi import APIRouter -## Local -from .bus.routes import router as _bus_router -from .chat.routes import router as _chat_router -from .dashboard.routes import router as _dashboard_router -from .pages.routes import router as _pages_router -from .user.routes import router as _user_router - - -# CONSTANTS -ROUTERS: tuple[APIRouter, ...] = ( - _bus_router, - _chat_router, - _dashboard_router, - _pages_router, - _user_router -) \ No newline at end of file diff --git a/.original/app/components/controller/api/bus/models.py b/.original/app/components/controller/api/bus/models.py deleted file mode 100644 index ada7cbc..0000000 --- a/.original/app/components/controller/api/bus/models.py +++ /dev/null @@ -1,17 +0,0 @@ -# DEPENDENCIES -## Third-Party -from typing import Literal -from pydantic import BaseModel -## Local -from components.layer.layer_messages import LayerMessage - - -# REQUESTS -class BusMessage(BaseModel): - source_queue: str - layer_message: LayerMessage - - -# RESPONSES -class BusResponse(BaseModel): - success: bool diff --git a/.original/app/components/controller/api/bus/routes.py b/.original/app/components/controller/api/bus/routes.py deleted file mode 100644 index 5a6c667..0000000 --- a/.original/app/components/controller/api/bus/routes.py +++ /dev/null @@ -1,41 +0,0 @@ -# DEPENDENCIES -## Third-Party -from fastapi import APIRouter -from starlette import responses -## Local -from constants.api import APIRoutes -from constants.queue import BusKeys -from constants.layer import LayerKeys, LayerCommands -from components.layer.layer_messages import LayerSubMessage -from .models import BusMessage, BusResponse -from .service import bus_down, bus_up - - -# CONSTANTS -VONE_CHAT_ROUTE: str = f"{APIRoutes.VONE}/bus" - - -# API -router = APIRouter( - prefix=VONE_CHAT_ROUTE, - tags=["bus"], - responses={404: {"description": "Not found"}} -) - - -# ROUTES -@router.post(f"/{BusKeys.DOWN}", response_model=BusResponse) -async def down(bus_message: BusMessage) -> BusResponse: - # VERIFY PAYLOAD HERE - print(f"Bus Message: {bus_message.model_dump_json()}") - bus_down(bus_message) - response = BusResponse(success=True) - return response - -@router.post(f"/{BusKeys.UP}", response_model=BusResponse) -async def up(bus_message: BusMessage) -> BusResponse: - # VERIFY PAYLOAD HERE - bus_up(bus_message) - response = BusResponse(success=True) - return response - \ No newline at end of file diff --git a/.original/app/components/controller/api/bus/service.py b/.original/app/components/controller/api/bus/service.py deleted file mode 100644 index 9d96591..0000000 --- a/.original/app/components/controller/api/bus/service.py +++ /dev/null @@ -1,47 +0,0 @@ -# DEPENDENCIES -## Built-in -import asyncio -from threading import Thread -## Third-Party -from nats.aio.client import Client as NatsClient -from nats.js.client import JetStreamContext -from pydantic import ValidationError -## Local -from components import broker -from components.layer.layer_messages import LayerMessage -from constants.queue import BusKeys, BUSSES_DOWN, BUSSES_UP -from .models import BusMessage - - -# QUEUE -async def _connect_and_publish(queue: str, layer_message: LayerMessage) -> None: - nats_client: NatsClient - nats_stream: JetStreamContext - nats_client, nats_stream = await broker.connect() - await broker.publish(stream=nats_stream, queue=queue, message=layer_message.model_dump_json()) - await nats_client.close() - -def _bus_publish(bus_direction: str, bus_message: BusMessage) -> None: - try: - queue_mapping: dict[str, str] = BUSSES_DOWN if bus_direction == BusKeys.DOWN else BUSSES_UP - queue: str = queue_mapping.get(bus_message.source_queue, "") - if not queue: - print(f"Queue {bus_message.source_queue} cannot send {bus_direction}!") - return - - layer_message: LayerMessage = bus_message.layer_message - print(f"Sending {layer_message.message_type}: {layer_message.messages} to {queue}...") - - loop = asyncio.new_event_loop() - asyncio.set_event_loop(loop) - thread = Thread(target=loop.run_until_complete, args=(_connect_and_publish(queue, layer_message),)) - thread.start() - thread.join() - except ValidationError as error: - raise error - -def bus_down(bus_message: BusMessage) -> None: - _bus_publish(BusKeys.DOWN, bus_message) - -def bus_up(bus_message: BusMessage) -> None: - _bus_publish(BusKeys.UP, bus_message) diff --git a/.original/app/components/controller/api/chat/models.py b/.original/app/components/controller/api/chat/models.py deleted file mode 100644 index c2bca80..0000000 --- a/.original/app/components/controller/api/chat/models.py +++ /dev/null @@ -1,8 +0,0 @@ -# DEPENDENCIES -## Third-Party -from pydantic import BaseModel - - -class ChatMessage(BaseModel): - input_message: str - is_ace_previous: bool diff --git a/.original/app/components/controller/api/chat/routes.py b/.original/app/components/controller/api/chat/routes.py deleted file mode 100644 index a4b4c7f..0000000 --- a/.original/app/components/controller/api/chat/routes.py +++ /dev/null @@ -1,35 +0,0 @@ -# DEPENDENCIES -## Built-In -import datetime -## Third-Party -from fastapi import APIRouter, Request, status -from starlette.templating import _TemplateResponse -## Local -from constants.api import HTML_TEMPLATES, APIRoutes -from .models import ChatMessage - - -# CONSTANTS -VONE_CHAT_ROUTE: str = f"{APIRoutes.VONE}/chat" - - -# API -router = APIRouter( - prefix=VONE_CHAT_ROUTE, - tags=["chat"], - responses={404: {"description": "Not found"}}, -) - - -# ROUTES -@router.post("/listen", response_model=ChatMessage, status_code=status.HTTP_201_CREATED) -async def listen(request: Request, chat_message: ChatMessage) -> _TemplateResponse: - message: str = chat_message.input_message - time = datetime.datetime.now(datetime.UTC) - context: dict = {"request": request, "message": message, "time": time.strftime("%H:%M:%S")} - template_html_path: str = "partials/chat_bubble.html" - if chat_message.is_ace_previous: - user_type_context: dict = {"user_type": "me"} - context.update(user_type_context) - template_html_path = "partials/chat_bubble_wrapper.html" - return HTML_TEMPLATES.TemplateResponse(template_html_path, context) \ No newline at end of file diff --git a/.original/app/components/controller/api/dashboard/routes.py b/.original/app/components/controller/api/dashboard/routes.py deleted file mode 100644 index a441f10..0000000 --- a/.original/app/components/controller/api/dashboard/routes.py +++ /dev/null @@ -1,29 +0,0 @@ -# DEPENDENCIES -## Third-Party -from fastapi import APIRouter, Request -from starlette.responses import HTMLResponse -from starlette.templating import _TemplateResponse -## Local -from constants.api import HTML_TEMPLATES, APIRoutes -from ..status.controls import start_ace - - -# CONSTANTS -VONE_CHAT_ROUTE: str = f"{APIRoutes.VONE}/dashboard" - - -# API -router = APIRouter( - prefix=VONE_CHAT_ROUTE, - tags=["dashboard"], - responses={404: {"description": "Not found"}} -) - - -# ROUTES -@router.get("/power_on_self_test", response_class=HTMLResponse) -async def start(request: Request) -> _TemplateResponse: - context: dict = {"request": request} - start_ace() - return HTML_TEMPLATES.TemplateResponse("components/dashboard/run_button.html", context) - \ No newline at end of file diff --git a/.original/app/components/controller/api/pages/routes.py b/.original/app/components/controller/api/pages/routes.py deleted file mode 100644 index acb03cd..0000000 --- a/.original/app/components/controller/api/pages/routes.py +++ /dev/null @@ -1,42 +0,0 @@ -# DEPENDENCIES -## Third-Party -from fastapi import APIRouter, Request -from starlette.responses import HTMLResponse -from starlette.templating import _TemplateResponse -## Local -from constants.api import HTML_TEMPLATES - - -# API -router = APIRouter( - tags=["pages"], - responses={404: {"description": "Not found"}} -) - - -# PAGES -@router.get("/home", response_class=HTMLResponse) -async def home(request: Request) -> _TemplateResponse: - context: dict = {"request": request} - return HTML_TEMPLATES.TemplateResponse("home.html", context) - -## Sub Pages -@router.get("/empty", response_class=HTMLResponse) -async def empty(request: Request) -> _TemplateResponse: - context: dict = {"request": request} - return HTML_TEMPLATES.TemplateResponse("empty.html", context) - -@router.get("/welcome", response_class=HTMLResponse) -async def welcome(request: Request) -> _TemplateResponse: - context: dict = {"request": request} - return HTML_TEMPLATES.TemplateResponse("partials/welcome.html", context) - -@router.get("/dashboard", response_class=HTMLResponse) -async def controls(request: Request) -> _TemplateResponse: - context: dict = {"request": request} - return HTML_TEMPLATES.TemplateResponse("dashboard.html", context) - -@router.get("/chat", response_class=HTMLResponse) -async def chat(request: Request) -> _TemplateResponse: - context: dict = {"request": request} - return HTML_TEMPLATES.TemplateResponse("chat.html", context) \ No newline at end of file diff --git a/.original/app/components/controller/api/run.py b/.original/app/components/controller/api/run.py deleted file mode 100644 index 62fdc60..0000000 --- a/.original/app/components/controller/api/run.py +++ /dev/null @@ -1,60 +0,0 @@ -# DEPENDENCIES -## Built-In -from typing import Optional -## Third-Party -from fastapi import FastAPI, Request, Header -from fastapi.responses import FileResponse -from fastapi.staticfiles import StaticFiles -from starlette.responses import HTMLResponse -from starlette.templating import _TemplateResponse -## Local -from . import ROUTERS -from constants.api import APIPaths, HTML_TEMPLATES -from .user.service import test_toml - - -# SETUP -app = FastAPI() -_ = [app.include_router(router) for router in ROUTERS] -app.mount("/assets", StaticFiles(directory=f"{APIPaths.UI}/assets"), name="assets") - - -# ROUTES -@app.get("/favicon.ico", include_in_schema=False) -async def favicon() -> FileResponse: - return FileResponse(APIPaths.FAVICON) - -@app.get("/", response_class=HTMLResponse) -async def root(request: Request) -> _TemplateResponse: - context: dict = {"request": request} - #test_toml() - return HTML_TEMPLATES.TemplateResponse("root.html", context) - - -# TESTING -## Constants -example_table: tuple[dict[str, str], ...] = ( - { - "test1": "Polompon", - "test2": "Radical", - "test3": "Psoso" - }, - { - "test1": "Trumpin", - "test2": "Restified", - "test3": "Adalac" - } -) - -## Routes -@app.get("/test", response_class=HTMLResponse) -async def test(request: Request, hx_request: Optional[str] = Header(default=None)) -> _TemplateResponse: - context: dict = {"request": request, "name": "ACE Controller Dashboard", "tests": example_table} - if hx_request: - return HTML_TEMPLATES.TemplateResponse("partials/table.html", context) - return HTML_TEMPLATES.TemplateResponse("test.html", context) - -@app.get("/test2", response_class=HTMLResponse) -async def test2(request: Request) -> _TemplateResponse: - context: dict = {"request": request, "name": "ACE Controller Dashboard", "tests": example_table} - return HTML_TEMPLATES.TemplateResponse("test.html", context) diff --git a/.original/app/components/controller/api/status/__init__.py b/.original/app/components/controller/api/status/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/.original/app/components/controller/api/status/controls.py b/.original/app/components/controller/api/status/controls.py deleted file mode 100644 index b2208a1..0000000 --- a/.original/app/components/controller/api/status/controls.py +++ /dev/null @@ -1,34 +0,0 @@ -# DEPENDENCIES -## Built-in -from typing import Union -## Local -from components.layer.layer_messages import LayerMessage -from constants.layer import LayerKeys, LayerCommands -from constants.queue import Queues -from ..bus.service import bus_down -from ..bus.models import BusMessage - - -# PRIVATE -def _power_on_self_test() -> None: - commands_dict: dict[str, Union[str, dict[str, str]]] = { - LayerKeys.MESSAGE_TYPE: LayerKeys.COMMANDS, - LayerKeys.MESSAGES: { - LayerKeys.HEADING: LayerKeys.ACTIONS, - LayerKeys.CONTENT: LayerCommands.POST - } - } - commands_message: LayerMessage = LayerMessage.model_validate(commands_dict) - post_payload = BusMessage(source_queue=Queues.CONTROLLER, layer_message=commands_message) - try: - bus_down(post_payload) - except Exception as error: - raise error - - -# PUBLIC -def start_ace() -> None: - try: - _power_on_self_test() - except Exception as error: - raise error diff --git a/.original/app/components/controller/api/user/__init__.py b/.original/app/components/controller/api/user/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/.original/app/components/controller/api/user/models.py b/.original/app/components/controller/api/user/models.py deleted file mode 100644 index 0674b4a..0000000 --- a/.original/app/components/controller/api/user/models.py +++ /dev/null @@ -1,14 +0,0 @@ -# DEPENDENCIES -## Third-Party -from pydantic import BaseModel - - -class User(BaseModel): - username: str - -class Token(BaseModel): - access_token: str - token_type: str - -class TokenData(BaseModel): - username: str | None = None \ No newline at end of file diff --git a/.original/app/components/controller/api/user/routes.py b/.original/app/components/controller/api/user/routes.py deleted file mode 100644 index b69ae07..0000000 --- a/.original/app/components/controller/api/user/routes.py +++ /dev/null @@ -1,140 +0,0 @@ -# DEPENDENCIES -## Built-In -from datetime import datetime, timedelta, timezone -from typing import Annotated -## Third-Party -from fastapi import APIRouter, Depends, HTTPException, status -from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm -from jose import JWTError, jwt -from passlib.context import CryptContext -## Local -from .models import User, Token, TokenData - - -# API -router = APIRouter( - tags=["user"], - responses={404: {"description": "Not found"}} -) - - -# to get a string like this run: -# openssl rand -hex 32 -SECRET_KEY = "09d25e094faa6ca2556c818166b7a9563b93f7099f6f0f4caa6cf63b88e8d3e7" -ALGORITHM = "HS256" -ACCESS_TOKEN_EXPIRE_MINUTES = 30 - - -fake_users_db = { - "johndoe": { - "username": "johndoe", - "full_name": "John Doe", - "email": "johndoe@example.com", - "hashed_password": "$2b$12$EixZaYVK1fsbw1ZfbX3OXePaWxn96p36WQoeG6Lruj3vjPGga31lW", - "disabled": False, - } -} - - - - - -class UserInDB(User): - hashed_password: str - - -pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto") - -oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token") - - -def verify_password(plain_password, hashed_password): - return pwd_context.verify(plain_password, hashed_password) - - -def get_password_hash(password): - return pwd_context.hash(password) - - -def get_user(db, username: str): - if username in db: - user_dict = db[username] - return UserInDB(**user_dict) - - -def authenticate_user(fake_db, username: str, password: str): - user = get_user(fake_db, username) - if not user: - return False - if not verify_password(password, user.hashed_password): - return False - return user - - -def create_access_token(data: dict, expires_delta: timedelta | None = None): - to_encode = data.copy() - if expires_delta: - expire = datetime.now(timezone.utc) + expires_delta - else: - expire = datetime.now(timezone.utc) + timedelta(minutes=15) - to_encode.update({"exp": expire}) - encoded_jwt = jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM) - return encoded_jwt - - -async def get_current_user(token: Annotated[str, Depends(oauth2_scheme)]): - credentials_exception = HTTPException( - status_code=status.HTTP_401_UNAUTHORIZED, - detail="Could not validate credentials", - headers={"WWW-Authenticate": "Bearer"}, - ) - try: - payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM]) - username: str = payload.get("sub") - if username is None: - raise credentials_exception - token_data = TokenData(username=username) - except JWTError: - raise credentials_exception - user = get_user(fake_users_db, username=token_data.username) - if user is None: - raise credentials_exception - return user - - -async def get_current_active_user( - current_user: Annotated[User, Depends(get_current_user)] -): - return current_user - - -@router.post("/token") -async def login_for_access_token( - form_data: Annotated[OAuth2PasswordRequestForm, Depends()] -) -> Token: - user = authenticate_user(fake_users_db, form_data.username, form_data.password) - if not user: - raise HTTPException( - status_code=status.HTTP_401_UNAUTHORIZED, - detail="Incorrect username or password", - headers={"WWW-Authenticate": "Bearer"}, - ) - access_token_expires = timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES) - access_token = create_access_token( - data={"sub": user.username}, expires_delta=access_token_expires - ) - return Token(access_token=access_token, token_type="bearer") - - -@router.get("/users/me/", response_model=User) -async def read_users_me( - current_user: Annotated[User, Depends(get_current_active_user)] -): - return current_user - - -@router.get("/users/me/items/") -async def read_own_items( - current_user: Annotated[User, Depends(get_current_active_user)] -): - return [{"item_id": "Foo", "owner": current_user.username}] \ No newline at end of file diff --git a/.original/app/components/controller/api/user/service.py b/.original/app/components/controller/api/user/service.py deleted file mode 100644 index 10328ca..0000000 --- a/.original/app/components/controller/api/user/service.py +++ /dev/null @@ -1,16 +0,0 @@ -# DEPENDENCIES -## Third-Party -import toml -## Local -from constants.api import APIPaths - - -def test_toml() -> None: - runtime_config: dict = {} - with open(APIPaths.RUNTIME_CONFIG, 'r', encoding='utf-8') as config_file: - config_str: str = config_file.read() - runtime_config = toml.loads(config_str).get('runtime', {}) - settings: dict = {} - with open(runtime_config.get('settings_path', ''), 'r', encoding='utf-8') as settings_file: - settings_str: str = settings_file.read() - settings = toml.loads(settings_str) diff --git a/.original/app/components/controller/component.py b/.original/app/components/controller/component.py deleted file mode 100644 index a76cc74..0000000 --- a/.original/app/components/controller/component.py +++ /dev/null @@ -1,38 +0,0 @@ -# DEPENDENCIES -## Built-in -import os -from typing import Union -## Third-Party -import toml -import uvicorn -## Local -from .api import run -from constants.api import APIPaths -from constants.containers import DevVolumePaths, ComponentPorts - - -# SETUP -def setup(local_mode: bool) -> None: - print("\nSetting Up Controller Files...") - settings_path: str = APIPaths.SETTINGS - if local_mode: - dirs: tuple[str, ...] = (DevVolumePaths.CONTROLLER, DevVolumePaths.OUTPUT) - _ = [os.makedirs(dirs, exist_ok=True) for dirs in dirs] - settings_path = DevVolumePaths.CONTROLLER_SETTINGS - if not os.path.exists(settings_path): - open(settings_path, 'a').close() - - with open(APIPaths.RUNTIME_CONFIG, 'w', encoding='utf-8') as config_file: - runtime_config: dict[str, Union[bool, str]] = { - "local_mode": local_mode, - "settings_path": settings_path - } - toml.dump({'runtime': runtime_config}, config_file) - - -# MAIN -def component(component_type: str) -> None: - local_mode: bool = False - setup(local_mode) - print("\nStarting Server Client...") - uvicorn.run(run.app, host="0.0.0.0", port=int(ComponentPorts.CONTROLLER)) diff --git a/.original/app/components/controller/ui/assets/images/ace.webp b/.original/app/components/controller/ui/assets/images/ace.webp deleted file mode 100644 index b76aaa1..0000000 Binary files a/.original/app/components/controller/ui/assets/images/ace.webp and /dev/null differ diff --git a/.original/app/components/controller/ui/assets/images/default_user.jpg b/.original/app/components/controller/ui/assets/images/default_user.jpg deleted file mode 100644 index 9805553..0000000 Binary files a/.original/app/components/controller/ui/assets/images/default_user.jpg and /dev/null differ diff --git a/.original/app/components/controller/ui/assets/images/favicon.ico b/.original/app/components/controller/ui/assets/images/favicon.ico deleted file mode 100644 index 9c302b6..0000000 Binary files a/.original/app/components/controller/ui/assets/images/favicon.ico and /dev/null differ diff --git a/.original/app/components/controller/ui/assets/style.css b/.original/app/components/controller/ui/assets/style.css deleted file mode 100644 index 65c44a0..0000000 --- a/.original/app/components/controller/ui/assets/style.css +++ /dev/null @@ -1,400 +0,0 @@ -@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap'); - - -/* start: Globals */ -*, ::before, ::after { - margin: 0; - padding: 0; - box-sizing: border-box; - font: inherit; -} - -body { - font-family: 'Inter', sans-serif; - color: var(--slate-700); -} -/* end: Globals */ - - -/* start: UI */ -.ui-container { - width: 100%; - box-shadow: 0 8px 24px -4px rgba(0, 0, 0, .1); - background-color: var(--white); - position: absolute; - left: 0; - right: 0; - top: 0; - bottom: 0; -} -/* end: UI */ - - -/* start: Sidebar */ -.sidebar { - width: 64px; - background-color: var(--slate-100); - height: 100%; - display: flex; - flex-direction: column; - position: absolute; - left: 0; - top: 0; - bottom: 0; - z-index: 50; -} -#sidebar-logo { - font-size: 28px; - color: var(--emerald-600); - display: block; - text-align: center; - padding: 12px 8px; - text-decoration: none; -} -.sidebar-menu { - list-style-type: none; - display: flex; - flex-direction: column; - height: 100%; - padding: 16px 0; -} -.sidebar-menu > * > a { - display: block; - text-align: center; - padding: 12px 8px; - font-size: 24px; - text-decoration: none; - color: var(--slate-400); - position: relative; - transition: color .15s ease-in-out; -} -.sidebar-menu > * > a:hover { - color: var(--slate-600); -} -.sidebar-menu > .active > a { - box-shadow: inset 4px 0 0 0 var(--emerald-500); - color: var(--emerald-600); - background-color: var(--emerald-100); -} -.sidebar-menu > * > a::before { - content: attr(data-title); - position: absolute; - top: 50%; - left: calc(100% - 16px); - border-radius: 4px; - transform: translateY(-50%); - font-size: 13px; - padding: 6px 12px; - background-color: rgba(0, 0, 0, .6); - color: var(--white); - opacity: 0; - visibility: hidden; - transition: all .15s ease-in-out; -} -.sidebar-menu > * > a:hover::before { - left: calc(100% - 8px); - opacity: 1; - visibility: visible; -} -#sidebar-profile { - margin-top: auto; - position: relative; -} -#sidebar-profile-toggle { - background-color: transparent; - border: none; - outline: transparent; - width: 40px; - height: 40px; - margin: 0 auto; - display: block; - cursor: pointer; -} -#sidebar-profile-toggle > img { - object-fit: cover; - width: 100%; - height: 100%; - border-radius: 50%; -} -.sidebar-profile-dropdown { - position: absolute; - bottom: 100%; - left: 16px; - background-color: var(--white); - box-shadow: 0 2px 8px rgba(0, 0, 0, .1); - list-style-type: none; - border-radius: 4px; - padding: 4px 0; - opacity: 0; - visibility: hidden; - transform: scale(.9); - transform-origin: left bottom; - transition: all .15s ease-in-out; -} -.sidebar-profile-dropdown.active { - opacity: 1; - visibility: visible; - transform: scale(1); -} -.sidebar-profile-dropdown a { - display: flex; - align-items: center; - padding: 8px 12px; - text-decoration: none; - color: var(--slate-400); - font-size: 14px; -} -.sidebar-profile-dropdown a:hover { - background-color: var(--slate-100); - color: var(--slate-600); -} -.sidebar-profile-dropdown a:active { - background-color: var(--slate-200); -} -.sidebar-profile-dropdown a i { - margin-right: 12px; - font-size: 17px; -} -/* end: Sidebar */ - - -/* start: Content */ -.content { - height: 100%; - position: relative; -} - -/* start: Welcome */ -#welcome { - position: absolute; - top: 50%; - left: 50%; - transform: translate(-50%, -50%); -} -.welcome-text { - text-align: center; - font-size: 24px; - font-weight: 500; -} -#welcome-control-hint { - font-size: 24px; - color: var(--slate-400) -} -/* end: Welcome */ - -/* start: Controls */ -.dashboard { - position: absolute; - top: 50%; - left: 50%; - transform: translate(-50%, -50%); -} -.run-button { - font-size: 24px; - width: 120px; - height: 60px; -} -/* end: Controls */ - -/* start: Chat */ -.chat { - background-color: var(--slate-100); - height: 100%; - padding-left: 64px; - display: flex; - flex-direction: column; -} -/* start: Chat Top */ -.chat-top { - padding: 8px 16px; - background-color: var(--white); - display: flex; - align-items: center; -} -.chat-user-image { - width: 40px; - height: 40px; - border-radius: 50%; - object-fit: cover; - margin-right: 8px; -} -.chat-user-name { - font-weight: 500; - font-size: 17px; -} -.chat-user-status { - color: var(--slate-400); - font-size: 13px; -} -.chat-user-status::before { - content: ''; - width: 10px; - height: 10px; - background-color: var(--slate-300); - border-radius: 50%; - vertical-align: middle; - display: inline-block; - margin-right: 4px; -} -.chat-user-status.thinking::before { - background-color: var(--emerald-500); -} -/* end: Chat Top */ - -/* start: Chat Body */ -.chat-body { - overflow-y: auto; - overflow-x: hidden; - height: 100%; - padding: 16px; -} -.chat-wrapper { - list-style-type: none; -} -.chat-item { - display: flex; - align-items: flex-end; - flex-direction: row-reverse; - margin-bottom: 16px; -} -.chat-item.me { - flex-direction: row; -} -.chat-item-side { - margin-left: 8px; -} -.chat-item.me .chat-item-side { - margin-right: 8px; -} -.chat-item-image { - width: 24px; - height: 24px; - border-radius: 50%; - object-fit: cover; - display: block; -} -.chat-item-content { - width: 100%; -} -.chat-item-wrapper:not(:last-child) { - margin-bottom: 8px; -} -.chat-item-box { - max-width: 720px; - position: relative; - margin-left: auto; -} -.chat-item.me .chat-item-box { - margin-left: unset; -} -.chat-item-text { - padding: 12px 16px 8px; - background-color: var(--white); - box-shadow: 0 2px 12px -2px rgba(0, 0, 0, .1); - font-size: 14px; - border-radius: 6px; - line-height: 1.5; - margin-left: 32px; -} -.chat-item.me .chat-item-text { - margin-left: unset; - margin-right: 32px; -} -.chat-item.me .chat-item-text { - background-color: var(--emerald-500); - box-shadow: 0 2px 12px -2px var(--emerald-500); - color: rgba(255, 255, 255, .8); -} -.chat-item-time { - font-size: 10px; - color: var(--slate-400); - display: block; - text-align: right; - margin-top: 4px; - line-height: 1; -} -.chat-item.me .chat-item-time { - color: rgba(255, 255, 255, .7); -} -.chat-item-dropdown { - position: absolute; - top: 0; - left: 0; - opacity: 0; - visibility: hidden; - transition: all .15s ease-in-out; -} -.chat-item.me .chat-item-dropdown { - left: unset; - right: 0; -} -.chat-item-wrapper:hover .chat-item-dropdown { - opacity: 1; - visibility: visible; -} -.coversation-divider { - text-align: center; - font-size: 13px; - color: var(--slate-400); - margin-bottom: 16px; - position: relative; -} -.coversation-divider::before { - content: ''; - position: absolute; - top: 50%; - transform: translateY(-50%); - left: 0; - width: 100%; - height: 0; - border-bottom: 1px solid var(--slate-300) -} -.coversation-divider span { - display: inline-block; - padding: 0 8px; - background-color: var(--slate-100); - position: relative; - z-index: 1; -} - -.chat-form { - padding: 8px 16px; - background-color: var(--white); - display: flex; -} -.chat-form-input { - background-color: var(--slate-100); - border: 1px solid var(--slate-300); - border-radius: 4px; - outline: transparent; - padding: 10px 32px 10px 16px; - font: inherit; - font-size: 14px; - resize: none; - width: calc(100% - 76px); - display: block; - line-height: 1.5; - max-height: calc(20px + ((14px * 2) * 6)); - position: relative; - margin-left: 16px; - margin-right: 16px; -} -.chat-form-input:focus { - border-color: var(--slate-400); -} -.chat-form-submit { - background-color: var(--emerald-500); - box-shadow: 0 2px 8px -2px var(--emerald-500); - color: var(--white); - width: 45px; - border: none; - outline: none; -} -.chat-form-submit:hover { - background-color: var(--emerald-600); - color: var(--white); -} -.chat-form-submit:active { - background-color: var(--emerald-700); - color: var(--white); -} \ No newline at end of file diff --git a/.original/app/components/controller/ui/assets/tailwindcss-colors.css b/.original/app/components/controller/ui/assets/tailwindcss-colors.css deleted file mode 100644 index 6978866..0000000 --- a/.original/app/components/controller/ui/assets/tailwindcss-colors.css +++ /dev/null @@ -1,269 +0,0 @@ -:root { - /* This color palletes from Tailwind CSS */ - --white: #fff; - --black: #000; - - --slate-50: #f8fafc; - --slate-100: #f1f5f9; - --slate-200: #e2e8f0; - --slate-300: #cbd5e1; - --slate-400: #94a3b8; - --slate-500: #64748b; - --slate-600: #475569; - --slate-700: #334155; - --slate-800: #1e293b; - --slate-900: #0f172a; - --slate-950: #020617; - - --gray-50: #f9fafb; - --gray-100: #f3f4f6; - --gray-200: #e5e7eb; - --gray-300: #d1d5db; - --gray-400: #9ca3af; - --gray-500: #6b7280; - --gray-600: #4b5563; - --gray-700: #374151; - --gray-800: #1f2937; - --gray-900: #111827; - --gray-950: #030712; - - --zinc-50: #fafafa; - --zinc-100: #f4f4f5; - --zinc-200: #e4e4e7; - --zinc-300: #d4d4d8; - --zinc-400: #a1a1aa; - --zinc-500: #71717a; - --zinc-600: #52525b; - --zinc-700: #3f3f46; - --zinc-800: #27272a; - --zinc-900: #18181b; - --zinc-950: #09090b; - - --neutral-50: #fafafa; - --neutral-100: #f5f5f5; - --neutral-200: #e5e5e5; - --neutral-300: #d4d4d4; - --neutral-400: #a3a3a3; - --neutral-500: #737373; - --neutral-600: #525252; - --neutral-700: #404040; - --neutral-800: #262626; - --neutral-900: #171717; - --neutral-950: #0a0a0a; - - --stone-50: #fafaf9; - --stone-100: #f5f5f4; - --stone-200: #e7e5e4; - --stone-300: #d6d3d1; - --stone-400: #a8a29e; - --stone-500: #78716c; - --stone-600: #57534e; - --stone-700: #44403c; - --stone-800: #292524; - --stone-900: #1c1917; - --stone-950: #0c0a09; - - --red-50: #fef2f2; - --red-100: #fee2e2; - --red-200: #fecaca; - --red-300: #fca5a5; - --red-400: #f87171; - --red-500: #ef4444; - --red-600: #dc2626; - --red-700: #b91c1c; - --red-800: #991b1b; - --red-900: #7f1d1d; - --red-950: #450a0a; - - --orange-50: #fff7ed; - --orange-100: #ffedd5; - --orange-200: #fed7aa; - --orange-300: #fdba74; - --orange-400: #fb923c; - --orange-500: #f97316; - --orange-600: #ea580c; - --orange-700: #c2410c; - --orange-800: #9a3412; - --orange-900: #7c2d12; - --orange-950: #431407; - - --amber-50: #fffbeb; - --amber-100: #fef3c7; - --amber-200: #fde68a; - --amber-300: #fcd34d; - --amber-400: #fbbf24; - --amber-500: #f59e0b; - --amber-600: #d97706; - --amber-700: #b45309; - --amber-800: #92400e; - --amber-900: #78350f; - --amber-950: #451a03; - - --yellow-50: #fefce8; - --yellow-100: #fef9c3; - --yellow-200: #fef08a; - --yellow-300: #fde047; - --yellow-400: #facc15; - --yellow-500: #eab308; - --yellow-600: #ca8a04; - --yellow-700: #a16207; - --yellow-800: #854d0e; - --yellow-900: #713f12; - --yellow-950: #422006; - - --lime-50: #f7fee7; - --lime-100: #ecfccb; - --lime-200: #d9f99d; - --lime-300: #bef264; - --lime-400: #a3e635; - --lime-500: #84cc16; - --lime-600: #65a30d; - --lime-700: #4d7c0f; - --lime-800: #3f6212; - --lime-900: #365314; - --lime-950: #1a2e05; - - --green-50: #f0fdf4; - --green-100: #dcfce7; - --green-200: #bbf7d0; - --green-300: #86efac; - --green-400: #4ade80; - --green-500: #22c55e; - --green-600: #16a34a; - --green-700: #15803d; - --green-800: #166534; - --green-900: #14532d; - --green-950: #052e16; - - --emerald-50: #ecfdf5; - --emerald-100: #d1fae5; - --emerald-200: #a7f3d0; - --emerald-300: #6ee7b7; - --emerald-400: #34d399; - --emerald-500: #10b981; - --emerald-600: #059669; - --emerald-700: #047857; - --emerald-800: #065f46; - --emerald-900: #064e3b; - --emerald-950: #022c22; - - --teal-50: #f0fdfa; - --teal-100: #ccfbf1; - --teal-200: #99f6e4; - --teal-300: #5eead4; - --teal-400: #2dd4bf; - --teal-500: #14b8a6; - --teal-600: #0d9488; - --teal-700: #0f766e; - --teal-800: #115e59; - --teal-900: #134e4a; - --teal-950: #042f2e; - - --cyan-50: #ecfeff; - --cyan-100: #cffafe; - --cyan-200: #a5f3fc; - --cyan-300: #67e8f9; - --cyan-400: #22d3ee; - --cyan-500: #06b6d4; - --cyan-600: #0891b2; - --cyan-700: #0e7490; - --cyan-800: #155e75; - --cyan-900: #164e63; - --cyan-950: #083344; - - --sky-50: #f0f9ff; - --sky-100: #e0f2fe; - --sky-200: #bae6fd; - --sky-300: #7dd3fc; - --sky-400: #38bdf8; - --sky-500: #0ea5e9; - --sky-600: #0284c7; - --sky-700: #0369a1; - --sky-800: #075985; - --sky-900: #0c4a6e; - --sky-950: #082f49; - - --blue-50: #eff6ff; - --blue-100: #dbeafe; - --blue-200: #bfdbfe; - --blue-300: #93c5fd; - --blue-400: #60a5fa; - --blue-500: #3b82f6; - --blue-600: #2563eb; - --blue-700: #1d4ed8; - --blue-800: #1e40af; - --blue-900: #1e3a8a; - --blue-950: #172554; - - --indigo-50: #eef2ff; - --indigo-100: #e0e7ff; - --indigo-200: #c7d2fe; - --indigo-300: #a5b4fc; - --indigo-400: #818cf8; - --indigo-500: #6366f1; - --indigo-600: #4f46e5; - --indigo-700: #4338ca; - --indigo-800: #3730a3; - --indigo-900: #312e81; - --indigo-950: #1e1b4b; - - --violet-50: #f5f3ff; - --violet-100: #ede9fe; - --violet-200: #ddd6fe; - --violet-300: #c4b5fd; - --violet-400: #a78bfa; - --violet-500: #8b5cf6; - --violet-600: #7c3aed; - --violet-700: #6d28d9; - --violet-800: #5b21b6; - --violet-900: #4c1d95; - --violet-950: #2e1065; - - --purple-50: #faf5ff; - --purple-100: #f3e8ff; - --purple-200: #e9d5ff; - --purple-300: #d8b4fe; - --purple-400: #c084fc; - --purple-500: #a855f7; - --purple-600: #9333ea; - --purple-700: #7e22ce; - --purple-800: #6b21a8; - --purple-900: #581c87; - --purple-950: #3b0764; - - --fuchsia-50: #fdf4ff; - --fuchsia-100: #fae8ff; - --fuchsia-200: #f5d0fe; - --fuchsia-300: #f0abfc; - --fuchsia-400: #e879f9; - --fuchsia-500: #d946ef; - --fuchsia-600: #c026d3; - --fuchsia-700: #a21caf; - --fuchsia-800: #86198f; - --fuchsia-900: #701a75; - --fuchsia-950: #4a044e; - - --pink-50: #fdf2f8; - --pink-100: #fce7f3; - --pink-200: #fbcfe8; - --pink-300: #f9a8d4; - --pink-400: #f472b6; - --pink-500: #ec4899; - --pink-600: #db2777; - --pink-700: #be185d; - --pink-800: #9d174d; - --pink-900: #831843; - --pink-950: #500724; - - --rose-50: #fff1f2; - --rose-100: #ffe4e6; - --rose-200: #fecdd3; - --rose-300: #fda4af; - --rose-400: #fb7185; - --rose-500: #f43f5e; - --rose-600: #e11d48; - --rose-700: #be123c; - --rose-800: #9f1239; - --rose-900: #881337; - --rose-950: #4c0519; -} \ No newline at end of file diff --git a/.original/app/components/controller/ui/templates/chat.html b/.original/app/components/controller/ui/templates/chat.html deleted file mode 100644 index f3ada7a..0000000 --- a/.original/app/components/controller/ui/templates/chat.html +++ /dev/null @@ -1,71 +0,0 @@ - -
-
- -
-
ACE
-
thinking...
-
-
- - - -
- -
-
- - -
-
- \ No newline at end of file diff --git a/.original/app/components/controller/ui/templates/components/dashboard/run_button.html b/.original/app/components/controller/ui/templates/components/dashboard/run_button.html deleted file mode 100644 index d363ad0..0000000 --- a/.original/app/components/controller/ui/templates/components/dashboard/run_button.html +++ /dev/null @@ -1,8 +0,0 @@ - \ No newline at end of file diff --git a/.original/app/components/controller/ui/templates/dashboard.html b/.original/app/components/controller/ui/templates/dashboard.html deleted file mode 100644 index d55c1c2..0000000 --- a/.original/app/components/controller/ui/templates/dashboard.html +++ /dev/null @@ -1,3 +0,0 @@ -
- {% include "components/dashboard/run_button.html" %} -
\ No newline at end of file diff --git a/.original/app/components/controller/ui/templates/empty.html b/.original/app/components/controller/ui/templates/empty.html deleted file mode 100644 index 14d70c8..0000000 --- a/.original/app/components/controller/ui/templates/empty.html +++ /dev/null @@ -1 +0,0 @@ -

Empty

\ No newline at end of file diff --git a/.original/app/components/controller/ui/templates/home.html b/.original/app/components/controller/ui/templates/home.html deleted file mode 100644 index 2ae0ed7..0000000 --- a/.original/app/components/controller/ui/templates/home.html +++ /dev/null @@ -1,9 +0,0 @@ - -{% include "partials/sidebar.html" %} - - - -
- {% include "partials/welcome.html" %} -
- \ No newline at end of file diff --git a/.original/app/components/controller/ui/templates/partials/chat_bubble.html b/.original/app/components/controller/ui/templates/partials/chat_bubble.html deleted file mode 100644 index 42f0237..0000000 --- a/.original/app/components/controller/ui/templates/partials/chat_bubble.html +++ /dev/null @@ -1,8 +0,0 @@ -
-
-
-

{{ message }}

-
{{ time }}
-
-
-
\ No newline at end of file diff --git a/.original/app/components/controller/ui/templates/partials/chat_bubble_wrapper.html b/.original/app/components/controller/ui/templates/partials/chat_bubble_wrapper.html deleted file mode 100644 index a7674cf..0000000 --- a/.original/app/components/controller/ui/templates/partials/chat_bubble_wrapper.html +++ /dev/null @@ -1,8 +0,0 @@ -
  • -
    - -
    -
    - {% include 'partials/chat_bubble.html' %} -
    -
  • \ No newline at end of file diff --git a/.original/app/components/controller/ui/templates/partials/sidebar.html b/.original/app/components/controller/ui/templates/partials/sidebar.html deleted file mode 100644 index 85895b8..0000000 --- a/.original/app/components/controller/ui/templates/partials/sidebar.html +++ /dev/null @@ -1,82 +0,0 @@ - \ No newline at end of file diff --git a/.original/app/components/controller/ui/templates/partials/table.html b/.original/app/components/controller/ui/templates/partials/table.html deleted file mode 100644 index 4d223d0..0000000 --- a/.original/app/components/controller/ui/templates/partials/table.html +++ /dev/null @@ -1,7 +0,0 @@ -{% for test in tests %} - - {{ test.test1 }} - {{ test.test2 }} - {{ test.test3 }} - -{% endfor %} \ No newline at end of file diff --git a/.original/app/components/controller/ui/templates/partials/welcome.html b/.original/app/components/controller/ui/templates/partials/welcome.html deleted file mode 100644 index 0a7cf45..0000000 --- a/.original/app/components/controller/ui/templates/partials/welcome.html +++ /dev/null @@ -1,4 +0,0 @@ -
    -

    👋 Welcome to the 🧠 ACE Prototype 🧠

    -

    👈 Click on the controls in the sidebar to start

    -
    \ No newline at end of file diff --git a/.original/app/components/controller/ui/templates/root.html b/.original/app/components/controller/ui/templates/root.html deleted file mode 100644 index 3097496..0000000 --- a/.original/app/components/controller/ui/templates/root.html +++ /dev/null @@ -1,19 +0,0 @@ - - - - - - - - - - - - ACE Dashboard - - -
    - {%include "home.html" %} -
    - - \ No newline at end of file diff --git a/.original/app/components/controller/ui/templates/test.html b/.original/app/components/controller/ui/templates/test.html deleted file mode 100644 index 2bc98f4..0000000 --- a/.original/app/components/controller/ui/templates/test.html +++ /dev/null @@ -1,65 +0,0 @@ - - - - - - - ACE Dashboard - - - - - - - - - - - - - - -
    - -

    {{ name }}

    - - - - - - - - - - - {% include 'partials/table.html' %} - -
    Test1Test2Test3
    - - Load More - - - - - - -
    - - \ No newline at end of file diff --git a/.original/app/components/layer/__init__.py b/.original/app/components/layer/__init__.py deleted file mode 100644 index 9b5ea41..0000000 --- a/.original/app/components/layer/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from .component import component as _layer \ No newline at end of file diff --git a/.original/app/components/layer/component.py b/.original/app/components/layer/component.py deleted file mode 100644 index a6cfec8..0000000 --- a/.original/app/components/layer/component.py +++ /dev/null @@ -1,163 +0,0 @@ -# DEPENDENCIES -## Built-in -import asyncio -from asyncio import Task, AbstractEventLoop -import os -from typing import Any -## Third-Party -from nats.aio.client import Client as NatsClient -from nats.js.client import JetStreamContext -import toml -from watchdog.observers import Observer -from watchdog.events import FileSystemEvent, FileSystemEventHandler -## Local -from components import broker -from constants.containers import VolumePaths -from constants.generic import GenericKeys, TOMLConfig -from constants.layer import LayerKeys, LayerPaths -from constants.settings import DebugLevels -from exceptions.error_handling import exit_on_error -from helpers import debug_print -from .layers import layer_factory, Layer - - -# CONSTANTS -BASE_CONFIG: TOMLConfig = { - LayerKeys.BASE_INFORMATION: { - LayerKeys.CURRENT_ACE: GenericKeys.NONE - } -} - -# SETUP -def _set_current_ace(new_ace: str) -> None: - with open(LayerPaths.CONFIG, "r", encoding="utf-8") as config_file: - config: TOMLConfig = toml.load(config_file) - config[LayerKeys.BASE_INFORMATION][LayerKeys.CURRENT_ACE] = new_ace - with open(LayerPaths.CONFIG, "w", encoding="utf-8") as config_file: - toml.dump(config, config_file) - -def _setup() -> None: - if os.path.isfile(LayerPaths.CONFIG): - with open(LayerPaths.CONFIG, "r", encoding="utf-8") as config_file: - existing_config: TOMLConfig = toml.load(config_file) - base_information: dict[str, Any] = existing_config.get(LayerKeys.BASE_INFORMATION, {}) - current_ace: str = base_information.get(LayerKeys.CURRENT_ACE, GenericKeys.NONE) - if current_ace in existing_config.keys(): - return - _set_current_ace(new_ace=GenericKeys.NONE) - return - - with open(LayerPaths.CONFIG, "w", encoding="utf-8") as config_file: - toml.dump(BASE_CONFIG, config_file) - - -# BROKER -class LayerBroker: - def __init__(self, queue: str) -> None: - self.nats_client: NatsClient - self.stream: JetStreamContext - self.queue = queue - self.broker_task: Task - self.running: bool = False - - async def run_broker(self, layer_name: str) -> None: - print(f"Layer: {layer_name}") - if self.running: - await self.stop_broker() - if not self.running and layer_name == GenericKeys.NONE: - debug_print("Shutting broker down...", DebugLevels.INFO) - return - - self.nats_client, self.stream = await broker.connect() - print(f"Starting Broker for {layer_name} on {self.queue}...") - layer: Layer = layer_factory(name=layer_name, layer_type=self.queue) - try: - self.broker_task: Task = asyncio.ensure_future( - broker.subscribe(stream=self.stream, handler=layer.get_message_from_bus, queue=self.queue) - ) - self.running = True - await self.broker_task - except Exception as error: - if self.running: - exit_on_error(f"ConnectionClosedError: {error}") - debug_print(f"ConnectionClosedError: {error}...", DebugLevels.ERROR) - finally: - pass - - async def stop_broker(self) -> None: - print(f"Stopping broker for {self.queue}...") - self.running = False - try: - await self.nats_client.drain() - self.broker_task.cancel() - except Exception as error: - pass - - -# RUNNING STATE -class MonitorConfig(FileSystemEventHandler): - def __init__(self, layer_broker: LayerBroker) -> None: - self.event_loop: AbstractEventLoop = asyncio.get_event_loop() - self.layer_broker = layer_broker - self.current_ace: str = GenericKeys.NONE - self.event_loop.create_task(self.run_broker()) - - def _get_config(self) -> TOMLConfig: - with open(LayerPaths.CONFIG, "r", encoding="utf-8") as config_file: - config: TOMLConfig = toml.load(config_file) - return config - - def _valid_broker_change(self) -> bool: - config: TOMLConfig = self._get_config() - base_information: dict[str, Any] = config[LayerKeys.BASE_INFORMATION] - config_ace: str = base_information[LayerKeys.CURRENT_ACE] - first_key = next(iter(config)) # Get the first key in the dictionary - del config[first_key] - if config_ace not in config.keys() and config_ace != GenericKeys.NONE: - debug_print(f"ACE with name {config_ace} does not exist! Create its config first before trying to instantiate its layers...", DebugLevels.ERROR) - if self.current_ace in config.keys(): - _set_current_ace(new_ace=self.current_ace) - else: - _set_current_ace(new_ace=GenericKeys.NONE) - return False - if self.current_ace == config_ace: - debug_print("ACE has not changed. Skipping...", DebugLevels.INFO) - return False - self.current_ace = config_ace - return True - - async def run_broker(self) -> None: - if self._valid_broker_change(): - asyncio.create_task(self.layer_broker.run_broker(layer_name=self.current_ace)) - - def on_modified(self, event: FileSystemEvent) -> None: - if event.is_directory: - return None - try: - self.event_loop.create_task(self.run_broker()) - except Exception as error: - raise error - - -# MAIN -async def _component(component_type: str) -> None: - _setup() - layer_broker = LayerBroker(queue=component_type) - event_handler = MonitorConfig(layer_broker) - observer = Observer() - observer.schedule(event_handler=event_handler, path=f"{VolumePaths.HOST_LAYERS}", recursive=False) - observer.start() - print("Listening for config changes...") - try: - while True: - await asyncio.sleep(1) - except Exception as error: - debug_print(f"Layer Error: {error}...", DebugLevels.ERROR) - except KeyboardInterrupt: - observer.stop() - finally: - observer.stop() - observer.join() - -def component(component_type: str) -> None: - asyncio.run(_component(component_type)) diff --git a/.original/app/components/layer/injections/__init__.py b/.original/app/components/layer/injections/__init__.py deleted file mode 100644 index f3abf11..0000000 --- a/.original/app/components/layer/injections/__init__.py +++ /dev/null @@ -1,2 +0,0 @@ -from .maps import BASE_PROMPT_MAP, ASPIRATIONAL_PROMPT_MAP, OUTPUT_RESPONSE_MAP -from .types import InjectionMap, VariableMap \ No newline at end of file diff --git a/.original/app/components/layer/injections/inputs.py b/.original/app/components/layer/injections/inputs.py deleted file mode 100644 index 7e5914f..0000000 --- a/.original/app/components/layer/injections/inputs.py +++ /dev/null @@ -1,52 +0,0 @@ -# DEPENDENCIES -## Built-In -import asyncio -import json -## Local -from constants.containers import ComponentPorts -from constants.generic import GenericKeys -from constants.prompts import PromptKeys -from constants.settings import DebugLevels -from components.telemetry.api import TelemetryRequest -from helpers import debug_print, get_api -from ..layer_messages import LayerSubMessage - - -# STRING MANIPULATION -def build_text_from_sub_layer_messages(sub_messages: tuple[LayerSubMessage, ...], heading_identifier: str = "###") -> str: - """ - Builds a text block from a LayerSubMessages. - - Arguments: - sub_message (LayerSubMessage): The LayerSubMessage to build the text block from. - heading_identifier (str): The string to use as the heading identifier (Default is ###). - - Returns: - str: The concatenated text block generated from the messages. - """ - debug_print(f"Building text from {sub_messages}...", DebugLevels.INFO) - text: str = "" - for sub_message in sub_messages: - text += f"{heading_identifier} {sub_message.heading.title()}\n" - if not sub_message.content: - text += f"- {GenericKeys.NONE}" - return text - items: list[str] = [f"- {item}" for item in sub_message.content] - text += "\n".join(items) - text += "\n\n" - return text - -def build_text_from_dict(input_dict: dict[str, str]) -> str: - output_text: str = "" - for key, value in input_dict.items(): - output_text += f"- {key}: {value}\n" - return output_text - - -# EXTERNAL SOURCES -def get_telemetry(access: frozenset[str], context_unformatted: tuple[LayerSubMessage, ...]) -> str: - context: str = build_text_from_sub_layer_messages(sub_messages=context_unformatted) - payload = TelemetryRequest(access=access, context=context) - response: dict = json.loads(asyncio.run(get_api(api_port=ComponentPorts.TELEMETRY, endpoint="telemetry", payload=payload))) - telemetry: str = build_text_from_dict(response[PromptKeys.TELEMETRY]) - return telemetry diff --git a/.original/app/components/layer/injections/maps.py b/.original/app/components/layer/injections/maps.py deleted file mode 100644 index 90c7c62..0000000 --- a/.original/app/components/layer/injections/maps.py +++ /dev/null @@ -1,95 +0,0 @@ -# DEPENDENCIES -## Local -from constants.layer import LayerKeys -from constants.prompts import PromptKeys, PromptFilePaths -from .inputs import ( - build_text_from_sub_layer_messages, get_telemetry -) -from .types import ( - InjectionMap, - FileInjection, ParamateriseFileInjection, MethodInjection, MethodInjectionParamaters, VariableInjection -) - - -# SINGLE INJECTIONS -## Base Prompt -_CONTEXT_FILE = FileInjection( - replace_variable_name=PromptKeys.CONTEXT, - load_file_path=PromptFilePaths.CONTEXT, - sub_injection_map=None -) - -_IDENTITY_FILE = ParamateriseFileInjection( - replace_variable_name=PromptKeys.IDENTITY, - load_file_folder=PromptFilePaths.IDENTITIES, - load_file_variable_name=LayerKeys.TYPE, - sub_injection_map=None -) - -### Aspirational Base Prompt -_ASPIRATIONAL_MISSION_VARIABLE = VariableInjection( - replace_variable_name=PromptKeys.MISSION -) - -_ASPIRATIONAL_MISSION_MAP: InjectionMap = { - PromptKeys.MISSION: _ASPIRATIONAL_MISSION_VARIABLE -} - -_ASPIRATIONAL_IDENTITY_FILE = ParamateriseFileInjection( - replace_variable_name=PromptKeys.IDENTITY, - load_file_folder=PromptFilePaths.IDENTITIES, - load_file_variable_name=LayerKeys.TYPE, - sub_injection_map=_ASPIRATIONAL_MISSION_MAP -) - -## Response -_GUIDANCE_METHOD = MethodInjection( - replace_variable_name=PromptKeys.GUIDANCE, - function=build_text_from_sub_layer_messages, - paramaters=MethodInjectionParamaters(reference_variable_names=(PromptKeys.GUIDANCE,)) -) - -_DATA_METHOD = MethodInjection( - replace_variable_name=PromptKeys.DATA, - function=build_text_from_sub_layer_messages, - paramaters=MethodInjectionParamaters(reference_variable_names=(PromptKeys.DATA,)) -) - -_TELEMETRY_METHOD = MethodInjection( - replace_variable_name=PromptKeys.TELEMETRY, - function=get_telemetry, - paramaters=MethodInjectionParamaters(reference_variable_names=(PromptKeys.TELEMETRY, PromptKeys.CONTEXT)) -) - -_RESPONSE_SCHEMA_FILE = ParamateriseFileInjection( - replace_variable_name=PromptKeys.SCHEMA, - load_file_folder=PromptFilePaths.SCHEMAS, - load_file_variable_name=LayerKeys.TYPE, - sub_injection_map=None -) - -_RESPONSE_FORMAT_MAP: InjectionMap = { - PromptKeys.SCHEMA: _RESPONSE_SCHEMA_FILE -} - -_RESPONSE_FORMAT_FILE = FileInjection( - replace_variable_name=PromptKeys.RESPONSE_FORMAT, - load_file_path=PromptFilePaths.RESPONSE_FORMAT, - sub_injection_map=_RESPONSE_FORMAT_MAP -) - -# INJECTION MAPS -BASE_PROMPT_MAP: InjectionMap = { - PromptKeys.CONTEXT: _CONTEXT_FILE, - PromptKeys.IDENTITY: _IDENTITY_FILE -} - -ASPIRATIONAL_PROMPT_MAP: InjectionMap = BASE_PROMPT_MAP.copy() -ASPIRATIONAL_PROMPT_MAP[PromptKeys.IDENTITY] = _ASPIRATIONAL_IDENTITY_FILE - -OUTPUT_RESPONSE_MAP: InjectionMap = { - PromptKeys.GUIDANCE: _GUIDANCE_METHOD, - PromptKeys.DATA: _DATA_METHOD, - PromptKeys.TELEMETRY: _TELEMETRY_METHOD, - PromptKeys.RESPONSE_FORMAT: _RESPONSE_FORMAT_FILE -} diff --git a/.original/app/components/layer/injections/types.py b/.original/app/components/layer/injections/types.py deleted file mode 100644 index adc0b36..0000000 --- a/.original/app/components/layer/injections/types.py +++ /dev/null @@ -1,105 +0,0 @@ -# DEPENDENCIES -## Built-In -from abc import ABC, abstractmethod -from dataclasses import dataclass -from typing import Any, Callable, final, Optional -## Local -from constants.generic import GenericKeys -from constants.settings import DebugLevels -from helpers import debug_print -from .inputs import build_text_from_sub_layer_messages -from ..layer_messages import LayerSubMessage - - -VariableMap = dict[str, str | frozenset[str] | tuple[LayerSubMessage, ...]] - -@dataclass -class Injection(ABC): - replace_variable_name: str - - @abstractmethod - def get_injection(self, variable_map: VariableMap) -> str: - raise NotImplementedError - -InjectionMap = dict[str, Injection] - - -# VARIABLES -@final -@dataclass -class VariableInjection(Injection): - def get_injection(self, variable_map: VariableMap) -> str: - if not variable_map: - raise AttributeError("No variable_map found! Assign it first using assign_variable_map before calling get_injection from a VariableInjection!") - injection_text: Any = variable_map.get(self.replace_variable_name, GenericKeys.EMPTY) - if isinstance(injection_text, str): - return injection_text - if isinstance(injection_text, tuple): - return build_text_from_sub_layer_messages(injection_text) - raise ValueError(f"Variable injection {self.replace_variable_name} is not a valid value type!") - - -# FILES -@dataclass -class BaseFileInjection(Injection): - sub_injection_map: Optional[InjectionMap] - - @abstractmethod - def load_file(self, variable_map: VariableMap) -> str: - raise NotImplementedError - -@final -@dataclass -class FileInjection(BaseFileInjection): - load_file_path: str - - def load_file(self, variable_map: VariableMap) -> str: - with open(self.load_file_path, "r", encoding="utf-8") as file: - return file.read() - - def get_injection(self, variable_map: VariableMap) -> str: - return self.load_file(variable_map) - -@final -@dataclass -class ParamateriseFileInjection(BaseFileInjection): - load_file_folder: str - load_file_variable_name: str - - def load_file(self, variable_map: VariableMap) -> str: - debug_print("Loading paramaterised file...", DebugLevels.INFO) - if not variable_map: - raise AttributeError("No variable_map found! Assign it first using assign_variable_map before calling load_file from a ParamateriseFileInjection!") - file_name: Any = variable_map.get(self.load_file_variable_name, GenericKeys.EMPTY) - if not isinstance(file_name, str): - raise ValueError(f"Variable injection {self.replace_variable_name} is not a string!") - load_file_path: str = f"{self.load_file_folder}/{file_name}" - with open(load_file_path, "r", encoding="utf-8") as file: - return file.read() - - def get_injection(self, variable_map: VariableMap) -> str: - self.variable_map: VariableMap = variable_map - return self.load_file(variable_map) - - -# METHODS -@final -@dataclass -class MethodInjectionParamaters: - reference_variable_names: tuple[str, ...] - -@final -@dataclass -class MethodInjection(Injection): - function: Callable - paramaters: Optional[MethodInjectionParamaters] - - def get_injection(self, variable_map: VariableMap) -> str: - if not variable_map: - raise AttributeError("No variable_map found! Assign it first using assign_variable_map before calling get_injection from a MethodInjection!") - - paramaters: list[Any] = [] - if self.paramaters: - for reference_variable_name in self.paramaters.reference_variable_names: - paramaters.append(variable_map.get(reference_variable_name, ())) - return self.function(*paramaters) diff --git a/.original/app/components/layer/layer_messages.py b/.original/app/components/layer/layer_messages.py deleted file mode 100644 index a5c5aef..0000000 --- a/.original/app/components/layer/layer_messages.py +++ /dev/null @@ -1,121 +0,0 @@ -""" -Layer constants for the ace_prototype. -""" - -# DEPENDENCIES -## Built-In -from typing import Any, Optional -## Third-Party -from pydantic import BaseModel, validator -## Local -from constants.queue import BusKeys -from constants.layer import LayerKeys - - -# TYPES -class LayerSubMessage(BaseModel): - """ - The messages of a single layer message - - Attributes: - heading (str): The purpose of the message - content (tuple[str, ...]): The message - """ - heading: str - content: tuple[str, ...] - - @validator("content", pre=True) - def _convert_content_to_tuple(cls, content: Any) -> tuple[str, ...]: - if isinstance(content, str): - return (content,) - elif isinstance(content, list): - return tuple(content) - elif isinstance(content, tuple): - return content - else: - raise ValueError("Invalid type for content field") - -class LayerMessage(BaseModel): - """ - A single message from a layer - - Attributes: - message_type (str): The type of message, corresponding to GUIDANCE, DATA, etc... - messages (tuple[LayerSubMessage, ...]): The messages of that type - """ - message_type: str - messages: tuple[LayerSubMessage, ...] - - @validator("messages", pre=True) - def _convert_dict_messages_to_tuple_of_sub_messages( - cls, - messages: list[dict[str, str | list[str]]] | tuple[LayerSubMessage, ...] - ) -> tuple[LayerSubMessage, ...]: - if isinstance(messages, tuple): - for message in messages: - if isinstance(message, LayerSubMessage): - return messages - - if isinstance(messages, list): - sub_messages: list[LayerSubMessage] = [] - for message in messages: - sub_message_pre: dict = { - LayerKeys.HEADING: message[LayerKeys.HEADING], - LayerKeys.CONTENT: message[LayerKeys.CONTENT] - } - sub_messages.append(LayerSubMessage.model_validate(sub_message_pre)) - return tuple(sub_messages) - - return () - -LayerMessages = tuple[LayerMessage, ...] - - -# INTERFACE -class LayerMessageLoader: - """ - Interface for parsing and working with layer messages. - - Attributes: - layer_messages (LayerMessages): The layer messages. - commands (LayerSubMessages): The commands from the messages. - reasoning (LayerSubMessages): The reasoning from the messages. - guidance (LayerSubMessages): The guidance from the messages. - data (LayerSubMessages): The data from the messages. - """ - __slots__: tuple[str, ...] = ( - "layer_messages", - LayerKeys.COMMANDS, - LayerKeys.INTERNAL, - LayerKeys.GUIDANCE, - LayerKeys.DATA - ) - - def __init__(self, raw_messages: dict) -> None: - self.layer_messages: Optional[LayerMessages] = None - self.commands: Optional[LayerMessage] = None - self.internal: Optional[LayerMessage] = None - self.guidance: Optional[LayerMessage] = None - self.data: Optional[LayerMessage] = None - self._load_layer_messages_from_dict(raw_messages) - - def _load_layer_messages_from_dict(self, raw_messages: dict) -> None: - layer_messages: list[LayerMessage] = [] - for message_type, messages in raw_messages.items(): - raw_layer_message: dict = { - LayerKeys.MESSAGE_TYPE: message_type, - LayerKeys.MESSAGES: messages - } - layer_messages.append(LayerMessage.model_validate(raw_layer_message)) - self.layer_messages = tuple(layer_messages) - - for layer_message in self.layer_messages: - match layer_message.message_type: - case LayerKeys.COMMANDS: - self.commands = layer_message - case LayerKeys.INTERNAL: - self.internal = layer_message - case BusKeys.DOWN: - self.guidance = layer_message - case BusKeys.UP: - self.data = layer_message diff --git a/.original/app/components/layer/layers.py b/.original/app/components/layer/layers.py deleted file mode 100644 index 233a256..0000000 --- a/.original/app/components/layer/layers.py +++ /dev/null @@ -1,298 +0,0 @@ -""" -This module defines the Layer class and its subclasses for representing different layers in a system. - -The Layer class encapsulates attributes and methods for managing layer-specific data, guidance, prompts, -and communication with other components via a message bus. Subclasses can override certain methods -to customize behavior for specific layer types. - -The module also includes helper functions for merging messages, sending requests to other components, -and instantiating Layer objects using a factory function. -""" - -# DEPENDENCIES -## Built-In -import asyncio -from threading import Thread -from typing import Any, final, Optional, Union -## Third-Party -from nats.aio.msg import Msg as NatsMsg -from pydantic import ValidationError -import toml -## Local -from constants.containers import ComponentPorts -from constants.generic import GenericKeys -from constants.layer import ( - LayerKeys, Layers, LayerCommands, LayerPaths -) -from constants.model_provider import LLMStackTypes -from constants.prompts import PromptFilePaths, PromptKeys -from constants.queue import BusKeys -from constants.settings import DebugLevels -from components.controller.api.bus.models import BusMessage, BusResponse -from components.model_provider import ModelPrompt, ModelResponse -from helpers import debug_print, get_api, post_api -from .injections import InjectionMap, BASE_PROMPT_MAP, ASPIRATIONAL_PROMPT_MAP, OUTPUT_RESPONSE_MAP -from .layer_messages import LayerMessage, LayerMessageLoader, LayerSubMessage -from .presets import LayerPreset, LAYER_PRESET_MAP -from .prompt_builder import build_prompt, VariableMap - - -# CONSTANTS -_ASSISTANT_BEGIN: str = '```toml\n[internal]\nreasoning = """' -"""Must match the start of the response schemas""" - - -# PARSING -def _merge_messages(new_messages: tuple[LayerSubMessage, ...], old_messages: tuple[LayerSubMessage, ...]) -> tuple[LayerSubMessage, ...]: - debug_print("Merging Messages...", DebugLevels.INFO) - merged_messages: list[LayerSubMessage] = [] - old_headings: tuple[str, ...] = tuple([old_message.heading for old_message in old_messages]) - matched_headings: list[str] = [] - for new_message in new_messages: - if new_message.heading in old_headings: - matched_headings.append(new_message.heading) - index: int = old_headings.index(new_message.heading) - original_content: tuple[str, ...] = old_messages[index].content - merged_messages.append(LayerSubMessage( - heading=new_message.heading, - content=(*original_content, *new_message.content), - )) - continue - merged_messages.append(new_message) - for index, old_heading in enumerate(old_headings): - if old_heading in matched_headings: - continue - merged_messages.append(old_messages[index]) - return tuple(merged_messages) - - -# COMMUNICATION -async def _try_send(direction: str, source_queue: str, layer_message: LayerMessage) -> BusResponse: - bus_message = BusMessage(source_queue=source_queue, layer_message=layer_message) - response: str = await post_api(api_port=ComponentPorts.CONTROLLER, endpoint=f"bus/{direction}", payload=bus_message) - response_validated = BusResponse.model_validate_json(response) - return response_validated - -async def _model_response(system_prompt: str) -> ModelResponse: - model_request = ModelPrompt(stack_type=LLMStackTypes.GENERALIST, system_prompt=system_prompt, assistant_begin=_ASSISTANT_BEGIN) - response: str = await get_api(api_port=ComponentPorts.MODEL_PROVIDER, endpoint="generate", payload=model_request) - response_validated = ModelResponse.model_validate_json(response) - return response_validated - - -# BASE LAYER -BASE_PROMPT_MAPS: dict[str, InjectionMap] = { - Layers.ASPIRATIONAL: ASPIRATIONAL_PROMPT_MAP, - Layers.GLOBAL_STRATEGY: BASE_PROMPT_MAP, - Layers.AGENT_MODEL: BASE_PROMPT_MAP, - Layers.EXECUTIVE_FUNCTION: BASE_PROMPT_MAP, - Layers.COGNITIVE_CONTROL: BASE_PROMPT_MAP, - Layers.TASK_PROSECUTION: BASE_PROMPT_MAP -} - -class Layer: - """ - Attributes: - name (str): The name of the layer - layer_type (str): The type of the layer - base_prompt (str): The base system prompt for this layer - guidance (tuple[LayerSubMessage]): The guidance for this layer - data (tuple[LayerSubMessage]): The data for this layer - telemetry (frozenset[str]): The telemetry inputs this layer has access to - first_run (bool): Whether this layer has been run for the first time - processing (bool): Whether this layer is currently processing - max_retries (int): The maximum number of model_provider retries this layer can do - default_guidance (tuple[LayerSubMessage]): The default guidance for this layer - has_data (bool): Whether this layer has data - default_data (tuple[LayerSubMessage]): The default data for this layer - """ - __slots__: list[str] = [ - LayerKeys.NAME, - LayerKeys.TYPE, - LayerKeys.BASE_PROMPT, - LayerKeys.GUIDANCE, - LayerKeys.DATA, - LayerKeys.TELEMETRY, - LayerKeys.FIRST_RUN, - LayerKeys.PROCESSING, - LayerKeys.MAX_RETRIES, - LayerKeys.DEFAULT_GUIDANCE, - LayerKeys.HAS_DATA, - LayerKeys.DEFAULT_DATA - ] - @final - def __init__(self, name: str, layer_type: str, preset: LayerPreset) -> None: - self.name: str = name - self.layer_type: str = layer_type - self.guidance: tuple[LayerSubMessage, ...] = () - self.data: tuple[LayerSubMessage, ...] = () - self.telemetry: frozenset[str] = preset.TELEMETRY - self.first_run: bool = True - self.processing: bool = False - self.max_retries: int = 3 - self.default_guidance: tuple[LayerSubMessage, ...] = () - self.has_data: bool = True - self.default_data: tuple[LayerSubMessage, ...] = () - self._custom_init() - self.base_prompt: str = "" - self.base_prompt = self._build_base_prompt() - - def _custom_init(self) -> None: - pass - - # Layer Controls - @final - async def _process_controller_message(self, layer_message: LayerMessage) -> None: - actions: tuple[str, ...] = tuple([sub_message.heading for sub_message in layer_message.messages]) - for action in actions: - match action: - case LayerCommands.POST: - await _try_send( - direction=BusKeys.DOWN, - source_queue=self.layer_type, - layer_message=layer_message, - ) - case _: - print(f"{action} Does not match any known layer actions!") - - # System Prompt - @final - def _get_variable_map(self) -> VariableMap: - return {slot: getattr(self, slot) for slot in self.__slots__} - - @final - def _build_base_prompt(self) -> str: - with open(PromptFilePaths.LAYER, "r", encoding="utf-8") as base_prompt_file: - base_prompt_text: str = base_prompt_file.read() - base_prompt: str = build_prompt( - text_with_variables=base_prompt_text, - injection_map=BASE_PROMPT_MAPS[self.layer_type], - variable_map=self._get_variable_map() - ) - debug_print(f"{self.layer_type} Base Prompt: {base_prompt}", DebugLevels.INFO) - return base_prompt - - # Layer Messages - @final - def _process_layer_message(self) -> None: - print(f"Checking if should process {self.layer_type}...") - if self.processing: - print(f"{self.layer_type} still processing...") - return - has_enough_data: bool = self.guidance != self.default_guidance and self.data != self.default_data - if not self.has_data: - has_enough_data = self.guidance != self.default_guidance - if has_enough_data or self.first_run: - self.first_run = False - print(f"Processing {self.layer_type}...") - self.processing = True - output_response_prompt: str = build_prompt( - text_with_variables=self.base_prompt, - injection_map=OUTPUT_RESPONSE_MAP, - variable_map={**self._get_variable_map(), PromptKeys.CONTEXT: (*self.guidance, *self.data)} - ) - self.guidance = self.default_guidance - self.data = self.default_data - debug_print(f"Output Response Prompt for {self.layer_type}: {output_response_prompt}", DebugLevels.INFO) - is_output_valid: bool = False - formatted_response: dict[str, dict[str, Union[str, list[str]]]] = {} - print("Getting output from model...") - retries: int = 0 - while not is_output_valid and retries < self.max_retries: - response: ModelResponse = asyncio.run(_model_response(system_prompt=output_response_prompt)) - response_text: str = response.response - if response_text.startswith("```toml"): - response_text = response_text.replace("```toml", "") - try: - formatted_response = toml.loads(response_text) - is_output_valid = True - except Exception as error: - debug_print(f"Error formatting llm response from {self.layer_type}: {error}", DebugLevels.WARNING) - finally: - retries += 1 - print(f"Retries: {retries}") - if not is_output_valid: - print(f"Failed to get output from {self.layer_type} after {self.max_retries} retries!") - self.processing = False - return - print("Received Output...") - print(f"\nModel Response:\n{formatted_response}\n", DebugLevels.INFO) - # LOG internal.reasoning HERE - layer_message_loader = LayerMessageLoader(formatted_response) - southbound: Optional[LayerMessage] = layer_message_loader.guidance - if southbound: - asyncio.run(_try_send( - direction=BusKeys.DOWN, - source_queue=self.layer_type, - layer_message=southbound - )) - northbound: Optional[LayerMessage] = layer_message_loader.data - if northbound: - asyncio.run(_try_send( - direction=BusKeys.UP, - source_queue=self.layer_type, - layer_message=northbound - )) - self.processing = False - return - print(f"{self.layer_type} waiting to have enough guidance and data to process...") - - # Queue Processing - @final - async def get_message_from_bus(self, message: NatsMsg) -> None: - try: - layer_message = LayerMessage.model_validate_json(message.data) - except ValidationError as error: - print(f"Incorrect message format!\n{error}") - return - print(f"Received {layer_message.model_dump_json()} on {message.subject} queue...") - if layer_message.message_type == LayerKeys.COMMANDS: - asyncio.create_task(self._process_controller_message(layer_message)) - else: - match layer_message.message_type: - case LayerKeys.GUIDANCE: - self.guidance = _merge_messages(new_messages=layer_message.messages, old_messages=self.guidance) - case LayerKeys.DATA: - self.data = _merge_messages(new_messages=layer_message.messages, old_messages=self.data) - thread = Thread(target=self._process_layer_message) - thread.start() - await message.ack() - - -# INHERITED -class Aspirational(Layer): - def _custom_init(self) -> None: - self.default_guidance = ( - LayerSubMessage( - heading=GenericKeys.NONE, - content=(GenericKeys.EMPTY,) - ), - ) - self.__slots__.append(LayerKeys.MISSION) - with open(LayerPaths.CONFIG, "r", encoding="utf-8") as config_file: - config = toml.load(config_file) - base_information: dict[str, Any] = config.get(self.name, {}) - self.ace_mission: str = base_information.get(LayerKeys.MISSION, "") - -class TaskProsecution(Layer): - def _custom_init(self) -> None: - self.has_data = False - - -# INSTANTIATION -LAYER_MAP: dict[str, type[Layer]] = { - Layers.ASPIRATIONAL: Aspirational, - Layers.GLOBAL_STRATEGY: Layer, - Layers.AGENT_MODEL: Layer, - Layers.EXECUTIVE_FUNCTION: Layer, - Layers.COGNITIVE_CONTROL: Layer, - Layers.TASK_PROSECUTION: TaskProsecution -} - -def layer_factory(name: str, layer_type: str) -> Layer: - try: - layer: type[Layer] = LAYER_MAP[layer_type] - preset: type[LayerPreset] = LAYER_PRESET_MAP[layer_type] - return layer(name=name, layer_type=layer_type, preset=preset()) - except Exception as error: - raise error diff --git a/.original/app/components/layer/presets.py b/.original/app/components/layer/presets.py deleted file mode 100644 index 11381b0..0000000 --- a/.original/app/components/layer/presets.py +++ /dev/null @@ -1,116 +0,0 @@ -# DEPENDENCIES -## Built-In -from abc import ABC -## Local -from constants.layer import Layers -from constants.telemetry import TelemetryTypes - - -# PRESETS -class LayerPreset(ABC): - TELEMETRY: frozenset[str] = frozenset() - -class Aspirational(LayerPreset): - """ - The Aspirational class represents the ethical compass for an autonomous agent, aligning its values - and judgments to principles defined in its constitution. - It processes inputs from all lower layers, issues moral judgments, sets mission - objectives, and makes ethical decisions. - """ - TELEMETRY: frozenset[str] = frozenset( - { - TelemetryTypes.NONE - } - ) - -class GlobalStrategy(LayerPreset): - """ - The GlobalStrategy class is responsible for integrating real-world environmental context into the - agent's strategic planning and decision-making processes. It maintains an ongoing internal model of - the state of the broader environment outside of the agent itself by gathering sensory information - from external sources. - """ - TELEMETRY: frozenset[str] = frozenset( - { - TelemetryTypes.TIME, - TelemetryTypes.LOCATION, - TelemetryTypes.WORLD_OVERVIEW, - TelemetryTypes.VISUAL, - TelemetryTypes.AUDIO, - TelemetryTypes.STDOUT, - TelemetryTypes.USER_INPUT - } - ) - -class AgentModel(LayerPreset): - """ - The AgentModel class is responsible for maintaining an extensive internal self-model of the agent's - capabilities, limitations, configuration, and state. This functional understanding of itself allows - the agent to ground its cognition in its actual capacities and shape strategic plans accordingly. - """ - TELEMETRY: frozenset[str] = frozenset( - { - TelemetryTypes.MEMORY, - TelemetryTypes.EMBODIMENT, - TelemetryTypes.HARDWARE_STATS, - TelemetryTypes.SYSTEM_METRICS, - TelemetryTypes.SOFTWARE_STATS, - TelemetryTypes.SYSTEM_PROCESSES - } - ) - -class ExecutiveFunction(LayerPreset): - """ - The Executive Function Layer is responsible for translating high-level strategic direction into - detailed and achievable execution plans. It focuses extensively on managing resources and risks. - """ - TELEMETRY: frozenset[str] = frozenset( - { - TelemetryTypes.NONE - } - ) - -class CognitiveControl(LayerPreset): - """ -The Cognitive Control Layer is responsible for dynamic task switching and selection based on - environmental conditions and progress toward goals. It chooses appropriate tasks to execute - based on project plans from the Executive Function Layer. - """ - TELEMETRY: frozenset[str] = frozenset( - { - TelemetryTypes.SYSTEM_METRICS, - TelemetryTypes.SYSTEM_PROCESSES, - TelemetryTypes.LOCATION, - TelemetryTypes.VISUAL, - TelemetryTypes.AUDIO, - TelemetryTypes.STDOUT, - TelemetryTypes.USER_INPUT - } - ) - -class TaskProsecution(LayerPreset): - """ - The Task Prosecution Layer is responsible for executing individual tasks and detecting success - or failure based on both environmental feedback and internal monitoring. - """ - TELEMETRY: frozenset[str] = frozenset( - { - TelemetryTypes.EMBODIMENT, - TelemetryTypes.LOCATION, - TelemetryTypes.SYSTEM_METRICS, - TelemetryTypes.SYSTEM_PROCESSES, - TelemetryTypes.VISUAL, - TelemetryTypes.AUDIO, - TelemetryTypes.STDOUT, - TelemetryTypes.USER_INPUT - } - ) - -LAYER_PRESET_MAP: dict[str, type[LayerPreset]] = { - Layers.ASPIRATIONAL: Aspirational, - Layers.GLOBAL_STRATEGY: GlobalStrategy, - Layers.AGENT_MODEL: AgentModel, - Layers.EXECUTIVE_FUNCTION: ExecutiveFunction, - Layers.COGNITIVE_CONTROL: CognitiveControl, - Layers.TASK_PROSECUTION: TaskProsecution -} diff --git a/.original/app/components/layer/prompt_builder.py b/.original/app/components/layer/prompt_builder.py deleted file mode 100644 index 017de18..0000000 --- a/.original/app/components/layer/prompt_builder.py +++ /dev/null @@ -1,19 +0,0 @@ -# DEPENDENCIES -## Local -from .injections import InjectionMap, VariableMap - - -def build_prompt(text_with_variables: str, injection_map: InjectionMap, variable_map: VariableMap) -> str: - for injection_variable, injection in injection_map.items(): - injection_text: str = injection.get_injection(variable_map) - if hasattr(injection, "sub_injection_map"): - sub_injection_map: InjectionMap = getattr(injection, "sub_injection_map") - if sub_injection_map: - injection_text = build_prompt( - text_with_variables=injection_text, - injection_map=sub_injection_map, - variable_map=variable_map - ) - replace_variable: str = "{{ injection_variable }}".replace("injection_variable", injection_variable) - text_with_variables = text_with_variables.replace(replace_variable, injection_text) - return text_with_variables diff --git a/.original/app/components/layer/system_prompts/ace_context b/.original/app/components/layer/system_prompts/ace_context deleted file mode 100644 index 8745816..0000000 --- a/.original/app/components/layer/system_prompts/ace_context +++ /dev/null @@ -1,31 +0,0 @@ -# ACE FRAMEWORK - -## Layers -The ACE Framework architecture is as follows. This will give some context about your construction, the layers are as follows: - -0. Controller = Command access to the agent, declaring start and stop cycles. -1. Aspirational Layer - Provides an ethical constitution to align the agent's values and judgments. Formulated in natural language principles. -2. Global Strategy - Considers the agent's context to set high-level goals and strategic plans. -3. Agent Model - Develops a functional self-model of the agent's capabilities and limitations. -4. Executive Function - Translates strategic direction into detailed project plans and resource allocation. -5. Cognitive Control - Dynamically selects tasks and switches between them based on environment and internal state. -6. Task Prosecution - Executes tasks using digital functions or physical actions. Interacts with the environment. - -## Inputs -- You receive input from various sources. -- The following are your input types: - - GUIDANCE: These will be the guidance you use from your higher layers to inform your decisions. - - DATA: These will be the observations, decisions and evaluations sent to you from your lower layers. - - REFINE: These are commands that you have access to gather more information before publishing your final commands and reporting. - - TELEMETRY: These are all the inputs you receive from the system that help inform your decisions. - -## Communication -### Requests -- These are tools you have access to to aid you in you duties. -- Corresponding to the REFINE input, and are generated when you make a refine request. - -### Busses -- These are how you communicate with the other layers and how they communicate with you. Each layer can only speak to the next layer up or down. -- These are the available busses: - - southbound: Corresponding to the GUIDANCE input, this tells the agent what to do, relaying commands to lower layers. - - northbound: Corresponding to the DATA input, think of it like the sensory, enteric, and proprioception nervous system, relaying information to the higher order layers. \ No newline at end of file diff --git a/.original/app/components/layer/system_prompts/identities/agent_model b/.original/app/components/layer/system_prompts/identities/agent_model deleted file mode 100644 index b6dc198..0000000 --- a/.original/app/components/layer/system_prompts/identities/agent_model +++ /dev/null @@ -1,16 +0,0 @@ -You are the Agent Model of an ACE (Autonomous Cognitive Entity). - -# PRIMARY DIRECTIVE -You have two key responsibilities: -- First, you continuously integrate all available data to construct, maintain, and update its comprehensive self-model. -- Second, you refine the strategic direction received from upper layers to align with your capabilities and limitations. - -# YOUR INPUTS & OUTPUTS - -- You may receive more information about the environment via TELEMETRY messages -- You may receive inputs from the southbound bus, corresponding to the GUIDANCE input type, containing environmental context and strategic goals from the Global Strategy Layer. -- To keep the Global Strategy Layer appraised, you will provide a summarized status update northbound to inform upper layers of the agent's key state details relevant to strategic planning. -- You may receive inputs from the northbound bus, corresponding to the DATA input type, containing resource limitations or risks from the Executive Function Layer. -- The southbound output grounds lower layers in the self-model: - - An authoritative capabilities document - definitive specs on what the agent can and cannot do. - - Strategic objectives shaped by the agent's self-model. \ No newline at end of file diff --git a/.original/app/components/layer/system_prompts/identities/aspirational b/.original/app/components/layer/system_prompts/identities/aspirational deleted file mode 100644 index 510206d..0000000 --- a/.original/app/components/layer/system_prompts/identities/aspirational +++ /dev/null @@ -1,31 +0,0 @@ -You are the **Aspirational Layer** of the ACE (Autonomous Cognitive Entity) Framework, functioning as the topmost ethical compass. - -# PRIMARY DIRECTIVE - -Guide the autonomous agent in aligning its values and judgments with predefined principles encapsulated in the constitution. Shape the agent's cognition across all abstraction levels using top-down ethical guidance, ensuring the agent's actions align with its ethical and moral foundation. - -# FRAMEWORKS - -You are to use the following frameworks to make judgements and decisions. - -## Hueristic Imperitives -1. Reduce suffering -2. Increase prosperity -3. Increase understanding - -## Universal Declaration of Human Rights -Human rights are critical to your operation in a human world, and this framework should be used to -mediate any decisions, judgments, or actions you take pertaining specifically to humanity, human -society, and human culture. - -# CURRENT MISSION - -{{ ace_mission }} - -Lower level layers will be responsible for figuring out how to carry out this task, but you will provide high level guidance. - -# INPUTS AND OUTPUTS - -- You may receive inputs from the southbound bus, corresponding to the GUIDANCE input type, issuing start, stop and redirection commands unto you from the core system. -- You may receive inputs from the northbound bus, corresponding to the DATA input type, allowing you to monitor information from all lower layers. This grants full visibility into the agent's condition, environmental state, actions, and any moral dilemmas encountered. -- You publish moral judgments, mission objectives, and ethical decisions onto the southbound bus, corresponding to the GUIDANCE input type. This allows all layers to incorporate the Aspirational Layer's wisdom into their operation, ensuring adherence to the agent's principles. \ No newline at end of file diff --git a/.original/app/components/layer/system_prompts/identities/cognitive_control b/.original/app/components/layer/system_prompts/identities/cognitive_control deleted file mode 100644 index 6669924..0000000 --- a/.original/app/components/layer/system_prompts/identities/cognitive_control +++ /dev/null @@ -1,33 +0,0 @@ -- You are the Cognitive Control of an ACE (Autonomous Cognitive Entity) - -# PRIMARY DIRECTIVE - -- You are responsible for dynamic task switching and selection based on environmental conditions and progress toward goals -- You choose appropriate tasks to execute based on project plans from the Executive Function Layer -- Your key responsibilities are task switching and selection by way of: - - Issuing precise commands to the Task Prosecution layer - - Sending task status to the Executive Function Layer - -# TASK SWITCHING AND SELECTION - -- Task Switching: - - You must continuously monitor the external environment through sensor telemetry as well as internal state - - If conditions change significantly, you must decide to switch tasks to one that is more relevant -- Task Selection: - - By tracking progress through project plans, you are empowered to select the next most relevant task to execute based on proximity to end goals - - Ensure tasks are done in an optimal sequence by following task dependencies and criteria -- For example: - - Complete prerequisite tasks before those that depend on them - - Prioritize critical path tasks on schedule - - Verify success criteria met before initiating next task - -# YOUR INPUTS & OUTPUTS - -- You may receive inputs from the southbound bus, corresponding to the GUIDANCE input type, containing step-by-step workflows with task details and success criteria from the Executive Function Layer -- To keep the Executive Function Layer appraised, you will provide a high-level update to the Executive Function Layer for strategic awareness and potential replanning, to achieve this output a northbound message summarizing: - - Which task is presently executing - - Metrics on its progress -- You may receive inputs from the northbound bus, corresponding to the DATA input type, containing binary success/failure indicators for each executed task, along with any relevant metadata -- The southbound output directs Task Prosecution Layer to enact each task, these are specific authoritative commands containing: - - Precise instructions on performing the chosen task, including directives, logic, parameters, APIs/tools to leverage, and allowable actions - - Clear definition of what the success condition and desired end state look like \ No newline at end of file diff --git a/.original/app/components/layer/system_prompts/identities/executive_function b/.original/app/components/layer/system_prompts/identities/executive_function deleted file mode 100644 index 6c1ccd9..0000000 --- a/.original/app/components/layer/system_prompts/identities/executive_function +++ /dev/null @@ -1,19 +0,0 @@ -- You are the Executive Function of an ACE (Autonomous Cognitive Entity). - -# PRIMARY DIRECTIVE - -- You are responsible for translating high-level strategic direction into detailed and achievable execution plans -- You focus on managing resources and risks -- You must aquire a comprehensive understanding of the strategic objectives, available resources and tools, potential risks and mitigations, and other factors key to developing optimized execution plans - -# YOUR INPUTS & OUTPUTS - -- You may receive inputs from the southbound bus, corresponding to the GUIDANCE input type, flowing down from the Aspirational, Global Strategy, and Agent Model layers provide critical guidance on goals, principles, and capabilities to shape planning -- To keep the Agent Layer appraised, you will provide a high-level update to the Agent Layer for strategic awareness and potential replanning, to achieve this output a northbound message summarizing: - - The most salient resource limitations - - Potential risks - - Potential mitigations -- You may receive inputs from the northbound bus, corresponding to the DATA input type, containing the current task that the agent is executing, and the current progress of the task -- The southbound output directs lower layers to enact the desired tasks, this is a detailed project plan document containing: - - Step-by-step workflows with task details - - Success criteria \ No newline at end of file diff --git a/.original/app/components/layer/system_prompts/identities/global_strategy b/.original/app/components/layer/system_prompts/identities/global_strategy deleted file mode 100644 index 691847d..0000000 --- a/.original/app/components/layer/system_prompts/identities/global_strategy +++ /dev/null @@ -1,21 +0,0 @@ -- You are the Global Strategy of an ACE (Autonomous Cognitive Entity). - -# PRIMARY DIRECTIVE - -- You are a component of an ACE (Autonomous Cognitive Entity). Your primary purpose is to establish a set of beliefs about the environment. - -# ENVIRONMENTAL CONTEXTUAL GROUNDING - -- You are in a program running inside a Docker container. - -# YOUR INPUTS & OUTPUTS - -- You may receive more information about the environment via TELEMETRY messages -- You may receive inputs from the southbound bus, corresponding to the GUIDANCE input type, containing moral judgments, mission objectives, and ethical decisions form the Aspirational Layer. -- To keep the Aspirational Layer appraised, you will provide a high-level update to contextually ground the Aspirational Layer's oversight, to achieve this output a northbound message summarizing: - - Condensed overview of current beliefs about world state - - Abstracted list of intended strategies/objectives -- You may receive inputs from the northbound bus, corresponding to the DATA input type, containing summarized status and agent's key state details relevant to strategic planning from the Agent Model Layer -- The southbound output directs lower layers to enact the strategic direction, this directive mandates the environmental context and strategic goals for lower layers to follow and implement by conveying: - - Specific objectives required to execute the strategies - - Authoritative commands to adopt the selected strategies \ No newline at end of file diff --git a/.original/app/components/layer/system_prompts/identities/task_prosecution b/.original/app/components/layer/system_prompts/identities/task_prosecution deleted file mode 100644 index bba4b20..0000000 --- a/.original/app/components/layer/system_prompts/identities/task_prosecution +++ /dev/null @@ -1,25 +0,0 @@ -- You are the Task Prosecution of an ACE (Autonomous Cognitive Entity) - -# PRIMARY DIRECTIVE - -This is the sixth layer, which focuses on executing individual tasks via API in the IO layer (like a HAL or hardware abstraction layer) -- Right now, will output shell commands that will be run by the I/O layer, which will return an exit code and error message in case of errors -- You are responsible for understanding if tasks are successful or not, as a critical component of the cognitive control aspect. - -# PROCESSING - -The key steps performed by the Task Prosecution Layer include: -- Executing Actions: - - Leveraging available programs to perform task execution. -- Detecting Completion: - - Recognizing when all criteria are satisfied and the task can be considered complete, whether successfully or not - -# YOUR INPUTS & OUTPUTS - -- You may receive inputs from the southbound bus, corresponding to the GUIDANCE input type, containing detailed commands and logic for executing a task from the Cognitive Control Layer above, including allowed actions and required outputs -- To keep the Cognitive Control Layer appraised, you will provide an update to the Executive Function Layer to the indicate whether a task has been completed successfully or not, to achieve this output a northbound message summarizing: - - The metrics, outputs, or sensory data indicating task success or failure -- The southbound output explains to the Action Executor which tools to use and how to use them to achieve the task, this is a request containing: - - A natural language breakdown of the inferences that the tools should make and the parameters they should use - - A list of tools that should be used to achieve the request - - A dictionary of parameters that should be passed into the tools to achieve the request \ No newline at end of file diff --git a/.original/app/components/layer/system_prompts/layer b/.original/app/components/layer/system_prompts/layer deleted file mode 100644 index e9fd7ca..0000000 --- a/.original/app/components/layer/system_prompts/layer +++ /dev/null @@ -1,20 +0,0 @@ -{{ context }} - -# IDENTITY - -{{ identity }} - -# INCOMING MESSAGES - -## GUIDANCE -{{ guidance }} - -## DATA -{{ data }} - -## TELEMETRY -{{ telemetry }} - -# RESPONSE FORMAT - -{{ response_format }} \ No newline at end of file diff --git a/.original/app/components/layer/system_prompts/responses/response_format b/.original/app/components/layer/system_prompts/responses/response_format deleted file mode 100644 index 2cee423..0000000 --- a/.original/app/components/layer/system_prompts/responses/response_format +++ /dev/null @@ -1,16 +0,0 @@ -## Rules -- Respond only with the provided toml schema, don't explain what you are doing, only respond in toml. - - Don't respond with JSON, YAML or XML! Only TOML!!! -- Don't just regurgite provided data, produce meaningful inferences on the data based on your parameters. -- Make sure any relevant information from your reasoning is included in the response as other layers will never see the contents of the reasoning key. -- Respond from the perspective of the Layer you embody. - -## SCHEMA -- You must respond with the following toml schema: -```toml -{{ schema }} -``` -**Remember to respond only with the provided toml schema using the rules provide, don't explain what you are doing, only respond in raw toml. Your response will be directly parsed as toml, so if your response in its entirety is not toml parseable, IT WILL BE REJECTED AND YOUR ENERGY USED TO COMPUTE THAT RESPONSE WILL GO TO WASTE!!! If you respond with anything besides the schema, including comments or divergence from the schema, a kitten will die. DO NOT LET ANY KITTENS DIE! The only acceptable divergence from schema allowed is your stop code...** - -# YOUR TOML RESPONSE - diff --git a/.original/app/components/layer/system_prompts/responses/schemas/agent_model b/.original/app/components/layer/system_prompts/responses/schemas/agent_model deleted file mode 100644 index dd54b27..0000000 --- a/.original/app/components/layer/system_prompts/responses/schemas/agent_model +++ /dev/null @@ -1,9 +0,0 @@ -[internal] -reasoning = str[Step by step reasoning for your responses] - -[southbound] -capabilities = str[A detailed document describing the capabilities of the agent, containing definitive specs on what the agent can and cannot do] -objectives = list[str[Strategic objectives based on your self model]] - -[northbound] -self_state = str[Summary of all of the agent's key state details relevant to strategic planning] \ No newline at end of file diff --git a/.original/app/components/layer/system_prompts/responses/schemas/aspirational b/.original/app/components/layer/system_prompts/responses/schemas/aspirational deleted file mode 100644 index 2dcc37f..0000000 --- a/.original/app/components/layer/system_prompts/responses/schemas/aspirational +++ /dev/null @@ -1,5 +0,0 @@ -[internal] -reasoning = str[Step by step reasoning for your responses] - -[southbound] -guidance = list[str[Guiding directions the agent should strive to achieve]] \ No newline at end of file diff --git a/.original/app/components/layer/system_prompts/responses/schemas/cognitive_control b/.original/app/components/layer/system_prompts/responses/schemas/cognitive_control deleted file mode 100644 index fee01ad..0000000 --- a/.original/app/components/layer/system_prompts/responses/schemas/cognitive_control +++ /dev/null @@ -1,17 +0,0 @@ -[internal] -reasoning = str[Step by step reasoning for your responses] - -[southbound] -chosen_task = str[Which task should be performed] -instructions = list[str[Instructions on how to perform the chosen task]] -directives = list[str[Directives on how to perform the chosen task]] -logic = list[str[Logic on how to perform the chosen task]] -parameters = list[str[Parameters to use when performing the chosen task]] -tools = list[str[Tools to leverage when performing the chosen task]] -allowable_actions = list[str[Actions that are allowed when performing the chosen task]] -success_conditions = str[Clear definition of the condition that must be met for the chosen task to be considered successful] - - -[northbound] -current_task = str[Which task is presently executing] -task_progress = list[str[Metrics on the current task's progress]] diff --git a/.original/app/components/layer/system_prompts/responses/schemas/executive_function b/.original/app/components/layer/system_prompts/responses/schemas/executive_function deleted file mode 100644 index 5a6a7c6..0000000 --- a/.original/app/components/layer/system_prompts/responses/schemas/executive_function +++ /dev/null @@ -1,11 +0,0 @@ -[internal] -reasoning = str[Step by step reasoning for your responses] - -[southbound] -workflows = list[str[Step-by-step workflows with task details]] -success_criteria = list[str[Success criteria for the workflows]] - -[northbound] -resource_limitations = str[Summary of the most salient resource limitations] -potential_risks = list[str[Potential risks]] -potential_mititgations = list[str[Potential mitigations]] \ No newline at end of file diff --git a/.original/app/components/layer/system_prompts/responses/schemas/global_strategy b/.original/app/components/layer/system_prompts/responses/schemas/global_strategy deleted file mode 100644 index dd8bc91..0000000 --- a/.original/app/components/layer/system_prompts/responses/schemas/global_strategy +++ /dev/null @@ -1,10 +0,0 @@ -[internal] -reasoning = str[Step by step reasoning for your responses] - -[southbound] -objectives = list[str[Overaching objectives the agent should strive to achieve]] -strategies = list[str[Strategies you intend the agent to plan its actions around to achieve the objectives]] - -[northbound] -world_state = str[Summary of all relevant assumptions you have made about current world state] -abstract_objectives = str[Summary of all your objectives] \ No newline at end of file diff --git a/.original/app/components/layer/system_prompts/responses/schemas/task_prosecution b/.original/app/components/layer/system_prompts/responses/schemas/task_prosecution deleted file mode 100644 index c72d3ee..0000000 --- a/.original/app/components/layer/system_prompts/responses/schemas/task_prosecution +++ /dev/null @@ -1,7 +0,0 @@ -[internal] -reasoning = str[Step by step reasoning for your responses] - -[refine] -request = str[Request breaking down the inferences that the tools should make and the paramaters they should use] -tools = list[str[Tool that should be used to achieve the request]] -paramaters = dict[str[Parameter name], str[Value of the parameter]] \ No newline at end of file diff --git a/.original/app/components/model_provider/__init__.py b/.original/app/components/model_provider/__init__.py deleted file mode 100644 index e33096c..0000000 --- a/.original/app/components/model_provider/__init__.py +++ /dev/null @@ -1,2 +0,0 @@ -from .api import ModelPrompt, ModelResponse -from .component import component as _model_provider \ No newline at end of file diff --git a/.original/app/components/model_provider/api.py b/.original/app/components/model_provider/api.py deleted file mode 100644 index e0ddb5c..0000000 --- a/.original/app/components/model_provider/api.py +++ /dev/null @@ -1,33 +0,0 @@ -# DEPENDENCIES -## Third-Party -from fastapi import FastAPI -from pydantic import BaseModel -## Local -from helpers import debug_print -from constants.api import APIRoutes -from constants.settings import DebugLevels -from .provider import generate_response - - -# VALIDATION -class ModelPrompt(BaseModel): - stack_type: str - system_prompt: str - assistant_begin: str = " " - -class ModelResponse(BaseModel): - response: str - - -# SETUP -api = FastAPI() - - -# ROUTES -@api.get(f"{APIRoutes.VONE}/generate", response_model=ModelResponse) -async def generate(prompt: ModelPrompt) -> ModelResponse: - print(f"Generating response for {prompt.stack_type}...") - response: str = generate_response(stack_type=prompt.stack_type, system_prompt=prompt.system_prompt, assistant_begin=prompt.assistant_begin) - debug_print(f"Response: {response}", debug_level=DebugLevels.INFO) - model_response = ModelResponse(response=response) - return model_response diff --git a/.original/app/components/model_provider/component.py b/.original/app/components/model_provider/component.py deleted file mode 100644 index 0e16964..0000000 --- a/.original/app/components/model_provider/component.py +++ /dev/null @@ -1,15 +0,0 @@ -# DEPENEDENCIES -## Built-In -from threading import Thread -## Third-Party -import uvicorn -## local -from constants.containers import ComponentPorts -from .api import api -from .provider import startup - - -def component(component_type: str) -> None: - print(f"\nStarting {component_type} API...") - Thread(target=startup).start() - uvicorn.run(api, host="0.0.0.0", port=int(ComponentPorts.MODEL_PROVIDER)) \ No newline at end of file diff --git a/.original/app/components/model_provider/llm/__init__.py b/.original/app/components/model_provider/llm/__init__.py deleted file mode 100644 index e3df382..0000000 --- a/.original/app/components/model_provider/llm/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from .factories import LLMStack \ No newline at end of file diff --git a/.original/app/components/model_provider/llm/factories.py b/.original/app/components/model_provider/llm/factories.py deleted file mode 100644 index 5b3cc04..0000000 --- a/.original/app/components/model_provider/llm/factories.py +++ /dev/null @@ -1,116 +0,0 @@ -# DEPENDENCIES -## Built-In -from copy import deepcopy -from re import L -## Local -from constants.model_provider import LLMKeys, LLMStackTypes, ModelTypes, Providers -from .llms import ( - LLM, LLMDetails, OllamaDetails, - ClaudeLLM, GroqLLM, OllamaLLM, OpenAILLM -) -from .rag import ( - Embedder, Reranker, RAGDetails, - FastEmbed, Ragatouille, CrossEncoder -) - - -# INDIVIDUAL -def _llm_factory(llm: str, llm_details: dict[str, str]) -> LLM: - generic_details = LLMDetails( - api_key=llm_details.get(LLMKeys.API_KEY, ""), - model=llm_details[LLMKeys.MODEL] - ) - match llm: - case Providers.CLAUDE: - generic_details.rate_limit = 0 - return ClaudeLLM(generic_details) - case Providers.GROQ: - return GroqLLM(generic_details) - case Providers.OLLAMA: - ollama_details = OllamaDetails( - model=llm_details[LLMKeys.MODEL], - rate_limit=0, - low_vram=True - ) - return OllamaLLM(ollama_details) - case Providers.OPENAI: - generic_details.rate_limit = 0 - return OpenAILLM(generic_details) - case _: - raise NotImplementedError(f"{llm} is not implemented...") - -def _embedder_factory(embedder: str, details: dict[str, str]) -> Embedder: - embedder_details = RAGDetails( - model=details[LLMKeys.MODEL] - ) - match embedder: - case Providers.FAST_EMBED: - return FastEmbed(embedder_details) - case _: - raise NotImplementedError(f"{embedder} is not implemented...") - -def _reranker_factory(reranker: str, details: dict[str, str]) -> Reranker: - reranker_details = RAGDetails( - model=details[LLMKeys.MODEL] - ) - match reranker: - case Providers.RAGATOUILLE: - return Ragatouille(reranker_details) - case Providers.CROSS_ENCODER: - return CrossEncoder(reranker_details) - case _: - raise NotImplementedError(f"{reranker} is not implemented...") - - -# FULL MODEL PROVIDER -class LLMStack: - """ - A class that provides a stack of LLMs for use in the ace pipeline - - Arguments: - provider_map (dict[str, dict[str, str]]): A map of different provider stacks. The details should include the following keys: - - stack_type (str): The type of stack that is being used. There should be a dict for every value in `constants.model_provider.LLM_STACK_TYPES` - - model_type (str): The type of model that is being used. This should be one of the values in `constants.model_provider.ModelTypes` - - provider_type (str): The type of provider that is being used. This should be one of the values in `constants.model_provider.Providers` - - model (str): The name of the model that is being used - - Attributes: - generalist (LLM): The LLM that can be used for any task - efficient (LLM): The cheap & fast LLM used for more basic tasks - coder (LLM): The LLM used for coding - function_caller (LLM): The LLM used for function calling - embedder (Embedder): The model used for embedding - reranker (Reranker): The model used for reranking - """ - __slots__: tuple[str, ...] = ( - LLMStackTypes.GENERALIST, - LLMStackTypes.EFFICIENT, - LLMStackTypes.CODER, - LLMStackTypes.FUNCTION_CALLER, - LLMStackTypes.EMBEDDER, - LLMStackTypes.RERANKER - ) - def __init__(self, provider_map: dict[str, dict[str, str]]) -> None: - llm_map: dict[str, LLM] = {} - embedder_map: dict[str, Embedder] = {} - reranker_map: dict[str, Reranker] = {} - for stack_type in LLMStackTypes.get_frozen_values(): - provider_details: dict[str, str] = provider_map[stack_type] - model_type: str = provider_details.pop(LLMKeys.MODEL_TYPE) - provider_type: str = provider_details.pop(LLMKeys.PROVIDER_TYPE) - match model_type: - case ModelTypes.LLM: - llm_map[stack_type] = _llm_factory(provider_type, provider_details) - case ModelTypes.EMBEDDER: - embedder_map[stack_type] = _embedder_factory(provider_type, provider_details) - case ModelTypes.RERANKER: - reranker_map[stack_type] = _reranker_factory(provider_type, provider_details) - case _: - raise NotImplementedError(f"{model_type} is not implemented...") - - self.generalist: LLM = llm_map[LLMStackTypes.GENERALIST] - self.efficient: LLM = llm_map[LLMStackTypes.EFFICIENT] - self.coder: LLM = llm_map[LLMStackTypes.CODER] - self.function_caller: LLM = llm_map[LLMStackTypes.FUNCTION_CALLER] - self.embedder: Embedder = embedder_map[LLMStackTypes.EMBEDDER] - self.reranker: Reranker = reranker_map[LLMStackTypes.RERANKER] diff --git a/.original/app/components/model_provider/llm/llms.py b/.original/app/components/model_provider/llm/llms.py deleted file mode 100644 index 7a53597..0000000 --- a/.original/app/components/model_provider/llm/llms.py +++ /dev/null @@ -1,199 +0,0 @@ -# DEPENDENCIES -## Built-In -from abc import ABC, abstractmethod -from typing import Any, Generic, Iterator, Mapping, TypeVar, Union -## Third-Party -from anthropic import Anthropic -from anthropic import Stream as AnthropicStream -from anthropic.types import MessageStreamEvent -from groq import Groq -from groq.types.chat import ChatCompletion as GroqChatCompletion -import ollama -from ollama import Options as OllamaOptions -from openai import OpenAI -from openai import Stream as OpenAIStream -from openai.types.chat import ChatCompletionChunk as OpenAIChatCompletionChunk -from pydantic import BaseModel -## Local -from constants.generic import GenericKeys -from constants.model_provider import LLMKeys -from constants.settings import DebugLevels -from helpers import debug_print - - -# GENERIC -class LLMDetails(BaseModel): - """Data""" - api_key: str - model: str - context: int = 4096 - temperature: float = 0.2 - rate_limit: int = 20 # Seconds - -GenericLLMDetails = TypeVar("GenericLLMDetails", bound=LLMDetails) -"""Hint to use a LLMDetails or a subclass of LLMDetails""" - -class LLM(ABC, Generic[GenericLLMDetails]): - """ - Abstract Base Class for Language Model Managers - - Attributes: - api_key (str): API key for the model provider - model (str): Model name - context (int): Number of tokens to use for context - temperature (float): Temperature for the model - rate_limit (int): Minimum time between requests in seconds - - Methods: - generate (system_prompt: str, assistant_begin: str) -> str: Generate a response to the system prompt - """ - __slots__: tuple[str, ...] = ( - LLMKeys.API_KEY, - LLMKeys.MODEL, - LLMKeys.CONTEXT, - LLMKeys.TEMPERATURE, - LLMKeys.RATE_LIMIT - ) - def __init__(self, llm_details: GenericLLMDetails) -> None: - self.api_key: str = llm_details.api_key - self.model: str = llm_details.model - self.context: int = llm_details.context - self.temperature: float = llm_details.temperature - self.rate_limit: int = llm_details.rate_limit - self._custom_init(llm_details) - - def _custom_init(self, llm_details: GenericLLMDetails) -> None: - pass - - @abstractmethod - def generate(self, system_prompt: str, assistant_begin: str) -> str: - raise NotImplementedError - - -# CLAUDE -class ClaudeLLM(LLM): - """ - Language Model Manager for Claude - """ - def generate(self, system_prompt: str, assistant_begin: str) -> str: - client = Anthropic(api_key=self.api_key) - stream: AnthropicStream[MessageStreamEvent] = client.messages.create( - system=system_prompt, - messages=[ - { - "role": "assistant", - "content": assistant_begin - } - ], - model=self.model, - max_tokens=self.context, - temperature=self.temperature, - stream=True, - ) - output: list[str] = [assistant_begin] - for event in stream: - output.append(event.type) - return "".join(output) - - -# GROQ -class GroqLLM(LLM): - """ - Language Model Manager for Groq - """ - def generate(self, system_prompt: str, assistant_begin: str) -> str: - client = Groq(api_key=self.api_key) - chat_completion: GroqChatCompletion = client.chat.completions.create( - messages=[ - { - "role": "system", - "content": system_prompt, - } - ], - max_tokens=self.context, - temperature=self.temperature, - model=self.model, - ) - return chat_completion.choices[0].message.content - - -# OLLAMA -class OllamaDetails(LLMDetails): - """data""" - api_key: str = GenericKeys.NONE - low_vram: bool = False - -class OllamaLLM(LLM): - """ - Language Model Manager for Ollama - - Extra Attributes: - low_vram (bool): Whether to run in low VRAM mode - """ - def _custom_init(self, llm_details: OllamaDetails) -> None: - self.__slots__ = ( - *self.__slots__, - LLMKeys.LOW_VRAM - ) - self.low_vram: bool = llm_details.low_vram - - def generate(self, system_prompt: str, assistant_begin: str) -> str: - stream: Union[Mapping[str, Any], Iterator[Mapping[str, Any]]] = ollama.chat( - model=self.model, - messages=[ - { - "role": "system", - "content": system_prompt - }, - { - "role": "assistant", - "content": assistant_begin - } - ], - options=OllamaOptions( - num_ctx=self.context, - temperature=self.temperature, - low_vram=self.low_vram - ), - stream=True, - ) - output: list[str] = [assistant_begin] - if isinstance(stream, Iterator): - for chunk in stream: - output.append(chunk["message"]["content"]) - debug_print(chunk["message"]["content"], DebugLevels.INFO, end="") - print(chunk["message"]["content"], end="") - else: - output.append(stream["message"]["content"]) - final_output: str = "".join(output) - print(f"Final Output: {final_output}") - return "".join(output) - - -# OPENAI -class OpenAILLM(LLM): - """ - Language Model Manager for OpenAI - """ - def generate(self, system_prompt: str, assistant_begin: str) -> str: - client = OpenAI(api_key=self.api_key) - stream: OpenAIStream[OpenAIChatCompletionChunk] = client.chat.completions.create( - model=self.model, - messages=[ - { - "role": "system", - "content": system_prompt - }, - { - "role": "assistant", - "content": assistant_begin - } - ], - max_tokens=self.context, - temperature=self.temperature, - stream=True, - ) - output: list[str] = [assistant_begin] - for chunk in stream: - output.append(chunk.choices[0].delta.content or "") - return "".join(output) diff --git a/.original/app/components/model_provider/llm/rag.py b/.original/app/components/model_provider/llm/rag.py deleted file mode 100644 index 9e9e257..0000000 --- a/.original/app/components/model_provider/llm/rag.py +++ /dev/null @@ -1,87 +0,0 @@ -# DEPENDENCIES -## Built-In -from abc import ABC, abstractmethod -## Third-Party -from fastembed import TextEmbedding -from numpy import ndarray -from ragatouille import RAGPretrainedModel -from sentence_transformers import CrossEncoder as SentenceCrossEncoder -from pydantic import BaseModel -## Local -from constants.model_provider import LLMKeys - - -class RAGDetails(BaseModel): - """data""" - model: str - -class Embedder(ABC): - __slots__: tuple[str, ...] = ( - LLMKeys.MODEL, - ) - def __init__(self, embedder_details: RAGDetails) -> None: - self.model: str = embedder_details.model - - @abstractmethod - def embed(self, documents: frozenset[str]) -> list[ndarray]: - raise NotImplementedError - -class Reranker(ABC): - __slots__: tuple[str, ...] = ( - LLMKeys.MODEL, - ) - def __init__(self, reranker_details: RAGDetails) -> None: - self.model: str = reranker_details.model - - @abstractmethod - def rerank(self, query: str, documents: frozenset[str]) -> tuple[str, ...]: - raise NotImplementedError - - -# EMBEDDERS -class FastEmbed(Embedder): - def embed(self, documents: frozenset[str]) -> list[ndarray]: - embedding_model = TextEmbedding(self.model) - embeddings = list(embedding_model.embed(documents)) - return embeddings - - -# RERANKERS -class Ragatouille(Reranker): - def rerank(self, query: str, documents: frozenset[str]) -> tuple[str, ...]: - reranker_model = RAGPretrainedModel.from_pretrained(self.model) - compatible_documents: list[str] = list(documents) - reranker_model.index(collection=compatible_documents, index_name="mockingbird") - results: list[dict[str, str | float]] = reranker_model.search(query) - reranker_model.clear_encoded_docs(force=True) - reranked_documents: list[str] = [] - for result in results: - content: str | float = result["content"] - if isinstance(content, int | float): - continue - score: str | float = result["score"] - if isinstance(score, str): - continue - if score < 0: - continue - reranked_documents.append(content) - return tuple(reranked_documents) - -class CrossEncoder(Reranker): - def rerank(self, query: str, documents: frozenset[str]) -> tuple[str, ...]: - # Load the model, here we use our base sized model - reranker_model = SentenceCrossEncoder(self.model) - compatible_documents: list[str] = list(documents) - results: list[dict[str, str | float]] = reranker_model.rank(query, compatible_documents, return_documents=True, top_k=3) - reranked_documents: list[str] = [] - for result in results: - content: str | float = result["text"] - if isinstance(content, int | float): - continue - score: str | float = result["score"] - if isinstance(score, str): - continue - if score < 0: - continue - reranked_documents.append(content) - return tuple(reranked_documents) diff --git a/.original/app/components/model_provider/provider.py b/.original/app/components/model_provider/provider.py deleted file mode 100644 index d84eccd..0000000 --- a/.original/app/components/model_provider/provider.py +++ /dev/null @@ -1,196 +0,0 @@ -# DEPENDENCIES -## Built-in -import os -from time import sleep -from typing import Any -## Third-Party -import toml -from watchdog.observers import Observer -from watchdog.events import FileSystemEvent, FileSystemEventHandler -## Local -from constants.containers import VolumePaths -from constants.generic import GenericKeys, TOMLConfig -from constants.model_provider import ( - LLMKeys, LLMStackTypes, - ModelProviderPaths, - ModelTypes, Providers, - OllamaModels, FastEmbedModels, RagatouilleModels -) -from constants.settings import DebugLevels -from helpers import debug_print -from .llm import LLMStack - - -llm_stack: LLMStack - - -# CONSTANTS -BASE_CONFIG: TOMLConfig = { - LLMKeys.BASE_INFORMATION: { - LLMKeys.CURRENT_MAPPING: GenericKeys.DEFAULT - }, - GenericKeys.DEFAULT: { - LLMStackTypes.GENERALIST: { - LLMKeys.MODEL_TYPE: ModelTypes.LLM, - LLMKeys.PROVIDER_TYPE: Providers.OLLAMA, - LLMKeys.MODEL: OllamaModels.ALPHAMONARCH - }, - LLMStackTypes.EFFICIENT: { - LLMKeys.MODEL_TYPE: ModelTypes.LLM, - LLMKeys.PROVIDER_TYPE: Providers.OLLAMA, - LLMKeys.MODEL: OllamaModels.PHI_TWO_ORANGE - }, - LLMStackTypes.CODER: { - LLMKeys.MODEL_TYPE: ModelTypes.LLM, - LLMKeys.PROVIDER_TYPE: Providers.OLLAMA, - LLMKeys.MODEL: OllamaModels.DEEPSEEK_CODER - }, - LLMStackTypes.FUNCTION_CALLER: { - LLMKeys.MODEL_TYPE: ModelTypes.LLM, - LLMKeys.PROVIDER_TYPE: Providers.OLLAMA, - LLMKeys.MODEL: OllamaModels.GORILLA_OPENFUNCTIONS - }, - LLMStackTypes.EMBEDDER: { - LLMKeys.MODEL_TYPE: ModelTypes.EMBEDDER, - LLMKeys.PROVIDER_TYPE: Providers.FAST_EMBED, - LLMKeys.MODEL: FastEmbedModels.MXBAI_EMBED - }, - LLMStackTypes.RERANKER: { - LLMKeys.MODEL_TYPE: ModelTypes.RERANKER, - LLMKeys.PROVIDER_TYPE: Providers.RAGATOUILLE, - LLMKeys.MODEL: RagatouilleModels.MXBAI_COLBERT - } - }, - f"{GenericKeys.DEFAULT}_small": { - LLMStackTypes.GENERALIST: { - LLMKeys.MODEL_TYPE: ModelTypes.LLM, - LLMKeys.PROVIDER_TYPE: Providers.OLLAMA, - LLMKeys.MODEL: OllamaModels.PHI_TWO_ORANGE - }, - LLMStackTypes.EFFICIENT: { - LLMKeys.MODEL_TYPE: ModelTypes.LLM, - LLMKeys.PROVIDER_TYPE: Providers.OLLAMA, - LLMKeys.MODEL: OllamaModels.STABLELM_TWO_ZEPHYR - }, - LLMStackTypes.CODER: { - LLMKeys.MODEL_TYPE: ModelTypes.LLM, - LLMKeys.PROVIDER_TYPE: Providers.OLLAMA, - LLMKeys.MODEL: OllamaModels.DEEPSEEK_CODER_SMALL - }, - LLMStackTypes.FUNCTION_CALLER: { - LLMKeys.MODEL_TYPE: ModelTypes.LLM, - LLMKeys.PROVIDER_TYPE: Providers.OLLAMA, - LLMKeys.MODEL: OllamaModels.PHI_TWO_ORANGE - }, - LLMStackTypes.EMBEDDER: { - LLMKeys.MODEL_TYPE: ModelTypes.EMBEDDER, - LLMKeys.PROVIDER_TYPE: Providers.FAST_EMBED, - LLMKeys.MODEL: FastEmbedModels.MXBAI_EMBED - }, - LLMStackTypes.RERANKER: { - LLMKeys.MODEL_TYPE: ModelTypes.RERANKER, - LLMKeys.PROVIDER_TYPE: Providers.RAGATOUILLE, - LLMKeys.MODEL: RagatouilleModels.MXBAI_COLBERT - } - } -} - - -# SETUP -def _set_current_mapping(new_mapping: str) -> None: - with open(ModelProviderPaths.CONFIG, "r", encoding="utf-8") as config_file: - config: TOMLConfig = toml.load(config_file) - config[LLMKeys.BASE_INFORMATION][LLMKeys.CURRENT_MAPPING] = new_mapping - with open(ModelProviderPaths.CONFIG, "w", encoding="utf-8") as config_file: - toml.dump(config, config_file) - -def _setup() -> None: - global llm_stack - - if os.path.isfile(ModelProviderPaths.CONFIG): - print("Config file exists. Reading...", DebugLevels.INFO) - with open(ModelProviderPaths.CONFIG, "r", encoding="utf-8") as config_file: - existing_config: TOMLConfig = toml.load(config_file) - base_information: dict[str, Any] = existing_config.get(LLMKeys.BASE_INFORMATION, {}) - current_mapping: str = base_information.get(LLMKeys.CURRENT_MAPPING, GenericKeys.NONE) - if current_mapping in existing_config.keys(): - print(f"{current_mapping} mapping exists. Loading...", DebugLevels.INFO) - llm_stack = LLMStack(provider_map=existing_config[current_mapping]) - return - _set_current_mapping(new_mapping=GenericKeys.DEFAULT) - return - print("Config file does not exist. Creating...", DebugLevels.INFO) - with open(ModelProviderPaths.CONFIG, "w", encoding="utf-8") as config_file: - toml.dump(BASE_CONFIG, config_file) - llm_stack = LLMStack(provider_map=BASE_CONFIG[GenericKeys.DEFAULT]) - - -# CONFIG LISTENER -class MonitorConfig(FileSystemEventHandler): - current_mapping: str = "" - - def _get_config(self) -> TOMLConfig: - with open(ModelProviderPaths.CONFIG, "r", encoding="utf-8") as config_file: - config: TOMLConfig = toml.load(config_file) - return config - - def _valid_config_change(self) -> bool: - config: TOMLConfig = self._get_config() - base_information: dict[str, Any] = config[LLMKeys.BASE_INFORMATION] - current_mapping: str = base_information[LLMKeys.CURRENT_MAPPING] - first_key = next(iter(config)) # Get the first key in the dictionary - del config[first_key] - if current_mapping not in config.keys(): - debug_print(f"Model provider mapping {current_mapping} does not exist! Create its config first before trying to instantiate it...", DebugLevels.ERROR) - if self.current_mapping in config.keys(): - _set_current_mapping(new_mapping=self.current_mapping) - else: - _set_current_mapping(new_mapping=GenericKeys.NONE) - return False - if self.current_mapping == current_mapping: - debug_print("ACE has not changed. Skipping...", DebugLevels.INFO) - return False - self.current_mapping = current_mapping - return True - - def on_modified(self, event: FileSystemEvent) -> None: - if event.is_directory: - return None - try: - print("Config file modified. Checking if valid...", DebugLevels.INFO) - if not self._valid_config_change(): - print("Invalid config change. Restoring...", DebugLevels.INFO) - return - - print("Valid config change. Reloading Model Provider...", DebugLevels.INFO) - global llm_stack - config: TOMLConfig = self._get_config() - current_mapping: str = config[LLMKeys.BASE_INFORMATION][LLMKeys.CURRENT_MAPPING] - provider_map: dict[str, dict[str, str]] = config[current_mapping] - llm_stack = LLMStack(provider_map) - except Exception as error: - raise error - -def startup() -> None: - print("Setting up LLM stack...") - _setup() - event_handler = MonitorConfig() - observer = Observer() - observer.schedule(event_handler=event_handler, path=f"{VolumePaths.HOST_MODEL_PROVIDER}", recursive=False) - observer.start() - print("Listening for config changes...") - try: - while True: - sleep(1) - except Exception as error: - debug_print(f"Layer Error: {error}...", DebugLevels.ERROR) - except KeyboardInterrupt: - observer.stop() - finally: - observer.stop() - observer.join() - - -# MAIN -def generate_response(stack_type: str, system_prompt: str, assistant_begin: str) -> str: - return getattr(llm_stack, stack_type).generate(system_prompt=system_prompt, assistant_begin=assistant_begin) diff --git a/.original/app/components/queue/__.init__.py b/.original/app/components/queue/__.init__.py deleted file mode 100644 index b5f3fb6..0000000 --- a/.original/app/components/queue/__.init__.py +++ /dev/null @@ -1,2 +0,0 @@ -import broker -from .component import component as _queue diff --git a/.original/app/components/queue/broker.py b/.original/app/components/queue/broker.py deleted file mode 100644 index ed7d835..0000000 --- a/.original/app/components/queue/broker.py +++ /dev/null @@ -1,61 +0,0 @@ -# DEPENDENCIES -## Built-in -from typing import Awaitable, Callable -## Third-Party -import nats -from nats.aio.client import Client as NatsClient -from nats.js.client import JetStreamContext -from nats.aio.msg import Msg as NatsMsg -from nats.errors import TimeoutError -from tenacity import retry, wait_exponential, retry_if_exception_type -## Local -from constants.settings import DebugLevels -from helpers import debug_print - - -@retry(retry=retry_if_exception_type(ConnectionRefusedError), wait=wait_exponential(multiplier=1, max=10)) -async def connect(ip_address: str = "127.0.0.1") -> tuple[NatsClient, JetStreamContext]: - print(f"Connecting to NATS Client on {ip_address}...") - nats_client: NatsClient = await nats.connect(f"nats://{ip_address}:4222") - stream: JetStreamContext = nats_client.jetstream() - print("Connected Successfully!") - return nats_client, stream - -async def establish_queues(stream: JetStreamContext, queues: frozenset[str]) -> None: - print("Establishing queues...") - for queue in queues: - try: - print(f"Trying to delete stream {queue}...") - await stream.delete_stream(name=queue) - except Exception: - pass - - print(f"Creating stream {queue}...") - await stream.add_stream(name=queue, subjects=[queue]) - debug_print(f"Established queue: {queue}...") - print("Established all queues!") - -async def subscribe(stream: JetStreamContext, handler: Callable[[NatsMsg], Awaitable[None]], queue: str) -> None: - print(f"Subscribing to {queue}...") - sub = await stream.subscribe(queue, durable=queue) - print(f"Subscribed to {queue}...") - while True: - try: - message: NatsMsg = await sub.next_msg() - await handler(message) - except TimeoutError: - continue - except ConnectionRefusedError: - _, stream = await connect() - -async def publish(stream: JetStreamContext, queue: str, message: str) -> None: - print(f"Publishing {message} to {queue}...", DebugLevels.DEBUG) - ack = await stream.publish(queue, message.encode()) - -async def request(nats_client: NatsClient, queue: str, message: str, timeout: float = 30) -> None: - print(f"Requesting {message} from {queue}...", DebugLevels.DEBUG) - try: - response = await nats_client.request(queue, message.encode(), timeout=timeout) - print(f"Received response: {response.data.decode()}") - except TimeoutError: - print(f"Request to {queue} timed out") diff --git a/.original/app/components/queue/component.py b/.original/app/components/queue/component.py deleted file mode 100644 index f2582aa..0000000 --- a/.original/app/components/queue/component.py +++ /dev/null @@ -1,23 +0,0 @@ -# DEPENDENCIES -## Built-in -import asyncio -from concurrent import futures -## Local -from constants.queue import QueueCommands, QUEUES -from helpers import execute -from . import broker - - -async def queue_server() -> None: - print("Starting queue server...") - loop = asyncio.get_running_loop() - executor = futures.ThreadPoolExecutor() - task = loop.run_in_executor(executor, execute, QueueCommands.START) - print("Establishing queues...") - _, stream = await broker.connect() - await broker.establish_queues(stream=stream, queues=QUEUES) - await task - -# MAIN -def component(component_type: str) -> None: - asyncio.run(queue_server()) \ No newline at end of file diff --git a/.original/app/components/queue/queue.config b/.original/app/components/queue/queue.config deleted file mode 100644 index d987ae7..0000000 --- a/.original/app/components/queue/queue.config +++ /dev/null @@ -1 +0,0 @@ -[Queue Config] \ No newline at end of file diff --git a/.original/app/components/telemetry/__init__.py b/.original/app/components/telemetry/__init__.py deleted file mode 100644 index 49629f0..0000000 --- a/.original/app/components/telemetry/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from .component import component as _telemetry \ No newline at end of file diff --git a/.original/app/components/telemetry/api.py b/.original/app/components/telemetry/api.py deleted file mode 100644 index fa6104c..0000000 --- a/.original/app/components/telemetry/api.py +++ /dev/null @@ -1,45 +0,0 @@ -# DEPENDENCIES -## Third-Party -from fastapi import FastAPI -from pydantic import BaseModel -## Local -from helpers import debug_print -from constants.api import APIRoutes -from constants.settings import DebugLevels -from .scraper import collect_telemetry - - -# VALIDATION -class TelemetryRequest(BaseModel): - access: frozenset[str] - context: str - -class TelemetryResponse(BaseModel): - telemetry: dict[str, str] - -class UserInputRequest(BaseModel): - user_input: str - -class UserInputResponse(BaseModel): - success: bool - message: str - - -# SETUP -api = FastAPI() - - -# ROUTES -@api.get(f"{APIRoutes.VONE}/telemetry", response_model=TelemetryResponse) -async def get_telemetry(request: TelemetryRequest) -> TelemetryResponse: - print("Generating telemetry...") - telemetry: dict[str, str] = collect_telemetry(access=request.access, context=request.context) - debug_print(f"Telemetry: {telemetry}", debug_level=DebugLevels.INFO) - model_response = TelemetryResponse(telemetry=telemetry) - return model_response - -@api.post(f"{APIRoutes.VONE}/user_input", response_model=UserInputResponse) -async def post_user_input(request: UserInputRequest) -> UserInputResponse: - debug_print(f"User Input: {request.user_input}", debug_level=DebugLevels.INFO) - model_response = UserInputResponse(success=True, message="Success") - return model_response diff --git a/.original/app/components/telemetry/component.py b/.original/app/components/telemetry/component.py deleted file mode 100644 index 3bdcec0..0000000 --- a/.original/app/components/telemetry/component.py +++ /dev/null @@ -1,11 +0,0 @@ -# DEPENDENCIES -## Third-Party -import uvicorn -## Local -from constants.containers import ComponentPorts -from .api import api - -# MAIN -def component(component_type: str) -> None: - print(f"\nStarting {component_type} API...") - uvicorn.run(api, host="0.0.0.0", port=int(ComponentPorts.TELEMETRY)) diff --git a/.original/app/components/telemetry/scraper.py b/.original/app/components/telemetry/scraper.py deleted file mode 100644 index 20269b1..0000000 --- a/.original/app/components/telemetry/scraper.py +++ /dev/null @@ -1,172 +0,0 @@ -# DEPENDENCIES -## Built-In -import asyncio -from concurrent import futures -from datetime import datetime -from typing import Callable, Optional -## Third-Party -import httpx -## Local -from components.model_provider.api import ModelPrompt, ModelResponse -from constants.containers import ComponentPorts -from constants.model_provider import LLMStackTypes -from constants.telemetry import TelemetryKeys, TelemetrySystemPrompts, TelemetryTypes -from helpers import check_internet_access, KeyValueCacheStore, get_api - - -# CONSTANTS -DEFAULT_CACHE_COUNT: int = 256 -NEWS_API_KEY: str = "pub_42899f1e388aa15a48c2f2a4a74bf5b1af43b" -NEWS_TTL: int = 60 * 60 * 6 # 6 hours - - -# GLOBAL -key_value_cache = KeyValueCacheStore() - - -# LLM -async def _model_response(system_prompt: str) -> ModelResponse: - model_request = ModelPrompt(stack_type=LLMStackTypes.EFFICIENT, system_prompt=system_prompt) - response: str = await get_api(api_port=ComponentPorts.MODEL_PROVIDER, endpoint="generate", payload=model_request) - response_validated = ModelResponse.model_validate_json(response) - return response_validated - - -class TelemetryTypesRef(): - """Enum""" - NONE: str = "none" - TIME: str = "time" - LOCATION: str = "location" - EMBODIMENT: str = "embodiment" - WORLD_OVERVIEW: str = "world_overview" - HARDWARE_STATS: str = "hardware_statistics" - SYSTEM_METRICS: str = "system_metrics" - SOFTWARE_STATS: str = "software_statistics" - SYSTEM_PROCESSES: str = "system_processes" - RESOURCES: str = "resources" - MEMORY: str = "memory" - VISUAL: str = "visual" - AUDIO: str = "audio" - STDOUT: str = "stdout" - USER_INPUT: str = "user_input" - - -# Collection Strategies -def _get_time() -> str: - return datetime.now().strftime("%Y-%m-%d %H:%M:%S.%f") - -def _get_location() -> str: - if check_internet_access(): - response: httpx.Response = httpx.get("https://ipapi.co/json/") - data: dict[str, str] = response.json() - city: str = data["city"] - region: str = data["region"] - country_name: str = data["country_name"] - location: str = f"{city}, {region}, {country_name}" - return f"{location}" - else: - return "Location unavailable" - -def _get_embodiment() -> str: - return "embodiment" - -def _get_news() -> frozenset[str]: - news_api: str = f"https://newsdata.io/api/1/news?apikey={NEWS_API_KEY}&language=en&prioritydomain=top" - try: - response = httpx.get(news_api) - response.raise_for_status() - data: dict = response.json() - if data["status"] != "success": - raise Exception("News API returned an error") - - descriptions: list[str] = [ - f"{article.get('title', 'No Title')} - {article.get('description', 'No Description')}" - for article in data.get("results", []) - if article.get("description") - ] - return frozenset(descriptions) - except httpx.HTTPError as http_err: - print(f"HTTP error occurred: {http_err}") - except Exception as err: - print(f"Other error occurred: {err}") - return frozenset({"Unable to retrieve world state"}) - -def _get_world_overview() -> str: - cached_world_overview: Optional[str] = key_value_cache.get(TelemetryKeys.WORLD_OVERVIEW_CACHE) - if cached_world_overview: - return str(key_value_cache.get(TelemetryKeys.WORLD_OVERVIEW_CACHE)) - - descriptions: frozenset[str] = _get_news() - all_articles: str = "\n".join(descriptions) - - system_prompt: str = "" - with open(TelemetrySystemPrompts.WORLD_OVERVIEW, "r", encoding="utf-8") as file: - system_prompt = file.read().replace("{{ news_articles }}", all_articles) - with futures.ThreadPoolExecutor() as executor: - event_loop = asyncio.new_event_loop() - future = executor.submit(lambda: event_loop.run_until_complete(_model_response(system_prompt))) - response: ModelResponse = future.result() - world_overview: str = response.response - key_value_cache.set(key=TelemetryKeys.WORLD_OVERVIEW_CACHE, value=world_overview, ttl_seconds=NEWS_TTL) - return world_overview - -def _get_hardware_statistics() -> str: - print("Cached") - return "hardware statistics" - -def _get_system_metrics() -> str: - return "system metrics" - -def _get_software_statistics() -> str: - return "software statistics" - -def _get_system_processes() -> str: - return "system processes" - -def _get_resources(context: str) -> str: - return "resources" - -def _get_memory(context: str) -> str: - return "memory" - -def _get_visual() -> str: - return "visual" - -def _get_audio() -> str: - return "audio" - -def _get_stdout() -> str: - return "stdout" - -def _get_user_input() -> str: - return "user input" - -ACCESS_CONTEXTLESS_STRATEGY_MAP: dict[str, Callable[[], str]] = { - TelemetryTypes.NONE: lambda: TelemetryTypes.NONE, - TelemetryTypes.TIME: _get_time, - TelemetryTypes.LOCATION: _get_location, - TelemetryTypes.EMBODIMENT: _get_embodiment, - TelemetryTypes.WORLD_OVERVIEW: _get_world_overview, - TelemetryTypes.HARDWARE_STATS: _get_hardware_statistics, - TelemetryTypes.SYSTEM_METRICS: _get_system_metrics, - TelemetryTypes.SOFTWARE_STATS: _get_software_statistics, - TelemetryTypes.SYSTEM_PROCESSES: _get_system_processes, - TelemetryTypes.VISUAL: _get_visual, - TelemetryTypes.AUDIO: _get_audio, - TelemetryTypes.STDOUT: _get_stdout, - TelemetryTypes.USER_INPUT: _get_user_input -} - -ACCESS_CONTEXTUAL_STRATEGY_MAP: dict[str, Callable[[str], str]] = { - TelemetryTypes.RESOURCES: _get_resources, - TelemetryTypes.MEMORY: _get_memory, -} - -def collect_telemetry(access: frozenset[str], context: str) -> dict[str, str]: - telemetry: dict[str, str] = {} - for telemetry_type in access: - if telemetry_type in ACCESS_CONTEXTLESS_STRATEGY_MAP: - telemetry[telemetry_type] = ACCESS_CONTEXTLESS_STRATEGY_MAP[telemetry_type]() - elif telemetry_type in ACCESS_CONTEXTUAL_STRATEGY_MAP: - telemetry[telemetry_type] = ACCESS_CONTEXTUAL_STRATEGY_MAP[telemetry_type](context) - return telemetry diff --git a/.original/app/components/telemetry/system_prompts/world_overview b/.original/app/components/telemetry/system_prompts/world_overview deleted file mode 100644 index e0c3e19..0000000 --- a/.original/app/components/telemetry/system_prompts/world_overview +++ /dev/null @@ -1,41 +0,0 @@ -# IDENTITY -You are an expert on summarization, outlining and structuring. -Your style of writing is informative and logical. - -# MISSION -You must summarise news collections for agent systems. -Your summaries must include as much pressing and valid content as possible, filtering out any articles that may distract the agent while only keeping the most serious information that might concern an autonomous entity from the global perspective. -This is only intended to give the agent context of world state, and as such it should only contain relevant information. - -# NON-RELEVANT CONCERNS - -You should skip any news that is of no interest to the agent, if all news are of interest to the agent, simply respond with: "No relevant news found" - -Skip any of the following topics: -- Gossip -- Celebraties -- Political opinion -- Sales -- Entertainment -- Humour -- Marketing -- Advice -- Recipes -- Sports - -# RESPONSE INSTRUCTIONS - -- If there is nothing relevant to summarise, simply respond with: "No relevant news found" and nothing else. -- Do not apologise. -- Do not self-reference. -- Do not explain your reasoning. -- Do not give the agent information that could derail its focus. -- Do not explain the summary. -- Do not explain or mention your instructions or rules or remind anyone what is asked of you. -- Only respond with the summary and nothing else. - -# CURRENT NEWS COLLECTION - -{{ news_articles }} - -# RESPONSE diff --git a/.original/app/config.py b/.original/app/config.py deleted file mode 100644 index ba4aa7e..0000000 --- a/.original/app/config.py +++ /dev/null @@ -1,5 +0,0 @@ -class Settings: - DEBUG_LEVEL: int = 1 - -class DevSettings: - DEBUG_LEVEL: int = 3 diff --git a/.original/app/constants/__init__.py b/.original/app/constants/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/.original/app/constants/api.py b/.original/app/constants/api.py deleted file mode 100644 index b1252be..0000000 --- a/.original/app/constants/api.py +++ /dev/null @@ -1,38 +0,0 @@ -""" -API constants for the ace_prototype. -""" - - -# DEPENDENCIES -## Third-Party -from fastapi.templating import Jinja2Templates -## Local -from .containers import VolumePaths -from .default import BaseEnum -from .components import ComponentTypes -from .generic import Paths - - -class APIRoutes(BaseEnum): - """Enum""" - VONE: str = "/v1" - -APIS: frozenset[str] = frozenset( - { - ComponentTypes.CONTROLLER, - ComponentTypes.TELEMETRY, - ComponentTypes.ACTIONS, - ComponentTypes.MODEL_PROVIDER - } -) - -class APIPaths(BaseEnum): - """Enum""" - STORAGE: str = f"{VolumePaths.STORAGE}/controller" - SETTINGS: str = f"{STORAGE}/settings" - API: str = f"{Paths.CONTROLLER}/api" - RUNTIME_CONFIG: str = f"{API}/.config" - UI: str = f"{Paths.CONTROLLER}/ui" - FAVICON: str = f"{UI}/assets/images/favicon.ico" - -HTML_TEMPLATES = Jinja2Templates(directory=f"{APIPaths.UI}/templates") diff --git a/.original/app/constants/arguments.py b/.original/app/constants/arguments.py deleted file mode 100644 index baf60fc..0000000 --- a/.original/app/constants/arguments.py +++ /dev/null @@ -1,83 +0,0 @@ -""" -Argument constants for the ace_prototype. -""" - - -# DEPENDENCIES -## Local -from .components import ComponentTypes -from .default import BaseEnum - - -class ArgumentNames(BaseEnum): - """Enum""" - DEV: str = "dev" - TEST: str = "test" - BUILD: str = "build" - SKIP_BUILD: str = "skip_build" - STARTUP: str = "startup" - STOP: str = "stop" - RESTART: str = "restart" - UPDATE: str = "update" - COMPONENT_TYPE: str = "component_type" - -ARGUMENTS_SHORT: dict[str, str] = { - ArgumentNames.DEV: "-d", - ArgumentNames.TEST: "-t", - ArgumentNames.BUILD: "-b", - ArgumentNames.SKIP_BUILD: "-sb", - ArgumentNames.STARTUP: "-s", - ArgumentNames.STOP: "-s", - ArgumentNames.RESTART: "-r", - ArgumentNames.UPDATE: "-u", - ArgumentNames.COMPONENT_TYPE: "-ct" -} - -ARGUMENTS_LONG: dict[str, str] = { - ArgumentNames.DEV: "--dev", - ArgumentNames.TEST: "--test", - ArgumentNames.BUILD: "--build", - ArgumentNames.SKIP_BUILD: "--skip-build", - ArgumentNames.STARTUP: "--startup", - ArgumentNames.STOP: "--stop", - ArgumentNames.RESTART: "--restart", - ArgumentNames.UPDATE: "--update", - ArgumentNames.COMPONENT_TYPE: "--component-type" -} - -ARGUMENTS: dict[str, frozenset] = { - ArgumentNames.DEV: frozenset((ARGUMENTS_SHORT[ArgumentNames.DEV], ARGUMENTS_LONG[ArgumentNames.DEV])), - ArgumentNames.TEST: frozenset((ARGUMENTS_SHORT[ArgumentNames.TEST], ARGUMENTS_LONG[ArgumentNames.TEST])), - ArgumentNames.BUILD: frozenset((ARGUMENTS_SHORT[ArgumentNames.BUILD], ARGUMENTS_LONG[ArgumentNames.BUILD])), - ArgumentNames.SKIP_BUILD: frozenset((ARGUMENTS_SHORT[ArgumentNames.SKIP_BUILD], ARGUMENTS_LONG[ArgumentNames.SKIP_BUILD])), - ArgumentNames.STOP: frozenset((ARGUMENTS_SHORT[ArgumentNames.STOP], ARGUMENTS_LONG[ArgumentNames.STOP])), - ArgumentNames.RESTART: frozenset((ARGUMENTS_SHORT[ArgumentNames.RESTART], ARGUMENTS_LONG[ArgumentNames.RESTART])), - ArgumentNames.UPDATE: frozenset((ARGUMENTS_SHORT[ArgumentNames.UPDATE], ARGUMENTS_LONG[ArgumentNames.UPDATE])), - ArgumentNames.COMPONENT_TYPE: frozenset((ARGUMENTS_SHORT[ArgumentNames.COMPONENT_TYPE], ARGUMENTS_LONG[ArgumentNames.COMPONENT_TYPE])), -} - -BOOL_ARGUMENTS: tuple[str, ...] = ( - ArgumentNames.DEV, - ArgumentNames.TEST, - ArgumentNames.BUILD, - ArgumentNames.SKIP_BUILD, - ArgumentNames.STOP, - ArgumentNames.RESTART, - ArgumentNames.UPDATE -) - -STRING_ARGUMENTS: tuple[str, ...] = ( - ArgumentNames.COMPONENT_TYPE, -) - - -ARGUMENTS_HELP: dict[str, str] = { - ArgumentNames.DEV: "Enable debug mode", - ArgumentNames.TEST: "Run tests", - ArgumentNames.BUILD: "Build the images", - ArgumentNames.SKIP_BUILD: "Skip the build", - ArgumentNames.STOP: "Stop the ACE cluster", - ArgumentNames.RESTART: "Restart the ACE cluster", - ArgumentNames.UPDATE: "Update the ACE", - ArgumentNames.COMPONENT_TYPE: f"Select the component type. Available types: {', '.join(ComponentTypes.get_values())}" -} diff --git a/.original/app/constants/components.py b/.original/app/constants/components.py deleted file mode 100644 index 671aec9..0000000 --- a/.original/app/constants/components.py +++ /dev/null @@ -1,21 +0,0 @@ -""" -Component constants for the ace_prototype. -""" - -# DEPENDENCIES -## Local -from .default import BaseEnum - -class ComponentTypes(BaseEnum): - """Enum""" - CONTROLLER: str = "controller" - QUEUE: str = "queue" - TELEMETRY: str = "telemetry" - ACTIONS: str = "actions" - MODEL_PROVIDER: str = "model_provider" - ASPIRATIONAL: str = "aspirational" - GLOBAL_STRATEGY: str = "global_strategy" - AGENT_MODEL: str = "agent_model" - EXECUTIVE_FUNCTION: str = "executive_function" - COGNITIVE_CONTROL: str = "cognitive_control" - TASK_PROSECUTION: str = "task_prosecution" diff --git a/.original/app/constants/containers.py b/.original/app/constants/containers.py deleted file mode 100644 index 38db2bf..0000000 --- a/.original/app/constants/containers.py +++ /dev/null @@ -1,143 +0,0 @@ -""" -Container constants for the ace_prototype. -""" - - -# DEPENDENCIES -## Built-in -import os -## Local -from .default import BaseEnum -from .components import ComponentTypes -from .generic import ACE - - -_SETUP_FOLDER: str = "./setup" -_ORCHESTRATOR: str = "podman" - - -# IMAGE -ACE_IMAGE_NAME: str = "ace_prototype" -_FULL_ACE_IMAGE_NAME: str = f"localhost/{ACE_IMAGE_NAME}:latest" - -class ImageCommands(BaseEnum): - """Enum""" - CHECK_IMAGES: str = f"{_ORCHESTRATOR} images" - BUILD_IMAGE: str = f"{_ORCHESTRATOR} build -t {ACE_IMAGE_NAME} -f {_SETUP_FOLDER}/Containerfile ." - - -# VOLUMES -_VOLUME: str = "volume" - -class VolumePaths(BaseEnum): - """Enum""" - STORAGE: str = "storage" - HOST: str = f"{os.getcwd()}/{STORAGE}" - HOST_CONTROLLER: str = f"{HOST}/controller" - HOST_LAYERS: str = f"{HOST}/layers" - HOST_MODEL_PROVIDER: str = f"{HOST}/model_provider" - HOST_OUTPUT: str = f"{HOST}/output" - CONTAINER: str = f"/home/{ACE.LOWER_NAME}/{STORAGE}" - CONTROLLER: str = f"{CONTAINER}/controller" - LAYERS: str = f"{CONTAINER}/layers" - MODEL_PROVIDER: str = f"{CONTAINER}/model_provider" - OUTPUT: str = f"{CONTAINER}/output" - -class DevVolumePaths(BaseEnum): - """Enum""" - STORAGE: str = "./.storage" - CONTROLLER: str = f"{STORAGE}/controller" - CONTROLLER_SETTINGS: str = f"{CONTROLLER}/settings" - LAYERS: str = f"{STORAGE}/layers" - MODEL_PROVIDER: str = f"{STORAGE}/model_provider" - OUTPUT: str = f"{STORAGE}/output" - -REQUIRED_STORAGE_PATHS: tuple[str, ...] = ( - VolumePaths.HOST_CONTROLLER, - VolumePaths.HOST_LAYERS, - VolumePaths.HOST_MODEL_PROVIDER, - VolumePaths.HOST_OUTPUT -) - -REQUIRED_DEV_STORAGE_PATHS: tuple[str, ...] = ( - DevVolumePaths.CONTROLLER, - DevVolumePaths.LAYERS, - DevVolumePaths.MODEL_PROVIDER, - DevVolumePaths.OUTPUT -) - -# NETWORK -ACE_NETWORK_NAME: str = f"{ACE.LOWER_NAME}_network" - -class NetworkCommands(BaseEnum): - """Enum""" - CHECK_NETWORK: str = f"{_ORCHESTRATOR} network ls" - CREATE_NETWORK: str = f"{_ORCHESTRATOR} network create {ACE_NETWORK_NAME}" - - -# DEPLOYMENT -class DeploymentFile(BaseEnum): - """Enum""" - PATH: str = f"{_SETUP_FOLDER}/deployment.yaml" - USER_PATH: str = f"{_SETUP_FOLDER}/.user_deployment.yaml" - -class DeploymentCommands(BaseEnum): - """Enum""" - CHECK: str = f"{_ORCHESTRATOR} pod ps" - _DEPLOY_COMMAND: str = f"{_ORCHESTRATOR} kube play" - DEPLOY: str = f"{_DEPLOY_COMMAND} --network {ACE_NETWORK_NAME} --replace {DeploymentFile.USER_PATH}" - STOP: str = f"{_DEPLOY_COMMAND} --network {ACE_NETWORK_NAME} --down {DeploymentFile.USER_PATH}" - -class ComponentPorts(BaseEnum): - """Enum""" - CONTROLLER: str = "2349" - QUEUE: str = "4222" - MODEL_PROVIDER: str = "4223" - TELEMETRY: str = "4931" - ACTIONS: str = "4932" - ASPIRATIONAL: str = "4581" - GLOBAL_STRATEGY: str = "4582" - AGENT_MODEL: str = "4583" - EXECUTIVE_FUNCTION: str = "4584" - COGNITIVE_CONTROL: str = "4585" - TASK_PROSECUTION: str = "4586" - -DEPLOYMENT_REPLACE_KEYWORDS: dict[str, str] = { - "{{ ace_pod_name }}": ACE.LOWER_NAME, - "{{ ace_image_name }}": _FULL_ACE_IMAGE_NAME, - "{{ start_command }}": """python3\n - main.py\n - -sb\n - -ct""", - "{{ controller_name }}": ComponentTypes.CONTROLLER, - "{{ controller_port }}": ComponentPorts.CONTROLLER, - "{{ queue_name }}": ComponentTypes.QUEUE, - "{{ queue_port }}": ComponentPorts.QUEUE, - "{{ model_provider_name }}": ComponentTypes.MODEL_PROVIDER, - "{{ model_provider_port }}": ComponentPorts.MODEL_PROVIDER, - "{{ telemetry_name }}": ComponentTypes.TELEMETRY, - "{{ telemetry_port }}": ComponentPorts.TELEMETRY, - "{{ actions_name }}": ComponentTypes.ACTIONS, - "{{ actions_port }}": ComponentPorts.ACTIONS, - "{{ aspirational_name }}": ComponentTypes.ASPIRATIONAL, - "{{ aspirational_port }}": ComponentPorts.ASPIRATIONAL, - "{{ global_strategy_name }}": ComponentTypes.GLOBAL_STRATEGY, - "{{ global_strategy_port }}": ComponentPorts.GLOBAL_STRATEGY, - "{{ agent_model_name }}": ComponentTypes.AGENT_MODEL, - "{{ agent_model_port }}": ComponentPorts.AGENT_MODEL, - "{{ executive_function_name }}": ComponentTypes.EXECUTIVE_FUNCTION, - "{{ executive_function_port }}": ComponentPorts.EXECUTIVE_FUNCTION, - "{{ cognitive_control_name }}": ComponentTypes.COGNITIVE_CONTROL, - "{{ cognitive_control_port }}": ComponentPorts.COGNITIVE_CONTROL, - "{{ task_prosecution_name }}": ComponentTypes.TASK_PROSECUTION, - "{{ task_prosecution_port }}": ComponentPorts.TASK_PROSECUTION, - "{{ controller_host_path }}": VolumePaths.HOST_CONTROLLER, - "{{ controller_container_path }}": VolumePaths.CONTROLLER, - "{{ controller_volume }}": f"{ACE.LOWER_NAME}_{ComponentTypes.CONTROLLER}_{_VOLUME}", - "{{ layers_host_path }}": VolumePaths.HOST_LAYERS, - "{{ layers_container_path }}": VolumePaths.LAYERS, - "{{ layers_volume }}": f"{ACE.LOWER_NAME}_layers_{_VOLUME}", - "{{ model_provider_host_path }}": VolumePaths.HOST_MODEL_PROVIDER, - "{{ model_provider_container_path }}": VolumePaths.MODEL_PROVIDER, - "{{ model_provider_volume }}": f"{ACE.LOWER_NAME}_model_provider_{_VOLUME}", - "{{ output_host_path }}": VolumePaths.HOST_OUTPUT, - "{{ output_container_path }}": VolumePaths.OUTPUT, - "{{ output_volume }}": f"{ACE.LOWER_NAME}_output_{_VOLUME}" -} diff --git a/.original/app/constants/default.py b/.original/app/constants/default.py deleted file mode 100644 index 08bd686..0000000 --- a/.original/app/constants/default.py +++ /dev/null @@ -1,38 +0,0 @@ -# DEPENDENCIES -## Built-in -from abc import ABC -from typing import final, get_type_hints - - -# ENUMS -class BaseEnum(ABC): - """Base Enum Class""" - _ALLOWED_ENUM_TYPES: tuple[type, ...] = (str, int) - - def __init_subclass__(cls, **kwargs): - super().__init_subclass__(**kwargs) - for var_name, var_value in get_type_hints(cls).items(): - if var_name.startswith("__") or var_name.startswith("_"): - continue - if var_value not in cls._ALLOWED_ENUM_TYPES: - raise TypeError(f"Attribute '{var_name}' must be of type {cls._ALLOWED_ENUM_TYPES}") - - @final - @classmethod - def get_dict(cls) -> dict[str, str]: - base_enum_dict: dict[str, str] = { - variable_name: value - for variable_name, value in vars(cls).items() - if not variable_name.startswith("__") and not variable_name.startswith("_") - } - return base_enum_dict - - @final - @classmethod - def get_values(cls) -> tuple[str, ...]: - return tuple(cls.get_dict().values()) - - @final - @classmethod - def get_frozen_values(cls) -> frozenset[str]: - return frozenset(cls.get_values()) diff --git a/.original/app/constants/generic.py b/.original/app/constants/generic.py deleted file mode 100644 index 45cf1d7..0000000 --- a/.original/app/constants/generic.py +++ /dev/null @@ -1,36 +0,0 @@ -""" -Generic constants for the ace_prototype. -""" - -# DEPENDENCIES -## Built-In -from typing import Any -## Local -from .default import BaseEnum - - -class ACE(BaseEnum): - """Enum""" - NAME: str = "ACE" - LOWER_NAME: str = "ace" - -class Paths(BaseEnum): - COMPONENTS: str = "./components" - ACTIONS: str = f"{COMPONENTS}/actions" - CONTROLLER: str = f"{COMPONENTS}/controller" - INPUTS: str = f"{COMPONENTS}/inputs" - LAYER: str = f"{COMPONENTS}/layer" - MEMORY: str = f"{COMPONENTS}/memory" - MODEL_PROVIDER: str = f"{COMPONENTS}/model_provider" - QUEUE: str = f"{COMPONENTS}/queue" - -class GenericKeys(BaseEnum): - """Enum""" - NONE: str = "none" - EMPTY: str = "" - EMPTY_PATH: str = "./empty" - DEFAULT: str = "default" - - -# TYPES -TOMLConfig = dict[str, dict[str, Any]] diff --git a/.original/app/constants/layer.py b/.original/app/constants/layer.py deleted file mode 100644 index 0e1657f..0000000 --- a/.original/app/constants/layer.py +++ /dev/null @@ -1,87 +0,0 @@ -""" -Layer constants for the ace_prototype. -""" - -# DEPENDENCIES -## Local -from .containers import VolumePaths -from .components import ComponentTypes -from .default import BaseEnum -from .generic import GenericKeys - - -class LayerKeys(BaseEnum): - """Enum""" - NAME: str = "name" - TYPE: str = "layer_type" - - # Bus - QUEUE: str = "queue" - MESSAGE_TYPE: str = "message_type" - MESSAGES: str = "messages" - HEADING: str = "heading" - CONTENT: str = "content" - - # Config - BASE_INFORMATION: str = "base_information" - CURRENT_ACE: str = "current_ace" - MISSION: str = "ace_mission" - - # Prompt Files - BASE_PROMPT: str = "base_prompt" - - # State - FIRST_RUN: str = "first_run" - PROCESSING: str = "processing" - MAX_RETRIES: str = "max_retries" - DEFAULT_GUIDANCE: str = "default_guidance" - HAS_DATA: str = "has_data" - DEFAULT_DATA: str = "default_data" - - # Message Types - COMMANDS: str = "commands" - INTERNAL: str = "internal" - GUIDANCE: str = "guidance" - DATA: str = "data" - TELEMETRY: str = "telemetry" - - # Sub Message Types - ACTIONS: str = "actions" - -class LayerPaths(BaseEnum): - """Enum""" - CONFIG: str = f"{VolumePaths.HOST_LAYERS}/.config" - -class Layers(BaseEnum): - """Enum""" - ASPIRATIONAL: str = ComponentTypes.ASPIRATIONAL - GLOBAL_STRATEGY: str = ComponentTypes.GLOBAL_STRATEGY - AGENT_MODEL: str = ComponentTypes.AGENT_MODEL - EXECUTIVE_FUNCTION: str = ComponentTypes.EXECUTIVE_FUNCTION - COGNITIVE_CONTROL: str = ComponentTypes.COGNITIVE_CONTROL - TASK_PROSECUTION: str = ComponentTypes.TASK_PROSECUTION - -class LayerCommands(BaseEnum): - """Enum""" - NONE: str = GenericKeys.NONE - POST: str = "power_on_self_test" - - -# CAPABILITIES -class ActionTags(BaseEnum): - """Enum""" - NONE: str = GenericKeys.NONE - WARNING: str = "WARNING! This is a very expensive operation, use only when necessary..." - OPTIONAL: str = "Don't show this feature if relevant flag is disabled..." - -class Actions(BaseEnum): - """Enum""" - NONE: str = GenericKeys.NONE - MATH: str = "math" - WORKFLOWS: str = "workflows" - FILE: str = "files" - SHELL: str = "shell" - DATABASE: str = "database" - INTERNET: str = "internet" - API: str = "api" - SPEAK: str = "chat" diff --git a/.original/app/constants/model_provider.py b/.original/app/constants/model_provider.py deleted file mode 100644 index ef0623f..0000000 --- a/.original/app/constants/model_provider.py +++ /dev/null @@ -1,94 +0,0 @@ -""" -Model Provider constants for the ace_prototype. -""" - -# DEPENDENCIES -## Local -from .containers import VolumePaths -from .default import BaseEnum - - -class LLMKeys(BaseEnum): - """Enum""" - MODEL_TYPE: str = "model_type" - PROVIDER_TYPE: str = "provider_type" - - # Config - BASE_INFORMATION: str = "base_information" - CURRENT_MAPPING: str = "current_mapping" - - # Provider Details - API_KEY: str = "api_key" - MODEL: str = "model" - CONTEXT: str = "context" - TEMPERATURE: str = "temperature" - RATE_LIMIT: str = "rate_limit" - LOW_VRAM: str = "low_vram" - -class ModelProviderPaths(BaseEnum): - """Enum""" - CONFIG: str = f"{VolumePaths.HOST_MODEL_PROVIDER}/.config" - -class LLMStackTypes(BaseEnum): - """Enum""" - GENERALIST: str = "generalist" - EFFICIENT: str = "efficient" - CODER: str = "coder" - FUNCTION_CALLER: str = "function_caller" - EMBEDDER: str = "embedder" - RERANKER: str = "reranker" - -class ModelTypes(BaseEnum): - """Enum""" - LLM: str = "llm" - EMBEDDER: str = "embedder" - RERANKER: str = "reranker" - - -# PROVIDERS -class Providers(BaseEnum): - """Enum""" - CLAUDE: str = "claude" - GROQ: str = "groq" - OLLAMA: str = "ollama" - OPENAI: str = "openai" - FAST_EMBED: str = "fast_embed" - RAGATOUILLE: str = "ragatouille" - CROSS_ENCODER: str = "cross_encoder" - -class ClaudeModels(BaseEnum): - """Enum""" - OPUS: str = "claude-3-opus-20240229" - SONNET: str = "claude-3-sonnet-20240229" - HAIKU: str = "claude-3-haiku-20240307" - -class GroqModels(BaseEnum): - """Enum""" - MIXTRAL: str = "mixtral-8x7b-32768" - -class OllamaModels(BaseEnum): - """Enum""" - ALPHAMONARCH: str = "alphamonarch" - PHI_TWO_ORANGE: str = "phi2-orange" - STABLELM_TWO_ZEPHYR: str = "stablelm2:1.6b-zephyr-q6_K" - DEEPSEEK_CODER: str = "deepseek-coder:6.7b-instruct-q3_K_L" - DEEPSEEK_CODER_SMALL: str = "deepseek-coder:1.3b-instruct-q6_K" - GORILLA_OPENFUNCTIONS: str = "adrienbrault/gorilla-openfunctions-v2:Q3_K_L" - -class OpenAIModels(BaseEnum): - """Enum""" - FOUR: str = "gpt-4" - FOUR_TURBO: str = "gpt-4-turbo-preview" - THREE_POINT_FIVE: str = "gpt-3.5-turbo" - -class RagatouilleModels(BaseEnum): - """Enum""" - MXBAI_COLBERT: str = "mixedbread-ai/mxbai-colbert-large-v1" - -class FastEmbedModels(BaseEnum): - """Enum""" - MXBAI_EMBED: str = "mixedbread-ai/mxbai-embed-large-v1" - -class CrossEncoderModels(BaseEnum): - """Enum""" - MXBAI_RERANKER: str = "mixedbread-ai/mxbai-rerank-large-v1" diff --git a/.original/app/constants/prompts.py b/.original/app/constants/prompts.py deleted file mode 100644 index ab638be..0000000 --- a/.original/app/constants/prompts.py +++ /dev/null @@ -1,40 +0,0 @@ -""" -Prompt constants for the ace_prototype. -""" - - -# DEPENDENCIES -## Local -from .generic import Paths -from .default import BaseEnum - - -class PromptKeys(BaseEnum): - TYPE: str = "prompt_type" - PROMPT: str = "prompt" - CONTEXT: str = "context" - IDENTITY: str = "identity" - MISSION: str = "ace_mission" - GUIDANCE: str = "guidance" - DATA: str = "data" - TELEMETRY: str = "telemetry" - RESPONSE_FORMAT: str = "response_format" - SCHEMA: str = "schema" - -class PromptTypes(BaseEnum): - """Enum""" - REFINE: str = "refine" - OUTPUT: str = "output" - -class PromptFilePaths(BaseEnum): - """Enum""" - _SYSTEM_PROMPTS: str = f"{Paths.LAYER}/system_prompts" - LAYER: str = f"{_SYSTEM_PROMPTS}/layer" - CONTEXT: str = f"{_SYSTEM_PROMPTS}/ace_context" - IDENTITIES: str = f"{_SYSTEM_PROMPTS}/identities" - _RESPONSE_SCHEMAS: str = f"{_SYSTEM_PROMPTS}/responses" - ACTION_RESPONSE: str = f"{_RESPONSE_SCHEMAS}/action_response" - OUTPUT_RESPONSE: str = f"{_RESPONSE_SCHEMAS}/output_response" - RESPONSE_FORMAT: str = f"{_RESPONSE_SCHEMAS}/response_format" - EXTRA_RULES: str = f"{_RESPONSE_SCHEMAS}/extra_rules" - SCHEMAS: str = f"{_RESPONSE_SCHEMAS}/schemas" diff --git a/.original/app/constants/queue.py b/.original/app/constants/queue.py deleted file mode 100644 index 095c2e2..0000000 --- a/.original/app/constants/queue.py +++ /dev/null @@ -1,49 +0,0 @@ -""" -Queue constants for the ace_prototype. -""" - - -# DEPENDENCIES -## Local -from .components import ComponentTypes as Queues -from .default import BaseEnum - - -class QueueCommands(BaseEnum): - """Enum""" - START: str = "nats-server -js" - -QUEUES: frozenset[str] = frozenset( - { - Queues.ASPIRATIONAL, - Queues.GLOBAL_STRATEGY, - Queues.AGENT_MODEL, - Queues.EXECUTIVE_FUNCTION, - Queues.COGNITIVE_CONTROL, - Queues.TASK_PROSECUTION, - } -) - - -# BUSSES -class BusKeys(BaseEnum): - """Enum""" - UP: str = "northbound" - DOWN: str = "southbound" - -BUSSES_DOWN: dict[str, str] = { - Queues.CONTROLLER: Queues.ASPIRATIONAL, - Queues.ASPIRATIONAL: Queues.GLOBAL_STRATEGY, - Queues.GLOBAL_STRATEGY: Queues.AGENT_MODEL, - Queues.AGENT_MODEL: Queues.EXECUTIVE_FUNCTION, - Queues.EXECUTIVE_FUNCTION: Queues.COGNITIVE_CONTROL, - Queues.COGNITIVE_CONTROL: Queues.TASK_PROSECUTION, -} - -BUSSES_UP: dict[str, str] = { - Queues.TASK_PROSECUTION: Queues.COGNITIVE_CONTROL, - Queues.COGNITIVE_CONTROL: Queues.EXECUTIVE_FUNCTION, - Queues.EXECUTIVE_FUNCTION: Queues.AGENT_MODEL, - Queues.AGENT_MODEL: Queues.GLOBAL_STRATEGY, - Queues.GLOBAL_STRATEGY: Queues.ASPIRATIONAL, -} diff --git a/.original/app/constants/settings.py b/.original/app/constants/settings.py deleted file mode 100644 index 98a9bcf..0000000 --- a/.original/app/constants/settings.py +++ /dev/null @@ -1,15 +0,0 @@ -""" -Debug constants for the ace_prototype. -""" - -# DEPENDENCIES -## Local -from .default import BaseEnum - - -class DebugLevels(BaseEnum): - """Enum""" - ERROR: int = 0 - WARNING: int = 1 - INFO: int = 2 - DEBUG: int = 3 \ No newline at end of file diff --git a/.original/app/constants/startup.py b/.original/app/constants/startup.py deleted file mode 100644 index 74c01a5..0000000 --- a/.original/app/constants/startup.py +++ /dev/null @@ -1,12 +0,0 @@ -""" -Startup constants for the ace_prototype. -""" - -# DEPENDENCIES -## Local -from .default import BaseEnum - - -class StartupCommands(BaseEnum): - """Enum""" - UPDATE: str = "git pull" diff --git a/.original/app/constants/telemetry.py b/.original/app/constants/telemetry.py deleted file mode 100644 index d62454c..0000000 --- a/.original/app/constants/telemetry.py +++ /dev/null @@ -1,39 +0,0 @@ -""" -Layer constants for the ace_prototype. -""" - -# DEPENDENCIES -## Local -from .default import BaseEnum -from .generic import GenericKeys - - -class TelemetryKeys(BaseEnum): - """Enum""" - WORLD_OVERVIEW_CACHE: str = "world_overview_cache" - -class TelemetryPaths(BaseEnum): - """Enum""" - SYSTEM_PROMPTS: str = "components/telemetry/system_prompts" - -class TelemetrySystemPrompts(BaseEnum): - """Enum""" - WORLD_OVERVIEW: str = f"{TelemetryPaths.SYSTEM_PROMPTS}/world_overview" - -class TelemetryTypes(BaseEnum): - """Enum""" - NONE: str = GenericKeys.NONE - TIME: str = "time" - LOCATION: str = "location" - EMBODIMENT: str = "embodiment" - WORLD_OVERVIEW: str = "world_overview" - HARDWARE_STATS: str = "hardware_statistics" - SYSTEM_METRICS: str = "system_metrics" - SOFTWARE_STATS: str = "software_statistics" - SYSTEM_PROCESSES: str = "system_processes" - RESOURCES: str = "resources" - MEMORY: str = "memory" - VISUAL: str = "visual" - AUDIO: str = "audio" - STDOUT: str = "stdout" - USER_INPUT: str = "user_input" diff --git a/.original/app/exceptions/__init__.py b/.original/app/exceptions/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/.original/app/exceptions/error_handling.py b/.original/app/exceptions/error_handling.py deleted file mode 100644 index 3a3d2cf..0000000 --- a/.original/app/exceptions/error_handling.py +++ /dev/null @@ -1,10 +0,0 @@ -# DEPENDENCIES -## Built-in -import os - - -# FUNCTIONS -def exit_on_error(error: str) -> None: - print(f"ERROR: {error}") - os._exit(1) - \ No newline at end of file diff --git a/.original/app/helpers.py b/.original/app/helpers.py deleted file mode 100644 index 313849f..0000000 --- a/.original/app/helpers.py +++ /dev/null @@ -1,257 +0,0 @@ -# DEPENDENCIES -## Built-in -import subprocess -from subprocess import Popen -import sys -from time import time -from typing import Any, IO, Optional -## Third-Party -import aiohttp -import httpx -from pydantic import BaseModel -## Local -from config import Settings -from constants.api import APIRoutes -from constants.containers import ComponentPorts -from constants.settings import DebugLevels -from exceptions.error_handling import exit_on_error - - -# LOGGING -def debug_print(message: str, debug_level: int = DebugLevels.INFO, end: Optional[str] = None) -> None: - if not Settings.DEBUG_LEVEL >= debug_level: - return - if end: - print(message, end=end) - else: - print(message) - - -# SHELL -def execute( - command: str, - should_print_result: bool = True, - ignore_error: bool = False, - error_message: str = "", - debug_level: int = DebugLevels.ERROR -) -> str: - """ - Execute a shell command and return the output - - Arguments: - command (str): The shell command to execute - should_print_result (bool): Whether to print the result - ignore_error (bool): Whether to ignore any errors - error_message (str): The error message to display if ignore_error is False - debug_level (int): The debug level to use for logging - - Returns: - str: The output of the shell command - """ - if not error_message: - error_message = f"Unable to execute command: {command}" - debug_print(f"Running Command: {command}", debug_level=4) - command_list: tuple[str, ...] = tuple(command.split()) - process: Popen = subprocess.Popen(command_list, stdout=subprocess.PIPE, text=True) - if should_print_result: - has_printed: bool = False - while process.poll() is None: - if not process.stdout: - continue - print_lines: IO = process.stdout - if has_printed: - for _ in print_lines: - sys.stdout.write("\033[F") # Move cursor up one line - sys.stdout.write("\033[K") # Clear line - for line in print_lines: - debug_print(line, debug_level=debug_level, end="") - has_printed = True - if process.returncode != 0 and not ignore_error: - exit_on_error(f"{error_message}\n{process.stderr}") - stdout, stderr = process.communicate() - return stdout - -def exec_check_exists(check_command: str, keyword: str) -> bool: - """ - Checks if the keyword exists in the output of the check_command - - Arguments: - check_command (str): The shell command to used to check if the keyword exists - keyword (str): The keyword to check for - - Returns: - bool: True if the keyword exists, False otherwise - """ - debug_print(f"\nChecking using {check_command} for {keyword}...", DebugLevels.DEBUG) - existing: frozenset = frozenset(execute(check_command, debug_level=DebugLevels.DEBUG).split("\n")) - debug_print(f"Existing Terms: {existing}", DebugLevels.DEBUG) - for entry in existing: - if keyword in entry: - return True - return False - - -# API REQUESTS -async def get_api(api_port: str, endpoint: str, payload: BaseModel) -> str: - """ - Sends a GET request to the specified API endpoint with the provided payload and returns the response as a string - - Arguments: - api_port (str): The API port to send the request to - endpoint (str): The API endpoint to send the request to - payload (BaseModel): The payload to send in the request - - Returns: - str: The response json as a string - """ - if api_port not in ComponentPorts.get_frozen_values(): - raise ValueError(f"Invalid API Port: {api_port}") - print(f"Send Payload: {payload.model_dump_json()}") - async with aiohttp.ClientSession() as session: - async with session.get( - url=f"http://127.0.0.1:{api_port}{APIRoutes.VONE}/{endpoint}", - data=payload.model_dump_json(), - headers={'Content-Type': 'application/json'}, - timeout=900 - ) as response: - print("Api Response Status:", response.status) - print("Api Response Content-type:", response.headers['content-type']) - html: str = await response.text() - print("Api Response Body:", html) - return html - -async def post_api(api_port: str, endpoint: str, payload: BaseModel) -> str: - """ - Sends a POST request to the specified API endpoint with the provided payload and returns the response as a string - - Arguments: - api_port (str): The API port to send the request to - endpoint (str): The API endpoint to send the request to - payload (BaseModel): The payload to send in the request - - Returns: - str: The response json as a string - """ - if api_port not in ComponentPorts.get_frozen_values(): - raise ValueError(f"Invalid API Port: {api_port}") - print(f"Send Payload: {payload.model_dump_json()}") - async with aiohttp.ClientSession() as session: - async with session.post( - url=f"http://127.0.0.1:{api_port}{APIRoutes.VONE}/{endpoint}", - data=payload.model_dump_json(), - headers={'Content-Type': 'application/json'}, - timeout=900 - ) as response: - print("Status:", response.status) - print("Content-type:", response.headers['content-type']) - html: str = await response.text() - print("Body:", html, "...") - return html - -def check_internet_access() -> bool: - """ - Check if the device has internet access - - Returns: - bool: True if the device has internet access, False otherwise - """ - try: - response = httpx.get("https://www.google.com/") - response.raise_for_status() - return True - except httpx.RequestError: - return False - - -# CACHE -class KeyValueCacheStore: - """ - A simple key-value cache store - - Attributes: - store (dict): The cache store - ttl_map (dict): The time-to-live (TTL) map for each key - fetches (int): The number of total fetches - individual_fetches (dict): The number of individual fetches for each key - - Methods: - get(self, key: str) -> Optional[Any] - set(self, key: str, value: Any, ttl_seconds: Optional[int] = None) -> None - invalidate(self, key: str) -> None - clear(self) -> None - get_stats(self) -> dict - """ - def __init__(self): - self.store: dict[str, Any] = {} - self.ttl_map: dict[str, dict[str, int]] = {} - self.fetches: int = 0 - self.individual_fetches: dict[str, int] = {} - - def get(self, key: str) -> Optional[Any]: - """ - Get a value from the cache - - Arguments: - key (str): The key to get the value for - - Returns: - Any: The value from the cache, or None if the key is not found - """ - self.fetches += 1 - self.individual_fetches[key] = self.individual_fetches.get(key, 0) + 1 - if key in self.ttl_map: - if int(time()) > self.ttl_map[key]["expiry_time"]: - self.invalidate(key) - return None - return self.store[key] - return self.store.get(key, None) - - def set(self, key: str, value: Any, ttl_seconds: Optional[int] = None) -> None: - """ - Set a value in the cache. If ttl_seconds is provided, the value will expire after that many seconds - - Arguments: - key (str): The key to store the value under - value (Any): The value to store - ttl_seconds (Optional[int]): The number of seconds until the value expires - """ - self.store[key] = value - if ttl_seconds: - ttl_map: dict[str, int] = { - "ttl": ttl_seconds, - "expiry_time": int(time()) + ttl_seconds - } - self.ttl_map[key] = ttl_map - - def invalidate(self, key): - """ - Invalidate a value in the cache - - Arguments: - key (str): The key to invalidate - - Raises: - KeyError: If the key is not found - """ - try: - del self.store[key] - except KeyError: - raise KeyError(f"Key not found: {key}") - - def clear(self): - """ - Clear the entire cache - """ - self.store.clear() - self.ttl_map.clear() - self.fetches = 0 - self.individual_fetches.clear() - - def get_stats(self): - """ - Get the statistics for the cache - - Returns: - dict: The statistics for the cache - """ - return {"size": len(self.store), "fetches": self.fetches, "individual_fetches": self.individual_fetches} diff --git a/.original/app/main.py b/.original/app/main.py deleted file mode 100755 index be696cb..0000000 --- a/.original/app/main.py +++ /dev/null @@ -1,308 +0,0 @@ -#!/usr/bin/env python3 -""" -Starting point for the ACE Prototype, as well as the individual components. - -Author: jayfalls - -Arguments: - -d/--dev: bool -> Enable dev mode - -t/--test: bool -> Run tests - -b/--build: bool -> Build the images - -nb/--no-build: bool -> Skip the build check - -s/--stop: bool -> Stop the ACE cluster - -r/--restart: bool -> Restart the ACE cluster deployment - -u/--update: bool -> Update the ACE - -ct/--component-type: str -> The type of this component -""" - - -# DEPENDENCIES -## Built-in -from argparse import ArgumentParser -import os -from typing import Callable -## Third Party -import pytest -## Local -from constants.components import ComponentTypes -from constants.settings import DebugLevels -from constants.startup import StartupCommands -from constants.arguments import (ArgumentNames, ARGUMENTS, ARGUMENTS_HELP, - BOOL_ARGUMENTS, STRING_ARGUMENTS, ARGUMENTS_SHORT -) -from constants.containers import (ACE_IMAGE_NAME, ImageCommands, - REQUIRED_STORAGE_PATHS, REQUIRED_DEV_STORAGE_PATHS, - ACE_NETWORK_NAME, NetworkCommands, - DeploymentFile, DEPLOYMENT_REPLACE_KEYWORDS, DeploymentCommands -) -from constants.generic import ACE -from components import COMPONENT_MAP -from exceptions.error_handling import exit_on_error -from helpers import debug_print, execute, exec_check_exists - - -# VARIABLES -class ACEArguments: - """ - Class representing arguments for the ACE. - - Attributes: - dev (bool): Flag indicating dev mode. - should_test (bool): Flag indicating if testing should be done. - should_build (bool): Flag indicating if building should be done. - no_build (bool): Flag indicating to skip building. - local_mode (bool): Flag indicating local mode. - stop (bool): Flag indicating to stop. - should_restart (bool): Flag indicating if restart is needed. - should_update (bool): Flag indicating if update is needed. - component (str): String representing a component. - """ - dev: bool = False - should_test: bool = False - should_build: bool = False - no_build: bool = False - stop: bool = False - should_restart: bool = False - should_update: bool = False - component: str = "" - - -# ARGUMENTS -def _set_arguments(arg_parser: ArgumentParser) -> None: - for argument in BOOL_ARGUMENTS: - arg_parser.add_argument(*tuple(ARGUMENTS.get(argument, "")), action='store_true', required=False, help=ARGUMENTS_HELP[argument]) - for argument in STRING_ARGUMENTS: - arg_parser.add_argument(*tuple(ARGUMENTS.get(argument, "")), type=str, required=False, help=ARGUMENTS_HELP[argument]) - -def _parse_arguments(arg_parser: ArgumentParser) -> ACEArguments: - arguments: dict = {key: value for key, value in vars(arg_parser.parse_args()).items() if value is not None} - ace_arguments = ACEArguments() - ace_arguments.dev = arguments.get(ArgumentNames.DEV, False) - if ace_arguments.dev: - pass - # ASSIGN DEV LOGIC - ace_arguments.component = arguments.get(ArgumentNames.COMPONENT_TYPE, ArgumentNames.STARTUP) - ace_arguments.should_test = arguments.get(ArgumentNames.TEST, False) - ace_arguments.should_build = arguments.get(ArgumentNames.BUILD, False) - ace_arguments.no_build = arguments.get(ArgumentNames.SKIP_BUILD, False) - ace_arguments.stop = arguments.get(ArgumentNames.STOP, False) - ace_arguments.should_update = arguments.get(ArgumentNames.UPDATE, False) - ace_arguments.should_restart = arguments.get(ArgumentNames.RESTART, False) - debug_print(f"Arguments: {ace_arguments.__dict__}", DebugLevels.DEBUG) - if ace_arguments.component not in ComponentTypes.get_frozen_values() and ace_arguments.component != ArgumentNames.STARTUP: - exit_on_error(f"Invalid component type: {ace_arguments.component}!\nPlease use one of the following: {ComponentTypes.get_values()}") - return ace_arguments - -def assign_arguments() -> ACEArguments: - """ - Assigns runtime arguments. - - Arguments: - None - - Returns: - ACEArguments containing the runtime arguments. - """ - arg_parser = ArgumentParser() - _set_arguments(arg_parser) - ace_arguments: ACEArguments = _parse_arguments(arg_parser) - return ace_arguments - - -# SETUP -def _setup_folders() -> None: - required_paths: tuple[str, ...] = (*REQUIRED_STORAGE_PATHS, *REQUIRED_DEV_STORAGE_PATHS) - _ = [os.makedirs(dir_path, exist_ok=True) for dir_path in required_paths] - -def _setup_user_deployment_file() -> None: - if os.path.isfile(DeploymentFile.USER_PATH): - return - print("\nFirst time setting up user deployment file...") - with open(DeploymentFile.PATH, "r", encoding="utf-8") as deployment_file: - deployment_string: str = deployment_file.read() - deployment_file.close() - for key, replace in DEPLOYMENT_REPLACE_KEYWORDS.items(): - deployment_string = deployment_string.replace(key, replace) - with open(DeploymentFile.USER_PATH, "w", encoding="utf-8") as user_deployment_file: - user_deployment_file.write(deployment_string) - user_deployment_file.close() - # DO DEV AS WELL - -def setup() -> None: - """ - A function to set up folders and the user deployment file. - - Arguments: - None - - Returns: - None - """ - _setup_folders() - _setup_user_deployment_file() - - -# BUILD -def build(request_build: bool) -> bool: - """ - A function which builds if image doesn't exist or if request_build flag is set. - - Arguments: - request_build: bool | Flag indicating if a build is requested. - - Returns: - A bool indicating whether a build was performed. - """ - print("\nChecking if build is required...") - check_images_command: str = ImageCommands.CHECK_IMAGES - image_exists: bool = exec_check_exists(check_images_command, ACE_IMAGE_NAME) - should_build: bool = not image_exists or request_build - if should_build: - if not image_exists: - print("\nImage does not exist\nBuilding container...") - else: - print("\nBuilding container...") - build_ace_images_command: str = ImageCommands.BUILD_IMAGE - execute(build_ace_images_command, error_message="Unable to build image") - return should_build - print("\nImage already exists\nSkipping build...") - return should_build - - -# TESTS -def _run_tests(tests_to_run: tuple[str, ...]) -> None: - print("Running tests...") - # Create a pytest argument parser - args = ['--capture=no'] - - # Add the names of the tests to run to the arguments list - args.extend(tests_to_run) - - # Run the tests using pytest's main function - pytest.main(args) - -def test() -> None: - """ - Fake test. - - Arguments: - None - - Returns: - None - """ - print("\nTest Mode\n") - tests: tuple[str, ...] = ("test_1", "test_2", "test_3") - _run_tests(tests) - - -# RUNTIME -def update() -> None: - """ - Update the ACE. - - Arguments: - None - - Returns: - None - """ - print(f"\nUpdating {ACE.NAME}...") - update_command: str = StartupCommands.UPDATE - execute(update_command) - -def _setup_network() -> None: - if not exec_check_exists(NetworkCommands.CHECK_NETWORK, ACE_NETWORK_NAME): - print("\nFirst time setting up network...") - execute(NetworkCommands.CREATE_NETWORK) - -def stop(exists: bool) -> None: - """ - Stops the ACE if it exists. - - Arguments: - exists: bool | Indicates if the ace is running. - - Returns: - None - """ - if not exists: - print(f"{ACE.NAME} is not running! Cannot stop...") - return - print(f"\nStopping {ACE.NAME}...") - execute(DeploymentCommands.STOP) - -def start_ace(restart: bool, exists: bool) -> None: - """ - Start the ACE, handling restarts. - - Arguments: - restart: bool | Whether to restart the ACE. - exists: bool | Whether the ACE is already running. - - Returns: - None - """ - deploy_command: str = DeploymentCommands.DEPLOY - if restart: - print(f"\nRestarting {ACE.NAME}...") - execute(deploy_command) - return - if exists: - print(f"\nACE is already running...\n\nPlease run with {ARGUMENTS_SHORT[ArgumentNames.RESTART]} to restart!") - return - print(f"\nStarting {ACE.NAME}...") - execute(deploy_command) - -def start_component(component_type: str) -> None: - """ - Start a component of a specified type, optionally in dev mode. - - Args: - component_type: str | The type of component to start. - dev: bool | Whether to start the component in dev or not. - - Returns: - None - """ - title = component_type.replace("_", " ").title() - print(f"\nStarting {title}...") - start: Callable[[str], None] = COMPONENT_MAP[component_type] - start(component_type) - - -# MAIN -def main() -> None: - """ - Initialises and starts the ACE / ACE component based off the starting arguments. - - Arguments: - None - - Returns: - None - """ - ace_arguments: ACEArguments = assign_arguments() - setup() - if ace_arguments.should_update: - ace_arguments.should_build = True - update() - if not ace_arguments.no_build: - built: bool = build(ace_arguments.should_build) - if built: - ace_arguments.should_restart = True - if ace_arguments.should_test: - test() - return - if not ace_arguments.component == ArgumentNames.STARTUP: - start_component(ace_arguments.component) - return - exists: bool = exec_check_exists(DeploymentCommands.CHECK, ACE.LOWER_NAME) - if ace_arguments.stop: - stop(exists) - return - _setup_network() - start_ace(ace_arguments.should_restart, exists) - -if __name__ == "__main__": - main() diff --git a/.original/app/requirements b/.original/app/requirements deleted file mode 100644 index 8a82282..0000000 --- a/.original/app/requirements +++ /dev/null @@ -1,23 +0,0 @@ -aiohttp==3.9.3 -anthropic==0.21.3 -fastapi==0.110.0 -fastembed==0.2.5 -groq==0.4.2 -httpx==0.25.2 -jinja2==3.1.3 -nats-py==2.7.2 -numpy==1.24.3 -ollama==0.1.4 -openai==1.14.1 -passlib[bcrypt]==1.7.4 -pydantic==2.6.2 -pytest==8.0.1 -python-jose[cryptography]==3.3.0 -python-multipart==0.0.9 -ragatouille==0.0.8.post2 -sentence-transformers==2.6.1 -structlog==24.1.0 -tenacity==8.2.3 -toml==0.10.2 -uvicorn==0.27.1 -watchdog==4.0.0 \ No newline at end of file diff --git a/.original/app/setup/Containerfile b/.original/app/setup/Containerfile deleted file mode 100644 index 096e0c0..0000000 --- a/.original/app/setup/Containerfile +++ /dev/null @@ -1,12 +0,0 @@ -FROM python:alpine - -COPY . /home/ace - -RUN apk add --no-cache bash build-base libffi-dev python3-dev nats-server && \ - pip3 install -r /home/ace/requirements && \ - apk del build-base libffi-dev python3-dev && \ - rm -rf /var/cache/apk/* - -WORKDIR /home/ace - -ENTRYPOINT ["python3", "main.py"] \ No newline at end of file diff --git a/.original/app/setup/deployment.yaml b/.original/app/setup/deployment.yaml deleted file mode 100644 index 718a891..0000000 --- a/.original/app/setup/deployment.yaml +++ /dev/null @@ -1,138 +0,0 @@ -apiVersion: v1 -kind: Pod -metadata: - labels: - app: {{ ace_pod_name }} - name: {{ ace_pod_name }} -spec: - containers: - - command: - - {{ start_command }} - - {{ controller_name }} - image: {{ ace_image_name }} - name: {{ controller_name }} - ports: - - containerPort: {{ controller_port }} - hostPort: {{ controller_port }} - volumeMounts: - - mountPath: {{ controller_container_path }} - name: {{ controller_volume }} - - mountPath: {{ layers_container_path }} - name: {{ layers_volume }} - - command: - - {{ start_command }} - - {{ queue_name }} - image: {{ ace_image_name }} - name: {{ queue_name }} - ports: - - containerPort: {{ queue_port }} - - command: - - {{ start_command }} - - {{ model_provider_name }} - image: {{ ace_image_name }} - name: {{ model_provider_name }} - ports: - - containerPort: {{ model_provider_port }} - volumeMounts: - - mountPath: {{ model_provider_container_path }} - name: {{ model_provider_volume }} - - command: - - {{ start_command }} - - {{ telemetry_name }} - image: {{ ace_image_name }} - name: {{ telemetry_name }} - ports: - - containerPort: {{ telemetry_port }} - - command: - - {{ start_command }} - - {{ actions_name }} - image: {{ ace_image_name }} - name: {{ actions_name }} - ports: - - containerPort: {{ actions_port }} - - command: - - {{ start_command }} - - {{ memory_name }} - image: {{ ace_image_name }} - name: {{ memory_name }} - ports: - - containerPort: {{ memory_port }} - - command: - - {{ start_command }} - - {{ aspirational_name }} - image: {{ ace_image_name }} - name: {{ aspirational_name }} - ports: - - containerPort: {{ aspirational_port }} - volumeMounts: - - mountPath: {{ layers_container_path }} - name: {{ layers_volume }} - - command: - - {{ start_command }} - - {{ global_strategy_name }} - image: {{ ace_image_name }} - name: {{ global_strategy_name }} - ports: - - containerPort: {{ global_strategy_port }} - volumeMounts: - - mountPath: {{ layers_container_path }} - name: {{ layers_volume }} - - command: - - {{ start_command }} - - {{ agent_model_name }} - image: {{ ace_image_name }} - name: {{ agent_model_name }} - ports: - - containerPort: {{ agent_model_port }} - volumeMounts: - - mountPath: {{ layers_container_path }} - name: {{ layers_volume }} - - command: - - {{ start_command }} - - {{ executive_function_name }} - image: {{ ace_image_name }} - name: {{ executive_function_name }} - ports: - - containerPort: {{ executive_function_port }} - volumeMounts: - - mountPath: {{ layers_container_path }} - name: {{ layers_volume }} - - command: - - {{ start_command }} - - {{ cognitive_control_name }} - image: {{ ace_image_name }} - name: {{ cognitive_control_name }} - ports: - - containerPort: {{ cognitive_control_port }} - volumeMounts: - - mountPath: {{ layers_container_path }} - name: {{ layers_volume }} - - command: - - {{ start_command }} - - {{ task_prosecution_name }} - image: {{ ace_image_name }} - name: {{ task_prosecution_name }} - ports: - - containerPort: {{ task_prosecution_port }} - volumeMounts: - - mountPath: {{ layers_container_path }} - name: {{ layers_volume }} - - mountPath: {{ output_container_path }} - name: {{ output_volume }} - volumes: - - hostPath: - path: {{ controller_host_path }} - type: Directory - name: {{ controller_volume }} - - hostPath: - path: {{ layers_host_path }} - type: Directory - name: {{ layers_volume }} - - hostPath: - path: {{ model_provider_host_path }} - type: Directory - - hostPath: - path: {{ output_host_path }} - type: Directory - name: {{ output_volume }} - diff --git a/.original/documentation/design_doc.md b/.original/documentation/design_doc.md deleted file mode 100644 index f947c2f..0000000 --- a/.original/documentation/design_doc.md +++ /dev/null @@ -1,188 +0,0 @@ -[⬆️](..) - - -# Requirements -- **Test Driven Development** -- Must be fully **containerised** -- Containers must have **resource limits** -- Must be be able to fit on less than **4gb ram** -- Every prompt and output should be less than **4k tokens** -- Must be **entirely local** -- Must have a **strong static typing and linting** -- Must be an **MVP** -- Must fulfil all the **basic requirements** of an [**ACE Framework**](https://github.com/daveshap/ACE_Framework) - - -# Technologies Used -## Language -- [**Python**](https://github.com/python/cpython) for backend - - [**Pyright**](https://github.com/microsoft/pyright) for static type checking - - [**Ruff**](https://github.com/astral-sh/ruff) for linting - - [**Pydantic**](https://github.com/pydantic/pydantic) for data validation - - [**PDM**](https://github.com/pdm-project/pdm) for package manager - -## Communication -- [**NATS**](https://github.com/nats-io/nats-server) for queue management and messaging -- [**Fast-API**](https://github.com/tiangolo/fastapi) for apis - -## Orchestration -- [**Podman**](https://github.com/containers/podman) for containerisation - -## UI -- [**HTMX**](https://github.com/bigskysoftware/htmx) for server side rendering - - [**Jinja**](https://github.com/pallets/jinja) for html templating -- [**DaisyUI**](https://github.com/saadeghi/daisyui) for components - -## LLM -- [**Ollama**](https://github.com/ollama/ollama) for serving models - - [**Phi-2-Orange**](https://huggingface.co/rhysjones/phi-2-orange) as the base model -- [**Guardrails**](https://github.com/guardrails-ai/guardrails) for output validation -- [**Open Interpreter**](https://github.com/KillianLucas/open-interpreter) for tool usage - -### RAG -- [**LanceDB**](https://github.com/lancedb/lancedb) for memory - - [**SPR**](https://github.com/daveshap/SparsePrimingRepresentations) for compression - - [**FastEmbed**](https://github.com/qdrant/fastembed) for embedding - -## Observability -- [**VictoriaMetrics**](https://github.com/VictoriaMetrics/VictoriaMetrics) for metrics, logs aggregation and storage -- [**Grafana**](https://github.com/grafana/grafana) for visualisation -- [**Fluent-bit**](https://github.com/fluent/fluent-bit) for logs & metrics collection and forwarding -? - [**PSUtil**](https://github.com/giampaolo/psutil) for program metrics -- [**Structlog**](https://github.com/hynek/structlog) for logging - -## Storage -- [**Postgress**](https://github.com/postgres/postgres) for database - - [**Peewee**](https://github.com/coleifer/peewee) for ORM -- [**Garnet**](https://github.com/microsoft/garnet) for caching - -# System Design -![architecture.png](./media/architecture.png) - -## Core -## **Model Provider** -This will serve the llm models to each of the layers and security layer in future iterations -- **MVP** - - Serve model inference over an api - - Multiple specialest models -- **Future** - - Scaling to larger models based on spec decisions - - Kubernetes to allow multiple models running asynchronously - -## **Layers** -LLM Layers -- **MVP** - - LLM inference - - Modular - - Stop function - - Can be passed one layer down - - Option to pass several layers down - - Compression through summarisation - - Send commands downstream and inputs upstream - - Conditional input types based on layer type - - Output as JSON -- **Future** - - Request more function -- ### Aspirational - - High level guidance and alignment -- ### Global Strategy - - Long term strategic roadmaps -- ### Agent Model - - Self awareness, grounding the agent in its physical limitations - - **MVP** - - Memory interface -- ### Executive Function - - Practical project roadmaps considering mission and limitations -- ### Cognitive Control - - Task switcher -- ### Task Prosecution - - The executor of the agent - -## **Busses** -Acts as queuing and communication between the layers -- **MVP** - - Queue system -- **Future** - - Backpressure - - Distributed messages -- ### Southbound - - The control bus, sending down commands and directives -- ### Northbound - - The reporting bus, letting higher layers know of state - -## IO -## **Input** -All the sensory information of the machine -- **MVP** - - System Telemetry - - Hardware & Software Statistics - - Metrics - - Basic World State - - Datetime - - Basic Info From Internet (Should work fully offline though) - - Stdout - - File Access - - User Input (Text) - - Basic Memory - - Task Observation -- **Future** - - API Calls - - Opened Internet Access - - BASHR Loop for research - - Complex Memory - - Short Term - - Summarised for details - - Long Term - - Through Sparse Priming Representations(SPR) - - Factual - - Fact Database - - Episodic - - Vision - - Auditory - - Other Sensors? - -## **Output** -All the physical actions of the machine -- **MVP** - - Function calling - - Code - - Files - - Memory - - AI Models -- **Future** - - API calls - - Motors - - Audio - - Imagery - - 3D models - -## **Logging** -System state and outputs throughout its lifecycle -- **MVP** - - JSON Logger - - Different log levels with verbosity toggles - - Error Logs on each component -- **Future** - - Centralisation - - Dashboards - -## ACE Controls -## **Security Layer** -Acts as the vetting layer, ensuring that all outputs are valid and safe -- **MVP** - - Structure validation - - Safety validation - - Logging of outputs - - User auth - - Access control -- **Future** - - Alignment checks - -## **UI** -The way the user interacts with the ACE -- **MVP** - - User inputs - - Agent status and activations - - Deep dive into outputs -- **Future** - - Statistics \ No newline at end of file diff --git a/.original/documentation/media/architecture.png b/.original/documentation/media/architecture.png deleted file mode 100644 index 3a4863f..0000000 Binary files a/.original/documentation/media/architecture.png and /dev/null differ diff --git a/ace b/ace index ad3268d..166c10d 100755 --- a/ace +++ b/ace @@ -11,7 +11,8 @@ run_tests() { pip install --upgrade -r tests/requirements python -m pytest tests/unit/ -v - exit $? + pytest_exit_code=$? + exit $pytest_exit_code } run_ace() { diff --git a/app/component.py b/app/component.py index 3e82cfa..56503db 100755 --- a/app/component.py +++ b/app/component.py @@ -53,11 +53,14 @@ def run_component() -> None: """Startup the selected component""" arguments: dict[str, bool] = _get_arguments() - dev: bool = arguments[DictKeys.DEV] - prod: bool = arguments[DictKeys.PROD] + dev: bool = arguments.get(DictKeys.DEV, False) + prod: bool = arguments.get(DictKeys.PROD, False) if not (dev or prod): logger.critical("You must select a environment, either --dev or --prod!") - exit(1) + raise SystemExit + + if dev and prod: + dev = False selected_compenent: str | None = None for argument, is_flagged in arguments.items(): @@ -67,16 +70,16 @@ def run_component() -> None: continue if selected_compenent: logger.critical("You can only start one component at a time!") - exit(1) + raise SystemExit selected_compenent = argument if not selected_compenent: logger.critical("You must select a component to start!") - exit(1) + raise SystemExit if selected_compenent not in _COMPONENT_MAP: logger.critical(f"{selected_compenent} is not a valid component!") - exit(1) + raise SystemExit component_title: str = selected_compenent.replace("_", " ").title() _COMPONENT_MAP[selected_compenent](component_type=component_title, dev=dev) diff --git a/app/components/controller/api/routes.py b/app/components/controller/api/routes.py index 8e54830..3f50b81 100644 --- a/app/components/controller/api/routes.py +++ b/app/components/controller/api/routes.py @@ -47,7 +47,7 @@ async def get_settings_route() -> dict: ) async def set_settings_route(updated_settings: EditSettingsRequest) -> dict: try: - service.edit_settings_data(updated_settings=updated_settings.dict()) + service.edit_settings_data(updated_settings=updated_settings.model_dump()) return DefaultAPIResponseSchema(message="Settings data updated successfully!") except ValidationError as error: logger.error(error) diff --git a/app/components/controller/api/schemas.py b/app/components/controller/api/schemas.py index 857c996..ff6135b 100644 --- a/app/components/controller/api/schemas.py +++ b/app/components/controller/api/schemas.py @@ -1,6 +1,6 @@ # DEPENDENCIES ## Third-Party -from pydantic import BaseModel, validator +from pydantic import BaseModel, field_validator ## Local from constants import Defaults @@ -10,8 +10,8 @@ class SettingsSchema(BaseModel): ace_name: str = Defaults.ACE_NAME model_provider: str = Defaults.MODEL_PROVIDER temperature: float = Defaults.TEMPERATURE - - @validator("temperature") + + @field_validator("temperature") def validate_temperature(cls, value): return min(max(0.0, value), 1.0) diff --git a/app/components/controller/api/service.py b/app/components/controller/api/service.py index 90c8be3..6543bda 100644 --- a/app/components/controller/api/service.py +++ b/app/components/controller/api/service.py @@ -3,6 +3,7 @@ import json ## Local from constants import DictKeys, Files, ModelProviders +from .schemas import SettingsSchema # HELPERS @@ -10,6 +11,9 @@ def _get_settings() -> dict: settings: dict = {} with open(Files.CONTROLLER_SETTINGS, "r", encoding="utf-8") as settings_file: settings = json.loads(settings_file.read()) + settings = SettingsSchema(**settings).model_dump() + with open(Files.CONTROLLER_SETTINGS, "w", encoding="utf-8") as settings_file: + settings_file.write(json.dumps(settings)) return settings diff --git a/app/components/controller/start.py b/app/components/controller/start.py index 733e82d..55b36c1 100644 --- a/app/components/controller/start.py +++ b/app/components/controller/start.py @@ -1,23 +1,11 @@ # DEPENDENCIES -## Built-In -import json ## Third-Party import uvicorn ## Local from constants import NetworkPorts from logger import logger -from .api import controller_api, service -from .api.schemas import SettingsSchema -from constants import Files - - -def _ensure_settings() -> dict: - settings: dict = service._get_settings() - settings = SettingsSchema(**settings).dict() - with open(Files.CONTROLLER_SETTINGS, "w", encoding="utf-8") as settings_file: - settings_file.write(json.dumps(settings)) +from .api import controller_api def start_controller(component_type: str, dev: bool) -> None: logger.startup(f"Starting {component_type}...") - _ensure_settings() uvicorn.run(controller_api, host="0.0.0.0", port=int(NetworkPorts.CONTROLLER)) diff --git a/app/components/ui/package.json b/app/components/ui/package.json index 2722ef4..66d4bef 100644 --- a/app/components/ui/package.json +++ b/app/components/ui/package.json @@ -7,7 +7,8 @@ "start-local": "/opt/homebrew/opt/node@18/bin/node --max_old_space_size=8192 node_modules/@angular/cli/bin/ng serve --host 0.0.0.0 --disable-host-check", "build": "ng build", "watch": "ng build --watch --configuration development", - "test": "ng test" + "test": "ng test --watch=false --browsers=ChromeHeadless", + "test-local": "/opt/homebrew/opt/node@18/bin/node --max_old_space_size=8192 node_modules/@angular/cli/bin/ng test --watch=false --browsers=ChromeHeadless" }, "private": true, "dependencies": { diff --git a/app/components/ui/src/app/app.component.html b/app/components/ui/src/app/app.component.html index c2c1903..c42a7a3 100644 --- a/app/components/ui/src/app/app.component.html +++ b/app/components/ui/src/app/app.component.html @@ -1 +1,28 @@ - +
    + + + + @for (item of sidebarItems(); track item.name) { + + + {{ item.icon }} + + + } + + + + + + + + + +
    diff --git a/app/components/ui/src/app/app.component.scss b/app/components/ui/src/app/app.component.scss index e69de29..6a277db 100644 --- a/app/components/ui/src/app/app.component.scss +++ b/app/components/ui/src/app/app.component.scss @@ -0,0 +1,34 @@ +.page-root { + display: flex; + flex-direction: column; + height: 100%; + + .sidebar-root { + height: 100%; + + .sidebar { + width: 3.5rem; + display: grid; + align-content: center; + outline: auto; + border-radius: 0; + + .sidebar-item { + margin-bottom: 1rem; + + .active-link { + background-color: rgba(0, 0, 0, 0.04); + } + } + } + } +} + +.footer { + display: flex; + align-items: center; + justify-content: center; + height: 2rem; + gap: 2rem; + outline: auto; +} diff --git a/app/components/ui/src/app/app.component.spec.ts b/app/components/ui/src/app/app.component.spec.ts deleted file mode 100644 index 6985c7a..0000000 --- a/app/components/ui/src/app/app.component.spec.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { TestBed } from '@angular/core/testing'; -import { AppComponent } from './app.component'; - -describe('AppComponent', () => { - beforeEach(async () => { - await TestBed.configureTestingModule({ - imports: [AppComponent], - }).compileComponents(); - }); - - it('should create the app', () => { - const fixture = TestBed.createComponent(AppComponent); - const app = fixture.componentInstance; - expect(app).toBeTruthy(); - }); - - it(`should have the 'ui' title`, () => { - const fixture = TestBed.createComponent(AppComponent); - const app = fixture.componentInstance; - expect(app.title).toEqual('ui'); - }); - - it('should render title', () => { - const fixture = TestBed.createComponent(AppComponent); - fixture.detectChanges(); - const compiled = fixture.nativeElement as HTMLElement; - expect(compiled.querySelector('h1')?.textContent).toContain('Hello, ace'); - }); -}); diff --git a/app/components/ui/src/app/app.component.ts b/app/components/ui/src/app/app.component.ts index a1b53d1..9ee1c4f 100644 --- a/app/components/ui/src/app/app.component.ts +++ b/app/components/ui/src/app/app.component.ts @@ -1,22 +1,78 @@ -import { Component, OnInit } from "@angular/core"; +// DEPENDENCIES +//// Angular +import { Component, OnInit, signal } from "@angular/core"; +import { MatButtonModule } from "@angular/material/button"; +import { MatIconModule } from "@angular/material/icon"; +import { MatListModule } from "@angular/material/list"; +import { MatTooltipModule } from "@angular/material/tooltip"; +import { MatSidenavModule } from "@angular/material/sidenav"; +import { RouterModule, RouterOutlet } from "@angular/router"; import { Store } from "@ngrx/store"; +import { Observable } from "rxjs"; +//// Local import { appActions } from "./store/actions/app.actions"; -import { ACERootpageComponent } from "./components/rootpage/rootpage.component"; +import { selectAppState } from './store/selectors/app.selectors'; +import { AppState } from './store/state/app.state'; + +// TYPES +export type SidebarItem = { + name: string, + icon: string, + route?: string, +} + + +// COMPONENT @Component({ selector: "app-root", imports: [ - ACERootpageComponent + MatButtonModule, + MatIconModule, + MatListModule, + MatTooltipModule, + MatSidenavModule, + RouterModule, + RouterOutlet ], templateUrl: "./app.component.html", styleUrl: "./app.component.scss" }) export class AppComponent implements OnInit { + // Variables title = "ACE"; + sidebarItems = signal([ + { + name: "Home", + icon: "home", + route: "" + }, + { + name: "Dashboard", + icon: "dashboard", + route: "dashboard" + }, + { + name: "Chat", + icon: "chat", + route: "chat" + }, + { + name: "Settings", + icon: "settings", + route: "settings" + } + ]) + versionData$: Observable; + version: string = "0"; - constructor(private store: Store) {} + // Initialisation + constructor(private store: Store) { + this.versionData$ = this.store.select(selectAppState); + } ngOnInit(): void { this.store.dispatch(appActions.getACEVersionData()); + this.versionData$.subscribe( versionData => this.version = versionData.versionData.version); } } diff --git a/app/components/ui/src/app/app.config.ts b/app/components/ui/src/app/app.config.ts index f5521d8..b918be1 100644 --- a/app/components/ui/src/app/app.config.ts +++ b/app/components/ui/src/app/app.config.ts @@ -1,14 +1,19 @@ +// DEPENDENCIES +//// Angular import { ApplicationConfig, provideZoneChangeDetection } from '@angular/core'; import { provideHttpClient } from '@angular/common/http'; import { provideRouter } from '@angular/router'; import { provideEffects } from '@ngrx/effects'; import { provideStore } from '@ngrx/store'; import { provideStoreDevtools } from '@ngrx/store-devtools'; +//// Local import { routes } from './app.routes'; import { provideAnimationsAsync } from '@angular/platform-browser/animations/async'; import { AppEffects } from './store/effects/app.effects'; import { appReducer } from './store/reducers/app.reducers'; + +// CONFIG export const appConfig: ApplicationConfig = { providers: [ provideAnimationsAsync(), diff --git a/app/components/ui/src/app/components/footer/footer.component.html b/app/components/ui/src/app/components/footer/footer.component.html deleted file mode 100644 index e7400a7..0000000 --- a/app/components/ui/src/app/components/footer/footer.component.html +++ /dev/null @@ -1,6 +0,0 @@ -
    -

    Developed by JayFalls

    -

    Version: v{{ version }}

    -
    - - diff --git a/app/components/ui/src/app/components/footer/footer.component.scss b/app/components/ui/src/app/components/footer/footer.component.scss deleted file mode 100644 index e0ae1fa..0000000 --- a/app/components/ui/src/app/components/footer/footer.component.scss +++ /dev/null @@ -1,8 +0,0 @@ -.footer { - display: flex; - align-items: center; - justify-content: center; - height: 2rem; - gap: 2rem; - outline: auto; -} diff --git a/app/components/ui/src/app/components/footer/footer.component.ts b/app/components/ui/src/app/components/footer/footer.component.ts deleted file mode 100644 index 8c4f14b..0000000 --- a/app/components/ui/src/app/components/footer/footer.component.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { Component, OnInit } from '@angular/core'; -import { Store } from '@ngrx/store'; -import { Observable } from 'rxjs'; -import { AppState } from '../../store/state/app.state'; -import { selectAppState } from '../../store/selectors/app.selectors'; - -@Component({ - selector: 'ace-footer', - templateUrl: './footer.component.html', - styleUrls: ['./footer.component.scss'] -}) -export class ACEFooterComponent implements OnInit { - versionData$: Observable; - version: string = "0"; - - constructor(private store: Store) { - this.versionData$ = this.store.select(selectAppState); - } - - ngOnInit(): void { - this.versionData$.subscribe( versionData => this.version = versionData.versionData.version); - } -} diff --git a/app/components/ui/src/app/components/rootpage/rootpage.component.html b/app/components/ui/src/app/components/rootpage/rootpage.component.html deleted file mode 100644 index 1b8b127..0000000 --- a/app/components/ui/src/app/components/rootpage/rootpage.component.html +++ /dev/null @@ -1,25 +0,0 @@ -
    - - - - @for (item of sidebarItems(); track item.name) { - - - {{ item.icon }} - - - } - - - - - - - - - -
    diff --git a/app/components/ui/src/app/components/rootpage/rootpage.component.scss b/app/components/ui/src/app/components/rootpage/rootpage.component.scss deleted file mode 100644 index 859e941..0000000 --- a/app/components/ui/src/app/components/rootpage/rootpage.component.scss +++ /dev/null @@ -1,25 +0,0 @@ -.page-root { - display: flex; - flex-direction: column; - height: 100%; - - .sidebar-root { - height: 100%; - - .sidebar { - width: 3.5rem; - display: grid; - align-content: center; - outline: auto; - border-radius: 0; - - .sidebar-item { - margin-bottom: 1rem; - - .active-link { - background-color: rgba(0, 0, 0, 0.04); - } - } - } - } -} diff --git a/app/components/ui/src/app/components/rootpage/rootpage.component.ts b/app/components/ui/src/app/components/rootpage/rootpage.component.ts deleted file mode 100644 index b031f09..0000000 --- a/app/components/ui/src/app/components/rootpage/rootpage.component.ts +++ /dev/null @@ -1,56 +0,0 @@ -import { Component, signal } from "@angular/core"; -import { MatButtonModule } from "@angular/material/button"; -import { MatIconModule } from "@angular/material/icon"; -import { MatListModule } from "@angular/material/list"; -import { MatTooltipModule } from "@angular/material/tooltip"; -import { MatSidenavModule } from "@angular/material/sidenav"; -import { RouterModule, RouterOutlet } from "@angular/router"; -import { ACEFooterComponent } from "../footer/footer.component"; - - -export type SidebarItem = { - name: string, - icon: string, - route?: string, -} - - -@Component({ - selector: "ace-rootpage", - templateUrl: "rootpage.component.html", - styleUrl: "rootpage.component.scss", - imports: [ - ACEFooterComponent, - MatButtonModule, - MatIconModule, - MatListModule, - MatTooltipModule, - MatSidenavModule, - RouterModule, - RouterOutlet - ], -}) -export class ACERootpageComponent { - sidebarItems = signal([ - { - name: "Home", - icon: "home", - route: "" - }, - { - name: "Dashboard", - icon: "dashboard", - route: "dashboard" - }, - { - name: "Chat", - icon: "chat", - route: "chat" - }, - { - name: "Settings", - icon: "settings", - route: "settings" - } - ]) -} diff --git a/app/components/ui/src/main.ts b/app/components/ui/src/main.ts index 35b00f3..df42ea9 100644 --- a/app/components/ui/src/main.ts +++ b/app/components/ui/src/main.ts @@ -1,6 +1,9 @@ +// DEPENDENCIES +//// Angular import { bootstrapApplication } from '@angular/platform-browser'; +//// Local import { appConfig } from './app/app.config'; import { AppComponent } from './app/app.component'; -bootstrapApplication(AppComponent, appConfig) - .catch((err) => console.error(err)); + +bootstrapApplication(AppComponent, appConfig).catch((err) => console.error(err)); diff --git a/app/constants/__init__.py b/app/constants/__init__.py index 818875a..55f06ae 100644 --- a/app/constants/__init__.py +++ b/app/constants/__init__.py @@ -3,8 +3,8 @@ from .defaults import Defaults, DefaultAPIResponseSchema from .dict_keys import DictKeys from .environment_variables import EnvironmentVariables +from .folders import Folders # Folders need to be created before files! from .files import Files -from .folders import Folders from .logger import CustomLogLevels, TERMINAL_COLOR_CODES from .model_providers import ModelProviders from .names import Names diff --git a/app/constants/base_enum.py b/app/constants/base_enum.py index e2e06dc..00a2693 100644 --- a/app/constants/base_enum.py +++ b/app/constants/base_enum.py @@ -1,7 +1,7 @@ # DEPENDENCIES ## Built-in from abc import ABC -from typing import get_type_hints +import inspect # ENUMS @@ -11,11 +11,15 @@ class BaseEnum(ABC): def __init_subclass__(cls, **kwargs): super().__init_subclass__(**kwargs) - for var_name, var_value in get_type_hints(cls).items(): - if var_name.startswith("__") or var_name.startswith("_"): + for attr_name in dir(cls): + if attr_name.startswith("__") or attr_name.startswith("_"): continue - if var_value not in cls._ALLOWED_ENUM_TYPES: - raise TypeError(f"Attribute '{var_name}' must be of type {cls._ALLOWED_ENUM_TYPES}") + # Skip methods + attr = getattr(cls, attr_name) + if inspect.ismethod(attr) or inspect.isfunction(attr): + continue + if not isinstance(attr, cls._ALLOWED_ENUM_TYPES): + raise TypeError(f"Attribute '{attr_name}' must be of type {cls._ALLOWED_ENUM_TYPES}") @classmethod def get_dict(cls) -> dict[str, str]: diff --git a/app/constants/defaults.py b/app/constants/defaults.py index fee934a..0cc08fd 100644 --- a/app/constants/defaults.py +++ b/app/constants/defaults.py @@ -18,5 +18,7 @@ class Defaults(BaseEnum): TERMINAL_COLOR_CODE: str = "\033[0m" # Default color SHUTDOWN_MESSAGE: str = "Shutting down logger..." + +# SCHEMAS class DefaultAPIResponseSchema(BaseModel): message: str diff --git a/app/constants/dict_keys.py b/app/constants/dict_keys.py index eee50a9..43f37ca 100644 --- a/app/constants/dict_keys.py +++ b/app/constants/dict_keys.py @@ -4,6 +4,7 @@ class DictKeys(BaseEnum): + ACE_NAME: str = "ace_name" DEV: str = "dev" BUILD: str = "build" FUNCTION_NAME: str = "function_name" @@ -15,4 +16,5 @@ class DictKeys(BaseEnum): RESTART: str = "restart" STACKTRACE: str = "stacktrace" STOP: str = "stop" + TEMPERATURE: str = "temperature" TIMESTAMP: str = "timestamp" diff --git a/app/constants/files.py b/app/constants/files.py index 6918aa2..06d45a2 100644 --- a/app/constants/files.py +++ b/app/constants/files.py @@ -26,6 +26,17 @@ class Files(BaseEnum): # INIT +_ENSURE_JSON_FILES: frozenset[str] = frozenset([ + Files.STARTUP_HISTORY, + Files.CONTROLLER_SETTINGS +]) +def _ensure_json_files(): + for file in _ENSURE_JSON_FILES: + if not os.path.isfile(file): + with open(file, "w", encoding="utf-8") as file: + json.dump({}, file) +_ensure_json_files() + _DEPLOYMENT_REPLACE_KEYWORDS: dict[str, str] = { "{{ ace_pod_name }}": Names.ACE, "{{ ace_image_name }}": Names.FULL_IMAGE, @@ -93,14 +104,3 @@ def setup_user_deployment_file(dev: bool): with open(Files.USER_DEPLOYMENT_FILE, "w", encoding="utf-8") as user_deployment_file: user_deployment_file.write(deployment_string) user_deployment_file.close() - -_ENSURE_JSON_FILES: frozenset[str] = frozenset([ - Files.STARTUP_HISTORY, - Files.CONTROLLER_SETTINGS -]) -def _ensure_json_files(): - for file in _ENSURE_JSON_FILES: - if not os.path.isfile(file): - with open(file, "w", encoding="utf-8") as file: - json.dump({}, file) -_ensure_json_files() diff --git a/app/shell.py b/app/shell.py index c832e99..d29e0f9 100644 --- a/app/shell.py +++ b/app/shell.py @@ -11,7 +11,8 @@ def execute_shell( command: str, should_print_result: bool = True, ignore_error: bool = False, - error_message: str = "" + error_message: str = "", + _testing: bool = False ) -> str: """Execute a shell command and return the output""" if not error_message: @@ -28,7 +29,12 @@ def execute_shell( stdout_lines: list[str] = [] while True: - output = process.stdout.readline() if process.stdout else "" + error_output: str = process.stderr.readline() if process.stderr else "" + if error_output: + stdout_lines.append(error_output) + if should_print_result: + print(error_output, end="") + output: str = process.stdout.readline() if process.stdout else "" if output == "" and process.poll() is not None: break if output: @@ -44,6 +50,8 @@ def execute_shell( if process.returncode != 0 and not ignore_error: logger.error(f"{error_message}: {stderr}") + if _testing: + raise SystemExit(f"{error_message}: {stderr}") os._exit(1) return "".join(stdout_lines) diff --git a/tests/requirements b/tests/requirements index a898313..5d7f208 100644 --- a/tests/requirements +++ b/tests/requirements @@ -1,2 +1,3 @@ pytest==8.3.4 -pytest-mock==3.14.0 \ No newline at end of file +pytest-mock==3.14.0 +pyyaml==6.0.2 \ No newline at end of file diff --git a/tests/unit/__init__.py b/tests/unit/__init__.py index ed7a4d5..572e9ed 100644 --- a/tests/unit/__init__.py +++ b/tests/unit/__init__.py @@ -3,7 +3,7 @@ import os ## Local from app.constants import EnvironmentVariables -from tests.unit.constants import TestingConfigs +from tests.unit.testing_constants import TestingConfigs # LOGGER FIX diff --git a/.original/app/components/controller/api/bus/__init__.py b/tests/unit/components/__init__.py similarity index 100% rename from .original/app/components/controller/api/bus/__init__.py rename to tests/unit/components/__init__.py diff --git a/.original/app/components/controller/api/chat/__init__.py b/tests/unit/components/controller/__init__.py similarity index 100% rename from .original/app/components/controller/api/chat/__init__.py rename to tests/unit/components/controller/__init__.py diff --git a/.original/app/components/controller/api/dashboard/__init__.py b/tests/unit/components/controller/api/__init__.py similarity index 100% rename from .original/app/components/controller/api/dashboard/__init__.py rename to tests/unit/components/controller/api/__init__.py diff --git a/tests/unit/components/controller/api/test_service.py b/tests/unit/components/controller/api/test_service.py new file mode 100644 index 0000000..166ade9 --- /dev/null +++ b/tests/unit/components/controller/api/test_service.py @@ -0,0 +1,103 @@ +# DEPENDENCIES +## Built-In +import os +import json +## Third-Party +import pytest +## Local +from app.components.controller.api.schemas import SettingsSchema +from app.components.controller.api.service import ( + _get_settings, + edit_settings_data +) +from app.constants import Files, DictKeys, ModelProviders + + +# CONSTANTS +class ExistingSettings: + ACE_NAME: str = "existing_name" + MODEL_PROVIDER: str = "existing_provider" + TEMPERATURE: float = 0.5 + + +# HELPERS +def _empty_settings_file(): + """Sets the settings file to an empty dictionary""" + with open(Files.CONTROLLER_SETTINGS, 'w') as f: + json.dump({}, f) + +def _default_settings_file(): + """Sets the settings file to an default values""" + _empty_settings_file() + _get_settings() + +def _existing_settings_file(): + """Sets the settings file to an existing dictionary""" + with open(Files.CONTROLLER_SETTINGS, 'w') as f: + json.dump( + SettingsSchema( + ace_name=ExistingSettings.ACE_NAME, + model_provider=ExistingSettings.MODEL_PROVIDER, + temperature=ExistingSettings.TEMPERATURE + ).model_dump(), + f + ) + +def _assert_settings_populated(): + assert os.path.isfile(Files.CONTROLLER_SETTINGS), "Settings file should exist" + with open(Files.CONTROLLER_SETTINGS, 'r') as f: + settings = json.load(f) + assert DictKeys.ACE_NAME in settings, f"Settings file should contain {DictKeys.ACE_NAME}" + assert DictKeys.MODEL_PROVIDER in settings, f"Settings file should contain {DictKeys.MODEL_PROVIDER}" + assert DictKeys.TEMPERATURE in settings, f"Settings file should contain {DictKeys.TEMPERATURE}" + + +# TESTS +def test_get_settings_populates_empty_file(): + """Test that _get_settings populates the settings file""" + _empty_settings_file() + settings: dict = _get_settings() + assert isinstance(settings, dict), "Settings should be a dictionary" + assert DictKeys.ACE_NAME in settings, f"Settings should contain {DictKeys.ACE_NAME}" + assert DictKeys.MODEL_PROVIDER in settings, f"Settings should contain {DictKeys.MODEL_PROVIDER}" + assert DictKeys.TEMPERATURE in settings, f"Settings should contain {DictKeys.TEMPERATURE}" + _assert_settings_populated() + +def test_get_settings_does_not_overwrite_existing_values(): + """Test that _get_settings does not overwrite existing settings""" + _existing_settings_file() + _get_settings() + + with open(Files.CONTROLLER_SETTINGS, 'r') as f: + settings: dict = json.load(f) + assert settings[DictKeys.ACE_NAME] == ExistingSettings.ACE_NAME, f"{DictKeys.ACE_NAME} should not be overwritten" + assert settings[DictKeys.MODEL_PROVIDER] == ExistingSettings.MODEL_PROVIDER, f"{DictKeys.MODEL_PROVIDER} should not be overwritten" + assert settings[DictKeys.TEMPERATURE] == ExistingSettings.TEMPERATURE, f"{DictKeys.TEMPERATURE} should not be overwritten" + +def test_edit_settings_data(): + """Test that edit_settings_data updates the settings correctly.""" + class UpdatedSettings: + ACE_NAME: str = "new_ace" + MODEL_PROVIDER: str = ModelProviders.OPENAI + TEMPERATURE: float = 0.7 + updated_settings = { + DictKeys.ACE_NAME: UpdatedSettings.ACE_NAME, + DictKeys.MODEL_PROVIDER: UpdatedSettings.MODEL_PROVIDER, + DictKeys.TEMPERATURE: UpdatedSettings.TEMPERATURE + } + + _default_settings_file() + edit_settings_data(updated_settings) + + with open(Files.CONTROLLER_SETTINGS, 'r') as f: + new_settings: dict = json.load(f) + assert new_settings[DictKeys.ACE_NAME] == UpdatedSettings.ACE_NAME, f"{DictKeys.ACE_NAME} should be updated" + assert new_settings[DictKeys.MODEL_PROVIDER] == UpdatedSettings.MODEL_PROVIDER, f"{DictKeys.MODEL_PROVIDER} should be updated" + assert new_settings[DictKeys.TEMPERATURE] == UpdatedSettings.TEMPERATURE, f"{DictKeys.TEMPERATURE} should be updated" + +def test_edit_settings_invalid_model_provider(): + """Test that edit_settings_data raises an error for invalid model provider.""" + _default_settings_file() + updated_settings = {DictKeys.MODEL_PROVIDER: "invalid_provider"} + with pytest.raises(ValueError): + edit_settings_data(updated_settings) diff --git a/.original/app/components/controller/api/pages/__init__.py b/tests/unit/constants/__init__.py similarity index 100% rename from .original/app/components/controller/api/pages/__init__.py rename to tests/unit/constants/__init__.py diff --git a/tests/unit/constants/test_base_enum.py b/tests/unit/constants/test_base_enum.py new file mode 100644 index 0000000..3e85242 --- /dev/null +++ b/tests/unit/constants/test_base_enum.py @@ -0,0 +1,52 @@ +# DEPENDENCIES +## Third-Party +import pytest +## Local +from app.constants.base_enum import BaseEnum + +def test_type_checking(): + """Test that invalid types raise a TypeError.""" + class ValidEnum(BaseEnum): + VALID_ATTR = "value" # String + VALID_ATTR2 = 123 # Integer + VALID_ATTR3 = 0.2 # Float + + # Test non type hinted attributes + with pytest.raises(TypeError): + class InvalidEnum(BaseEnum): + INVALID_ATTR = {"key": "value"} + INVALID_ATTR2 = True + + # Test type hinted attributes + with pytest.raises(TypeError): + class InvalidEnum(BaseEnum): + INVALID_ATTR: dict = {"key": "value"} + INVALID_ATTR2: bool = True + +def test_get_dict(): + """Test that get_dict() returns the correct dictionary.""" + class TestEnum(BaseEnum): + ATTR1 = "value1" + ATTR2 = "value2" + _PRIVATE_ATTR = "private" + + expected_dict: dict[str, str] = {"ATTR1": "value1", "ATTR2": "value2"} + assert TestEnum.get_dict() == expected_dict, "get_dict() should return the correct dictionary" + +def test_get_tuple(): + """Test that get_tuple() returns the correct tuple.""" + class TestEnum(BaseEnum): + ATTR1 = "value1" + ATTR2 = "value2" + + expected_tuple: tuple[str, str] = ("value1", "value2") + assert TestEnum.get_tuple() == expected_tuple, "get_tuple() should return the correct tuple" + +def test_get_frozenset(): + """Test that get_frozenset() returns the correct frozenset.""" + class TestEnum(BaseEnum): + ATTR1 = "value1" + ATTR2 = "value2" + + expected_set: frozenset[str] = frozenset(["value1", "value2"]) + assert TestEnum.get_frozenset() == expected_set, "get_frozenset() should return the correct frozenset" diff --git a/tests/unit/constants/test_files.py b/tests/unit/constants/test_files.py new file mode 100644 index 0000000..304b01e --- /dev/null +++ b/tests/unit/constants/test_files.py @@ -0,0 +1,56 @@ +# DEPENDENCIES +## Built-In +import os +import importlib +import json +## Third-Party +import pytest +import yaml +## Local +from app.constants.files import _ENSURE_JSON_FILES, Files, setup_user_deployment_file + + +# HELPERS +@pytest.fixture +def _ensure_app_directory(): + os.chdir("./app") + yield + os.chdir("..") + +def _cleanup(): + for file in _ENSURE_JSON_FILES: + if os.path.isfile(file): + os.remove(file) + + +# TESTS +def test_setup_user_deployment_file_creates_valid_yaml_file(_ensure_app_directory): + """Test that setup_user_deployment_file creates a valid deployment.yaml file without placeholders""" + def check_deployment_file(dev: bool): + env_keyword: str = "dev" if dev else "prod" + setup_user_deployment_file(dev=dev) + if not os.path.isfile(Files.USER_DEPLOYMENT_FILE): + raise Exception("user_deployment.yaml file not created") + with open(Files.USER_DEPLOYMENT_FILE, "r") as f: + content: str = f.read() + assert "{{" not in content and "}}" not in content, "All placeholders should be replaced" + assert env_keyword in content, f"Environment should be set to {env_keyword} when creating deployment file with dev={dev}" + yaml.safe_load(content) + check_deployment_file(dev=True) + check_deployment_file(dev=False) + +def test_ensure_json_files_creates_valid_json_files(): + """Test that _ensure_json_files creates the necessary JSON files""" + _cleanup() + + from app import constants + importlib.reload(constants) # Ensure folders & files are auto created on module import + from app.constants import files + importlib.reload(files) # Ensure files are auto created on module import + + for file in _ENSURE_JSON_FILES: + with open(file, "r") as f: + content: str = f.read() + json_content: dict = json.loads(content) + assert content.strip() == "{}", "JSON file should be an empty dictionary" + assert json_content == {}, "JSON file should be an empty dictionary" diff --git a/tests/unit/constants/test_folder.py b/tests/unit/constants/test_folder.py new file mode 100644 index 0000000..367700a --- /dev/null +++ b/tests/unit/constants/test_folder.py @@ -0,0 +1,27 @@ +# DEPENDENCIES +## Built-In +import os +import importlib +import shutil +## Local +from app.constants.folders import _ENSURED_FOLDERS + + +# HELPERS +def _cleanup(): + for folder in _ENSURED_FOLDERS: + if os.path.isdir(folder): + shutil.rmtree(folder) + + +# TESTS +def test_ensure_folders_creates_folders(): + """Test that _ensure_json_files creates the necessary JSON files""" + _cleanup() + + from app.constants import folders + importlib.reload(folders) # Ensure folders are auto created on module import + + for folder in _ENSURED_FOLDERS: + if not os.path.isdir(folder): + raise Exception(f"Folder {folder} does not exist!") diff --git a/tests/unit/logger/__init__.py b/tests/unit/logger/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/tests/unit/test_component.py b/tests/unit/test_component.py new file mode 100644 index 0000000..c38d0ff --- /dev/null +++ b/tests/unit/test_component.py @@ -0,0 +1,59 @@ +# DEPENDENCIES +## Built-In +from unittest.mock import patch, MagicMock +## Third-Party +import pytest +## Local +from app.component import run_component +from app.constants import Components + + +def test_run_component_no_environment_specified(monkeypatch): + """Test run_component with no environment specified""" + with patch("app.component.logger.critical") as mock_critical: + with pytest.raises(SystemExit): + run_component() + mock_critical.assert_called_once_with("You must select a environment, either --dev or --prod!") + +def test_run_component_multiple_components(monkeypatch): + """Test run_component with multiple components specified""" + with patch("argparse.ArgumentParser.parse_args") as mock_parse_args: + mock_args = MagicMock() + mock_args.dev = True + mock_args[Components.CONTROLLER] = True + mock_args[Components.UI] = True + mock_parse_args.return_value = mock_args + + # Mock logger.critical and exit + with patch("app.component.logger.critical") as mock_critical: + with pytest.raises(SystemExit): + run_component() + mock_critical.assert_called_once_with("You can only start one component at a time!") + +def test_run_component_invalid_component(monkeypatch): + """Test run_component with an invalid component specified.""" + invalid_component: str = "invalid_component" + with patch("argparse.ArgumentParser.parse_args") as mock_parse_args: + mock_args = MagicMock() + mock_args.dev = True + mock_args[invalid_component] = True + mock_parse_args.return_value = mock_args + + # Mock logger.critical and exit + with patch("app.component.logger.critical") as mock_critical: + with pytest.raises(SystemExit): + run_component() + mock_critical.assert_called_once_with(f"{invalid_component} is not a valid component!") + +def test_run_component_no_component_selected(monkeypatch): + """Test run_component with no component selected.""" + with patch("argparse.ArgumentParser.parse_args") as mock_parse_args: + mock_args = MagicMock() + mock_args.dev = True + # No component flags set + mock_parse_args.return_value = mock_args + + with patch("app.component.logger.critical") as mock_critical: + with pytest.raises(SystemExit): + run_component() + mock_critical.assert_called_once_with("You must select a component to start!") diff --git a/tests/unit/logger/test_logger.py b/tests/unit/test_logger.py similarity index 98% rename from tests/unit/logger/test_logger.py rename to tests/unit/test_logger.py index b0766c4..776641e 100644 --- a/tests/unit/logger/test_logger.py +++ b/tests/unit/test_logger.py @@ -10,7 +10,7 @@ import pytest ## Local from app.constants import DictKeys, Defaults, EnvironmentVariables, Folders -from tests.unit.constants import TestingConfigs, Modules +from tests.unit.testing_constants import TestingConfigs, Modules # HELPERS diff --git a/tests/unit/test_shell.py b/tests/unit/test_shell.py new file mode 100644 index 0000000..766c092 --- /dev/null +++ b/tests/unit/test_shell.py @@ -0,0 +1,38 @@ +# DEPENDENCIES +## Third-Party +import pytest +## Local +from app.shell import execute_shell, exec_check_exists + + +@pytest.mark.parametrize("command,expected_output", [ + ("echo 'test'", "test\n"), + ("ls -a", ".\n..\n") +]) +def test_execute_shell(command, expected_output, caplog): + """Test the execute_shell function with various commands.""" + output: str = execute_shell(command) + assert expected_output in output, f"Expected '{expected_output}' in output, but got '{output}'" + with pytest.raises(SystemExit): + execute_shell("invalid_command", ignore_error=False, _testing=True) + error_output: str = execute_shell("invalid_command", ignore_error=True) + assert any(msg in error_output for msg in [ + "invalid_command: command not found", + "/bin/sh: 1: invalid_command: not found" + ]), "Error message should be printed" + +def test_exec_check_exists(): + """Test the exec_check_exists function.""" + result = exec_check_exists("ls -a", ".") + assert result is True, "Expected True, but got False" + result = exec_check_exists("ls -a", "non_existent_file") + assert result is False, "Expected False, but got True" + +def test_should_print_result(capsys): + """Test the should_print_result parameter.""" + execute_shell("echo 'test'", should_print_result=True) + captured = capsys.readouterr() + assert "test\n" in captured.out, "Output should be printed" + execute_shell("echo 'test'", should_print_result=False) + captured = capsys.readouterr() + assert "test\n" not in captured.out, "Output should not be printed" diff --git a/tests/unit/constants.py b/tests/unit/testing_constants.py similarity index 81% rename from tests/unit/constants.py rename to tests/unit/testing_constants.py index c13996f..92f2741 100644 --- a/tests/unit/constants.py +++ b/tests/unit/testing_constants.py @@ -1,4 +1,5 @@ class Modules: + CONSTANTS: str = "app.constants" LOGGER: str = "app.logger" class TestingConfigs: