Skip to content

Commit

Permalink
Add Scheduler Demo
Browse files Browse the repository at this point in the history
  • Loading branch information
yorevs committed Sep 11, 2024
1 parent 2c9d346 commit 8e51eb9
Show file tree
Hide file tree
Showing 12 changed files with 289 additions and 101 deletions.
2 changes: 1 addition & 1 deletion dependencies.hspd
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
*/

/* HSPyLib projects */
package: hspylib, version: 1.12.46, mode: ge
package: hspylib, version: 1.12.47, mode: ge
package: hspylib-clitt, version: 0.9.132, mode: ge
package: hspylib-setman, version: 0.10.35, mode: ge

Expand Down
16 changes: 8 additions & 8 deletions src/demo/components/camera_demo.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
from askai.core.component.camera import camera
from askai.core.component.image_store import ImageMetadata, store
from askai.core.features.router.tools.terminal import open_command
from askai.core.support.utilities import display_text
import os
from textwrap import dedent

from clitt.core.term.cursor import cursor
from clitt.core.tui.line_input.line_input import line_input
from hspylib.core.tools.text_tools import strip_escapes
from textwrap import dedent
from utils import init_context

import os
from askai.core.component.camera import camera
from askai.core.component.image_store import ImageMetadata, store
from askai.core.features.router.tools.terminal import open_command
from utils import init_context

