From 0e72f290ae4d9518c3d412051cc4bd799e9af89f Mon Sep 17 00:00:00 2001 From: Jason Esterhuizen Date: Sat, 15 Feb 2025 18:35:54 +0200 Subject: [PATCH 01/15] [UPDATE] Cleanup --- app/components/controller/api/routes.py | 7 ++-- app/components/ui/src/app/app.component.ts | 6 +-- app/components/ui/src/app/app.config.ts | 4 +- .../ui/src/app/{store => }/state/app.state.ts | 2 +- .../app/{store => }/state/loadable.state.ts | 0 .../app/store/{actions => app}/app.actions.ts | 0 .../app/store/{effects => app}/app.effects.ts | 2 +- .../store/{reducers => app}/app.reducers.ts | 6 +-- .../store/{selectors => app}/app.selectors.ts | 2 +- app/containers/Containerfile | 5 ++- app/shell.py | 8 ++-- app/startup.py | 38 +++++++++---------- 12 files changed, 41 insertions(+), 39 deletions(-) rename app/components/ui/src/app/{store => }/state/app.state.ts (84%) rename app/components/ui/src/app/{store => }/state/loadable.state.ts (100%) rename app/components/ui/src/app/store/{actions => app}/app.actions.ts (100%) rename app/components/ui/src/app/store/{effects => app}/app.effects.ts (94%) rename app/components/ui/src/app/store/{reducers => app}/app.reducers.ts (77%) rename app/components/ui/src/app/store/{selectors => app}/app.selectors.ts (83%) diff --git a/app/components/controller/api/routes.py b/app/components/controller/api/routes.py index 04e7610..af10eda 100644 --- a/app/components/controller/api/routes.py +++ b/app/components/controller/api/routes.py @@ -19,10 +19,11 @@ controller_api = FastAPI() controller_api.add_middleware( CORSMiddleware, - allow_origins=["http://localhost:4200"], # Allow requests from your Angular app + allow_origins=["http://localhost:4200", "http://127.0.0.1:4200"], allow_credentials=True, - allow_methods=["*"], # Allow all HTTP methods - allow_headers=["*"], # Allow all headers + allow_methods=["*"], + allow_headers=["*"], + expose_headers=["*"] ) diff --git a/app/components/ui/src/app/app.component.ts b/app/components/ui/src/app/app.component.ts index c2dbbeb..206303f 100644 --- a/app/components/ui/src/app/app.component.ts +++ b/app/components/ui/src/app/app.component.ts @@ -10,9 +10,9 @@ 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 { appActions } from "./store/app/app.actions"; +import { selectAppState } from './store/app/app.selectors'; +import { AppState } from './state/app.state'; // TYPES diff --git a/app/components/ui/src/app/app.config.ts b/app/components/ui/src/app/app.config.ts index b918be1..e10d87c 100644 --- a/app/components/ui/src/app/app.config.ts +++ b/app/components/ui/src/app/app.config.ts @@ -9,8 +9,8 @@ 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 { appReducer } from './store/app/app.reducers'; // CONFIG diff --git a/app/components/ui/src/app/store/state/app.state.ts b/app/components/ui/src/app/state/app.state.ts similarity index 84% rename from app/components/ui/src/app/store/state/app.state.ts rename to app/components/ui/src/app/state/app.state.ts index 1c2859c..e1fe7e1 100644 --- a/app/components/ui/src/app/store/state/app.state.ts +++ b/app/components/ui/src/app/state/app.state.ts @@ -1,5 +1,5 @@ import { createDefaultLoadable, Loadable } from "./loadable.state"; -import { IACEVersionData } from "../../models/app.models"; +import { IACEVersionData } from "../models/app.models"; export interface AppState extends Loadable { versionData: IACEVersionData; 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/store/actions/app.actions.ts b/app/components/ui/src/app/store/app/app.actions.ts similarity index 100% rename from app/components/ui/src/app/store/actions/app.actions.ts rename to app/components/ui/src/app/store/app/app.actions.ts 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 94% 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..feea23e 100644 --- a/app/components/ui/src/app/store/effects/app.effects.ts +++ b/app/components/ui/src/app/store/app/app.effects.ts @@ -2,7 +2,7 @@ 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 { appActions } from "./app.actions"; import { IACEVersionData } from "../../models/app.models"; import { AppService } from "../../services/app.service"; diff --git a/app/components/ui/src/app/store/reducers/app.reducers.ts b/app/components/ui/src/app/store/app/app.reducers.ts similarity index 77% rename from app/components/ui/src/app/store/reducers/app.reducers.ts rename to app/components/ui/src/app/store/app/app.reducers.ts index 27458f4..665195d 100644 --- a/app/components/ui/src/app/store/reducers/app.reducers.ts +++ b/app/components/ui/src/app/store/app/app.reducers.ts @@ -1,7 +1,7 @@ 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"; +import { onLoadableError, onLoadableLoad, onLoadableSuccess } from "../../state/loadable.state"; +import { appActions } from "./app.actions"; +import { createInitialAppState } from "../../state/app.state"; export const appReducer = createReducer( createInitialAppState(), diff --git a/app/components/ui/src/app/store/selectors/app.selectors.ts b/app/components/ui/src/app/store/app/app.selectors.ts similarity index 83% rename from app/components/ui/src/app/store/selectors/app.selectors.ts rename to app/components/ui/src/app/store/app/app.selectors.ts index e2b001e..f576a7b 100644 --- a/app/components/ui/src/app/store/selectors/app.selectors.ts +++ b/app/components/ui/src/app/store/app/app.selectors.ts @@ -1,5 +1,5 @@ import { createSelector, createFeatureSelector } from "@ngrx/store"; -import { AppState } from "../state/app.state"; +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/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/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() From d006c3fdf3a6aeaff145fcc5267040526337c2a3 Mon Sep 17 00:00:00 2001 From: Jason Esterhuizen Date: Sat, 15 Feb 2025 20:27:46 +0200 Subject: [PATCH 02/15] [FEAT] Added base settings page --- app/components/ui/src/app/app.config.ts | 6 +- app/components/ui/src/app/constants.ts | 3 + .../src/app/{api.urls.ts => environment.ts} | 2 +- .../ui/src/app/models/settings.models.ts | 154 ++++++++++++++++++ .../ui/src/app/pages/chat/chat.component.html | 4 +- .../pages/dashboard/dashboard.component.html | 4 +- .../ui/src/app/pages/home/home.component.html | 4 +- .../model_garden/model-garden.component.html | 4 +- .../pages/settings/settings.component.html | 32 +++- .../pages/settings/settings.component.scss | 6 + .../app/pages/settings/settings.component.ts | 42 ++++- .../app/pages/settings/settings.service.ts | 0 .../pages/settings/store/settings.actions.ts | 0 .../pages/settings/store/settings.effects.ts | 0 .../pages/settings/store/settings.reducers.ts | 0 .../settings/store/settings.selectors.ts | 0 .../pages/settings/store/settings.state.ts | 0 .../ui/src/app/services/app.service.ts | 4 +- .../ui/src/app/services/settings.service.ts | 24 +++ .../ui/src/app/state/settings.state.ts | 65 ++++++++ .../ui/src/app/store/app/app.effects.ts | 2 - .../app/store/settings/settings.actions.ts | 11 ++ .../app/store/settings/settings.effects.ts | 24 +++ .../app/store/settings/settings.reducers.ts | 11 ++ .../app/store/settings/settings.selectors.ts | 4 + app/components/ui/src/index.html | 2 +- app/components/ui/src/styles.scss | 14 +- app/models/config/model_providers/__init__.py | 2 +- ...l_providers.py => individual_providers.py} | 0 documentation/personal.md | 17 ++ tests/unit/test_shell.py | 10 +- 31 files changed, 428 insertions(+), 23 deletions(-) create mode 100644 app/components/ui/src/app/constants.ts rename app/components/ui/src/app/{api.urls.ts => environment.ts} (75%) create mode 100644 app/components/ui/src/app/models/settings.models.ts delete mode 100644 app/components/ui/src/app/pages/settings/settings.service.ts delete mode 100644 app/components/ui/src/app/pages/settings/store/settings.actions.ts delete mode 100644 app/components/ui/src/app/pages/settings/store/settings.effects.ts delete mode 100644 app/components/ui/src/app/pages/settings/store/settings.reducers.ts delete mode 100644 app/components/ui/src/app/pages/settings/store/settings.selectors.ts delete mode 100644 app/components/ui/src/app/pages/settings/store/settings.state.ts create mode 100644 app/components/ui/src/app/services/settings.service.ts create mode 100644 app/components/ui/src/app/state/settings.state.ts create mode 100644 app/components/ui/src/app/store/settings/settings.actions.ts create mode 100644 app/components/ui/src/app/store/settings/settings.effects.ts create mode 100644 app/components/ui/src/app/store/settings/settings.reducers.ts create mode 100644 app/components/ui/src/app/store/settings/settings.selectors.ts rename app/models/config/model_providers/{indiividual_providers.py => individual_providers.py} (100%) create mode 100644 documentation/personal.md diff --git a/app/components/ui/src/app/app.config.ts b/app/components/ui/src/app/app.config.ts index e10d87c..f0882cb 100644 --- a/app/components/ui/src/app/app.config.ts +++ b/app/components/ui/src/app/app.config.ts @@ -11,15 +11,17 @@ import { routes } from './app.routes'; import { provideAnimationsAsync } from '@angular/platform-browser/animations/async'; import { AppEffects } from './store/app/app.effects'; import { appReducer } from './store/app/app.reducers'; +import { SettingsEffects } from './store/settings/settings.effects'; +import { settingsReducer } from './store/settings/settings.reducers'; // CONFIG export const appConfig: ApplicationConfig = { providers: [ provideAnimationsAsync(), - provideEffects([AppEffects]), + provideEffects([AppEffects, SettingsEffects]), provideHttpClient(), - provideStore({ app_data: appReducer }), + provideStore({ app_data: appReducer, 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..5a54c5e --- /dev/null +++ b/app/components/ui/src/app/constants.ts @@ -0,0 +1,3 @@ +export const VALUES = { + NOT_LOADED: "not_loaded", +} 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/settings.models.ts b/app/components/ui/src/app/models/settings.models.ts new file mode 100644 index 0000000..c53a7d4 --- /dev/null +++ b/app/components/ui/src/app/models/settings.models.ts @@ -0,0 +1,154 @@ +// { +// "ace_name": "PrototypeACE", +// "layer_settings": [ +// { +// "layer_name": "aspirational", +// "model_type": "reasoner" +// }, +// { +// "layer_name": "global_strategy", +// "model_type": "reasoner" +// }, +// { +// "layer_name": "agent_model", +// "model_type": "generalist" +// }, +// { +// "layer_name": "executive_function", +// "model_type": "generalist" +// }, +// { +// "layer_name": "cognitive_control", +// "model_type": "efficient" +// }, +// { +// "layer_name": "task_prosecution", +// "model_type": "function_caller" +// } +// ], +// "model_provider_settings": { +// "claude_settings": { +// "enabled": false, +// "api_key": "" +// }, +// "deepseek_settings": { +// "enabled": false, +// "api_key": "" +// }, +// "google_vertex_ai_settings": { +// "enabled": false, +// "api_key": "" +// }, +// "grok_settings": { +// "enabled": false, +// "api_key": "" +// }, +// "groq_settings": { +// "enabled": false, +// "api_key": "" +// }, +// "ollama_settings": { +// "enabled": true, +// "api_key": "" +// }, +// "openai_settings": { +// "enabled": false, +// "api_key": "" +// }, +// "three_d_model_type_settings": [], +// "audio_model_type_settings": [], +// "image_model_type_settings": [], +// "llm_model_type_settings": [ +// { +// "model_type": "coder", +// "model_id": "0194c745-de29-7bc6-ad6b-f7b5f8d0e414", +// "logical_temperature": 0.2, +// "creative_temperature": 0.7, +// "output_token_limit": 2048 +// }, +// { +// "model_type": "efficient", +// "model_id": "0194c74b-ff7d-7c91-a7b2-934a16aafb04", +// "logical_temperature": 0.2, +// "creative_temperature": 0.7, +// "output_token_limit": 2048 +// }, +// { +// "model_type": "function_caller", +// "model_id": "0194c751-721a-7470-a277-e76ccdb840b8", +// "logical_temperature": 0.2, +// "creative_temperature": 0.7, +// "output_token_limit": 2048 +// }, +// { +// "model_type": "generalist", +// "model_id": "0194c751-721a-7470-a277-e76ccdb840b8", +// "logical_temperature": 0.2, +// "creative_temperature": 0.7, +// "output_token_limit": 2048 +// }, +// { +// "model_type": "reasoner", +// "model_id": "0194c74e-13ea-72d5-9b2d-a46dfa196784", +// "logical_temperature": 0.2, +// "creative_temperature": 0.7, +// "output_token_limit": 2048 +// } +// ], +// "multimodal_model_type_settings": [], +// "rag_model_type_settings": [], +// "robotics_model_type_settings": [], +// "video_model_type_settings": [] +// } +// } + +// SECTIONS +//// Layers +export interface ILayerSetting { + layer_name: string; + model_type: string; +} + +//// Model Provider +////// Unique Providers +export interface IIndividualProviderSettings { + 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 { + claude_settings: IIndividualProviderSettings; + deepseek_settings: IIndividualProviderSettings; + google_vertex_ai_settings: IIndividualProviderSettings; + grok_settings: IIndividualProviderSettings; + groq_settings: IIndividualProviderSettings; + ollama_settings: IIndividualProviderSettings; + openai_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; + 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..75d01c8 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,31 @@ -

SETTINGS PAGE

+
+

Settings

+ +
+
+

Ace Name:

+

{{ settings.ace_name }}

+
+ +
+

Layer Settings:

+ +
    + @for (layer_setting of settings.layer_settings; track layer_setting.layer_name) { + + +
    + Layer: {{ formatLayerNames(layer_setting.layer_name) }} + + Model Type: + {{ layer_setting.model_type | titlecase }} + +
    +
    +
    + } +
+
+
+
+
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..f21bf09 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,6 @@ +.ace-name { + display: flex; + flex-direction: row; + align-items: baseline; + gap: 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..639c39f 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,45 @@ -import { Component } from "@angular/core"; +// DEPENDENCIES +//// Angular +import { TitleCasePipe } from "@angular/common"; +import { Component, OnInit } from "@angular/core"; +import { Store } from "@ngrx/store"; +import { Observable } from "rxjs"; +import {FormsModule} from '@angular/forms'; +import { MatCardModule } from "@angular/material/card"; +import {MatFormFieldModule} from '@angular/material/form-field'; +import {MatInputModule} from '@angular/material/input'; +import { MatListModule } from "@angular/material/list"; +import {MatSelectModule} from '@angular/material/select'; +//// Local +import { ISettings } from "../../models/settings.models"; +import { settingsActions } from "../../store/settings/settings.actions"; +import { selectSettingsState } from "../../store/settings/settings.selectors"; +import { SettingsState } from "../../state/settings.state"; @Component({ selector: "page-settings", - imports: [], + imports: [FormsModule, MatCardModule, MatFormFieldModule, MatInputModule, MatListModule, MatSelectModule, TitleCasePipe], templateUrl: "./settings.component.html", styleUrl: "./settings.component.scss" }) -export class SettingsComponent {} +export class SettingsComponent implements OnInit { + settings$: Observable; + + settings!: ISettings; + + constructor(private store: Store) { + this.settings$ = this.store.select(selectSettingsState); + } + + ngOnInit(): void { + this.store.dispatch(settingsActions.getSettings()); + this.settings$.subscribe( settings => this.settings = settings.settings); + } + + formatLayerNames(layerName: string): string { + return layerName + .split('_') + .map(word => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase()) + .join(' '); + } +} 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..9715bad 100644 --- a/app/components/ui/src/app/services/app.service.ts +++ b/app/components/ui/src/app/services/app.service.ts @@ -1,14 +1,14 @@ import { Injectable } from "@angular/core"; import { HttpClient } from "@angular/common/http"; import { Observable } from 'rxjs/internal/Observable'; -import { serviceURLs } from "../api.urls"; +import { environmentURLs } from "../environment"; export type HttpListResponseFailure = { status: string, message: string }; const endpoints = { - getACEVersionData: `${serviceURLs.controller}/version`, + getACEVersionData: `${environmentURLs.controller}/version`, }; 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..c1f7ee8 --- /dev/null +++ b/app/components/ui/src/app/services/settings.service.ts @@ -0,0 +1,24 @@ +import { Injectable } from "@angular/core"; +import { HttpClient } from "@angular/common/http"; +import { Observable } from 'rxjs/internal/Observable'; +import { environmentURLs } from "../environment"; + + +export type HttpListResponseFailure = { status: string, message: string }; + + +const endpoints = { + getSettings: `${environmentURLs.controller}/settings`, +}; + + +@Injectable({ + providedIn: "root" +}) +export class SettingsService { + constructor(private http: HttpClient) { } + + getSettings(): Observable { + return this.http.get(endpoints.getSettings); + } +} 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..45c1c82 --- /dev/null +++ b/app/components/ui/src/app/state/settings.state.ts @@ -0,0 +1,65 @@ +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, + layer_settings: [], + model_provider_settings: { + claude_settings: { + api_key: VALUES.NOT_LOADED, + enabled: false + }, + deepseek_settings: { + api_key: VALUES.NOT_LOADED, + enabled: false + }, + google_vertex_ai_settings: { + api_key: VALUES.NOT_LOADED, + enabled: false + }, + grok_settings: { + api_key: VALUES.NOT_LOADED, + enabled: false + }, + groq_settings: { + api_key: VALUES.NOT_LOADED, + enabled: false + }, + ollama_settings: { + api_key: VALUES.NOT_LOADED, + enabled: false + }, + openai_settings: { + api_key: VALUES.NOT_LOADED, + enabled: false + }, + 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/app/app.effects.ts b/app/components/ui/src/app/store/app/app.effects.ts index feea23e..aea97fd 100644 --- a/app/components/ui/src/app/store/app/app.effects.ts +++ b/app/components/ui/src/app/store/app/app.effects.ts @@ -1,6 +1,5 @@ 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 "./app.actions"; import { IACEVersionData } from "../../models/app.models"; @@ -10,7 +9,6 @@ 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), 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..34c02ff --- /dev/null +++ b/app/components/ui/src/app/store/settings/settings.actions.ts @@ -0,0 +1,11 @@ +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 }>() + }, +}); 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..8134b10 --- /dev/null +++ b/app/components/ui/src/app/store/settings/settings.effects.ts @@ -0,0 +1,24 @@ +import { inject, Injectable } from "@angular/core"; +import { createEffect, ofType, Actions } from "@ngrx/effects"; +import { map, catchError, of, switchMap } from "rxjs"; +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 }))) + )) + )) + + 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..b3577c6 --- /dev/null +++ b/app/components/ui/src/app/store/settings/settings.selectors.ts @@ -0,0 +1,4 @@ +import { createFeatureSelector } from "@ngrx/store"; +import { SettingsState } from "../../state/settings.state"; + +export const selectSettingsState = createFeatureSelector("settings"); 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..337c6b8 100644 --- a/app/components/ui/src/styles.scss +++ b/app/components/ui/src/styles.scss @@ -1,4 +1,16 @@ /* 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 { + padding-left: 3rem; + padding-top: 1rem; +} + +.headings { + padding-left: 1rem; +} 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 100% rename from app/models/config/model_providers/indiividual_providers.py rename to app/models/config/model_providers/individual_providers.py 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/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): From 2d0c94b0c406d9b5d98b97ea0100741b5dee6a2f Mon Sep 17 00:00:00 2001 From: Jason Esterhuizen Date: Sat, 15 Feb 2025 22:33:56 +0200 Subject: [PATCH 03/15] [FEAT] Added model_provider store --- app/components/controller/api/__init__.py | 22 +++- app/components/controller/api/routes.py | 105 ------------------ .../controller/api/routes/__init__.py | 2 + .../controller/api/routes/model_provider.py | 43 +++++++ app/components/controller/api/routes/root.py | 63 +++++++++++ app/components/controller/api/service.py | 59 ---------- .../controller/api/services/__init__.py | 4 + .../controller/api/services/model_provider.py | 20 ++++ .../controller/api/services/root.py | 32 ++++++ app/components/ui/src/app/app.config.ts | 8 +- app/components/ui/src/app/constants.ts | 8 +- .../src/app/models/model-provider.models.ts | 13 +++ .../ui/src/app/models/settings.models.ts | 104 ----------------- .../app/pages/settings/settings.component.ts | 18 +-- .../ui/src/app/services/app.service.ts | 3 +- .../app/services/model-provider.service.ts | 29 +++++ .../ui/src/app/services/settings.service.ts | 3 +- .../ui/src/app/state/model-provider.state.ts | 22 ++++ .../ui/src/app/state/settings.state.ts | 22 ++-- .../model-provider/model-provider.actions.ts | 14 +++ .../model-provider/model-provider.effects.ts | 32 ++++++ .../model-provider/model-provider.reducers.ts | 52 +++++++++ .../model-provider.selectors.ts | 6 + app/constants/__init__.py | 1 + app/constants/api_routes.py | 9 ++ .../config/model_providers/model_providers.py | 2 +- 26 files changed, 402 insertions(+), 294 deletions(-) delete mode 100644 app/components/controller/api/routes.py create mode 100644 app/components/controller/api/routes/__init__.py create mode 100644 app/components/controller/api/routes/model_provider.py create mode 100644 app/components/controller/api/routes/root.py delete mode 100644 app/components/controller/api/service.py create mode 100644 app/components/controller/api/services/__init__.py create mode 100644 app/components/controller/api/services/model_provider.py create mode 100644 app/components/controller/api/services/root.py create mode 100644 app/components/ui/src/app/models/model-provider.models.ts create mode 100644 app/components/ui/src/app/services/model-provider.service.ts create mode 100644 app/components/ui/src/app/state/model-provider.state.ts create mode 100644 app/components/ui/src/app/store/model-provider/model-provider.actions.ts create mode 100644 app/components/ui/src/app/store/model-provider/model-provider.effects.ts create mode 100644 app/components/ui/src/app/store/model-provider/model-provider.reducers.ts create mode 100644 app/components/ui/src/app/store/model-provider/model-provider.selectors.ts create mode 100644 app/constants/api_routes.py 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.py b/app/components/controller/api/routes.py deleted file mode 100644 index af10eda..0000000 --- a/app/components/controller/api/routes.py +++ /dev/null @@ -1,105 +0,0 @@ -# DEPENDENCIES -## Third-Party -from fastapi import FastAPI, HTTPException -from fastapi.middleware.cors import CORSMiddleware -from http import HTTPStatus -from pydantic import ValidationError -## Local -from constants import Defaults, Names -from logger import logger -from models.api_schemas.controller import ( - GetVersionDetailsResponse, - GetSettingsResponse, EditSettingsRequest, - GetLLMModelsResponse -) -from models.api_schemas.defaults import DefaultAPIResponse -from . import service - - -controller_api = FastAPI() -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=["*"] -) - - -# ROUTES -@controller_api.get( - "/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() - except ValidationError as error: - logger.error(error) - raise HTTPException(status_code=HTTPStatus.INTERNAL_SERVER_ERROR, detail="Version 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( - "/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() - except ValidationError as error: - logger.error(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) - -@controller_api.post( - "/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()) - return DefaultAPIResponse(message="Settings data updated successfully!") - except ValidationError as error: - logger.error(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) - -@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" -) -async def get_llm_models_route() -> list[GetLLMModelsResponse]: - try: - return 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/__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/root.py b/app/components/controller/api/routes/root.py new file mode 100644 index 0000000..9d984c6 --- /dev/null +++ b/app/components/controller/api/routes/root.py @@ -0,0 +1,63 @@ +# 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 ( + GetVersionDetailsResponse, + GetSettingsResponse, EditSettingsRequest +) +from models.api_schemas.defaults import DefaultAPIResponse +from ..services import root_service + + +root = APIRouter() + +@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 root_service.get_version_data() + except ValidationError as error: + logger.error(error) + raise HTTPException(status_code=HTTPStatus.INTERNAL_SERVER_ERROR, detail="Version data error!") + except Exception as error: + logger.error(error) + raise HTTPException(status_code=HTTPStatus.INTERNAL_SERVER_ERROR, detail=Defaults.INTERNAL_SERVER_ERROR_MESSAGE) + +@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 root_service.get_settings_data() + except ValidationError as error: + logger.error(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) + +@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: + 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) + 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) 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..3559eec --- /dev/null +++ b/app/components/controller/api/services/root.py @@ -0,0 +1,32 @@ +# 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_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)) diff --git a/app/components/ui/src/app/app.config.ts b/app/components/ui/src/app/app.config.ts index f0882cb..5d78379 100644 --- a/app/components/ui/src/app/app.config.ts +++ b/app/components/ui/src/app/app.config.ts @@ -10,8 +10,10 @@ import { provideStoreDevtools } from '@ngrx/store-devtools'; import { routes } from './app.routes'; import { provideAnimationsAsync } from '@angular/platform-browser/animations/async'; import { AppEffects } from './store/app/app.effects'; -import { appReducer } from './store/app/app.reducers'; +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'; @@ -19,9 +21,9 @@ import { settingsReducer } from './store/settings/settings.reducers'; export const appConfig: ApplicationConfig = { providers: [ provideAnimationsAsync(), - provideEffects([AppEffects, SettingsEffects]), + provideEffects([AppEffects, modelProviderEffects, SettingsEffects]), provideHttpClient(), - provideStore({ app_data: appReducer, settings: settingsReducer }), + 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 index 5a54c5e..513b485 100644 --- a/app/components/ui/src/app/constants.ts +++ b/app/components/ui/src/app/constants.ts @@ -1,3 +1,9 @@ -export const VALUES = { +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/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 index c53a7d4..3542a30 100644 --- a/app/components/ui/src/app/models/settings.models.ts +++ b/app/components/ui/src/app/models/settings.models.ts @@ -1,107 +1,3 @@ -// { -// "ace_name": "PrototypeACE", -// "layer_settings": [ -// { -// "layer_name": "aspirational", -// "model_type": "reasoner" -// }, -// { -// "layer_name": "global_strategy", -// "model_type": "reasoner" -// }, -// { -// "layer_name": "agent_model", -// "model_type": "generalist" -// }, -// { -// "layer_name": "executive_function", -// "model_type": "generalist" -// }, -// { -// "layer_name": "cognitive_control", -// "model_type": "efficient" -// }, -// { -// "layer_name": "task_prosecution", -// "model_type": "function_caller" -// } -// ], -// "model_provider_settings": { -// "claude_settings": { -// "enabled": false, -// "api_key": "" -// }, -// "deepseek_settings": { -// "enabled": false, -// "api_key": "" -// }, -// "google_vertex_ai_settings": { -// "enabled": false, -// "api_key": "" -// }, -// "grok_settings": { -// "enabled": false, -// "api_key": "" -// }, -// "groq_settings": { -// "enabled": false, -// "api_key": "" -// }, -// "ollama_settings": { -// "enabled": true, -// "api_key": "" -// }, -// "openai_settings": { -// "enabled": false, -// "api_key": "" -// }, -// "three_d_model_type_settings": [], -// "audio_model_type_settings": [], -// "image_model_type_settings": [], -// "llm_model_type_settings": [ -// { -// "model_type": "coder", -// "model_id": "0194c745-de29-7bc6-ad6b-f7b5f8d0e414", -// "logical_temperature": 0.2, -// "creative_temperature": 0.7, -// "output_token_limit": 2048 -// }, -// { -// "model_type": "efficient", -// "model_id": "0194c74b-ff7d-7c91-a7b2-934a16aafb04", -// "logical_temperature": 0.2, -// "creative_temperature": 0.7, -// "output_token_limit": 2048 -// }, -// { -// "model_type": "function_caller", -// "model_id": "0194c751-721a-7470-a277-e76ccdb840b8", -// "logical_temperature": 0.2, -// "creative_temperature": 0.7, -// "output_token_limit": 2048 -// }, -// { -// "model_type": "generalist", -// "model_id": "0194c751-721a-7470-a277-e76ccdb840b8", -// "logical_temperature": 0.2, -// "creative_temperature": 0.7, -// "output_token_limit": 2048 -// }, -// { -// "model_type": "reasoner", -// "model_id": "0194c74e-13ea-72d5-9b2d-a46dfa196784", -// "logical_temperature": 0.2, -// "creative_temperature": 0.7, -// "output_token_limit": 2048 -// } -// ], -// "multimodal_model_type_settings": [], -// "rag_model_type_settings": [], -// "robotics_model_type_settings": [], -// "video_model_type_settings": [] -// } -// } - // SECTIONS //// Layers export interface ILayerSetting { 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 639c39f..4a89983 100644 --- a/app/components/ui/src/app/pages/settings/settings.component.ts +++ b/app/components/ui/src/app/pages/settings/settings.component.ts @@ -11,10 +11,12 @@ import {MatInputModule} from '@angular/material/input'; import { MatListModule } from "@angular/material/list"; import {MatSelectModule} from '@angular/material/select'; //// Local +import { ILLMModelProvider } from "../../models/model-provider.models"; import { ISettings } from "../../models/settings.models"; +import { modelProviderActions } from "../../store/model-provider/model-provider.actions"; +import { selectLLMModels, selectLLMModelTypes } from "../../store/model-provider/model-provider.selectors"; import { settingsActions } from "../../store/settings/settings.actions"; import { selectSettingsState } from "../../store/settings/settings.selectors"; -import { SettingsState } from "../../state/settings.state"; @Component({ selector: "page-settings", @@ -23,17 +25,19 @@ import { SettingsState } from "../../state/settings.state"; styleUrl: "./settings.component.scss" }) export class SettingsComponent implements OnInit { - settings$: Observable; - + llmModels: ILLMModelProvider[] = []; + llmModelTypes: string[] = []; settings!: ISettings; - constructor(private store: Store) { - this.settings$ = this.store.select(selectSettingsState); - } + constructor(private store: Store) {} ngOnInit(): void { this.store.dispatch(settingsActions.getSettings()); - this.settings$.subscribe( settings => this.settings = settings.settings); + this.store.select(selectSettingsState).subscribe( settings => this.settings = settings.settings); + this.store.dispatch(modelProviderActions.getLLMModels()); + this.store.select(selectLLMModels).subscribe(llmModels => this.llmModels = llmModels); + this.store.dispatch(modelProviderActions.getLLMModelTypes()); + this.store.select(selectLLMModelTypes).subscribe(llmModelTypes => this.llmModelTypes = llmModelTypes); } formatLayerNames(layerName: string): string { diff --git a/app/components/ui/src/app/services/app.service.ts b/app/components/ui/src/app/services/app.service.ts index 9715bad..7bba5ed 100644 --- a/app/components/ui/src/app/services/app.service.ts +++ b/app/components/ui/src/app/services/app.service.ts @@ -1,6 +1,7 @@ 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"; @@ -8,7 +9,7 @@ export type HttpListResponseFailure = { status: string, message: string }; const endpoints = { - getACEVersionData: `${environmentURLs.controller}/version`, + getACEVersionData: `${environmentURLs.controller}${APIRoutes.ROOT}version`, }; 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 index c1f7ee8..db37d43 100644 --- a/app/components/ui/src/app/services/settings.service.ts +++ b/app/components/ui/src/app/services/settings.service.ts @@ -1,6 +1,7 @@ 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"; @@ -8,7 +9,7 @@ export type HttpListResponseFailure = { status: string, message: string }; const endpoints = { - getSettings: `${environmentURLs.controller}/settings`, + getSettings: `${environmentURLs.controller}${APIRoutes.ROOT}settings`, }; 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 index 45c1c82..e0fa146 100644 --- a/app/components/ui/src/app/state/settings.state.ts +++ b/app/components/ui/src/app/state/settings.state.ts @@ -1,5 +1,5 @@ import { createDefaultLoadable, Loadable } from "./loadable.state"; -import { VALUES } from '../constants' +import { Values } from '../constants' import { ISettings } from "../models/settings.models"; export interface SettingsState extends Loadable { @@ -10,35 +10,35 @@ export function createInitialSettingsState(): SettingsState { return { ...createDefaultLoadable(), settings: { - ace_name: VALUES.NOT_LOADED, + ace_name: Values.NOT_LOADED, layer_settings: [], model_provider_settings: { claude_settings: { - api_key: VALUES.NOT_LOADED, + api_key: Values.NOT_LOADED, enabled: false }, deepseek_settings: { - api_key: VALUES.NOT_LOADED, + api_key: Values.NOT_LOADED, enabled: false }, google_vertex_ai_settings: { - api_key: VALUES.NOT_LOADED, + api_key: Values.NOT_LOADED, enabled: false }, grok_settings: { - api_key: VALUES.NOT_LOADED, + api_key: Values.NOT_LOADED, enabled: false }, groq_settings: { - api_key: VALUES.NOT_LOADED, + api_key: Values.NOT_LOADED, enabled: false }, ollama_settings: { - api_key: VALUES.NOT_LOADED, + api_key: Values.NOT_LOADED, enabled: false }, openai_settings: { - api_key: VALUES.NOT_LOADED, + api_key: Values.NOT_LOADED, enabled: false }, three_d_model_type_settings: [], @@ -46,8 +46,8 @@ export function createInitialSettingsState(): SettingsState { image_model_type_settings: [], llm_model_type_settings: [ { - model_type: VALUES.NOT_LOADED, - model_id: VALUES.NOT_LOADED, + model_type: Values.NOT_LOADED, + model_id: Values.NOT_LOADED, logical_temperature: 0, creative_temperature: 0, output_token_limit: 0 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/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/models/config/model_providers/model_providers.py b/app/models/config/model_providers/model_providers.py index d35fe33..8e8f437 100644 --- a/app/models/config/model_providers/model_providers.py +++ b/app/models/config/model_providers/model_providers.py @@ -2,7 +2,7 @@ ## Third-Party from pydantic import BaseModel ## Local -from .indiividual_providers import IndividualProviderSettings +from .individual_providers import IndividualProviderSettings from . import model_types From 858ba186b237a329e912421fec49ff9a92a249fd Mon Sep 17 00:00:00 2001 From: Jason Esterhuizen Date: Sun, 16 Feb 2025 13:08:56 +0200 Subject: [PATCH 04/15] [FEAT] Got base design and data population working --- app/components/ui/src/app/app.component.html | 10 +- app/components/ui/src/app/app.component.ts | 16 +- .../ui/src/app/models/settings.models.ts | 6 + .../pages/settings/settings.component.html | 82 +++++++---- .../pages/settings/settings.component.scss | 9 +- .../app/pages/settings/settings.component.ts | 138 +++++++++++++++--- .../ui/src/app/state/settings.state.ts | 3 + .../app/store/settings/settings.selectors.ts | 3 +- app/components/ui/src/styles.scss | 12 +- app/models/config/controller.py | 4 +- app/models/config/defaults.py | 7 + app/models/config/ui.py | 7 + 12 files changed, 231 insertions(+), 66 deletions(-) create mode 100644 app/models/config/ui.py diff --git a/app/components/ui/src/app/app.component.html b/app/components/ui/src/app/app.component.html index c42a7a3..298466f 100644 --- a/app/components/ui/src/app/app.component.html +++ b/app/components/ui/src/app/app.component.html @@ -21,8 +21,10 @@ -
-

Developed by JayFalls

-

Version: v{{ version }}

-
+ @if ( uiSettings?.show_footer ) { +
+

Developed by Jayfalls

+

Version: v{{ version }}

+
+ } diff --git a/app/components/ui/src/app/app.component.ts b/app/components/ui/src/app/app.component.ts index 206303f..d2b94c0 100644 --- a/app/components/ui/src/app/app.component.ts +++ b/app/components/ui/src/app/app.component.ts @@ -8,11 +8,12 @@ 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 { IUISettings } from "./models/settings.models"; import { appActions } from "./store/app/app.actions"; +import { settingsActions } from "./store/settings/settings.actions"; import { selectAppState } from './store/app/app.selectors'; -import { AppState } from './state/app.state'; +import { selectUISettingsState } from "./store/settings/settings.selectors"; // TYPES @@ -68,16 +69,17 @@ export class AppComponent implements OnInit { route: "settings" } ]) - versionData$: Observable; + + uiSettings?: IUISettings; version: string = "0"; // 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.select(selectAppState).subscribe( versionData => this.version = versionData.versionData.version); + this.store.dispatch(settingsActions.getSettings()); + this.store.select(selectUISettingsState).subscribe( ui_settings => this.uiSettings = ui_settings ); } } diff --git a/app/components/ui/src/app/models/settings.models.ts b/app/components/ui/src/app/models/settings.models.ts index 3542a30..19aff30 100644 --- a/app/components/ui/src/app/models/settings.models.ts +++ b/app/components/ui/src/app/models/settings.models.ts @@ -1,4 +1,9 @@ // SECTIONS +//// UI +export interface IUISettings { + show_footer: boolean; +} + //// Layers export interface ILayerSetting { layer_name: string; @@ -45,6 +50,7 @@ export interface IModelProviderSetting { // 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/settings/settings.component.html b/app/components/ui/src/app/pages/settings/settings.component.html index 75d01c8..2868e06 100644 --- a/app/components/ui/src/app/pages/settings/settings.component.html +++ b/app/components/ui/src/app/pages/settings/settings.component.html @@ -1,31 +1,61 @@

Settings

-
-
-

Ace Name:

-

{{ settings.ace_name }}

-
- -
-

Layer Settings:

- -
    - @for (layer_setting of settings.layer_settings; track layer_setting.layer_name) { - - -
    - Layer: {{ formatLayerNames(layer_setting.layer_name) }} - - Model Type: - {{ layer_setting.model_type | titlecase }} - -
    -
    -
    + @if (settingsForm) { +
    +

    General:

    +
    + + Ace Name + + @if (aceNameControl?.hasError("required")) { + Please enter a name for the ace } -
-
-
-
+ @if (aceNameControl?.hasError("maxlength")) { + Max length of 32 characters + } + +
+ + + +

UI:

+
+ Show Footer +
+ + + +

ACE Layers:

+
+ @for (layerSetting of layerSettingsControl.controls; track layer_setting; let layer_setting = $index) { +
+

{{ formatLayerNames(layerSetting.value?.layer_name || "") }} Layer:

+ + Model Type + + @for (type of llmModelTypes; track type) { + {{ type | titlecase }} + } + + +
+ } +
+ + + +

Model Provider:

+
+
+ + + + } 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 f21bf09..62facf6 100644 --- a/app/components/ui/src/app/pages/settings/settings.component.scss +++ b/app/components/ui/src/app/pages/settings/settings.component.scss @@ -1,6 +1,5 @@ -.ace-name { - display: flex; - flex-direction: row; - align-items: baseline; - gap: 1rem; +.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 4a89983..152f141 100644 --- a/app/components/ui/src/app/pages/settings/settings.component.ts +++ b/app/components/ui/src/app/pages/settings/settings.component.ts @@ -3,47 +3,145 @@ import { TitleCasePipe } from "@angular/common"; import { Component, OnInit } from "@angular/core"; import { Store } from "@ngrx/store"; -import { Observable } from "rxjs"; -import {FormsModule} from '@angular/forms'; -import { MatCardModule } from "@angular/material/card"; -import {MatFormFieldModule} from '@angular/material/form-field'; -import {MatInputModule} from '@angular/material/input'; -import { MatListModule } from "@angular/material/list"; -import {MatSelectModule} from '@angular/material/select'; +import { filter, take } from "rxjs"; +import { FormArray, FormBuilder, FormGroup, ReactiveFormsModule, Validators } from "@angular/forms"; +import { MatButtonModule } from "@angular/material/button"; +import { MatCheckboxModule } from "@angular/material/checkbox"; +import { MatDividerModule } from "@angular/material/divider"; +import { MatIconModule } from "@angular/material/icon"; +import { MatFormFieldModule } from "@angular/material/form-field"; +import { MatInputModule } from "@angular/material/input"; +import { MatSelectModule } from "@angular/material/select"; //// Local import { ILLMModelProvider } from "../../models/model-provider.models"; import { ISettings } from "../../models/settings.models"; import { modelProviderActions } from "../../store/model-provider/model-provider.actions"; -import { selectLLMModels, selectLLMModelTypes } from "../../store/model-provider/model-provider.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: [FormsModule, MatCardModule, MatFormFieldModule, MatInputModule, MatListModule, MatSelectModule, TitleCasePipe], + imports: [ + ReactiveFormsModule, + MatButtonModule, + MatCheckboxModule, + MatDividerModule, + MatIconModule, + MatFormFieldModule, + MatInputModule, + MatSelectModule, + TitleCasePipe + ], templateUrl: "./settings.component.html", styleUrl: "./settings.component.scss" }) export class SettingsComponent implements OnInit { llmModels: ILLMModelProvider[] = []; llmModelTypes: string[] = []; + selectedLLMModelType: string = ""; settings!: ISettings; + settingsForm!: FormGroup; - constructor(private store: Store) {} + changesDetected: boolean = false; + + constructor( + private formBuilder: FormBuilder, + private store: Store + ) { + this.initialiseForm(); + } ngOnInit(): void { - this.store.dispatch(settingsActions.getSettings()); - this.store.select(selectSettingsState).subscribe( settings => this.settings = settings.settings); - this.store.dispatch(modelProviderActions.getLLMModels()); - this.store.select(selectLLMModels).subscribe(llmModels => this.llmModels = llmModels); this.store.dispatch(modelProviderActions.getLLMModelTypes()); - this.store.select(selectLLMModelTypes).subscribe(llmModelTypes => this.llmModelTypes = llmModelTypes); + 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.settingsForm = this.formBuilder.group({ + ace_name: ["", [Validators.required, Validators.maxLength(32)]], + ui_settings: this.formBuilder.group({ + show_footer: [true] + }), + layer_settings: this.formBuilder.array([]) + }); + + this.settingsForm.valueChanges.subscribe(() => { + this.changesDetected = true; + }) + } + + private createLayerSettingGroup(layer: any): FormGroup { + return this.formBuilder.group({ + layer_name: [layer?.layer_name || "", Validators.required], + model_type: [layer?.model_type || "", Validators.required] + }); + } + + private patchFormValues(): void { + if (!this.settings) return; + + this.settingsForm.patchValue({ + ace_name: this.settings.ace_name, + ui_settings: { + show_footer: this.settings.ui_settings.show_footer + } + }); + + const layerArray = this.layerSettingsControl; + layerArray.clear(); + + this.settings.layer_settings.forEach(layer => { + layerArray.push(this.createLayerSettingGroup(layer)); + }); + + this.settingsForm.markAsPristine(); + this.changesDetected = false; + } + + formatLayerNames(layerName?: string): string { + return (layerName || "") + .split("_") + .map(word => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase()) + .join(" "); + } + + 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.settingsForm.get("ace_name"); + } + + get uiSettingsControl() { + return this.settingsForm.get("ui_settings"); } - formatLayerNames(layerName: string): string { - return layerName - .split('_') - .map(word => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase()) - .join(' '); + get layerSettingsControl(): FormArray { + return this.settingsForm.get("layer_settings") as FormArray; } } diff --git a/app/components/ui/src/app/state/settings.state.ts b/app/components/ui/src/app/state/settings.state.ts index e0fa146..461c4c8 100644 --- a/app/components/ui/src/app/state/settings.state.ts +++ b/app/components/ui/src/app/state/settings.state.ts @@ -11,6 +11,9 @@ export function createInitialSettingsState(): SettingsState { ...createDefaultLoadable(), settings: { ace_name: Values.NOT_LOADED, + ui_settings: { + show_footer: true + }, layer_settings: [], model_provider_settings: { claude_settings: { diff --git a/app/components/ui/src/app/store/settings/settings.selectors.ts b/app/components/ui/src/app/store/settings/settings.selectors.ts index b3577c6..d963668 100644 --- a/app/components/ui/src/app/store/settings/settings.selectors.ts +++ b/app/components/ui/src/app/store/settings/settings.selectors.ts @@ -1,4 +1,5 @@ -import { createFeatureSelector } from "@ngrx/store"; +import { createFeatureSelector, createSelector } from "@ngrx/store"; import { SettingsState } from "../../state/settings.state"; export const selectSettingsState = createFeatureSelector("settings"); +export const selectUISettingsState = createSelector(selectSettingsState, (state: SettingsState) => state.settings.ui_settings); diff --git a/app/components/ui/src/styles.scss b/app/components/ui/src/styles.scss index 337c6b8..b059614 100644 --- a/app/components/ui/src/styles.scss +++ b/app/components/ui/src/styles.scss @@ -7,10 +7,18 @@ html, body { body { margin: 0; font-family: Roboto, "Helvetica Neue", sans-serif; } .main-page { - padding-left: 3rem; + position: relative; padding-top: 1rem; + padding-left: 3rem; + padding-right: 3rem; + padding-bottom: 1rem; } -.headings { +.subsection { padding-left: 1rem; + padding-right: 1rem; + + .subsection-divider { + margin-top: 1rem; + } } diff --git a/app/models/config/controller.py b/app/models/config/controller.py index 2845931..c29e53a 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 + ui_settings: UISettings = DEFAULT_UI_SETTINGS layer_settings: list[LayerSettings] = DEFAULT_LAYER_SETTINGS model_provider_settings: ModelProviderSettings = DEFAULT_MODEL_PROVIDER_SETTINGS diff --git a/app/models/config/defaults.py b/app/models/config/defaults.py index 071b3a7..58d7b7f 100644 --- a/app/models/config/defaults.py +++ b/app/models/config/defaults.py @@ -7,6 +7,13 @@ ) from .layers import LayerSettings from .model_providers import ModelProviderSettings, model_types +from .ui import UISettings + + +# UI +DEFAULT_UI_SETTINGS: UISettings = UISettings( + show_footer=True +) # LAYERS diff --git a/app/models/config/ui.py b/app/models/config/ui.py new file mode 100644 index 0000000..1682131 --- /dev/null +++ b/app/models/config/ui.py @@ -0,0 +1,7 @@ +# DEPENDENCIES +## Third-Party +from pydantic import BaseModel + + +class UISettings(BaseModel): + show_footer: bool From 12600555f8618199e6cc9b1c10068c4747fda229 Mon Sep 17 00:00:00 2001 From: Jason Esterhuizen Date: Sun, 16 Feb 2025 14:12:54 +0200 Subject: [PATCH 05/15] [FEAT] Added edit and defaults endpoints --- app/components/controller/api/routes/root.py | 18 ++++- .../controller/api/services/root.py | 6 +- .../pages/settings/settings.component.html | 10 +++ .../app/pages/settings/settings.component.ts | 69 ++++++++++++++++--- .../ui/src/app/services/settings.service.ts | 16 ++++- .../app/store/settings/settings.actions.ts | 8 ++- .../app/store/settings/settings.effects.ts | 29 ++++++++ app/constants/model_providers.py | 2 +- app/models/api_schemas/controller.py | 4 ++ .../model_providers/individual_providers.py | 1 + .../config/model_providers/model_providers.py | 45 ++++++++++-- app/models/data/initial.py | 4 +- app/version | 3 + 13 files changed, 190 insertions(+), 25 deletions(-) diff --git a/app/components/controller/api/routes/root.py b/app/components/controller/api/routes/root.py index 9d984c6..c1e3e33 100644 --- a/app/components/controller/api/routes/root.py +++ b/app/components/controller/api/routes/root.py @@ -23,7 +23,7 @@ ) async def get_version_route() -> dict: try: - return root_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!") @@ -61,3 +61,19 @@ async def set_settings_route(updated_settings: EditSettingsRequest) -> dict: except Exception as error: logger.error(error) raise HTTPException(status_code=HTTPStatus.INTERNAL_SERVER_ERROR, detail=Defaults.INTERNAL_SERVER_ERROR_MESSAGE) + +@root.delete( + f"{APIRoutes.ROOT}settings", + response_model=DefaultAPIResponse, + description=f"Delete the {Names.ACE} controller settings data" +) +async def delete_settings_route() -> dict: + try: + 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="Settings data error!") + except Exception as error: + logger.error(error) + 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/services/root.py b/app/components/controller/api/services/root.py index 3559eec..079be34 100644 --- a/app/components/controller/api/services/root.py +++ b/app/components/controller/api/services/root.py @@ -18,7 +18,7 @@ def _get_settings() -> dict: # ROOT -def get_version_data() -> dict: +def get_version() -> dict: with open(Files.VERSION, "r", encoding="utf-8") as settings_file: return json.loads(settings_file.read()) @@ -30,3 +30,7 @@ def edit_settings_data(updated_settings: dict): 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/src/app/pages/settings/settings.component.html b/app/components/ui/src/app/pages/settings/settings.component.html index 2868e06..9ea73a1 100644 --- a/app/components/ui/src/app/pages/settings/settings.component.html +++ b/app/components/ui/src/app/pages/settings/settings.component.html @@ -47,6 +47,16 @@

{{ formatLayerNames(layerSetting.value?.layer_name || "") }} Layer:

Model Provider:

+ +
+ + + +

About:

+
+

Developed by Jayfalls

+

+
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 152f141..9fcc942 100644 --- a/app/components/ui/src/app/pages/settings/settings.component.ts +++ b/app/components/ui/src/app/pages/settings/settings.component.ts @@ -14,7 +14,7 @@ import { MatInputModule } from "@angular/material/input"; import { MatSelectModule } from "@angular/material/select"; //// Local import { ILLMModelProvider } from "../../models/model-provider.models"; -import { ISettings } from "../../models/settings.models"; +import { IModelProviderSetting, ISettings } from "../../models/settings.models"; import { modelProviderActions } from "../../store/model-provider/model-provider.actions"; import { selectLLMModelTypes } from "../../store/model-provider/model-provider.selectors"; import { settingsActions } from "../../store/settings/settings.actions"; @@ -37,6 +37,11 @@ import { selectSettingsState } from "../../store/settings/settings.selectors"; styleUrl: "./settings.component.scss" }) export class SettingsComponent implements OnInit { + private defaultIndividualModelProviderSettings = { + enabled: false, + api_key: "" + } + llmModels: ILLMModelProvider[] = []; llmModelTypes: string[] = []; selectedLLMModelType: string = ""; @@ -75,7 +80,21 @@ export class SettingsComponent implements OnInit { ui_settings: this.formBuilder.group({ show_footer: [true] }), - layer_settings: this.formBuilder.array([]) + layer_settings: this.formBuilder.array([]), + model_provider_settings: this.formBuilder.group({ + claude_settings: this.formBuilder.group(this.defaultIndividualModelProviderSettings), + deepseek_settings: this.formBuilder.group(this.defaultIndividualModelProviderSettings), + google_vertex_ai_settings: this.formBuilder.group(this.defaultIndividualModelProviderSettings), + grok_settings: this.formBuilder.group(this.defaultIndividualModelProviderSettings), + groq_settings: this.formBuilder.group(this.defaultIndividualModelProviderSettings), + ollama_settings: this.formBuilder.group(this.defaultIndividualModelProviderSettings), + openai_settings: this.formBuilder.group(this.defaultIndividualModelProviderSettings), + 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.valueChanges.subscribe(() => { @@ -83,28 +102,52 @@ export class SettingsComponent implements OnInit { }) } - private createLayerSettingGroup(layer: any): FormGroup { - return this.formBuilder.group({ - layer_name: [layer?.layer_name || "", Validators.required], - model_type: [layer?.model_type || "", Validators.required] - }); - } - private patchFormValues(): void { if (!this.settings) return; + // General this.settingsForm.patchValue({ ace_name: this.settings.ace_name, + }); + + // UI + this.settingsForm.patchValue({ ui_settings: { show_footer: this.settings.ui_settings.show_footer } }); + // Layers const layerArray = this.layerSettingsControl; layerArray.clear(); this.settings.layer_settings.forEach(layer => { - layerArray.push(this.createLayerSettingGroup(layer)); + layerArray.push( + this.formBuilder.group({ + layer_name: [layer?.layer_name || "", Validators.required], + model_type: [layer?.model_type || "", Validators.required] + }) + ); + }); + + + // Model Provider + const modelProviderSettings: IModelProviderSetting = this.settings.model_provider_settings; + this.settingsForm.patchValue({ + model_provider_settings: { + claude_settings: modelProviderSettings.claude_settings || this.defaultIndividualModelProviderSettings, + deepseek_settings: modelProviderSettings.deepseek_settings || this.defaultIndividualModelProviderSettings, + google_vertex_ai_settings: modelProviderSettings.google_vertex_ai_settings || this.defaultIndividualModelProviderSettings, + grok_settings: modelProviderSettings.grok_settings || this.defaultIndividualModelProviderSettings, + groq_settings: modelProviderSettings.groq_settings || this.defaultIndividualModelProviderSettings, + ollama_settings: modelProviderSettings.ollama_settings || this.defaultIndividualModelProviderSettings, + openai_settings: modelProviderSettings.openai_settings || this.defaultIndividualModelProviderSettings, + three_d_model_type_settings: modelProviderSettings.three_d_model_type_settings || [], + audio_model_type_settings: modelProviderSettings.audio_model_type_settings || [], + image_model_type_settings: modelProviderSettings.image_model_type_settings || [], + llm_model_type_settings: modelProviderSettings.llm_model_type_settings || [], + rag_model_type_settings: modelProviderSettings.rag_model_type_settings || [] + } }); this.settingsForm.markAsPristine(); @@ -118,6 +161,10 @@ export class SettingsComponent implements OnInit { .join(" "); } + onReset() { + this.store.dispatch(settingsActions.resetSettings()); + } + onSave() { if (!this.settingsForm.valid) { return; @@ -130,7 +177,7 @@ export class SettingsComponent implements OnInit { this.changesDetected = false; this.settingsForm.markAsPristine(); - // this.store.dispatch(settingsActions.editSettings({ settings: updatedSettings })); + this.store.dispatch(settingsActions.editSettings({ settings: updatedSettings })); } get aceNameControl() { diff --git a/app/components/ui/src/app/services/settings.service.ts b/app/components/ui/src/app/services/settings.service.ts index db37d43..9b5ef03 100644 --- a/app/components/ui/src/app/services/settings.service.ts +++ b/app/components/ui/src/app/services/settings.service.ts @@ -1,8 +1,12 @@ +// 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 }; @@ -10,6 +14,8 @@ 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` }; @@ -20,6 +26,14 @@ export class SettingsService { constructor(private http: HttpClient) { } getSettings(): Observable { - return this.http.get(endpoints.getSettings); + 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/store/settings/settings.actions.ts b/app/components/ui/src/app/store/settings/settings.actions.ts index 34c02ff..bb61a90 100644 --- a/app/components/ui/src/app/store/settings/settings.actions.ts +++ b/app/components/ui/src/app/store/settings/settings.actions.ts @@ -6,6 +6,12 @@ export const settingsActions = createActionGroup({ events: { getSettings: emptyProps(), getSettingsSuccess: props<{ settings: ISettings }>(), - getSettingsFailure: props<{ error: Error }>() + 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 index 8134b10..f1faf3d 100644 --- a/app/components/ui/src/app/store/settings/settings.effects.ts +++ b/app/components/ui/src/app/store/settings/settings.effects.ts @@ -1,6 +1,9 @@ +// 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"; @@ -18,6 +21,32 @@ export class SettingsEffects { )) )) + 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/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/models/api_schemas/controller.py b/app/models/api_schemas/controller.py index 4e6004c..5f33551 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 + author: str + license: str + last_update: str + rebuild_date: str GetSettingsResponse: type[BaseModel] = ControllerSettingsSchema diff --git a/app/models/config/model_providers/individual_providers.py b/app/models/config/model_providers/individual_providers.py index 08b18a0..0b9163a 100644 --- a/app/models/config/model_providers/individual_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 8e8f437..4fc6e04 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 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=False, + 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/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/version b/app/version index f4db7ab..292137d 100644 --- a/app/version +++ b/app/version @@ -1,4 +1,7 @@ { "version": "0.0.0", + "author": "Jayfalls", + "license": "MIT", + "last_update": "1997-01-01", "rebuild_date": "2025-02-02" } \ No newline at end of file From 6539288047118a616461374b0ec762121c4b1f69 Mon Sep 17 00:00:00 2001 From: Jason Esterhuizen Date: Sun, 16 Feb 2025 14:15:16 +0200 Subject: [PATCH 06/15] [TEST] Testing precommit --- app/version | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/app/version b/app/version index 292137d..057a1ab 100644 --- a/app/version +++ b/app/version @@ -1,7 +1,7 @@ { - "version": "0.0.0", - "author": "Jayfalls", - "license": "MIT", - "last_update": "1997-01-01", - "rebuild_date": "2025-02-02" -} \ No newline at end of file + "version": "0.0.0", + "author": "Jayfalls", + "license": "MIT", + "last_update": "2025-02-16", + "rebuild_date": "2025-02-02" +} From 5e74509ba0fc221cc60e8f600a46e254e9105e3a Mon Sep 17 00:00:00 2001 From: Jason Esterhuizen Date: Sun, 16 Feb 2025 14:52:16 +0200 Subject: [PATCH 07/15] [UPDATE] Added extra version data --- README.md | 2 + app/components/ui/src/app/app.component.html | 4 +- app/components/ui/src/app/app.component.ts | 9 +-- .../ui/src/app/models/app.models.ts | 6 +- .../pages/settings/settings.component.html | 22 ++++--- .../app/pages/settings/settings.component.ts | 4 ++ .../ui/src/app/services/app.service.ts | 6 +- app/components/ui/src/app/state/app.state.ts | 12 ++-- .../ui/src/app/store/app/app.actions.ts | 8 +-- .../ui/src/app/store/app/app.effects.ts | 10 ++-- .../ui/src/app/store/app/app.reducers.ts | 6 +- .../ui/src/app/store/app/app.selectors.ts | 2 +- app/models/api_schemas/controller.py | 2 +- app/models/config/controller.py | 2 +- app/models/config/defaults.py | 58 +++++++++---------- app/version | 6 +- 16 files changed, 90 insertions(+), 69 deletions(-) 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/app/components/ui/src/app/app.component.html b/app/components/ui/src/app/app.component.html index 298466f..09adc7e 100644 --- a/app/components/ui/src/app/app.component.html +++ b/app/components/ui/src/app/app.component.html @@ -23,8 +23,8 @@ @if ( uiSettings?.show_footer ) {
-

Developed by Jayfalls

-

Version: v{{ version }}

+

Version: v{{ appVersionData?.version }}

+

Last Update: {{ appVersionData?.last_update }}

} diff --git a/app/components/ui/src/app/app.component.ts b/app/components/ui/src/app/app.component.ts index d2b94c0..b620b7e 100644 --- a/app/components/ui/src/app/app.component.ts +++ b/app/components/ui/src/app/app.component.ts @@ -9,10 +9,11 @@ import { MatSidenavModule } from "@angular/material/sidenav"; import { RouterModule, RouterOutlet } from "@angular/router"; import { Store } from "@ngrx/store"; //// Local +import { IAppVersionData } from "./models/app.models"; import { IUISettings } from "./models/settings.models"; import { appActions } from "./store/app/app.actions"; import { settingsActions } from "./store/settings/settings.actions"; -import { selectAppState } from './store/app/app.selectors'; +import { selectAppVersionDataState } from './store/app/app.selectors'; import { selectUISettingsState } from "./store/settings/settings.selectors"; @@ -71,14 +72,14 @@ export class AppComponent implements OnInit { ]) uiSettings?: IUISettings; - version: string = "0"; + appVersionData?: IAppVersionData; // Initialisation constructor(private store: Store) {} ngOnInit(): void { - this.store.dispatch(appActions.getACEVersionData()); - this.store.select(selectAppState).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(selectUISettingsState).subscribe( ui_settings => this.uiSettings = ui_settings ); } 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/pages/settings/settings.component.html b/app/components/ui/src/app/pages/settings/settings.component.html index 9ea73a1..f58494f 100644 --- a/app/components/ui/src/app/pages/settings/settings.component.html +++ b/app/components/ui/src/app/pages/settings/settings.component.html @@ -26,6 +26,13 @@

UI:

+

Model Provider:

+
+ +
+ + +

ACE Layers:

@for (layerSetting of layerSettingsControl.controls; track layer_setting; let layer_setting = $index) { @@ -45,18 +52,15 @@

{{ formatLayerNames(layerSetting.value?.layer_name || "") }} Layer:

-

Model Provider:

-
- -
- - -

About:

-

Developed by Jayfalls

+

Developed by {{ appVersionData.authors.join(", ") }}

+

Version: v{{ appVersionData.version }}

+

License: {{ appVersionData.license }}

+

Last Update: {{ appVersionData.last_update }}

+

Last Container Image Update: {{ appVersionData.rebuild_date }}

- +
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 9fcc942..591f8c5 100644 --- a/app/components/ui/src/app/pages/settings/settings.component.ts +++ b/app/components/ui/src/app/pages/settings/settings.component.ts @@ -13,9 +13,11 @@ import { MatFormFieldModule } from "@angular/material/form-field"; import { MatInputModule } from "@angular/material/input"; import { MatSelectModule } from "@angular/material/select"; //// 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"; @@ -42,6 +44,7 @@ export class SettingsComponent implements OnInit { api_key: "" } + appVersionData!: IAppVersionData; llmModels: ILLMModelProvider[] = []; llmModelTypes: string[] = []; selectedLLMModelType: string = ""; @@ -58,6 +61,7 @@ export class SettingsComponent implements OnInit { } ngOnInit(): void { + this.store.select(selectAppVersionDataState).subscribe( version_data => this.appVersionData = version_data ); this.store.dispatch(modelProviderActions.getLLMModelTypes()); this.store.select(selectLLMModelTypes).pipe( filter((model_types: string[]): model_types is string[] => model_types.length > 0), diff --git a/app/components/ui/src/app/services/app.service.ts b/app/components/ui/src/app/services/app.service.ts index 7bba5ed..978589f 100644 --- a/app/components/ui/src/app/services/app.service.ts +++ b/app/components/ui/src/app/services/app.service.ts @@ -9,7 +9,7 @@ export type HttpListResponseFailure = { status: string, message: string }; const endpoints = { - getACEVersionData: `${environmentURLs.controller}${APIRoutes.ROOT}version`, + getAppVersionData: `${environmentURLs.controller}${APIRoutes.ROOT}version`, }; @@ -19,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/state/app.state.ts b/app/components/ui/src/app/state/app.state.ts index e1fe7e1..3c77631 100644 --- a/app/components/ui/src/app/state/app.state.ts +++ b/app/components/ui/src/app/state/app.state.ts @@ -1,15 +1,19 @@ import { createDefaultLoadable, Loadable } from "./loadable.state"; -import { IACEVersionData } from "../models/app.models"; +import { IAppVersionData } from "../models/app.models"; export interface AppState extends Loadable { - versionData: IACEVersionData; + version_data: IAppVersionData; } export function createInitialAppState(): AppState { return { ...createDefaultLoadable(), - versionData: { - version: "0" + version_data: { + version: "0", + authors: [], + license: "", + last_update: "", + rebuild_date: "" } } } diff --git a/app/components/ui/src/app/store/app/app.actions.ts b/app/components/ui/src/app/store/app/app.actions.ts index 35b306d..a23f2b8 100644 --- a/app/components/ui/src/app/store/app/app.actions.ts +++ b/app/components/ui/src/app/store/app/app.actions.ts @@ -1,11 +1,11 @@ import { createActionGroup, props, emptyProps } from "@ngrx/store"; -import { IACEVersionData } from "../../models/app.models"; +import { IAppVersionData } from "../../models/app.models"; export const appActions = createActionGroup({ source: "app", events: { - getACEVersionData: emptyProps(), - getACEVersionDataSuccess: props<{ versionData: IACEVersionData }>(), - getACEVersionDataFailure: props<{ error: Error }>() + getAppVersionData: emptyProps(), + getAppVersionDataSuccess: props<{ version_data: IAppVersionData }>(), + getAppVersionDataFailure: props<{ error: Error }>() }, }); diff --git a/app/components/ui/src/app/store/app/app.effects.ts b/app/components/ui/src/app/store/app/app.effects.ts index aea97fd..f00ed80 100644 --- a/app/components/ui/src/app/store/app/app.effects.ts +++ b/app/components/ui/src/app/store/app/app.effects.ts @@ -2,7 +2,7 @@ import { inject, Injectable } from "@angular/core"; import { createEffect, ofType, Actions } from "@ngrx/effects"; import { map, catchError, of, switchMap } from "rxjs"; import { appActions } from "./app.actions"; -import { IACEVersionData } from "../../models/app.models"; +import { IAppVersionData } from "../../models/app.models"; import { AppService } from "../../services/app.service"; @@ -11,10 +11,10 @@ export class AppEffects { private actions$ = inject(Actions); 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 index 665195d..9fba492 100644 --- a/app/components/ui/src/app/store/app/app.reducers.ts +++ b/app/components/ui/src/app/store/app/app.reducers.ts @@ -5,7 +5,7 @@ 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)})) + 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 index f576a7b..86260c1 100644 --- a/app/components/ui/src/app/store/app/app.selectors.ts +++ b/app/components/ui/src/app/store/app/app.selectors.ts @@ -2,4 +2,4 @@ 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); +export const selectAppVersionDataState = createSelector(selectAppState, (state: AppState) => state.version_data); diff --git a/app/models/api_schemas/controller.py b/app/models/api_schemas/controller.py index 5f33551..5edbfe9 100644 --- a/app/models/api_schemas/controller.py +++ b/app/models/api_schemas/controller.py @@ -13,7 +13,7 @@ # RESPONSES class GetVersionDetailsResponse(BaseModel): version: str - author: str + authors: list[str] license: str last_update: str rebuild_date: str diff --git a/app/models/config/controller.py b/app/models/config/controller.py index c29e53a..d6fe407 100644 --- a/app/models/config/controller.py +++ b/app/models/config/controller.py @@ -12,5 +12,5 @@ class ControllerSettingsSchema(BaseModel): ace_name: str = Defaults.ACE_NAME ui_settings: UISettings = DEFAULT_UI_SETTINGS - layer_settings: list[LayerSettings] = DEFAULT_LAYER_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 58d7b7f..b05aac1 100644 --- a/app/models/config/defaults.py +++ b/app/models/config/defaults.py @@ -16,35 +16,6 @@ ) -# 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 - ) -] - - # MODEL PROVIDERS ## Model Types DEFAULT_LLM_MODEL_TYPE_SETTINGS: list[model_types.LLMModelTypeSetting] = [ @@ -99,3 +70,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/version b/app/version index 057a1ab..0e2a417 100644 --- a/app/version +++ b/app/version @@ -1,7 +1,9 @@ { "version": "0.0.0", - "author": "Jayfalls", + "authors": [ + "Jayfalls" + ], "license": "MIT", - "last_update": "2025-02-16", + "last_update": "", "rebuild_date": "2025-02-02" } From 66ee0e7798e4c215c1ec4f3b608ab969fae6ef90 Mon Sep 17 00:00:00 2001 From: Jason Esterhuizen Date: Sun, 16 Feb 2025 14:53:48 +0200 Subject: [PATCH 08/15] test From ffeb18b8abaafc9ef6c2a73f120d312fb39750bb Mon Sep 17 00:00:00 2001 From: Jason Esterhuizen Date: Sun, 16 Feb 2025 14:54:54 +0200 Subject: [PATCH 09/15] test --- app/version | 9 --------- 1 file changed, 9 deletions(-) diff --git a/app/version b/app/version index 0e2a417..e69de29 100644 --- a/app/version +++ b/app/version @@ -1,9 +0,0 @@ -{ - "version": "0.0.0", - "authors": [ - "Jayfalls" - ], - "license": "MIT", - "last_update": "", - "rebuild_date": "2025-02-02" -} From 12095313a882b598277c2dff5052c910de0406d8 Mon Sep 17 00:00:00 2001 From: Jason Esterhuizen Date: Sun, 16 Feb 2025 14:55:50 +0200 Subject: [PATCH 10/15] test --- app/version | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/app/version b/app/version index e69de29..0e2a417 100644 --- a/app/version +++ b/app/version @@ -0,0 +1,9 @@ +{ + "version": "0.0.0", + "authors": [ + "Jayfalls" + ], + "license": "MIT", + "last_update": "", + "rebuild_date": "2025-02-02" +} From 3d621c20fc57b3732393354bd5999e5448791f95 Mon Sep 17 00:00:00 2001 From: Jason Esterhuizen Date: Sun, 16 Feb 2025 14:58:36 +0200 Subject: [PATCH 11/15] test --- app/version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/version b/app/version index 0e2a417..049ac63 100644 --- a/app/version +++ b/app/version @@ -4,6 +4,6 @@ "Jayfalls" ], "license": "MIT", - "last_update": "", + "last_update": "2025-02-16 14:58:36", "rebuild_date": "2025-02-02" } From 07157daa09b6dc8a5dab15f57d910ca52e64ef47 Mon Sep 17 00:00:00 2001 From: Jason Esterhuizen Date: Sun, 16 Feb 2025 15:00:50 +0200 Subject: [PATCH 12/15] [FEAT] Formalised git hook --- .github/hooks/pre-commit | 13 +++++++++++++ ace | 10 ++++++++++ app/version | 2 +- 3 files changed, 24 insertions(+), 1 deletion(-) create mode 100644 .github/hooks/pre-commit 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/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/version b/app/version index 049ac63..3135f8b 100644 --- a/app/version +++ b/app/version @@ -4,6 +4,6 @@ "Jayfalls" ], "license": "MIT", - "last_update": "2025-02-16 14:58:36", + "last_update": "2025-02-16 15:00:50", "rebuild_date": "2025-02-02" } From 49d9dbcbeecf3d67723a81f1c1a6ee04783ecad7 Mon Sep 17 00:00:00 2001 From: Jason Esterhuizen Date: Sun, 16 Feb 2025 16:48:26 +0200 Subject: [PATCH 13/15] [FEAT] Added dark mode --- app/components/ui/angular.json | 1 + app/components/ui/src/app/app.component.html | 8 +- app/components/ui/src/app/app.component.scss | 1 + app/components/ui/src/app/app.component.ts | 32 ++++- .../ui/src/app/models/settings.models.ts | 1 + .../pages/settings/settings.component.html | 5 +- .../app/pages/settings/settings.component.ts | 17 ++- .../ui/src/app/state/settings.state.ts | 3 +- .../app/store/settings/settings.selectors.ts | 1 - app/components/ui/src/app/theme.ts | 19 +++ app/components/ui/src/styles.scss | 6 + app/components/ui/src/theme.scss | 128 ++++++++++++++++++ app/models/config/defaults.py | 1 + app/models/config/ui.py | 1 + app/version | 2 +- 15 files changed, 208 insertions(+), 18 deletions(-) create mode 100644 app/components/ui/src/app/theme.ts create mode 100644 app/components/ui/src/theme.scss 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 09adc7e..0f6956e 100644 --- a/app/components/ui/src/app/app.component.html +++ b/app/components/ui/src/app/app.component.html @@ -22,9 +22,13 @@ @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 b620b7e..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"; @@ -9,12 +9,13 @@ import { MatSidenavModule } from "@angular/material/sidenav"; import { RouterModule, RouterOutlet } from "@angular/router"; import { Store } from "@ngrx/store"; //// Local +import { ThemeService } from "./theme"; import { IAppVersionData } from "./models/app.models"; -import { IUISettings } from "./models/settings.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 { selectUISettingsState } from "./store/settings/settings.selectors"; +import { selectSettingsState } from "./store/settings/settings.selectors"; // TYPES @@ -32,8 +33,8 @@ export type SidebarItem = { MatButtonModule, MatIconModule, MatListModule, - MatTooltipModule, MatSidenavModule, + MatTooltipModule, RouterModule, RouterOutlet ], @@ -43,6 +44,7 @@ export type SidebarItem = { export class AppComponent implements OnInit { // Variables title = "ACE"; + themeService = inject(ThemeService); sidebarItems = signal([ { name: "Home", @@ -71,6 +73,7 @@ export class AppComponent implements OnInit { } ]) + settings?: ISettings; uiSettings?: IUISettings; appVersionData?: IAppVersionData; @@ -81,6 +84,25 @@ export class AppComponent implements OnInit { this.store.dispatch(appActions.getAppVersionData()); this.store.select(selectAppVersionDataState).subscribe( version_data => this.appVersionData = version_data ); this.store.dispatch(settingsActions.getSettings()); - this.store.select(selectUISettingsState).subscribe( ui_settings => this.uiSettings = ui_settings ); + 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/models/settings.models.ts b/app/components/ui/src/app/models/settings.models.ts index 19aff30..e291610 100644 --- a/app/components/ui/src/app/models/settings.models.ts +++ b/app/components/ui/src/app/models/settings.models.ts @@ -1,6 +1,7 @@ // SECTIONS //// UI export interface IUISettings { + dark_mode: boolean; show_footer: boolean; } 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 f58494f..7a72cae 100644 --- a/app/components/ui/src/app/pages/settings/settings.component.html +++ b/app/components/ui/src/app/pages/settings/settings.component.html @@ -20,8 +20,9 @@

General:

UI:

-
- Show Footer +
+ Dark Mode + Show Footer
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 591f8c5..7b3e3cf 100644 --- a/app/components/ui/src/app/pages/settings/settings.component.ts +++ b/app/components/ui/src/app/pages/settings/settings.component.ts @@ -6,12 +6,12 @@ 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 { MatCheckboxModule } from "@angular/material/checkbox"; import { MatDividerModule } from "@angular/material/divider"; import { MatIconModule } from "@angular/material/icon"; -import { MatFormFieldModule } from "@angular/material/form-field"; 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"; @@ -27,12 +27,12 @@ import { selectSettingsState } from "../../store/settings/settings.selectors"; imports: [ ReactiveFormsModule, MatButtonModule, - MatCheckboxModule, MatDividerModule, MatIconModule, - MatFormFieldModule, MatInputModule, + MatFormFieldModule, MatSelectModule, + MatSlideToggleModule, TitleCasePipe ], templateUrl: "./settings.component.html", @@ -79,9 +79,13 @@ export class SettingsComponent implements OnInit { } private initialiseForm(): void { + if (this.settingsForm) { + this.settingsForm.reset(); + } this.settingsForm = this.formBuilder.group({ ace_name: ["", [Validators.required, Validators.maxLength(32)]], ui_settings: this.formBuilder.group({ + dark_mode: [true], show_footer: [true] }), layer_settings: this.formBuilder.array([]), @@ -108,6 +112,7 @@ export class SettingsComponent implements OnInit { private patchFormValues(): void { if (!this.settings) return; + this.initialiseForm(); // General this.settingsForm.patchValue({ @@ -117,12 +122,13 @@ export class SettingsComponent implements OnInit { // UI this.settingsForm.patchValue({ ui_settings: { + dark_mode: this.settings.ui_settings.dark_mode, show_footer: this.settings.ui_settings.show_footer } }); // Layers - const layerArray = this.layerSettingsControl; + let layerArray = this.layerSettingsControl; layerArray.clear(); this.settings.layer_settings.forEach(layer => { @@ -134,7 +140,6 @@ export class SettingsComponent implements OnInit { ); }); - // Model Provider const modelProviderSettings: IModelProviderSetting = this.settings.model_provider_settings; this.settingsForm.patchValue({ diff --git a/app/components/ui/src/app/state/settings.state.ts b/app/components/ui/src/app/state/settings.state.ts index 461c4c8..e5e872f 100644 --- a/app/components/ui/src/app/state/settings.state.ts +++ b/app/components/ui/src/app/state/settings.state.ts @@ -3,7 +3,7 @@ import { Values } from '../constants' import { ISettings } from "../models/settings.models"; export interface SettingsState extends Loadable { - settings: ISettings; + settings: ISettings; } export function createInitialSettingsState(): SettingsState { @@ -12,6 +12,7 @@ export function createInitialSettingsState(): SettingsState { settings: { ace_name: Values.NOT_LOADED, ui_settings: { + dark_mode: true, show_footer: true }, layer_settings: [], diff --git a/app/components/ui/src/app/store/settings/settings.selectors.ts b/app/components/ui/src/app/store/settings/settings.selectors.ts index d963668..b7eb338 100644 --- a/app/components/ui/src/app/store/settings/settings.selectors.ts +++ b/app/components/ui/src/app/store/settings/settings.selectors.ts @@ -2,4 +2,3 @@ import { createFeatureSelector, createSelector } from "@ngrx/store"; import { SettingsState } from "../../state/settings.state"; export const selectSettingsState = createFeatureSelector("settings"); -export const selectUISettingsState = createSelector(selectSettingsState, (state: SettingsState) => state.settings.ui_settings); 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/styles.scss b/app/components/ui/src/styles.scss index b059614..c253bc4 100644 --- a/app/components/ui/src/styles.scss +++ b/app/components/ui/src/styles.scss @@ -15,8 +15,14 @@ body { margin: 0; font-family: Roboto, "Helvetica Neue", sans-serif; } } .subsection { + display: flex; padding-left: 1rem; padding-right: 1rem; + flex-direction: column; + + &.checkboxes { + row-gap: 0.5rem; + } .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/models/config/defaults.py b/app/models/config/defaults.py index b05aac1..5b7382f 100644 --- a/app/models/config/defaults.py +++ b/app/models/config/defaults.py @@ -12,6 +12,7 @@ # UI DEFAULT_UI_SETTINGS: UISettings = UISettings( + dark_mode=True, show_footer=True ) diff --git a/app/models/config/ui.py b/app/models/config/ui.py index 1682131..50f19b0 100644 --- a/app/models/config/ui.py +++ b/app/models/config/ui.py @@ -4,4 +4,5 @@ class UISettings(BaseModel): + dark_mode: bool show_footer: bool diff --git a/app/version b/app/version index 3135f8b..35ff7f3 100644 --- a/app/version +++ b/app/version @@ -4,6 +4,6 @@ "Jayfalls" ], "license": "MIT", - "last_update": "2025-02-16 15:00:50", + "last_update": "2025-02-16 16:48:26", "rebuild_date": "2025-02-02" } From 66b3c5c2833ccaf67dbd011e54dab44fcb3c1f83 Mon Sep 17 00:00:00 2001 From: Jason Esterhuizen Date: Sun, 23 Mar 2025 15:40:21 +0200 Subject: [PATCH 14/15] [UPDATE] Finalised settings ui --- app/components/ui/src/app/app.component.html | 2 +- .../ui/src/app/models/settings.models.ts | 9 +- .../pages/settings/settings.component.html | 32 +++-- .../app/pages/settings/settings.component.ts | 124 ++++++++---------- .../ui/src/app/state/settings.state.ts | 29 +--- app/components/ui/src/styles.scss | 4 +- app/models/config/defaults.py | 2 +- .../config/model_providers/model_providers.py | 2 +- app/version | 2 +- 9 files changed, 88 insertions(+), 118 deletions(-) diff --git a/app/components/ui/src/app/app.component.html b/app/components/ui/src/app/app.component.html index 0f6956e..5089ac9 100644 --- a/app/components/ui/src/app/app.component.html +++ b/app/components/ui/src/app/app.component.html @@ -27,7 +27,7 @@

Last Update: {{ appVersionData?.last_update }}

} diff --git a/app/components/ui/src/app/models/settings.models.ts b/app/components/ui/src/app/models/settings.models.ts index e291610..d967bc7 100644 --- a/app/components/ui/src/app/models/settings.models.ts +++ b/app/components/ui/src/app/models/settings.models.ts @@ -14,6 +14,7 @@ export interface ILayerSetting { //// Model Provider ////// Unique Providers export interface IIndividualProviderSettings { + name: string; enabled: boolean; api_key: string; } @@ -29,13 +30,7 @@ export interface ILLMModelTypeSettings { ////// Full export interface IModelProviderSetting { - claude_settings: IIndividualProviderSettings; - deepseek_settings: IIndividualProviderSettings; - google_vertex_ai_settings: IIndividualProviderSettings; - grok_settings: IIndividualProviderSettings; - groq_settings: IIndividualProviderSettings; - ollama_settings: IIndividualProviderSettings; - openai_settings: IIndividualProviderSettings; + individual_provider_settings: IIndividualProviderSettings[]; three_d_model_type_settings: string[]; audio_model_type_settings: string[]; 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 7a72cae..dd933d4 100644 --- a/app/components/ui/src/app/pages/settings/settings.component.html +++ b/app/components/ui/src/app/pages/settings/settings.component.html @@ -4,7 +4,7 @@

Settings

@if (settingsForm) {

General:

-
+
Ace Name @@ -20,7 +20,7 @@

General:

UI:

-
+
Dark Mode Show Footer
@@ -28,22 +28,36 @@

UI:

Model Provider:

-
- +
+

Individual Model Providers:

+
+ @for (provider of individualModelProviderSettingsControl.controls; track provider.value?.name; let i = $index) { + @if (provider.get("name")?.value !== "ollama") { +

{{ formatSnakeCase(provider.get('name')?.value) }}

+
+ Enabled + + API Key + + +
+ } + } +

ACE Layers:

-
- @for (layerSetting of layerSettingsControl.controls; track layer_setting; let layer_setting = $index) { -
-

{{ formatLayerNames(layerSetting.value?.layer_name || "") }} Layer:

+
+ @for (layerSetting of layerSettingsControl.controls; track layerSetting; let i = $index) { +
+

{{ formatSnakeCase(layerSetting.value?.layer_name || "") }} Layer:

Model Type @for (type of llmModelTypes; track type) { - {{ type | titlecase }} + {{ formatSnakeCase(type) }} } 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 7b3e3cf..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,6 +1,5 @@ // DEPENDENCIES //// Angular -import { TitleCasePipe } from "@angular/common"; import { Component, OnInit } from "@angular/core"; import { Store } from "@ngrx/store"; import { filter, take } from "rxjs"; @@ -32,24 +31,23 @@ import { selectSettingsState } from "../../store/settings/settings.selectors"; MatInputModule, MatFormFieldModule, MatSelectModule, - MatSlideToggleModule, - TitleCasePipe + MatSlideToggleModule ], templateUrl: "./settings.component.html", styleUrl: "./settings.component.scss" }) export class SettingsComponent implements OnInit { - private defaultIndividualModelProviderSettings = { - enabled: false, - api_key: "" - } - appVersionData!: IAppVersionData; llmModels: ILLMModelProvider[] = []; llmModelTypes: string[] = []; selectedLLMModelType: string = ""; settings!: ISettings; + settingsForm!: FormGroup; + generalForm!: FormGroup; + uiSettingsForm!: FormGroup; + modelProviderForm!: FormGroup; + layerSettingsForm!: FormArray; changesDetected: boolean = false; @@ -62,6 +60,7 @@ export class SettingsComponent implements OnInit { 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), @@ -79,60 +78,68 @@ export class SettingsComponent implements OnInit { } private initialiseForm(): void { - if (this.settingsForm) { - this.settingsForm.reset(); - } + 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({ - ace_name: ["", [Validators.required, Validators.maxLength(32)]], - ui_settings: this.formBuilder.group({ - dark_mode: [true], - show_footer: [true] - }), - layer_settings: this.formBuilder.array([]), - model_provider_settings: this.formBuilder.group({ - claude_settings: this.formBuilder.group(this.defaultIndividualModelProviderSettings), - deepseek_settings: this.formBuilder.group(this.defaultIndividualModelProviderSettings), - google_vertex_ai_settings: this.formBuilder.group(this.defaultIndividualModelProviderSettings), - grok_settings: this.formBuilder.group(this.defaultIndividualModelProviderSettings), - groq_settings: this.formBuilder.group(this.defaultIndividualModelProviderSettings), - ollama_settings: this.formBuilder.group(this.defaultIndividualModelProviderSettings), - openai_settings: this.formBuilder.group(this.defaultIndividualModelProviderSettings), - 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.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(); - // General - this.settingsForm.patchValue({ + this.generalForm.patchValue({ ace_name: this.settings.ace_name, }); - // UI - this.settingsForm.patchValue({ - ui_settings: { - dark_mode: this.settings.ui_settings.dark_mode, - show_footer: this.settings.ui_settings.show_footer - } + this.uiSettingsForm.patchValue({ + dark_mode: this.settings.ui_settings.dark_mode, + show_footer: this.settings.ui_settings.show_footer }); - // Layers - let layerArray = this.layerSettingsControl; - layerArray.clear(); + 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 => { - layerArray.push( + this.layerSettingsForm.push( this.formBuilder.group({ layer_name: [layer?.layer_name || "", Validators.required], model_type: [layer?.model_type || "", Validators.required] @@ -140,30 +147,11 @@ export class SettingsComponent implements OnInit { ); }); - // Model Provider - const modelProviderSettings: IModelProviderSetting = this.settings.model_provider_settings; - this.settingsForm.patchValue({ - model_provider_settings: { - claude_settings: modelProviderSettings.claude_settings || this.defaultIndividualModelProviderSettings, - deepseek_settings: modelProviderSettings.deepseek_settings || this.defaultIndividualModelProviderSettings, - google_vertex_ai_settings: modelProviderSettings.google_vertex_ai_settings || this.defaultIndividualModelProviderSettings, - grok_settings: modelProviderSettings.grok_settings || this.defaultIndividualModelProviderSettings, - groq_settings: modelProviderSettings.groq_settings || this.defaultIndividualModelProviderSettings, - ollama_settings: modelProviderSettings.ollama_settings || this.defaultIndividualModelProviderSettings, - openai_settings: modelProviderSettings.openai_settings || this.defaultIndividualModelProviderSettings, - three_d_model_type_settings: modelProviderSettings.three_d_model_type_settings || [], - audio_model_type_settings: modelProviderSettings.audio_model_type_settings || [], - image_model_type_settings: modelProviderSettings.image_model_type_settings || [], - llm_model_type_settings: modelProviderSettings.llm_model_type_settings || [], - rag_model_type_settings: modelProviderSettings.rag_model_type_settings || [] - } - }); - this.settingsForm.markAsPristine(); this.changesDetected = false; } - formatLayerNames(layerName?: string): string { + formatSnakeCase(layerName?: string): string { return (layerName || "") .split("_") .map(word => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase()) @@ -190,14 +178,14 @@ export class SettingsComponent implements OnInit { } get aceNameControl() { - return this.settingsForm.get("ace_name"); + return this.generalForm.get("ace_name"); } - get uiSettingsControl() { - return this.settingsForm.get("ui_settings"); + get individualModelProviderSettingsControl(): FormArray { + return this.modelProviderForm.get("individual_provider_settings") as FormArray; } get layerSettingsControl(): FormArray { - return this.settingsForm.get("layer_settings") as FormArray; + return this.layerSettingsForm; } } diff --git a/app/components/ui/src/app/state/settings.state.ts b/app/components/ui/src/app/state/settings.state.ts index e5e872f..74deea8 100644 --- a/app/components/ui/src/app/state/settings.state.ts +++ b/app/components/ui/src/app/state/settings.state.ts @@ -17,34 +17,7 @@ export function createInitialSettingsState(): SettingsState { }, layer_settings: [], model_provider_settings: { - claude_settings: { - api_key: Values.NOT_LOADED, - enabled: false - }, - deepseek_settings: { - api_key: Values.NOT_LOADED, - enabled: false - }, - google_vertex_ai_settings: { - api_key: Values.NOT_LOADED, - enabled: false - }, - grok_settings: { - api_key: Values.NOT_LOADED, - enabled: false - }, - groq_settings: { - api_key: Values.NOT_LOADED, - enabled: false - }, - ollama_settings: { - api_key: Values.NOT_LOADED, - enabled: false - }, - openai_settings: { - api_key: Values.NOT_LOADED, - enabled: false - }, + individual_provider_settings: [], three_d_model_type_settings: [], audio_model_type_settings: [], image_model_type_settings: [], diff --git a/app/components/ui/src/styles.scss b/app/components/ui/src/styles.scss index c253bc4..ee87f52 100644 --- a/app/components/ui/src/styles.scss +++ b/app/components/ui/src/styles.scss @@ -20,8 +20,8 @@ body { margin: 0; font-family: Roboto, "Helvetica Neue", sans-serif; } padding-right: 1rem; flex-direction: column; - &.checkboxes { - row-gap: 0.5rem; + &.multi-input { + row-gap: 0.75rem; } .subsection-divider { diff --git a/app/models/config/defaults.py b/app/models/config/defaults.py index 5b7382f..2d2448d 100644 --- a/app/models/config/defaults.py +++ b/app/models/config/defaults.py @@ -13,7 +13,7 @@ # UI DEFAULT_UI_SETTINGS: UISettings = UISettings( dark_mode=True, - show_footer=True + show_footer=False ) diff --git a/app/models/config/model_providers/model_providers.py b/app/models/config/model_providers/model_providers.py index 4fc6e04..e79f9b5 100644 --- a/app/models/config/model_providers/model_providers.py +++ b/app/models/config/model_providers/model_providers.py @@ -36,7 +36,7 @@ class ModelProviderSettings(BaseModel): ), IndividualProviderSettings( name=ModelProviders.OLLAMA, - enabled=False, + enabled=True, api_key="" ), IndividualProviderSettings( diff --git a/app/version b/app/version index 35ff7f3..e32bbef 100644 --- a/app/version +++ b/app/version @@ -4,6 +4,6 @@ "Jayfalls" ], "license": "MIT", - "last_update": "2025-02-16 16:48:26", + "last_update": "2025-03-23 15:40:21", "rebuild_date": "2025-02-02" } From cd633cb6dba316219fb6acaa98e666ffaeb81a24 Mon Sep 17 00:00:00 2001 From: Jason Esterhuizen Date: Sun, 23 Mar 2025 15:44:44 +0200 Subject: [PATCH 15/15] [BUG] Fixed tests --- app/version | 2 +- tests/unit/components/controller/api/test_service.py | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/app/version b/app/version index e32bbef..ecf10e7 100644 --- a/app/version +++ b/app/version @@ -4,6 +4,6 @@ "Jayfalls" ], "license": "MIT", - "last_update": "2025-03-23 15:40:21", + "last_update": "2025-03-23 15:44:44", "rebuild_date": "2025-02-02" } 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()