diff --git a/.github/hooks/pre-commit b/.github/hooks/pre-commit
new file mode 100644
index 0000000..9af2a43
--- /dev/null
+++ b/.github/hooks/pre-commit
@@ -0,0 +1,13 @@
+#!/bin/bash
+
+VERSION_FILE="app/version"
+
+CURRENT_DATE=$(date +"%Y-%m-%d %H:%M:%S")
+
+if jq --arg date "$CURRENT_DATE" '.last_update = $date' "$VERSION_FILE" > temp.json; then
+ mv temp.json "$VERSION_FILE"
+ git add "$VERSION_FILE"
+else
+ echo "Error: Failed to update version file"
+ exit 1
+fi
diff --git a/README.md b/README.md
index 689038d..d467078 100644
--- a/README.md
+++ b/README.md
@@ -6,6 +6,8 @@
# Dependencies
- Before running install
+ - [Git](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git)
+ - [JQ](https://jqlang.org/download/)
- [Podman](https://podman.io/docs/installation)
- [Ollama](https://ollama.com/download)
diff --git a/ace b/ace
index 166c10d..d7896cf 100755
--- a/ace
+++ b/ace
@@ -4,6 +4,14 @@
ACE_LOGGER_VERBOSE_ENV="."
+# SETUP
+setup() {
+ echo "Setting up environment..."
+ git_hooks_folder=".github/hooks"
+ mv -r "$git_hooks_folder" ".git/hooks"
+}
+
+
# STARTUP
run_tests() {
echo "Installing/Updating test dependencies..."
@@ -30,6 +38,8 @@ run_ace() {
main() {
# Store original arguments
original_args=("$@")
+
+ setup
# Initialize variables
run_tests=false
diff --git a/app/components/controller/api/__init__.py b/app/components/controller/api/__init__.py
index 1c363db..0a4c11a 100644
--- a/app/components/controller/api/__init__.py
+++ b/app/components/controller/api/__init__.py
@@ -1 +1,21 @@
-from .routes import controller_api
\ No newline at end of file
+# DEPENDENCIES
+## Third-Party
+from fastapi import FastAPI
+from fastapi.middleware.cors import CORSMiddleware
+## Local
+from .routes import root, model_provider
+
+
+controller_api = FastAPI()
+
+controller_api.include_router(root)
+controller_api.include_router(model_provider)
+
+controller_api.add_middleware(
+ CORSMiddleware,
+ allow_origins=["http://localhost:4200", "http://127.0.0.1:4200"],
+ allow_credentials=True,
+ allow_methods=["*"],
+ allow_headers=["*"],
+ expose_headers=["*"]
+)
diff --git a/app/components/controller/api/routes/__init__.py b/app/components/controller/api/routes/__init__.py
new file mode 100644
index 0000000..930bd09
--- /dev/null
+++ b/app/components/controller/api/routes/__init__.py
@@ -0,0 +1,2 @@
+from .model_provider import model_provider
+from .root import root
diff --git a/app/components/controller/api/routes/model_provider.py b/app/components/controller/api/routes/model_provider.py
new file mode 100644
index 0000000..9d89b08
--- /dev/null
+++ b/app/components/controller/api/routes/model_provider.py
@@ -0,0 +1,43 @@
+# DEPENDENCIES
+## Third-Party
+from fastapi import APIRouter, HTTPException
+from http import HTTPStatus
+from pydantic import ValidationError
+## Local
+from constants import APIRoutes, Defaults, Names
+from logger import logger
+from models.api_schemas.controller import GetLLMModelsResponse
+from ..services import model_provider_service
+
+
+model_provider = APIRouter()
+
+@model_provider.get(
+ f"{APIRoutes.MODEL_PROVIDER}llm/model-types",
+ response_model=tuple[str, ...],
+ description=f"Get the {Names.ACE} available LLM model types"
+)
+async def get_llm_model_types_route() -> tuple[str, ...]:
+ try:
+ return model_provider_service.get_llm_model_types()
+ except ValidationError as error:
+ logger.error(error)
+ raise HTTPException(status_code=HTTPStatus.INTERNAL_SERVER_ERROR, detail="LLM model types data error!")
+ except Exception as error:
+ logger.error(error)
+ raise HTTPException(status_code=HTTPStatus.INTERNAL_SERVER_ERROR, detail=Defaults.INTERNAL_SERVER_ERROR_MESSAGE)
+
+@model_provider.get(
+ f"{APIRoutes.MODEL_PROVIDER}llm/models",
+ response_model=list[GetLLMModelsResponse],
+ description=f"Get the {Names.ACE} available LLM models"
+)
+async def get_llm_models_route() -> list[GetLLMModelsResponse]:
+ try:
+ return model_provider_service.get_llm_models()
+ except ValidationError as error:
+ logger.error(error)
+ raise HTTPException(status_code=HTTPStatus.INTERNAL_SERVER_ERROR, detail="LLM model types data error!")
+ except Exception as error:
+ logger.error(error)
+ raise HTTPException(status_code=HTTPStatus.INTERNAL_SERVER_ERROR, detail=Defaults.INTERNAL_SERVER_ERROR_MESSAGE)
diff --git a/app/components/controller/api/routes.py b/app/components/controller/api/routes/root.py
similarity index 54%
rename from app/components/controller/api/routes.py
rename to app/components/controller/api/routes/root.py
index 04e7610..c1e3e33 100644
--- a/app/components/controller/api/routes.py
+++ b/app/components/controller/api/routes/root.py
@@ -1,40 +1,29 @@
# DEPENDENCIES
## Third-Party
-from fastapi import FastAPI, HTTPException
-from fastapi.middleware.cors import CORSMiddleware
+from fastapi import APIRouter, HTTPException
from http import HTTPStatus
from pydantic import ValidationError
## Local
-from constants import Defaults, Names
+from constants import APIRoutes, Defaults, Names
from logger import logger
from models.api_schemas.controller import (
GetVersionDetailsResponse,
- GetSettingsResponse, EditSettingsRequest,
- GetLLMModelsResponse
+ GetSettingsResponse, EditSettingsRequest
)
from models.api_schemas.defaults import DefaultAPIResponse
-from . import service
+from ..services import root_service
-controller_api = FastAPI()
-controller_api.add_middleware(
- CORSMiddleware,
- allow_origins=["http://localhost:4200"], # Allow requests from your Angular app
- allow_credentials=True,
- allow_methods=["*"], # Allow all HTTP methods
- allow_headers=["*"], # Allow all headers
-)
-
+root = APIRouter()
-# ROUTES
-@controller_api.get(
- "/version",
+@root.get(
+ f"{APIRoutes.ROOT}version",
response_model=GetVersionDetailsResponse,
description=f"Get the {Names.ACE}'s version data"
)
async def get_version_route() -> dict:
try:
- return service.get_version_data()
+ return root_service.get_version()
except ValidationError as error:
logger.error(error)
raise HTTPException(status_code=HTTPStatus.INTERNAL_SERVER_ERROR, detail="Version data error!")
@@ -42,14 +31,14 @@ async def get_version_route() -> dict:
logger.error(error)
raise HTTPException(status_code=HTTPStatus.INTERNAL_SERVER_ERROR, detail=Defaults.INTERNAL_SERVER_ERROR_MESSAGE)
-@controller_api.get(
- "/settings",
+@root.get(
+ f"{APIRoutes.ROOT}settings",
response_model=GetSettingsResponse,
description=f"Get the {Names.ACE} controller settings data"
)
async def get_settings_route() -> dict:
try:
- return service.get_settings_data()
+ return root_service.get_settings_data()
except ValidationError as error:
logger.error(error)
raise HTTPException(status_code=HTTPStatus.INTERNAL_SERVER_ERROR, detail="Settings data error!")
@@ -57,14 +46,14 @@ async def get_settings_route() -> dict:
logger.error(error)
raise HTTPException(status_code=HTTPStatus.INTERNAL_SERVER_ERROR, detail=Defaults.INTERNAL_SERVER_ERROR_MESSAGE)
-@controller_api.post(
- "/settings",
+@root.post(
+ f"{APIRoutes.ROOT}settings",
response_model=DefaultAPIResponse,
description=f"Edit the {Names.ACE} controller settings data"
)
async def set_settings_route(updated_settings: EditSettingsRequest) -> dict:
try:
- service.edit_settings_data(updated_settings=updated_settings.model_dump())
+ root_service.edit_settings_data(updated_settings=updated_settings.model_dump())
return DefaultAPIResponse(message="Settings data updated successfully!")
except ValidationError as error:
logger.error(error)
@@ -73,32 +62,18 @@ async def set_settings_route(updated_settings: EditSettingsRequest) -> dict:
logger.error(error)
raise HTTPException(status_code=HTTPStatus.INTERNAL_SERVER_ERROR, detail=Defaults.INTERNAL_SERVER_ERROR_MESSAGE)
-@controller_api.get(
- "/model-provider/model-types",
- response_model=dict[str, tuple[str, ...]],
- description=f"Get the {Names.ACE} available LLM model types"
-)
-async def get_model_types_route() -> dict[str, tuple[str, ...]]:
- try:
- return service.get_model_types()
- except ValidationError as error:
- logger.error(error)
- raise HTTPException(status_code=HTTPStatus.INTERNAL_SERVER_ERROR, detail="LLM model types data error!")
- except Exception as error:
- logger.error(error)
- raise HTTPException(status_code=HTTPStatus.INTERNAL_SERVER_ERROR, detail=Defaults.INTERNAL_SERVER_ERROR_MESSAGE)
-
-@controller_api.get(
- "/model-provider/model-type/llm",
- response_model=list[GetLLMModelsResponse],
- description=f"Get the {Names.ACE} available LLM models"
+@root.delete(
+ f"{APIRoutes.ROOT}settings",
+ response_model=DefaultAPIResponse,
+ description=f"Delete the {Names.ACE} controller settings data"
)
-async def get_llm_models_route() -> list[GetLLMModelsResponse]:
+async def delete_settings_route() -> dict:
try:
- return service.get_llm_models()
+ root_service.delete_settings_data()
+ return DefaultAPIResponse(message="Settings data deleted successfully!")
except ValidationError as error:
logger.error(error)
- raise HTTPException(status_code=HTTPStatus.INTERNAL_SERVER_ERROR, detail="LLM model types data error!")
+ raise HTTPException(status_code=HTTPStatus.INTERNAL_SERVER_ERROR, detail="Settings data error!")
except Exception as error:
logger.error(error)
- raise HTTPException(status_code=HTTPStatus.INTERNAL_SERVER_ERROR, detail=Defaults.INTERNAL_SERVER_ERROR_MESSAGE)
+ raise HTTPException(status_code=HTTPStatus.INTERNAL_SERVER_ERROR, detail=Defaults.INTERNAL_SERVER_ERROR_MESSAGE)
\ No newline at end of file
diff --git a/app/components/controller/api/service.py b/app/components/controller/api/service.py
deleted file mode 100644
index ee242ba..0000000
--- a/app/components/controller/api/service.py
+++ /dev/null
@@ -1,59 +0,0 @@
-# DEPENDENCIES
-## Built-In
-import json
-## Local
-from constants import (
- DictKeys,
- Files,
- ModelProviders,
- ModelTypes, ThreeDModelTypes, AudioModelTypes, ImageModelTypes, LLMModelTypes, MultiModalModelTypes, RAGModelTypes, RoboticsModelTypes, VideoModelTypes
-)
-from models.config.controller import ControllerSettingsSchema
-from models.data.initial import INTITAL_LLM_MODEL_PROVIDERS
-
-
-# HELPERS
-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 = ControllerSettingsSchema(**settings).model_dump()
- with open(Files.CONTROLLER_SETTINGS, "w", encoding="utf-8") as settings_file:
- settings_file.write(json.dumps(settings))
- return settings
-
-
-# GENERAL
-def get_version_data() -> dict:
- with open(Files.VERSION, "r", encoding="utf-8") as settings_file:
- return json.loads(settings_file.read())
-
-def get_settings_data() -> dict:
- return _get_settings()
-
-def edit_settings_data(updated_settings: dict):
- settings: dict = _get_settings()
- settings.update(updated_settings)
- with open(Files.CONTROLLER_SETTINGS, "w", encoding="utf-8") as settings_file:
- settings_file.write(json.dumps(settings))
-
-# MODEL PROVIDERS
-## Model Types
-def get_model_types() -> dict[str, tuple[str, ...]]:
- return {
- ModelTypes.THREE_D: ThreeDModelTypes.get_tuple(),
- ModelTypes.AUDIO: AudioModelTypes.get_tuple(),
- ModelTypes.IMAGE: ImageModelTypes.get_tuple(),
- ModelTypes.LLM: LLMModelTypes.get_tuple(),
- ModelTypes.MULTIMODAL: MultiModalModelTypes.get_tuple(),
- ModelTypes.RAG: RAGModelTypes.get_tuple(),
- ModelTypes.ROBOTICS: RoboticsModelTypes.get_tuple(),
- ModelTypes.VIDEO: VideoModelTypes.get_tuple()
- }
-
-def get_llm_models() -> list[dict]:
- llm_model_providers: list[dict] = [initial_llm_model_provider.model_dump() for initial_llm_model_provider in INTITAL_LLM_MODEL_PROVIDERS]
- with open(Files.CONTROLLER_LLM_MODELS, "r", encoding="utf-8") as llm_models_file:
- llm_models: dict = json.loads(llm_models_file.read())
- llm_model_providers.extend(llm_models)
- return llm_model_providers
diff --git a/app/components/controller/api/services/__init__.py b/app/components/controller/api/services/__init__.py
new file mode 100644
index 0000000..5f4be59
--- /dev/null
+++ b/app/components/controller/api/services/__init__.py
@@ -0,0 +1,4 @@
+from . import (
+ model_provider as model_provider_service,
+ root as root_service
+)
\ No newline at end of file
diff --git a/app/components/controller/api/services/model_provider.py b/app/components/controller/api/services/model_provider.py
new file mode 100644
index 0000000..124a039
--- /dev/null
+++ b/app/components/controller/api/services/model_provider.py
@@ -0,0 +1,20 @@
+# DEPENDENCIES
+## Built-In
+import json
+## Local
+from constants import (
+ Files,
+ ModelTypes, ThreeDModelTypes, AudioModelTypes, ImageModelTypes, LLMModelTypes, MultiModalModelTypes, RAGModelTypes, RoboticsModelTypes, VideoModelTypes
+)
+from models.data.initial import INTITAL_LLM_MODEL_PROVIDERS
+
+
+def get_llm_model_types() -> tuple[str, ...]:
+ return LLMModelTypes.get_tuple()
+
+def get_llm_models() -> list[dict]:
+ llm_model_providers: list[dict] = [initial_llm_model_provider.model_dump() for initial_llm_model_provider in INTITAL_LLM_MODEL_PROVIDERS]
+ with open(Files.CONTROLLER_LLM_MODELS, "r", encoding="utf-8") as llm_models_file:
+ llm_models: dict = json.loads(llm_models_file.read())
+ llm_model_providers.extend(llm_models)
+ return llm_model_providers
diff --git a/app/components/controller/api/services/root.py b/app/components/controller/api/services/root.py
new file mode 100644
index 0000000..079be34
--- /dev/null
+++ b/app/components/controller/api/services/root.py
@@ -0,0 +1,36 @@
+# DEPENDENCIES
+## Built-In
+import json
+## Local
+from constants import Files
+from models.config.controller import ControllerSettingsSchema
+
+
+# HELPERS
+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 = ControllerSettingsSchema(**settings).model_dump()
+ with open(Files.CONTROLLER_SETTINGS, "w", encoding="utf-8") as settings_file:
+ settings_file.write(json.dumps(settings))
+ return settings
+
+
+# ROOT
+def get_version() -> dict:
+ with open(Files.VERSION, "r", encoding="utf-8") as settings_file:
+ return json.loads(settings_file.read())
+
+def get_settings_data() -> dict:
+ return _get_settings()
+
+def edit_settings_data(updated_settings: dict):
+ settings: dict = _get_settings()
+ settings.update(updated_settings)
+ with open(Files.CONTROLLER_SETTINGS, "w", encoding="utf-8") as settings_file:
+ settings_file.write(json.dumps(settings))
+
+def delete_settings_data():
+ with open(Files.CONTROLLER_SETTINGS, "w", encoding="utf-8") as settings_file:
+ settings_file.write(json.dumps({}))
diff --git a/app/components/ui/angular.json b/app/components/ui/angular.json
index 54fd858..3a6fb06 100644
--- a/app/components/ui/angular.json
+++ b/app/components/ui/angular.json
@@ -33,6 +33,7 @@
],
"styles": [
"@angular/material/prebuilt-themes/azure-blue.css",
+ "src/theme.scss",
"src/styles.scss"
],
"scripts": []
diff --git a/app/components/ui/src/app/app.component.html b/app/components/ui/src/app/app.component.html
index c42a7a3..5089ac9 100644
--- a/app/components/ui/src/app/app.component.html
+++ b/app/components/ui/src/app/app.component.html
@@ -21,8 +21,14 @@
-
+ @if ( uiSettings?.show_footer ) {
+
+ }
diff --git a/app/components/ui/src/app/app.component.scss b/app/components/ui/src/app/app.component.scss
index 6a277db..4369619 100644
--- a/app/components/ui/src/app/app.component.scss
+++ b/app/components/ui/src/app/app.component.scss
@@ -31,4 +31,5 @@
height: 2rem;
gap: 2rem;
outline: auto;
+ background-color: gray;
}
diff --git a/app/components/ui/src/app/app.component.ts b/app/components/ui/src/app/app.component.ts
index c2dbbeb..ba52487 100644
--- a/app/components/ui/src/app/app.component.ts
+++ b/app/components/ui/src/app/app.component.ts
@@ -1,6 +1,6 @@
// DEPENDENCIES
//// Angular
-import { Component, OnInit, signal } from "@angular/core";
+import { Component, inject, OnInit, signal } from "@angular/core";
import { MatButtonModule } from "@angular/material/button";
import { MatIconModule } from "@angular/material/icon";
import { MatListModule } from "@angular/material/list";
@@ -8,11 +8,14 @@ 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 { selectAppState } from './store/selectors/app.selectors';
-import { AppState } from './store/state/app.state';
+import { ThemeService } from "./theme";
+import { IAppVersionData } from "./models/app.models";
+import { ISettings, IUISettings } from "./models/settings.models";
+import { appActions } from "./store/app/app.actions";
+import { settingsActions } from "./store/settings/settings.actions";
+import { selectAppVersionDataState } from './store/app/app.selectors';
+import { selectSettingsState } from "./store/settings/settings.selectors";
// TYPES
@@ -30,8 +33,8 @@ export type SidebarItem = {
MatButtonModule,
MatIconModule,
MatListModule,
- MatTooltipModule,
MatSidenavModule,
+ MatTooltipModule,
RouterModule,
RouterOutlet
],
@@ -41,6 +44,7 @@ export type SidebarItem = {
export class AppComponent implements OnInit {
// Variables
title = "ACE";
+ themeService = inject(ThemeService);
sidebarItems = signal([
{
name: "Home",
@@ -68,16 +72,37 @@ export class AppComponent implements OnInit {
route: "settings"
}
])
- versionData$: Observable;
- version: string = "0";
+
+ settings?: ISettings;
+ uiSettings?: IUISettings;
+ appVersionData?: IAppVersionData;
// Initialisation
- constructor(private store: Store) {
- this.versionData$ = this.store.select(selectAppState);
- }
+ constructor(private store: Store) {}
ngOnInit(): void {
- this.store.dispatch(appActions.getACEVersionData());
- this.versionData$.subscribe( versionData => this.version = versionData.versionData.version);
+ this.store.dispatch(appActions.getAppVersionData());
+ this.store.select(selectAppVersionDataState).subscribe( version_data => this.appVersionData = version_data );
+ this.store.dispatch(settingsActions.getSettings());
+ this.store.select(selectSettingsState).subscribe( settings => {
+ this.settings = settings.settings;
+ this.uiSettings = settings.settings.ui_settings;
+ this.themeService.setDarkMode(this.uiSettings.dark_mode);
+ })
+ }
+
+ toggleDarkMode() {
+ if (!this.settings) {
+ return;
+ }
+ this.store.dispatch(settingsActions.editSettings({
+ settings: {
+ ...this.settings,
+ ui_settings: {
+ ...this.settings.ui_settings,
+ dark_mode: !this.settings.ui_settings.dark_mode
+ }
+ }
+ }))
}
}
diff --git a/app/components/ui/src/app/app.config.ts b/app/components/ui/src/app/app.config.ts
index b918be1..5d78379 100644
--- a/app/components/ui/src/app/app.config.ts
+++ b/app/components/ui/src/app/app.config.ts
@@ -9,17 +9,21 @@ 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';
+import { AppEffects } from './store/app/app.effects';
+import { modelProviderEffects } from './store/model-provider/model-provider.effects';
+import { SettingsEffects } from './store/settings/settings.effects';
+import { appReducer } from './store/app/app.reducers';
+import { modelProviderReducer } from './store/model-provider/model-provider.reducers';
+import { settingsReducer } from './store/settings/settings.reducers';
// CONFIG
export const appConfig: ApplicationConfig = {
providers: [
provideAnimationsAsync(),
- provideEffects([AppEffects]),
+ provideEffects([AppEffects, modelProviderEffects, SettingsEffects]),
provideHttpClient(),
- provideStore({ app_data: appReducer }),
+ provideStore({ app_data: appReducer, model_provider: modelProviderReducer, settings: settingsReducer }),
provideStoreDevtools({
maxAge: 25,
logOnly: false
diff --git a/app/components/ui/src/app/constants.ts b/app/components/ui/src/app/constants.ts
new file mode 100644
index 0000000..513b485
--- /dev/null
+++ b/app/components/ui/src/app/constants.ts
@@ -0,0 +1,9 @@
+export const Values = {
+ NOT_LOADED: "not_loaded",
+}
+
+const ROOT = "/";
+export const APIRoutes = {
+ ROOT,
+ MODEL_PROVIDER: `${ROOT}model-provider/`,
+}
diff --git a/app/components/ui/src/app/api.urls.ts b/app/components/ui/src/app/environment.ts
similarity index 75%
rename from app/components/ui/src/app/api.urls.ts
rename to app/components/ui/src/app/environment.ts
index 38b1ad8..5aa9e8e 100644
--- a/app/components/ui/src/app/api.urls.ts
+++ b/app/components/ui/src/app/environment.ts
@@ -1,5 +1,5 @@
const defaultClusterURL: string = `http://127.0.0.1`;
-export const serviceURLs = {
+export const environmentURLs = {
controller: defaultClusterURL + ":2349"
};
diff --git a/app/components/ui/src/app/models/app.models.ts b/app/components/ui/src/app/models/app.models.ts
index ac57ffc..a5512ed 100644
--- a/app/components/ui/src/app/models/app.models.ts
+++ b/app/components/ui/src/app/models/app.models.ts
@@ -1,3 +1,7 @@
-export interface IACEVersionData {
+export interface IAppVersionData {
version: string
+ authors: string[]
+ license: string
+ last_update: string
+ rebuild_date: string
}
diff --git a/app/components/ui/src/app/models/model-provider.models.ts b/app/components/ui/src/app/models/model-provider.models.ts
new file mode 100644
index 0000000..c6ee086
--- /dev/null
+++ b/app/components/ui/src/app/models/model-provider.models.ts
@@ -0,0 +1,13 @@
+export interface ILLMModelProvider {
+ id: string;
+ model_provider: string;
+ name: string;
+ model_name: string;
+ default: boolean;
+ max_input_tokens: number;
+ max_output_tokens: number;
+ cost_per_million_input_tokens: number;
+ cost_per_million_output_tokens: number;
+ knowledge_cutoff: string;
+ rate_limits: string;
+}
diff --git a/app/components/ui/src/app/models/settings.models.ts b/app/components/ui/src/app/models/settings.models.ts
new file mode 100644
index 0000000..d967bc7
--- /dev/null
+++ b/app/components/ui/src/app/models/settings.models.ts
@@ -0,0 +1,52 @@
+// SECTIONS
+//// UI
+export interface IUISettings {
+ dark_mode: boolean;
+ show_footer: boolean;
+}
+
+//// Layers
+export interface ILayerSetting {
+ layer_name: string;
+ model_type: string;
+}
+
+//// Model Provider
+////// Unique Providers
+export interface IIndividualProviderSettings {
+ name: string;
+ enabled: boolean;
+ api_key: string;
+}
+
+////// Model Types
+export interface ILLMModelTypeSettings {
+ model_type: string;
+ model_id: string;
+ logical_temperature: number;
+ creative_temperature: number;
+ output_token_limit: number;
+}
+
+////// Full
+export interface IModelProviderSetting {
+ individual_provider_settings: IIndividualProviderSettings[];
+
+ three_d_model_type_settings: string[];
+ audio_model_type_settings: string[];
+ image_model_type_settings: string[];
+ llm_model_type_settings: ILLMModelTypeSettings[];
+ multimodal_model_type_settings: string[];
+ rag_model_type_settings: string[];
+ robotics_model_type_settings: string[];
+ video_model_type_settings: string[];
+}
+
+
+// FULL SETTINGS
+export interface ISettings {
+ ace_name: string;
+ ui_settings: IUISettings;
+ layer_settings: ILayerSetting[];
+ model_provider_settings: IModelProviderSetting;
+}
diff --git a/app/components/ui/src/app/pages/chat/chat.component.html b/app/components/ui/src/app/pages/chat/chat.component.html
index 368564b..a0cd3a8 100644
--- a/app/components/ui/src/app/pages/chat/chat.component.html
+++ b/app/components/ui/src/app/pages/chat/chat.component.html
@@ -1 +1,3 @@
-CHAT PAGE
+
+
Chat
+
diff --git a/app/components/ui/src/app/pages/dashboard/dashboard.component.html b/app/components/ui/src/app/pages/dashboard/dashboard.component.html
index b4d6702..045c851 100644
--- a/app/components/ui/src/app/pages/dashboard/dashboard.component.html
+++ b/app/components/ui/src/app/pages/dashboard/dashboard.component.html
@@ -1 +1,3 @@
-DASHBOARD PAGE
+
+
Dashboard
+
diff --git a/app/components/ui/src/app/pages/home/home.component.html b/app/components/ui/src/app/pages/home/home.component.html
index 3afe58b..6ada622 100644
--- a/app/components/ui/src/app/pages/home/home.component.html
+++ b/app/components/ui/src/app/pages/home/home.component.html
@@ -1 +1,3 @@
-HOME PAGE
+
+
Home
+
diff --git a/app/components/ui/src/app/pages/model_garden/model-garden.component.html b/app/components/ui/src/app/pages/model_garden/model-garden.component.html
index 16868f3..c5e8629 100644
--- a/app/components/ui/src/app/pages/model_garden/model-garden.component.html
+++ b/app/components/ui/src/app/pages/model_garden/model-garden.component.html
@@ -1 +1,3 @@
-MODEL GARDEN PAGE
+
+
Model Garden
+
diff --git a/app/components/ui/src/app/pages/settings/settings.component.html b/app/components/ui/src/app/pages/settings/settings.component.html
index d93694f..dd933d4 100644
--- a/app/components/ui/src/app/pages/settings/settings.component.html
+++ b/app/components/ui/src/app/pages/settings/settings.component.html
@@ -1 +1,90 @@
-SETTINGS PAGE
+
+
Settings
+
+ @if (settingsForm) {
+
+
+
+ }
+
diff --git a/app/components/ui/src/app/pages/settings/settings.component.scss b/app/components/ui/src/app/pages/settings/settings.component.scss
index e69de29..62facf6 100644
--- a/app/components/ui/src/app/pages/settings/settings.component.scss
+++ b/app/components/ui/src/app/pages/settings/settings.component.scss
@@ -0,0 +1,5 @@
+.save-button {
+ position: sticky;
+ float: right;
+ bottom: 1rem;
+}
diff --git a/app/components/ui/src/app/pages/settings/settings.component.ts b/app/components/ui/src/app/pages/settings/settings.component.ts
index 7b499b7..6a6a898 100644
--- a/app/components/ui/src/app/pages/settings/settings.component.ts
+++ b/app/components/ui/src/app/pages/settings/settings.component.ts
@@ -1,9 +1,191 @@
-import { Component } from "@angular/core";
+// DEPENDENCIES
+//// Angular
+import { Component, OnInit } from "@angular/core";
+import { Store } from "@ngrx/store";
+import { filter, take } from "rxjs";
+import { FormArray, FormBuilder, FormGroup, ReactiveFormsModule, Validators } from "@angular/forms";
+import { MatButtonModule } from "@angular/material/button";
+import { MatDividerModule } from "@angular/material/divider";
+import { MatIconModule } from "@angular/material/icon";
+import { MatInputModule } from "@angular/material/input";
+import { MatFormFieldModule } from "@angular/material/form-field";
+import { MatSelectModule } from "@angular/material/select";
+import { MatSlideToggleModule } from "@angular/material/slide-toggle";
+//// Local
+import { IAppVersionData } from "../../models/app.models";
+import { ILLMModelProvider } from "../../models/model-provider.models";
+import { IModelProviderSetting, ISettings } from "../../models/settings.models";
+import { modelProviderActions } from "../../store/model-provider/model-provider.actions";
+import { selectAppVersionDataState } from "../../store/app/app.selectors";
+import { selectLLMModelTypes } from "../../store/model-provider/model-provider.selectors";
+import { settingsActions } from "../../store/settings/settings.actions";
+import { selectSettingsState } from "../../store/settings/settings.selectors";
@Component({
selector: "page-settings",
- imports: [],
+ imports: [
+ ReactiveFormsModule,
+ MatButtonModule,
+ MatDividerModule,
+ MatIconModule,
+ MatInputModule,
+ MatFormFieldModule,
+ MatSelectModule,
+ MatSlideToggleModule
+ ],
templateUrl: "./settings.component.html",
styleUrl: "./settings.component.scss"
})
-export class SettingsComponent {}
+export class SettingsComponent implements OnInit {
+ appVersionData!: IAppVersionData;
+ llmModels: ILLMModelProvider[] = [];
+ llmModelTypes: string[] = [];
+ selectedLLMModelType: string = "";
+ settings!: ISettings;
+
+ settingsForm!: FormGroup;
+ generalForm!: FormGroup;
+ uiSettingsForm!: FormGroup;
+ modelProviderForm!: FormGroup;
+ layerSettingsForm!: FormArray;
+
+ changesDetected: boolean = false;
+
+ constructor(
+ private formBuilder: FormBuilder,
+ private store: Store
+ ) {
+ this.initialiseForm();
+ }
+
+ ngOnInit(): void {
+ this.store.select(selectAppVersionDataState).subscribe( version_data => this.appVersionData = version_data );
+ this.store.dispatch(modelProviderActions.getLLMModels());
+ this.store.dispatch(modelProviderActions.getLLMModelTypes());
+ this.store.select(selectLLMModelTypes).pipe(
+ filter((model_types: string[]): model_types is string[] => model_types.length > 0),
+ take(1)
+ ).subscribe(model_types => {
+ this.llmModelTypes = model_types;
+ this.initialiseForm();
+ });
+
+ this.store.dispatch(settingsActions.getSettings());
+ this.store.select(selectSettingsState).subscribe(settings => {
+ this.settings = settings.settings;
+ this.patchFormValues();
+ });
+ }
+
+ private initialiseForm(): void {
+ this.generalForm = this.formBuilder.group({
+ ace_name: ["", [Validators.required, Validators.maxLength(32)]]
+ });
+
+ this.uiSettingsForm = this.formBuilder.group({
+ dark_mode: [true],
+ show_footer: [true]
+ });
+
+ this.layerSettingsForm = this.formBuilder.array([]);
+
+ this.modelProviderForm = this.formBuilder.group({
+ individual_provider_settings: this.formBuilder.array([]),
+ three_d_model_type_settings: this.formBuilder.array([]),
+ audio_model_type_settings: this.formBuilder.array([]),
+ image_model_type_settings: this.formBuilder.array([]),
+ llm_model_type_settings: this.formBuilder.array([]),
+ rag_model_type_settings: this.formBuilder.array([])
+ });
+
+ this.settingsForm = this.formBuilder.group({
+ ...this.generalForm.controls,
+ ui_settings: this.uiSettingsForm,
+ layer_settings: this.layerSettingsForm,
+ model_provider_settings: this.modelProviderForm
+ });
+
+ this.settingsForm.valueChanges.subscribe(() => {
+ this.changesDetected = true;
+ });
+ }
+
+ private patchFormValues(): void {
+ if (!this.settings) return;
+
+ this.initialiseForm();
+
+ this.generalForm.patchValue({
+ ace_name: this.settings.ace_name,
+ });
+
+ this.uiSettingsForm.patchValue({
+ dark_mode: this.settings.ui_settings.dark_mode,
+ show_footer: this.settings.ui_settings.show_footer
+ });
+
+ const modelProviderSettings: IModelProviderSetting = this.settings.model_provider_settings;
+ this.individualModelProviderSettingsControl?.clear();
+
+ modelProviderSettings.individual_provider_settings.forEach(provider => {
+ this.individualModelProviderSettingsControl?.push(
+ this.formBuilder.group({
+ name: [provider.name],
+ enabled: [provider.enabled],
+ api_key: [provider.api_key]
+ })
+ );
+ });
+
+ this.layerSettingsForm.clear();
+ this.settings.layer_settings.forEach(layer => {
+ this.layerSettingsForm.push(
+ this.formBuilder.group({
+ layer_name: [layer?.layer_name || "", Validators.required],
+ model_type: [layer?.model_type || "", Validators.required]
+ })
+ );
+ });
+
+ this.settingsForm.markAsPristine();
+ this.changesDetected = false;
+ }
+
+ formatSnakeCase(layerName?: string): string {
+ return (layerName || "")
+ .split("_")
+ .map(word => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase())
+ .join(" ");
+ }
+
+ onReset() {
+ this.store.dispatch(settingsActions.resetSettings());
+ }
+
+ onSave() {
+ if (!this.settingsForm.valid) {
+ return;
+ }
+ const updatedSettings = {
+ ...this.settings,
+ ...this.settingsForm.value
+ };
+
+ this.changesDetected = false;
+ this.settingsForm.markAsPristine();
+
+ this.store.dispatch(settingsActions.editSettings({ settings: updatedSettings }));
+ }
+
+ get aceNameControl() {
+ return this.generalForm.get("ace_name");
+ }
+
+ get individualModelProviderSettingsControl(): FormArray {
+ return this.modelProviderForm.get("individual_provider_settings") as FormArray;
+ }
+
+ get layerSettingsControl(): FormArray {
+ return this.layerSettingsForm;
+ }
+}
diff --git a/app/components/ui/src/app/pages/settings/settings.service.ts b/app/components/ui/src/app/pages/settings/settings.service.ts
deleted file mode 100644
index e69de29..0000000
diff --git a/app/components/ui/src/app/pages/settings/store/settings.actions.ts b/app/components/ui/src/app/pages/settings/store/settings.actions.ts
deleted file mode 100644
index e69de29..0000000
diff --git a/app/components/ui/src/app/pages/settings/store/settings.effects.ts b/app/components/ui/src/app/pages/settings/store/settings.effects.ts
deleted file mode 100644
index e69de29..0000000
diff --git a/app/components/ui/src/app/pages/settings/store/settings.reducers.ts b/app/components/ui/src/app/pages/settings/store/settings.reducers.ts
deleted file mode 100644
index e69de29..0000000
diff --git a/app/components/ui/src/app/pages/settings/store/settings.selectors.ts b/app/components/ui/src/app/pages/settings/store/settings.selectors.ts
deleted file mode 100644
index e69de29..0000000
diff --git a/app/components/ui/src/app/pages/settings/store/settings.state.ts b/app/components/ui/src/app/pages/settings/store/settings.state.ts
deleted file mode 100644
index e69de29..0000000
diff --git a/app/components/ui/src/app/services/app.service.ts b/app/components/ui/src/app/services/app.service.ts
index da1a9b7..978589f 100644
--- a/app/components/ui/src/app/services/app.service.ts
+++ b/app/components/ui/src/app/services/app.service.ts
@@ -1,14 +1,15 @@
import { Injectable } from "@angular/core";
import { HttpClient } from "@angular/common/http";
import { Observable } from 'rxjs/internal/Observable';
-import { serviceURLs } from "../api.urls";
+import { APIRoutes } from "../constants";
+import { environmentURLs } from "../environment";
export type HttpListResponseFailure = { status: string, message: string };
const endpoints = {
- getACEVersionData: `${serviceURLs.controller}/version`,
+ getAppVersionData: `${environmentURLs.controller}${APIRoutes.ROOT}version`,
};
@@ -18,7 +19,7 @@ const endpoints = {
export class AppService {
constructor(private http: HttpClient) { }
- getACEVersionData(): Observable {
- return this.http.get(endpoints.getACEVersionData);
+ getAppVersionData(): Observable {
+ return this.http.get(endpoints.getAppVersionData);
}
}
diff --git a/app/components/ui/src/app/services/model-provider.service.ts b/app/components/ui/src/app/services/model-provider.service.ts
new file mode 100644
index 0000000..59316f7
--- /dev/null
+++ b/app/components/ui/src/app/services/model-provider.service.ts
@@ -0,0 +1,29 @@
+import { Injectable } from "@angular/core";
+import { HttpClient } from "@angular/common/http";
+import { Observable } from 'rxjs/internal/Observable';
+import { APIRoutes } from "../constants";
+import { environmentURLs } from "../environment";
+
+
+export type HttpListResponseFailure = { status: string, message: string };
+
+const endpoints = {
+ getLLMModels: `${environmentURLs.controller}${APIRoutes.MODEL_PROVIDER}llm/models`,
+ getLLMModelTypes: `${environmentURLs.controller}${APIRoutes.MODEL_PROVIDER}llm/model-types`
+};
+
+
+@Injectable({
+ providedIn: "root"
+})
+export class ModelProviderService {
+ constructor(private http: HttpClient) { }
+
+ getLLMModelTypes(): Observable {
+ return this.http.get(endpoints.getLLMModelTypes);
+ }
+
+ getLLMModels(): Observable {
+ return this.http.get(endpoints.getLLMModels);
+ }
+}
diff --git a/app/components/ui/src/app/services/settings.service.ts b/app/components/ui/src/app/services/settings.service.ts
new file mode 100644
index 0000000..9b5ef03
--- /dev/null
+++ b/app/components/ui/src/app/services/settings.service.ts
@@ -0,0 +1,39 @@
+// DEPENDENCIES
+//// Built-In
+import { Injectable } from "@angular/core";
+import { HttpClient } from "@angular/common/http";
+import { Observable } from 'rxjs/internal/Observable';
+//// Local
+import { APIRoutes } from "../constants";
+import { environmentURLs } from "../environment";
+import { ISettings } from "../models/settings.models";
+
+
+export type HttpListResponseFailure = { status: string, message: string };
+
+
+const endpoints = {
+ getSettings: `${environmentURLs.controller}${APIRoutes.ROOT}settings`,
+ editSettings: `${environmentURLs.controller}${APIRoutes.ROOT}settings`,
+ resetSettings: `${environmentURLs.controller}${APIRoutes.ROOT}settings`
+};
+
+
+@Injectable({
+ providedIn: "root"
+})
+export class SettingsService {
+ constructor(private http: HttpClient) { }
+
+ getSettings(): Observable {
+ return this.http.get(endpoints.getSettings);
+ }
+
+ editSettings(settings: ISettings): Observable {
+ return this.http.post(endpoints.editSettings, settings);
+ }
+
+ resetSettings(): Observable {
+ return this.http.delete(endpoints.resetSettings);
+ }
+}
diff --git a/app/components/ui/src/app/state/app.state.ts b/app/components/ui/src/app/state/app.state.ts
new file mode 100644
index 0000000..3c77631
--- /dev/null
+++ b/app/components/ui/src/app/state/app.state.ts
@@ -0,0 +1,21 @@
+import { createDefaultLoadable, Loadable } from "./loadable.state";
+import { IAppVersionData } from "../models/app.models";
+
+export interface AppState extends Loadable {
+ version_data: IAppVersionData;
+}
+
+export function createInitialAppState(): AppState {
+ return {
+ ...createDefaultLoadable(),
+ version_data: {
+ version: "0",
+ authors: [],
+ license: "",
+ last_update: "",
+ rebuild_date: ""
+ }
+ }
+}
+
+
diff --git a/app/components/ui/src/app/store/state/loadable.state.ts b/app/components/ui/src/app/state/loadable.state.ts
similarity index 100%
rename from app/components/ui/src/app/store/state/loadable.state.ts
rename to app/components/ui/src/app/state/loadable.state.ts
diff --git a/app/components/ui/src/app/state/model-provider.state.ts b/app/components/ui/src/app/state/model-provider.state.ts
new file mode 100644
index 0000000..8ec6083
--- /dev/null
+++ b/app/components/ui/src/app/state/model-provider.state.ts
@@ -0,0 +1,22 @@
+import { createDefaultLoadable, Loadable } from "./loadable.state";
+import { ILLMModelProvider } from "../models/model-provider.models";
+
+export interface ModelProviderLLMState extends Loadable {
+ models: ILLMModelProvider[],
+ model_types: string[]
+}
+
+export interface ModelProviderState extends Loadable {
+ llm: ModelProviderLLMState
+}
+
+export function createInitialModelProviderState(): ModelProviderState {
+ return {
+ ...createDefaultLoadable(),
+ llm: {
+ ...createDefaultLoadable(),
+ models: [],
+ model_types: []
+ }
+ }
+}
diff --git a/app/components/ui/src/app/state/settings.state.ts b/app/components/ui/src/app/state/settings.state.ts
new file mode 100644
index 0000000..74deea8
--- /dev/null
+++ b/app/components/ui/src/app/state/settings.state.ts
@@ -0,0 +1,42 @@
+import { createDefaultLoadable, Loadable } from "./loadable.state";
+import { Values } from '../constants'
+import { ISettings } from "../models/settings.models";
+
+export interface SettingsState extends Loadable {
+ settings: ISettings;
+}
+
+export function createInitialSettingsState(): SettingsState {
+ return {
+ ...createDefaultLoadable(),
+ settings: {
+ ace_name: Values.NOT_LOADED,
+ ui_settings: {
+ dark_mode: true,
+ show_footer: true
+ },
+ layer_settings: [],
+ model_provider_settings: {
+ individual_provider_settings: [],
+ three_d_model_type_settings: [],
+ audio_model_type_settings: [],
+ image_model_type_settings: [],
+ llm_model_type_settings: [
+ {
+ model_type: Values.NOT_LOADED,
+ model_id: Values.NOT_LOADED,
+ logical_temperature: 0,
+ creative_temperature: 0,
+ output_token_limit: 0
+ }
+ ],
+ multimodal_model_type_settings: [],
+ rag_model_type_settings: [],
+ robotics_model_type_settings: [],
+ video_model_type_settings: []
+ }
+ }
+ }
+}
+
+
diff --git a/app/components/ui/src/app/store/actions/app.actions.ts b/app/components/ui/src/app/store/actions/app.actions.ts
deleted file mode 100644
index 35b306d..0000000
--- a/app/components/ui/src/app/store/actions/app.actions.ts
+++ /dev/null
@@ -1,11 +0,0 @@
-import { createActionGroup, props, emptyProps } from "@ngrx/store";
-import { IACEVersionData } from "../../models/app.models";
-
-export const appActions = createActionGroup({
- source: "app",
- events: {
- getACEVersionData: emptyProps(),
- getACEVersionDataSuccess: props<{ versionData: IACEVersionData }>(),
- getACEVersionDataFailure: props<{ error: Error }>()
- },
-});
diff --git a/app/components/ui/src/app/store/app/app.actions.ts b/app/components/ui/src/app/store/app/app.actions.ts
new file mode 100644
index 0000000..a23f2b8
--- /dev/null
+++ b/app/components/ui/src/app/store/app/app.actions.ts
@@ -0,0 +1,11 @@
+import { createActionGroup, props, emptyProps } from "@ngrx/store";
+import { IAppVersionData } from "../../models/app.models";
+
+export const appActions = createActionGroup({
+ source: "app",
+ events: {
+ getAppVersionData: emptyProps(),
+ getAppVersionDataSuccess: props<{ version_data: IAppVersionData }>(),
+ getAppVersionDataFailure: props<{ error: Error }>()
+ },
+});
diff --git a/app/components/ui/src/app/store/effects/app.effects.ts b/app/components/ui/src/app/store/app/app.effects.ts
similarity index 51%
rename from app/components/ui/src/app/store/effects/app.effects.ts
rename to app/components/ui/src/app/store/app/app.effects.ts
index 8de462e..f00ed80 100644
--- a/app/components/ui/src/app/store/effects/app.effects.ts
+++ b/app/components/ui/src/app/store/app/app.effects.ts
@@ -1,22 +1,20 @@
import { inject, Injectable } from "@angular/core";
import { createEffect, ofType, Actions } from "@ngrx/effects";
-import { Store } from "@ngrx/store";
import { map, catchError, of, switchMap } from "rxjs";
-import { appActions } from "../actions/app.actions";
-import { IACEVersionData } from "../../models/app.models";
+import { appActions } from "./app.actions";
+import { IAppVersionData } from "../../models/app.models";
import { AppService } from "../../services/app.service";
@Injectable()
export class AppEffects {
private actions$ = inject(Actions);
- private store$ = inject(Store);
getACEVersionData$ = createEffect(() => this.actions$.pipe(
- ofType(appActions.getACEVersionData),
- switchMap(() => this.appService.getACEVersionData().pipe(
- map((versionData: IACEVersionData) => appActions.getACEVersionDataSuccess({ versionData })),
- catchError(error => of(appActions.getACEVersionDataFailure({ error })))
+ ofType(appActions.getAppVersionData),
+ switchMap(() => this.appService.getAppVersionData().pipe(
+ map((version_data: IAppVersionData) => appActions.getAppVersionDataSuccess({ version_data })),
+ catchError(error => of(appActions.getAppVersionDataFailure({ error })))
))
))
diff --git a/app/components/ui/src/app/store/app/app.reducers.ts b/app/components/ui/src/app/store/app/app.reducers.ts
new file mode 100644
index 0000000..9fba492
--- /dev/null
+++ b/app/components/ui/src/app/store/app/app.reducers.ts
@@ -0,0 +1,11 @@
+import { createReducer, on } from "@ngrx/store";
+import { onLoadableError, onLoadableLoad, onLoadableSuccess } from "../../state/loadable.state";
+import { appActions } from "./app.actions";
+import { createInitialAppState } from "../../state/app.state";
+
+export const appReducer = createReducer(
+ createInitialAppState(),
+ on(appActions.getAppVersionData, state => ({...onLoadableLoad(state)})),
+ on(appActions.getAppVersionDataSuccess, (state, { version_data }) => ({...onLoadableSuccess(state), version_data: {...version_data}})),
+ on(appActions.getAppVersionDataFailure, (state, { error }) => ({...onLoadableError(state, error)}))
+)
diff --git a/app/components/ui/src/app/store/app/app.selectors.ts b/app/components/ui/src/app/store/app/app.selectors.ts
new file mode 100644
index 0000000..86260c1
--- /dev/null
+++ b/app/components/ui/src/app/store/app/app.selectors.ts
@@ -0,0 +1,5 @@
+import { createSelector, createFeatureSelector } from "@ngrx/store";
+import { AppState } from "../../state/app.state";
+
+export const selectAppState = createFeatureSelector("app_data");
+export const selectAppVersionDataState = createSelector(selectAppState, (state: AppState) => state.version_data);
diff --git a/app/components/ui/src/app/store/model-provider/model-provider.actions.ts b/app/components/ui/src/app/store/model-provider/model-provider.actions.ts
new file mode 100644
index 0000000..f691e8a
--- /dev/null
+++ b/app/components/ui/src/app/store/model-provider/model-provider.actions.ts
@@ -0,0 +1,14 @@
+import { createActionGroup, props, emptyProps } from "@ngrx/store";
+import { ILLMModelProvider } from "../../models/model-provider.models";
+
+export const modelProviderActions = createActionGroup({
+ source: "model_provider",
+ events: {
+ getLLMModels: emptyProps(),
+ getLLMModelsSuccess: props<{ models: ILLMModelProvider[] }>(),
+ getLLMModelsFailure: props<{ error: Error }>(),
+ getLLMModelTypes: emptyProps(),
+ getLLMModelTypesSuccess: props<{ model_types: string[] }>(),
+ getLLMModelTypesFailure: props<{ error: Error }>()
+ },
+});
diff --git a/app/components/ui/src/app/store/model-provider/model-provider.effects.ts b/app/components/ui/src/app/store/model-provider/model-provider.effects.ts
new file mode 100644
index 0000000..0927fb6
--- /dev/null
+++ b/app/components/ui/src/app/store/model-provider/model-provider.effects.ts
@@ -0,0 +1,32 @@
+import { inject, Injectable } from "@angular/core";
+import { createEffect, ofType, Actions } from "@ngrx/effects";
+import { map, catchError, of, switchMap } from "rxjs";
+import { modelProviderActions } from "./model-provider.actions";
+import { ILLMModelProvider } from "../../models/model-provider.models";
+import { ModelProviderService } from "../../services/model-provider.service";
+
+
+@Injectable()
+export class modelProviderEffects {
+ private actions$ = inject(Actions);
+
+ getLLMModels$ = createEffect(() => this.actions$.pipe(
+ ofType(modelProviderActions.getLLMModels),
+ switchMap(() => this.modelProviderService.getLLMModels().pipe(
+ map((models: ILLMModelProvider[]) => modelProviderActions.getLLMModelsSuccess({ models })),
+ catchError(error => of(modelProviderActions.getLLMModelsFailure({ error })))
+ ))
+ ))
+
+ getLLMModelTypes$ = createEffect(() => this.actions$.pipe(
+ ofType(modelProviderActions.getLLMModelTypes),
+ switchMap(() => this.modelProviderService.getLLMModelTypes().pipe(
+ map((model_types: string[]) => modelProviderActions.getLLMModelTypesSuccess({ model_types })),
+ catchError(error => of(modelProviderActions.getLLMModelTypesFailure({ error })))
+ ))
+ ))
+
+ constructor(
+ private modelProviderService: ModelProviderService
+ ) { }
+}
diff --git a/app/components/ui/src/app/store/model-provider/model-provider.reducers.ts b/app/components/ui/src/app/store/model-provider/model-provider.reducers.ts
new file mode 100644
index 0000000..4e29710
--- /dev/null
+++ b/app/components/ui/src/app/store/model-provider/model-provider.reducers.ts
@@ -0,0 +1,52 @@
+import { createReducer, on } from "@ngrx/store";
+import { onLoadableError, onLoadableLoad, onLoadableSuccess } from "../../state/loadable.state";
+import { modelProviderActions } from "./model-provider.actions";
+import { createInitialModelProviderState } from "../../state/model-provider.state";
+
+export const modelProviderReducer = createReducer(
+ createInitialModelProviderState(),
+ on(modelProviderActions.getLLMModels, state => ({
+ ...state,
+ llm: {
+ ...state.llm,
+ ...onLoadableLoad(state.llm)
+ }
+ })),
+ on(modelProviderActions.getLLMModelsSuccess, (state, { models }) => ({
+ ...state,
+ llm: {
+ ...state.llm,
+ ...onLoadableSuccess(state.llm),
+ models: models
+ }
+ })),
+ on(modelProviderActions.getLLMModelsFailure, (state, { error }) => ({
+ ...state,
+ llm: {
+ ...state.llm,
+ ...onLoadableError(state.llm, error)
+ }
+ })),
+ on(modelProviderActions.getLLMModelTypes, state => ({
+ ...state,
+ llm: {
+ ...state.llm,
+ ...onLoadableLoad(state.llm)
+ }
+ })),
+ on(modelProviderActions.getLLMModelTypesSuccess, (state, { model_types }) => ({
+ ...state,
+ llm: {
+ ...state.llm,
+ ...onLoadableSuccess(state.llm),
+ model_types: model_types
+ }
+ })),
+ on(modelProviderActions.getLLMModelTypesFailure, (state, { error }) => ({
+ ...state,
+ llm: {
+ ...state.llm,
+ ...onLoadableError(state.llm, error)
+ }
+ }))
+)
diff --git a/app/components/ui/src/app/store/model-provider/model-provider.selectors.ts b/app/components/ui/src/app/store/model-provider/model-provider.selectors.ts
new file mode 100644
index 0000000..10cc020
--- /dev/null
+++ b/app/components/ui/src/app/store/model-provider/model-provider.selectors.ts
@@ -0,0 +1,6 @@
+import { createFeatureSelector, createSelector } from "@ngrx/store";
+import { ModelProviderState } from "../../state/model-provider.state";
+
+export const selectModelProviderState = createFeatureSelector("model_provider");
+export const selectLLMModels = createSelector(selectModelProviderState, (state: ModelProviderState) => state.llm.models);
+export const selectLLMModelTypes = createSelector(selectModelProviderState, (state: ModelProviderState) => state.llm.model_types);
diff --git a/app/components/ui/src/app/store/reducers/app.reducers.ts b/app/components/ui/src/app/store/reducers/app.reducers.ts
deleted file mode 100644
index 27458f4..0000000
--- a/app/components/ui/src/app/store/reducers/app.reducers.ts
+++ /dev/null
@@ -1,11 +0,0 @@
-import { createReducer, on } from "@ngrx/store";
-import { onLoadableError, onLoadableLoad, onLoadableSuccess } from "../state/loadable.state";
-import { appActions } from "../actions/app.actions";
-import { createInitialAppState } from "../state/app.state";
-
-export const appReducer = createReducer(
- createInitialAppState(),
- on(appActions.getACEVersionData, state => ({...onLoadableLoad(state)})),
- on(appActions.getACEVersionDataSuccess, (state, { versionData }) => ({...onLoadableSuccess(state), versionData: {...versionData}})),
- on(appActions.getACEVersionDataFailure, (state, { error }) => ({...onLoadableError(state, error)}))
-)
diff --git a/app/components/ui/src/app/store/selectors/app.selectors.ts b/app/components/ui/src/app/store/selectors/app.selectors.ts
deleted file mode 100644
index e2b001e..0000000
--- a/app/components/ui/src/app/store/selectors/app.selectors.ts
+++ /dev/null
@@ -1,5 +0,0 @@
-import { createSelector, createFeatureSelector } from "@ngrx/store";
-import { AppState } from "../state/app.state";
-
-export const selectAppState = createFeatureSelector("app_data");
-export const selectACEVersionData = createSelector(selectAppState, (state: AppState) => state.versionData);
diff --git a/app/components/ui/src/app/store/settings/settings.actions.ts b/app/components/ui/src/app/store/settings/settings.actions.ts
new file mode 100644
index 0000000..bb61a90
--- /dev/null
+++ b/app/components/ui/src/app/store/settings/settings.actions.ts
@@ -0,0 +1,17 @@
+import { createActionGroup, props, emptyProps } from "@ngrx/store";
+import { ISettings } from "../../models/settings.models";
+
+export const settingsActions = createActionGroup({
+ source: "settings",
+ events: {
+ getSettings: emptyProps(),
+ getSettingsSuccess: props<{ settings: ISettings }>(),
+ getSettingsFailure: props<{ error: Error }>(),
+ editSettings: props<{ settings: ISettings }>(),
+ editSettingsSuccess: emptyProps(),
+ editSettingsFailure: props<{ error: Error }>(),
+ resetSettings: emptyProps(),
+ resetSettingsSuccess: emptyProps(),
+ resetSettingsFailure: props<{ error: Error }>()
+ },
+});
diff --git a/app/components/ui/src/app/store/settings/settings.effects.ts b/app/components/ui/src/app/store/settings/settings.effects.ts
new file mode 100644
index 0000000..f1faf3d
--- /dev/null
+++ b/app/components/ui/src/app/store/settings/settings.effects.ts
@@ -0,0 +1,53 @@
+// DEPENDENCIES
+//// Angular
+import { inject, Injectable } from "@angular/core";
+import { createEffect, ofType, Actions } from "@ngrx/effects";
+import { map, catchError, of, switchMap } from "rxjs";
+//// Local
+import { settingsActions } from "./settings.actions";
+import { ISettings } from "../../models/settings.models";
+import { SettingsService } from "../../services/settings.service";
+
+
+@Injectable()
+export class SettingsEffects {
+ private actions$ = inject(Actions);
+
+ getSettings$ = createEffect(() => this.actions$.pipe(
+ ofType(settingsActions.getSettings),
+ switchMap(() => this.settingsService.getSettings().pipe(
+ map((settings: ISettings) => settingsActions.getSettingsSuccess({ settings })),
+ catchError(error => of(settingsActions.getSettingsFailure({ error })))
+ ))
+ ))
+
+ editSettings$ = createEffect(() => this.actions$.pipe(
+ ofType(settingsActions.editSettings),
+ switchMap(({ settings }) => this.settingsService.editSettings(settings).pipe(
+ map(() => settingsActions.getSettings()),
+ catchError(error => of(settingsActions.editSettingsFailure({ error })))
+ ))
+ ))
+
+ editSettingsSuccess$ = createEffect(() => this.actions$.pipe(
+ ofType(settingsActions.editSettingsSuccess),
+ map(() => settingsActions.getSettings())
+ ));
+
+ resetSettings$ = createEffect(() => this.actions$.pipe(
+ ofType(settingsActions.resetSettings),
+ switchMap(() => this.settingsService.resetSettings().pipe(
+ map(() => settingsActions.resetSettingsSuccess()),
+ catchError(error => of(settingsActions.resetSettingsFailure({ error })))
+ ))
+ ))
+
+ resetSettingsSuccess$ = createEffect(() => this.actions$.pipe(
+ ofType(settingsActions.resetSettingsSuccess),
+ map(() => settingsActions.getSettings())
+ ));
+
+ constructor(
+ private settingsService: SettingsService
+ ) { }
+}
diff --git a/app/components/ui/src/app/store/settings/settings.reducers.ts b/app/components/ui/src/app/store/settings/settings.reducers.ts
new file mode 100644
index 0000000..dd6c861
--- /dev/null
+++ b/app/components/ui/src/app/store/settings/settings.reducers.ts
@@ -0,0 +1,11 @@
+import { createReducer, on } from "@ngrx/store";
+import { onLoadableError, onLoadableLoad, onLoadableSuccess } from "../../state/loadable.state";
+import { settingsActions } from "./settings.actions";
+import { createInitialSettingsState } from "../../state/settings.state";
+
+export const settingsReducer = createReducer(
+ createInitialSettingsState(),
+ on(settingsActions.getSettings, state => ({...onLoadableLoad(state)})),
+ on(settingsActions.getSettingsSuccess, (state, { settings }) => ({...onLoadableSuccess(state), settings: {...settings}})),
+ on(settingsActions.getSettingsFailure, (state, { error }) => ({...onLoadableError(state, error)}))
+)
diff --git a/app/components/ui/src/app/store/settings/settings.selectors.ts b/app/components/ui/src/app/store/settings/settings.selectors.ts
new file mode 100644
index 0000000..b7eb338
--- /dev/null
+++ b/app/components/ui/src/app/store/settings/settings.selectors.ts
@@ -0,0 +1,4 @@
+import { createFeatureSelector, createSelector } from "@ngrx/store";
+import { SettingsState } from "../../state/settings.state";
+
+export const selectSettingsState = createFeatureSelector("settings");
diff --git a/app/components/ui/src/app/store/state/app.state.ts b/app/components/ui/src/app/store/state/app.state.ts
deleted file mode 100644
index 1c2859c..0000000
--- a/app/components/ui/src/app/store/state/app.state.ts
+++ /dev/null
@@ -1,17 +0,0 @@
-import { createDefaultLoadable, Loadable } from "./loadable.state";
-import { IACEVersionData } from "../../models/app.models";
-
-export interface AppState extends Loadable {
- versionData: IACEVersionData;
-}
-
-export function createInitialAppState(): AppState {
- return {
- ...createDefaultLoadable(),
- versionData: {
- version: "0"
- }
- }
-}
-
-
diff --git a/app/components/ui/src/app/theme.ts b/app/components/ui/src/app/theme.ts
new file mode 100644
index 0000000..dff6206
--- /dev/null
+++ b/app/components/ui/src/app/theme.ts
@@ -0,0 +1,19 @@
+import { Injectable, signal } from '@angular/core';
+import { BehaviorSubject } from 'rxjs';
+
+@Injectable({
+ providedIn: 'root'
+})
+export class ThemeService {
+ private darkModeSubject = new BehaviorSubject(false);
+ darkMode$ = this.darkModeSubject.asObservable();
+
+ setDarkMode(enabled: boolean) {
+ this.darkModeSubject.next(enabled);
+ if (enabled) {
+ document.body.classList.add('dark');
+ } else {
+ document.body.classList.remove('dark');
+ }
+ }
+}
diff --git a/app/components/ui/src/index.html b/app/components/ui/src/index.html
index 4fb474e..f85205d 100644
--- a/app/components/ui/src/index.html
+++ b/app/components/ui/src/index.html
@@ -9,7 +9,7 @@
-
+
diff --git a/app/components/ui/src/styles.scss b/app/components/ui/src/styles.scss
index 7e7239a..ee87f52 100644
--- a/app/components/ui/src/styles.scss
+++ b/app/components/ui/src/styles.scss
@@ -1,4 +1,30 @@
/* You can add global styles to this file, and also import other style files */
-html, body { height: 100%; }
+html, body {
+ height: 100%;
+ overscroll-behavior: none;
+}
body { margin: 0; font-family: Roboto, "Helvetica Neue", sans-serif; }
+
+.main-page {
+ position: relative;
+ padding-top: 1rem;
+ padding-left: 3rem;
+ padding-right: 3rem;
+ padding-bottom: 1rem;
+}
+
+.subsection {
+ display: flex;
+ padding-left: 1rem;
+ padding-right: 1rem;
+ flex-direction: column;
+
+ &.multi-input {
+ row-gap: 0.75rem;
+ }
+
+ .subsection-divider {
+ margin-top: 1rem;
+ }
+}
diff --git a/app/components/ui/src/theme.scss b/app/components/ui/src/theme.scss
new file mode 100644
index 0000000..2f53fa7
--- /dev/null
+++ b/app/components/ui/src/theme.scss
@@ -0,0 +1,128 @@
+/* These tokens are generated using https://themes.angular-material.dev/ */
+/* Preview: https://themes.angular-material.dev/?seed-error=%23ffb4ab&seed-neutral=%23767778&seed-neutral-variant=%2371787a&seed-primary=%233D636B&seed-secondary=%23bac9cd&seed-tertiary=%23d1c0e4 */
+/* Seed Colors: primary: #3D636B, secondary: #bac9cd, tertiary: #d1c0e4, error: #ffb4ab, neutral: #767778, neutral-variant: #71787a */
+
+@use "@angular/material" as mat;
+
+
+/* Light Theme */
+:root, :host {
+ @include mat.theme-overrides((
+ primary: #3e646c,
+ on-primary: #ffffff,
+ primary-container: #c1e9f3,
+ on-primary-container: #001f25,
+ inverse-primary: #a6cdd6,
+ primary-fixed: #c1e9f3,
+ primary-fixed-dim: #a6cdd6,
+ on-primary-fixed: #001f25,
+ on-primary-fixed-variant: #254c54,
+ secondary: #536164,
+ on-secondary: #ffffff,
+ secondary-container: #d6e5e9,
+ on-secondary-container: #101e21,
+ secondary-fixed: #d6e5e9,
+ secondary-fixed-dim: #bac9cd,
+ on-secondary-fixed: #101e21,
+ on-secondary-fixed-variant: #3b494d,
+ tertiary: #665978,
+ on-tertiary: #ffffff,
+ tertiary-container: #eddcff,
+ on-tertiary-container: #211631,
+ tertiary-fixed: #eddcff,
+ tertiary-fixed-dim: #d1c0e4,
+ on-tertiary-fixed: #211631,
+ on-tertiary-fixed-variant: #4e415f,
+ background: #fcf8f8,
+ on-background: #1c1b1b,
+ surface: #fcf8f8,
+ surface-dim: #ddd9d9,
+ surface-bright: #fcf8f8,
+ surface-container-lowest: #ffffff,
+ surface-container-low: #f7f3f2,
+ surface-container: #f1edec,
+ surface-container-high: #ebe7e7,
+ surface-container-highest: #e5e2e1,
+ on-surface: #1c1b1b,
+ shadow: #000000,
+ scrim: #000000,
+ surface-tint: #5d5e5f,
+ inverse-surface: #313030,
+ inverse-on-surface: #f4f0ef,
+ outline: #747779,
+ outline-variant: #c3c7c8,
+ neutral: #797776,
+ neutral10: #1c1b1b,
+ error: #ba1a1a,
+ error-container: #ffdad6,
+ on-error: #ffffff,
+ on-error-container: #410002,
+ surface-variant: #dfe3e4,
+ on-surface-variant: #434749,
+ neutral-variant: #747879,
+ neutral-variant20: #2d3132,
+ inverse-secondary: #bac9cd,
+ inverse-tertiary: #d1c0e4,
+ ));
+}
+
+/* Dark Theme */
+.dark {
+ @include mat.theme-overrides((
+ primary: #a6cdd6,
+ on-primary: #0a353d,
+ primary-container: #254c54,
+ on-primary-container: #c1e9f3,
+ inverse-primary: #3e646c,
+ primary-fixed: #c1e9f3,
+ primary-fixed-dim: #a6cdd6,
+ on-primary-fixed: #001f25,
+ on-primary-fixed-variant: #254c54,
+ secondary: #bac9cd,
+ on-secondary: #253336,
+ secondary-container: #3b494d,
+ on-secondary-container: #d6e5e9,
+ secondary-fixed: #d6e5e9,
+ secondary-fixed-dim: #bac9cd,
+ on-secondary-fixed: #101e21,
+ on-secondary-fixed-variant: #3b494d,
+ tertiary: #d1c0e4,
+ on-tertiary: #372b47,
+ tertiary-container: #4e415f,
+ on-tertiary-container: #eddcff,
+ tertiary-fixed: #eddcff,
+ tertiary-fixed-dim: #d1c0e4,
+ on-tertiary-fixed: #211631,
+ on-tertiary-fixed-variant: #4e415f,
+ background: #141313,
+ on-background: #e5e2e1,
+ surface: #141313,
+ surface-dim: #141313,
+ surface-bright: #3a3939,
+ surface-container-lowest: #0e0e0e,
+ surface-container-low: #1c1b1b,
+ surface-container: #201f1f,
+ surface-container-high: #2a2a2a,
+ surface-container-highest: #353434,
+ on-surface: #e5e2e1,
+ shadow: #000000,
+ scrim: #000000,
+ surface-tint: #c6c6c7,
+ inverse-surface: #e5e2e1,
+ inverse-on-surface: #313030,
+ outline: #8e9193,
+ outline-variant: #434749,
+ neutral: #797776,
+ neutral10: #1c1b1b,
+ error: #ffb4ab,
+ error-container: #93000a,
+ on-error: #690005,
+ on-error-container: #ffdad6,
+ surface-variant: #434749,
+ on-surface-variant: #c3c7c8,
+ neutral-variant: #747879,
+ neutral-variant20: #2d3132,
+ inverse-secondary: #536164,
+ inverse-tertiary: #665978,
+ ));
+}
diff --git a/app/constants/__init__.py b/app/constants/__init__.py
index ac3725b..96e898a 100644
--- a/app/constants/__init__.py
+++ b/app/constants/__init__.py
@@ -1,3 +1,4 @@
+from .api_routes import APIRoutes
from .components import Components
from .container_folders import ContainerFolders
from .defaults import Defaults
diff --git a/app/constants/api_routes.py b/app/constants/api_routes.py
new file mode 100644
index 0000000..0d0f159
--- /dev/null
+++ b/app/constants/api_routes.py
@@ -0,0 +1,9 @@
+# DEPENDENCIES
+## Local
+from .base_enum import BaseEnum
+
+
+class APIRoutes(BaseEnum):
+ """Enum"""
+ ROOT: str = "/"
+ MODEL_PROVIDER: str = f"{ROOT}model-provider/"
diff --git a/app/constants/model_providers.py b/app/constants/model_providers.py
index e9c7531..cc109b4 100644
--- a/app/constants/model_providers.py
+++ b/app/constants/model_providers.py
@@ -4,7 +4,7 @@
class ModelProviders(BaseEnum):
- CLAUDE: str = "claude"
+ ANTHROPIC: str = "anthropic"
DEEPSEEK: str = "deepseek"
GOOGLE_VERTEX_AI: str = "google_vertex_ai"
GROK: str = "grok"
diff --git a/app/containers/Containerfile b/app/containers/Containerfile
index 8688adf..bf4c125 100644
--- a/app/containers/Containerfile
+++ b/app/containers/Containerfile
@@ -1,9 +1,9 @@
FROM python:3.13-bookworm
-COPY . /home/ace
+COPY ./requirements /home/requirements
RUN apt-get update && apt-get install -y libffi-dev nats-server nodejs npm && \
- pip install -r /home/ace/requirements && \
+ pip install -r /home/requirements && \
apt-get purge -y libffi-dev python3-dev && \
npm install -g @angular/cli && \
cd /home/ace/components/ui && \
@@ -11,4 +11,5 @@ RUN apt-get update && apt-get install -y libffi-dev nats-server nodejs npm && \
ng analytics off && \
rm -rf /var/cache/*
+RUN mkdir /home/ace
WORKDIR /home/ace
diff --git a/app/models/api_schemas/controller.py b/app/models/api_schemas/controller.py
index 4e6004c..5edbfe9 100644
--- a/app/models/api_schemas/controller.py
+++ b/app/models/api_schemas/controller.py
@@ -13,6 +13,10 @@
# RESPONSES
class GetVersionDetailsResponse(BaseModel):
version: str
+ authors: list[str]
+ license: str
+ last_update: str
+ rebuild_date: str
GetSettingsResponse: type[BaseModel] = ControllerSettingsSchema
diff --git a/app/models/config/controller.py b/app/models/config/controller.py
index 2845931..d6fe407 100644
--- a/app/models/config/controller.py
+++ b/app/models/config/controller.py
@@ -3,12 +3,14 @@
from pydantic import BaseModel
## Local
from constants import Defaults
-from .defaults import DEFAULT_LAYER_SETTINGS, DEFAULT_MODEL_PROVIDER_SETTINGS
+from .defaults import DEFAULT_LAYER_SETTINGS, DEFAULT_MODEL_PROVIDER_SETTINGS, DEFAULT_UI_SETTINGS
from .layers import LayerSettings
from .model_providers import ModelProviderSettings
+from .ui import UISettings
class ControllerSettingsSchema(BaseModel):
ace_name: str = Defaults.ACE_NAME
- layer_settings: list[LayerSettings] = DEFAULT_LAYER_SETTINGS
+ ui_settings: UISettings = DEFAULT_UI_SETTINGS
model_provider_settings: ModelProviderSettings = DEFAULT_MODEL_PROVIDER_SETTINGS
+ layer_settings: list[LayerSettings] = DEFAULT_LAYER_SETTINGS
diff --git a/app/models/config/defaults.py b/app/models/config/defaults.py
index 071b3a7..2d2448d 100644
--- a/app/models/config/defaults.py
+++ b/app/models/config/defaults.py
@@ -7,35 +7,14 @@
)
from .layers import LayerSettings
from .model_providers import ModelProviderSettings, model_types
+from .ui import UISettings
-# LAYERS
-DEFAULT_LAYER_SETTINGS: list[LayerSettings] = [
- LayerSettings(
- layer_name=LayerTypes.ASPIRATIONAL,
- model_type=LLMModelTypes.REASONER
- ),
- LayerSettings(
- layer_name=LayerTypes.GLOBAL_STRATEGY,
- model_type=LLMModelTypes.REASONER
- ),
- LayerSettings(
- layer_name=LayerTypes.AGENT_MODEL,
- model_type=LLMModelTypes.GENERALIST
- ),
- LayerSettings(
- layer_name=LayerTypes.EXECUTIVE_FUNCTION,
- model_type=LLMModelTypes.GENERALIST
- ),
- LayerSettings(
- layer_name=LayerTypes.COGNITIVE_CONTROL,
- model_type=LLMModelTypes.EFFICIENT
- ),
- LayerSettings(
- layer_name=LayerTypes.TASK_PROSECUTION,
- model_type=LLMModelTypes.FUNCTION_CALLER
- )
-]
+# UI
+DEFAULT_UI_SETTINGS: UISettings = UISettings(
+ dark_mode=True,
+ show_footer=False
+)
# MODEL PROVIDERS
@@ -92,3 +71,32 @@
llm_model_type_settings=DEFAULT_LLM_MODEL_TYPE_SETTINGS,
rag_model_type_settings=DEFAULT_RAG_MODEL_TYPE_SETTINGS
)
+
+
+# LAYERS
+DEFAULT_LAYER_SETTINGS: list[LayerSettings] = [
+ LayerSettings(
+ layer_name=LayerTypes.ASPIRATIONAL,
+ model_type=LLMModelTypes.REASONER
+ ),
+ LayerSettings(
+ layer_name=LayerTypes.GLOBAL_STRATEGY,
+ model_type=LLMModelTypes.REASONER
+ ),
+ LayerSettings(
+ layer_name=LayerTypes.AGENT_MODEL,
+ model_type=LLMModelTypes.GENERALIST
+ ),
+ LayerSettings(
+ layer_name=LayerTypes.EXECUTIVE_FUNCTION,
+ model_type=LLMModelTypes.GENERALIST
+ ),
+ LayerSettings(
+ layer_name=LayerTypes.COGNITIVE_CONTROL,
+ model_type=LLMModelTypes.EFFICIENT
+ ),
+ LayerSettings(
+ layer_name=LayerTypes.TASK_PROSECUTION,
+ model_type=LLMModelTypes.FUNCTION_CALLER
+ )
+]
diff --git a/app/models/config/model_providers/__init__.py b/app/models/config/model_providers/__init__.py
index ffc476f..a2791c7 100644
--- a/app/models/config/model_providers/__init__.py
+++ b/app/models/config/model_providers/__init__.py
@@ -1,2 +1,2 @@
from .model_providers import ModelProviderSettings
-from .indiividual_providers import IndividualProviderSettings
\ No newline at end of file
+from .individual_providers import IndividualProviderSettings
\ No newline at end of file
diff --git a/app/models/config/model_providers/indiividual_providers.py b/app/models/config/model_providers/individual_providers.py
similarity index 91%
rename from app/models/config/model_providers/indiividual_providers.py
rename to app/models/config/model_providers/individual_providers.py
index 08b18a0..0b9163a 100644
--- a/app/models/config/model_providers/indiividual_providers.py
+++ b/app/models/config/model_providers/individual_providers.py
@@ -4,5 +4,6 @@
class IndividualProviderSettings(BaseModel):
+ name: str
enabled: bool = False
api_key: str = ""
diff --git a/app/models/config/model_providers/model_providers.py b/app/models/config/model_providers/model_providers.py
index d35fe33..e79f9b5 100644
--- a/app/models/config/model_providers/model_providers.py
+++ b/app/models/config/model_providers/model_providers.py
@@ -2,18 +2,49 @@
## Third-Party
from pydantic import BaseModel
## Local
-from .indiividual_providers import IndividualProviderSettings
+from constants import ModelProviders
+from .individual_providers import IndividualProviderSettings
from . import model_types
class ModelProviderSettings(BaseModel):
- claude_settings: IndividualProviderSettings = IndividualProviderSettings()
- deepseek_settings: IndividualProviderSettings = IndividualProviderSettings()
- google_vertex_ai_settings: IndividualProviderSettings = IndividualProviderSettings()
- grok_settings: IndividualProviderSettings = IndividualProviderSettings()
- groq_settings: IndividualProviderSettings = IndividualProviderSettings()
- ollama_settings: IndividualProviderSettings = IndividualProviderSettings(enabled=True)
- openai_settings: IndividualProviderSettings = IndividualProviderSettings()
+ individual_provider_settings: list[IndividualProviderSettings] = [
+ IndividualProviderSettings(
+ name=ModelProviders.ANTHROPIC,
+ enabled=False,
+ api_key=""
+ ),
+ IndividualProviderSettings(
+ name=ModelProviders.DEEPSEEK,
+ enabled=False,
+ api_key=""
+ ),
+ IndividualProviderSettings(
+ name=ModelProviders.GOOGLE_VERTEX_AI,
+ enabled=False,
+ api_key=""
+ ),
+ IndividualProviderSettings(
+ name=ModelProviders.GROK,
+ enabled=False,
+ api_key=""
+ ),
+ IndividualProviderSettings(
+ name=ModelProviders.GROQ,
+ enabled=False,
+ api_key=""
+ ),
+ IndividualProviderSettings(
+ name=ModelProviders.OLLAMA,
+ enabled=True,
+ api_key=""
+ ),
+ IndividualProviderSettings(
+ name=ModelProviders.OPENAI,
+ enabled=False,
+ api_key=""
+ )
+ ]
three_d_model_type_settings: list[model_types.ThreeDModelTypeSetting] = []
audio_model_type_settings: list[model_types.AudioModelTypeSetting] = []
diff --git a/app/models/config/ui.py b/app/models/config/ui.py
new file mode 100644
index 0000000..50f19b0
--- /dev/null
+++ b/app/models/config/ui.py
@@ -0,0 +1,8 @@
+# DEPENDENCIES
+## Third-Party
+from pydantic import BaseModel
+
+
+class UISettings(BaseModel):
+ dark_mode: bool
+ show_footer: bool
diff --git a/app/models/data/initial.py b/app/models/data/initial.py
index 6215eeb..7b552bc 100644
--- a/app/models/data/initial.py
+++ b/app/models/data/initial.py
@@ -11,7 +11,7 @@
# Claude
LLMModelProvider(
id="0194c740-6300-7184-8eff-2be664245b03",
- model_provider=ModelProviders.CLAUDE,
+ model_provider=ModelProviders.ANTHROPIC,
name="Claude 3.5 Haiku",
model_name="claude-3-5-haiku-latest",
default=True,
@@ -24,7 +24,7 @@
),
LLMModelProvider(
id="0194c740-98bb-76db-95ed-ef6cfa62839b",
- model_provider=ModelProviders.CLAUDE,
+ model_provider=ModelProviders.ANTHROPIC,
name="Claude 3.5 Sonnet",
model_name="claude-3-5-sonnet-latest",
default=True,
diff --git a/app/shell.py b/app/shell.py
index d29e0f9..e2f42fe 100644
--- a/app/shell.py
+++ b/app/shell.py
@@ -56,12 +56,12 @@ def execute_shell(
return "".join(stdout_lines)
-def exec_check_exists(check_command: str, keyword: str) -> bool:
+def shell_check_exists(shell_command: str, keyword_to_find: str) -> bool:
"""Checks if the keyword exists in the output of the check_command"""
- logger.debug(f'Checking using "{check_command}" for {keyword}...')
- existing_terms = frozenset(execute_shell(check_command).split("\n"))
+ logger.debug(f'Checking using "{shell_command}" for {keyword_to_find}...')
+ existing_terms = frozenset(execute_shell(shell_command).split("\n"))
logger.debug(f"Existing Terms: {existing_terms}")
for entry in existing_terms:
- if keyword in entry:
+ if keyword_to_find in entry:
return True
return False
diff --git a/app/startup.py b/app/startup.py
index 38fa6a5..806acd1 100755
--- a/app/startup.py
+++ b/app/startup.py
@@ -8,7 +8,7 @@
from constants import DictKeys, Files, Names, ShellCommands
from constants.files import setup_user_deployment_file
from logger import logger
-from shell import execute_shell, exec_check_exists
+from shell import execute_shell, shell_check_exists
# ARGUMENTS
@@ -22,17 +22,17 @@ def _get_arguments() -> dict[str, bool]:
# PREPARATION
-def setup_network() -> None:
- if not exec_check_exists(check_command=ShellCommands.CHECK_NETWORK, keyword=Names.NETWORK):
+def _setup_network() -> None:
+ if not shell_check_exists(check_command=ShellCommands.CHECK_NETWORK, keyword=Names.NETWORK):
logger.startup("First time setting up network...")
execute_shell(ShellCommands.CREATE_NETWORK)
-def update() -> bool:
+def _update() -> bool:
"""Update the git repo, returning True if container needs to be rebuilt"""
logger.startup(f"Updating {Names.ACE}...")
execute_shell(ShellCommands.UPDATE)
-def check_if_latest_build() -> bool:
+def _check_if_latest_build() -> bool:
"""Compares the update history and version files to check if the container needs to be rebuilt"""
updates: dict[str] = {}
history: dict[str] = {}
@@ -50,12 +50,12 @@ def check_if_latest_build() -> bool:
return True
return False
-def build_container(force_build: bool = False):
+def _build_container_image(force_build: bool = False):
"""
A function which builds if image doesn't exist or if force_build flag is set
"""
logger.startup("Checking if build is required...")
- image_exists: bool = exec_check_exists(check_command=ShellCommands.CHECK_IMAGES, keyword=Names.IMAGE)
+ image_exists: bool = shell_check_exists(check_command=ShellCommands.CHECK_IMAGES, keyword=Names.IMAGE)
should_build: bool = not image_exists or force_build
if not should_build:
logger.startup("Image already exists, skipping build...")
@@ -79,22 +79,22 @@ def build_container(force_build: bool = False):
# DEPLOYMENT
-def stop_cluster() -> None:
+def _stop_cluster() -> None:
"""
Stops the ACE cluster if it is running
"""
- exists: bool = exec_check_exists(ShellCommands.CHECK_PODS, Names.ACE)
+ exists: bool = shell_check_exists(ShellCommands.CHECK_PODS, Names.ACE)
if not exists:
logger.warn(f"{Names.ACE} is not running! Cannot stop...")
return
logger.startup(f"Stopping {Names.ACE}...")
execute_shell(ShellCommands.STOP_CLUSTER)
-def start_cluster(force_restart: bool) -> None:
+def _start_cluster(force_restart: bool) -> None:
"""
Start the ACE cluster if it isn't running, handling restarts
"""
- exists: bool = exec_check_exists(ShellCommands.CHECK_PODS, Names.ACE)
+ exists: bool = shell_check_exists(ShellCommands.CHECK_PODS, Names.ACE)
if not force_restart and exists:
logger.startup(f"ACE is already running... Run with --{DictKeys.RESTART} to restart!")
return
@@ -106,27 +106,27 @@ def start_cluster(force_restart: bool) -> None:
# MAIN
-def startup():
+def _startup():
arguments: dict[str, bool] = _get_arguments()
dev: bool = arguments[DictKeys.DEV]
force_build: bool = arguments[DictKeys.BUILD]
setup_user_deployment_file(dev)
- setup_network()
+ _setup_network()
if not dev:
- update()
- update_build: bool = check_if_latest_build()
+ _update()
+ update_build: bool = _check_if_latest_build()
if update_build:
force_build = True
- build_container(force_build=force_build)
+ _build_container_image(force_build=force_build)
execute_shell(ShellCommands.CLEAR_OLD_IMAGES)
if arguments[DictKeys.STOP]:
- stop_cluster()
+ _stop_cluster()
return
- start_cluster(force_restart=arguments[DictKeys.RESTART])
+ _start_cluster(force_restart=arguments[DictKeys.RESTART])
if __name__ == "__main__":
- startup()
+ _startup()
diff --git a/app/version b/app/version
index f4db7ab..ecf10e7 100644
--- a/app/version
+++ b/app/version
@@ -1,4 +1,9 @@
{
- "version": "0.0.0",
- "rebuild_date": "2025-02-02"
-}
\ No newline at end of file
+ "version": "0.0.0",
+ "authors": [
+ "Jayfalls"
+ ],
+ "license": "MIT",
+ "last_update": "2025-03-23 15:44:44",
+ "rebuild_date": "2025-02-02"
+}
diff --git a/documentation/personal.md b/documentation/personal.md
new file mode 100644
index 0000000..c4d9319
--- /dev/null
+++ b/documentation/personal.md
@@ -0,0 +1,17 @@
+[⬆️](..)
+
+## Component Commands
+- UI
+```shell
+npm10 run start-local
+```
+- Controller
+```shell
+source ../.venv/bin/activate
+ACE_LOGGER_FILE_NAME="controller" ACE_LOGGER_VERBOSE="" ./component.py --controller --dev
+```
+
+## Podman Weirdness
+```shell
+podman machine stop && podman machine start
+```
\ No newline at end of file
diff --git a/tests/unit/components/controller/api/test_service.py b/tests/unit/components/controller/api/test_service.py
index 981999d..c83afa9 100644
--- a/tests/unit/components/controller/api/test_service.py
+++ b/tests/unit/components/controller/api/test_service.py
@@ -8,7 +8,7 @@
from app.models.config.controller import ControllerSettingsSchema
from app.models.config.layers import LayerSettings
from app.models.config.model_providers import IndividualProviderSettings, ModelProviderSettings
-from app.components.controller.api.service import (
+from app.components.controller.api.services.root import (
_get_settings,
edit_settings_data
)
@@ -25,7 +25,6 @@ class ExistingSettings:
).model_dump()
]
MODEL_PROVIDER_SETTINGS = ModelProviderSettings(
- claude_settings=IndividualProviderSettings(enabled=True),
llm_model_type_settings = [],
rag_model_type_settings = []
).model_dump()
diff --git a/tests/unit/test_shell.py b/tests/unit/test_shell.py
index 766c092..79d4e09 100644
--- a/tests/unit/test_shell.py
+++ b/tests/unit/test_shell.py
@@ -2,7 +2,7 @@
## Third-Party
import pytest
## Local
-from app.shell import execute_shell, exec_check_exists
+from app.shell import execute_shell, shell_check_exists
@pytest.mark.parametrize("command,expected_output", [
@@ -21,11 +21,11 @@ def test_execute_shell(command, expected_output, caplog):
"/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", ".")
+def test_shell_check_exists():
+ """Test the shell_check_exists function."""
+ result = shell_check_exists("ls -a", ".")
assert result is True, "Expected True, but got False"
- result = exec_check_exists("ls -a", "non_existent_file")
+ result = shell_check_exists("ls -a", "non_existent_file")
assert result is False, "Expected False, but got True"
def test_should_print_result(capsys):