MENU = dedent(
f"""Camera Demo options
Expand All @@ -27,7 +27,7 @@


if __name__ == "__main__":
init_context(log_name="camera-demo")
init_context("camera-demo")
photo: ImageMetadata
while opt := line_input(MENU, placeholder="Select an option"):
cursor.write()
Expand Down
68 changes: 68 additions & 0 deletions src/demo/components/scheduler_demo.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-

"""
@project: HsPyLib-AskAI
@package: demo.components
@file: scheduler-demo.py
@created: Tue, 14 May 2024
@author: <B>H</B>ugo <B>S</B>aporetti <B>J</B>unior
@site: https://github.com/yorevs/askai
@license: MIT - Please refer to <https://opensource.org/licenses/MIT>
Copyright (c) 2024, HomeSetup
"""
import logging as log

from askai.core.component.scheduler import scheduler
from hspylib.core.zoned_datetime import now, SIMPLE_DATETIME_FORMAT

from utils import init_context


def echo(msg: str):
log.info(f"{msg} {now('[%H:%M:%S]')}")


@scheduler.every(2000, 2000)
def every_2_seconds():
echo("EVERY-2")


@scheduler.every(4000, 2000)
def every_4_seconds():
echo("EVERY-4")


@scheduler.every(8000, 2000)
def every_8_seconds():
echo("EVERY-8")


# @scheduler.at(16, 26, 0)
# def at_1():
# echo("AT-1")
#
#
# @scheduler.at(16, 26, 10)
# def at_2():
# echo("AT-2")
#
#
# @scheduler.at(16, 26, 20)
# def at_3():
# echo("AT-3")


@scheduler.after(second=20)
def after_20_seconds():
echo("AFTER-20s")


if __name__ == "__main__":
init_context("scheduler-demo", rich_logging=False, console_enable=True, log_level=log.INFO)
log.info("-=" * 40)
log.info(f"AskAI Scheduler Demo - {scheduler.now.strftime(SIMPLE_DATETIME_FORMAT)}")
log.info("-=" * 40)
scheduler.start()
scheduler.join()
2 changes: 1 addition & 1 deletion src/demo/others/screenshot_demo.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,5 @@
from utils import init_context

if __name__ == "__main__":
init_context(log_name="camera-demo")
init_context("camera-demo")
sysout(take_screenshot("gabiroba.jpeg"))
11 changes: 9 additions & 2 deletions src/demo/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,15 +22,22 @@

def init_context(
log_name: str | None = None,
context_size: int = 1000,
log_level: int = log.NOTSET,
rich_logging: bool = False,
console_enable: bool = False,
context_size: int = 1000,
engine_name: Literal["openai"] = "openai",
model_name: Literal["gpt-3.5-turbo", "gpt-4", "gpt-4o"] = "gpt-3.5-turbo",
) -> None:
"""Initialize AskAI context and startup components."""
if log_name:
log_dir: str = os.environ.get("HHS_LOG_DIR", os.getcwd())
log_init(f"{os.path.join(log_dir, ensure_endswith(log_name, '.log'))}", level=log_level)
log_init(
filename=f"{os.path.join(log_dir, ensure_endswith(log_name, '.log'))}",
level=log_level,
rich_logging=rich_logging,
console_enable=console_enable,
)
KeyboardInput.preload_history(cache.load_input_history(commands()))
shared.create_engine(engine_name=engine_name, model_name=model_name)
shared.create_context(context_size)
Expand Down
6 changes: 3 additions & 3 deletions src/main/askai/core/askai_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ def __init__(
query_string: QueryString,
):

configs.is_interactive = configs.is_interactive if not query_prompt else False
configs.is_interactive = interactive if not query_prompt else False
super().__init__(interactive, speak, debug, cacheable, tempo, engine_name, model_name)
os.environ["ASKAI_APP"] = (self.RunModes.ASKAI_CLI if interactive else self.RunModes.ASKAI_CMD).value
self._ready: bool = False
Expand Down Expand Up @@ -136,14 +136,14 @@ def _cb_mic_listening_event(self, ev: Event) -> None:
:param ev: The event object representing the microphone listening event.
"""
if ev.args.listening:
self._reply(msg.listening())
self._reply(AIReply.info(msg.listening()))

def _cb_device_changed_event(self, ev: Event) -> None:
"""Callback to handle audio input device change events.
:param ev: The event object representing the device change.
"""
cursor.erase_line()
self._reply(msg.device_switch(str(ev.args.device)))
self._reply(AIReply.info(msg.device_switch(str(ev.args.device))))

def _splash(self, interval: int = 250) -> None:
"""Display the AskAI splash screen until the system is fully started and ready. This method shows the splash
Expand Down
4 changes: 4 additions & 0 deletions src/main/askai/core/askai_configs.py
Original file line number Diff line number Diff line change
Expand Up @@ -205,5 +205,9 @@ def remove_device(self, device_name: str) -> None:
self._recorder_devices.remove(device_name)
settings.put("askai.recorder.devices", ", ".join(self._recorder_devices))

def clear_devices(self) -> None:
"""Remove all devices from the configuration list."""
self._recorder_devices.clear()


assert (configs := AskAiConfigs().INSTANCE) is not None
19 changes: 15 additions & 4 deletions src/main/askai/core/commander/commands/settings_cmd.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,14 @@
"""

from abc import ABC
from typing import Any, Optional

from askai.core.askai_configs import configs
from askai.core.askai_settings import settings
from askai.core.component.recorder import recorder
from askai.core.support.text_formatter import text_formatter
from askai.core.support.utilities import display_text
from hspylib.core.tools.commons import sysout
from setman.settings.settings_entry import SettingsEntry
from typing import Any, Optional


class SettingsCmd(ABC):
Expand Down Expand Up @@ -65,6 +66,16 @@ def get(key: str) -> Optional[SettingsEntry]:
@staticmethod
def reset() -> None:
"""Reset all settings to their default values."""
# Command arguments settings must be kept as it was.
is_interactive: bool = settings.get_bool("askai.interactive.enabled")
is_speak: bool = settings.get_bool("askai.speak.enabled")
is_debug: bool = settings.get_bool("askai.debug.enabled")
is_cache: bool = settings.get_bool("askai.cache.enabled")
settings.defaults()
# Include the current audio input.
settings.put("askai.recorder.devices", recorder.input_device[1] or "")
configs.clear_devices()
# Put back the command argument settings.
settings.put("askai.interactive.enabled", is_interactive)
settings.put("askai.speak.enabled", is_speak)
settings.put("askai.debug.enabled", is_debug)
settings.put("askai.cache.enabled", is_cache)
text_formatter.cmd_print(f"%GREEN%Factory settings reset!%NC%")
45 changes: 22 additions & 23 deletions src/main/askai/core/component/recorder.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,17 @@
Copyright (c) 2024, HomeSetup
"""
import logging as log
import operator
import sys
from pathlib import Path
from typing import Callable, Optional, TypeAlias

from askai.core.askai_configs import configs
from askai.core.askai_events import events
from askai.core.askai_messages import msg
from askai.core.component.cache_service import REC_DIR
from askai.core.component.scheduler import Scheduler
from askai.core.component.scheduler import scheduler
from askai.core.model.ai_reply import AIReply
from askai.core.support.utilities import display_text, seconds
from askai.exception.exceptions import InvalidInputDevice, InvalidRecognitionApiError
Expand All @@ -27,13 +33,7 @@
from hspylib.core.preconditions import check_argument, check_state
from hspylib.core.zoned_datetime import now_ms
from hspylib.modules.application.exit_status import ExitStatus
from pathlib import Path
from speech_recognition import AudioData, Microphone, Recognizer, RequestError, UnknownValueError, WaitTimeoutError
from typing import Callable, Optional, TypeAlias

import logging as log
import operator
import sys

InputDevice: TypeAlias = tuple[int, str]

Expand Down Expand Up @@ -63,21 +63,7 @@ def get_device_list(cls) -> list[InputDevice]:
devices.append((index, name))
return devices

def __init__(self):
self._rec: Recognizer = Recognizer()
self._devices: list[InputDevice] = []
self._device_index: int | None = None
self._input_device: InputDevice | None = None
self._old_device = None

def setup(self) -> None:
"""Setup the microphone recorder."""
self._devices = self.get_device_list()
log.debug("Available audio devices:\n%s", "\n".join([f"{d[0]} - {d[1]}" for d in self._devices]))
self._select_device()

@staticmethod
@Scheduler.every(3000, 5000)
def __device_watcher() -> None:
"""Monitor audio input devices for being plugged in or unplugged. This method periodically checks the status of
audio input devices to detect any changes.
Expand All @@ -96,6 +82,19 @@ def __device_watcher() -> None:
break
recorder.devices = new_list

def __init__(self):
self._rec: Recognizer = Recognizer()
self._devices: list[InputDevice] = []
self._device_index: int | None = None
self._input_device: InputDevice | None = None

def setup(self) -> None:
"""Setup the microphone recorder."""
self._devices = self.get_device_list()
log.debug("Available audio devices:\n%s", "\n".join([f"{d[0]} - {d[1]}" for d in self._devices]))
self._select_device()
scheduler.set_interval(3000, self.__device_watcher, 5000)

@property
def devices(self) -> list[InputDevice]:
return sorted(self._devices if self._devices else [], key=lambda x: x[0])
Expand Down Expand Up @@ -238,10 +237,10 @@ def _select_device(self) -> None:
"""Select a device for recording."""
available: list[str] = list(filter(lambda d: d, map(str.strip, configs.recorder_devices)))
device: InputDevice | None = None
devices: list[InputDevice] = list(reversed(self.devices))
devices: list[InputDevice] = self.devices
while devices and not device:
if available:
for dev in devices:
for dev in list(reversed(devices)): # Reverse to get any headphone or external device
if dev[1] in available and self.set_device(dev):
device = dev
break
Expand Down
Loading

0 comments on commit 8e51eb9

Please sign in to comment.